假設(shè)有一個(gè) User 表和一個(gè) Match 表。那么 Match 表應(yīng)該如何安排結(jié)構(gòu)呢?假設(shè) db 在記錄 John 和 Jane 之間的交互。會(huì)有一個(gè)基礎(chǔ)(base)用戶列和一個(gè)目標(biāo)(target)用戶列嗎?John 右滑了 Jane(一行)然后 Jane 右滑了 John(一行)所以總共是兩行?或者它是不是應(yīng)該設(shè)計(jì)為一行呢?一行設(shè)計(jì)意味著,沒(méi)有基礎(chǔ)用戶和目標(biāo)用戶,而是會(huì)有 user1 和 user2。jane 和 john 的位置最終會(huì)隨機(jī)決定。
兩行設(shè)計(jì):
Base User | Target User | Liked
row1 John|Jane|YES|YES
row2 Jane|John|YES|YES
一行設(shè)計(jì):
User1 | User2 | User1_Liked_User2 | User2_Liked_User1 | Match
row1 John|Jane|YES|YES|YES
我知道如果是一行設(shè)計(jì),缺點(diǎn)就是,要獲取 john 的配對(duì)數(shù)量需要看 user1 user2 兩列,但如果是兩行設(shè)計(jì)你只需要搜索 base user 行。
盡管一行處理方式可以省去一半的行數(shù),但表現(xiàn)會(huì)不會(huì)更好呢?或者說(shuō)現(xiàn)在 w/ dbs 的擴(kuò)展性已經(jīng)很強(qiáng)了,有沒(méi)有什么我們能忽略的呢?
為了論證,假設(shè)用戶數(shù)為 10 萬(wàn)。
如果 Tinder 在使用 RDBMS 的話……我想每個(gè)用戶被關(guān)聯(lián)到一個(gè)滑動(dòng)為 LIKES 和一個(gè)標(biāo)記為 IS_LIKED 都會(huì)有一條記錄。這種方式你可以拉出一個(gè)用戶記錄 (SELECT * FROM MATCH WHERE USER_ID = X) 然后回答所有關(guān)鍵問(wèn)題:
- 用戶喜歡誰(shuí)?(LIKES = TRUE)
- 誰(shuí)喜歡了用戶?(IS_LIKED = TRUE)
- 誰(shuí)是用戶的配對(duì)?(LIKES = TRUE and IS_LIKED = TRUE)
當(dāng)一個(gè)滑動(dòng)發(fā)生的時(shí)候,你合并了 2 個(gè)新的記錄到已經(jīng)存在的數(shù)據(jù)中。如果記錄是新的,只是一個(gè)直接插入。如果是一個(gè)更新,更新一個(gè)記錄的 LIKES 和另一個(gè)的 IS_LIKED 然后糾結(jié)了。
你可以給一個(gè)表只寫 “l(fā)ikes”,然后用一個(gè)介于兩個(gè)用戶之間的查詢來(lái)判斷 match,當(dāng) row 的數(shù)量為二的時(shí)候。這樣會(huì)導(dǎo)致更少的寫入,更小的索引,減少偏航(drift)的機(jī)會(huì)?
你是不是說(shuō)如果不是 RDBMS,設(shè)計(jì)會(huì)不同?
當(dāng)然。一個(gè)鍵值庫(kù)或 ORDBMS 可能需要一個(gè)不同的設(shè)計(jì)來(lái)有效的杠桿數(shù)據(jù)。
通常的,你會(huì)想要追蹤更多的信息,不只是 yes 或 no。例如,你應(yīng)該想要這個(gè)人滑動(dòng)的日期/時(shí)間。當(dāng)你有更多數(shù)據(jù)和滑動(dòng)相關(guān),用一行設(shè)計(jì)就越來(lái)越不實(shí)用了。
為了查詢的簡(jiǎn)單,你可以添加一個(gè) “swipe_from_other_was”,但查看一個(gè)特定滑動(dòng)的細(xì)節(jié)的時(shí)候,那它都會(huì)在一行里,對(duì)于另一個(gè)人的細(xì)節(jié),會(huì)在另一行里。
側(cè)面說(shuō)明一下,Match 會(huì)是那個(gè)表很爛的名字,Swipe 對(duì)于它的目標(biāo)來(lái)說(shuō)是更清晰的。
如果是這樣的話,要查詢 “John” 配對(duì)的數(shù)量不會(huì)變的太重了嗎?如果 John 右滑了 1000 人,然后我們要做一個(gè)次級(jí)查詢 FROM 一千人?;蛘吣阍诮ㄗh給 Match 一個(gè)單獨(dú)的表?一旦 john 喜歡了 jane,jane 喜歡了 john,Match 表插入一行?
我的第二段就是特別說(shuō)這個(gè)需求的。你可以添加一列,簡(jiǎn)單地說(shuō),如果 “target user” 也喜歡了 “base user”。基本上,有一個(gè)滑動(dòng)的時(shí)候,你為那個(gè)滑動(dòng)創(chuàng)建了一行,帶著所有相關(guān)數(shù)據(jù),然后在表里查詢另一個(gè)人的滑動(dòng),如果找到了,你在現(xiàn)有的和在新的行中更新額外的列會(huì)是正確的。這種方式你可以快速運(yùn)行分析或任何你需要做的事情。
側(cè)面說(shuō)明,一個(gè)相當(dāng)簡(jiǎn)單地 select 會(huì)給出這個(gè)信息。帶有正確的索引的話會(huì)相當(dāng)快,否則用 2 個(gè)查詢來(lái)做也可以。
SELECT other_swipe.base_user
FROM swipe
JOIN swipe other_swipe ON swipe.base_user = other_swipe.target_user
WHERE swipe.base_user = *John's id* AND swipe.liked = TRUE AND other_swipe.liked = TRUE
為什么不簡(jiǎn)單點(diǎn):
| UserId | ShownUserId | Liked |
|---|---|---|
| John | Jane | yes |
| John | Jill | no |
| Jane | Dave | yes |
| Jane | John | yes |
| Jill | Dave | no |
| Jill | John | yes |
John 喜歡誰(shuí):
select ShownUserId from UserLikes where UserId = 'John' and Liked = yes
誰(shuí)被 John 喜歡了而且也喜歡回了他:
select UserLikes.ShownUserId
from UserLikes
inner join UserLikes as AlsoLikesMe
on UserLikes.UserId = AlsoLikesMe.ShownUserId
and AlsoLikesMe.Liked = yes
where UserLikes.UserId = 'John' and UserLikes.Liked = yes
不要忘記 ShownUserId 上的索引。