由註冊用戶名而引起的一些思考

假設當前的場景是這樣的,用戶量會很大,有一個註冊接口,用戶在註冊時會輸入一系列信息,好比用戶名(主要想表示它爲冷數據)等等。要求:1.能承受必定的併發訪問。2.即便是併發調用,要必需要保證用戶名不能重複。3.單次註冊耗時儘量短。只考慮單個mysql,單個redis。mysql

 

第一個想到的是,爲了使得系統承受必定的併發,那麼須要在註冊接口進行限流,而限流算法就再也不這裏贅述了。redis

那麼,可能有這麼些解決方案:算法

1. 利用redis set結構來進行存在性判斷。優勢:速度快,無需進行額外的併發控制。缺點:因爲認爲用戶名爲冷數據,浪費了大量內存。sql

2. 利用redis boolFilter結構來進行存在性判斷。優勢:速度快,無需進行額外的併發控制,佔用內存較小。缺點:因爲用戶量很大,若是隻使用較小的內存,那麼極可能會存在不少誤判。併發

3. 直接使用mysql。優勢:速度較快,無需進行額外的併發控制,不佔用內存。優化

 

如今來考慮如何來mysql這一層,用戶名通常存在於用戶表中,而因爲用戶量會很大,那麼一定涉及分表,但通常用戶表的水平拆分依靠的變量爲用戶id,好比分爲10張,那麼用戶id%10,方便查詢用戶id時能夠鎖定到某張表,或者某個區間。那麼這跟用戶名是不要緊的,也就是說,當咱們要判斷一個用戶是否存在,依然要遍歷全部表才能獲得結果,而此時,假設咱們有在用戶表中,對用戶名(對應Java String類型)創建了索引,它依然是很耗時的。索引

 

再次確認需求,存在性判斷。接口

 

一樣是分表,那能不能依靠用戶名來分呢?原來的依靠用戶id來分也確實是有它的須要的,那麼咱們能夠把用戶名作成一張大表(將它冗餘出來),如今對這張用戶名大表來拆分。那麼怎樣分才能使得咱們在獲得一個用戶名時能夠鎖定到某張或者某個區間呢?這裏不難想到使用hash,也就是每一個用戶名都會對應着一個hash值,參照用戶表的拆分,咱們也能夠簡單地hash值%10,固然也可使用別的策略。總之,如今當用戶註冊時,輸入用戶名,能夠對它進行計算獲得一個hash值,最後鎖定到某張表,只需遍歷那張表就能夠知道答案,從速度上來講,這相比以前要遍歷全部表要快不少。內存

 

再考慮單張表遍歷的優化。咱們能夠再利用一下hash值,假設這個hash值的字節數大小爲8,咱們在用戶名錶上,增長一列字段名爲hash,意爲用戶名的hash值,咱們對其創建索引,那麼咱們在判斷時,遍歷的就是索引key爲bigint的 B+樹,數字的比較確定要比字符串的比較快。固然,一樣可能會存在誤判,這能夠依據用戶量適當調整hash值字節數大小,理論上,字節數越大,hash衝突越低。字符串

 

再次確認需求,存在性判斷。

 

set結構能夠O(1)時間判斷存在。當前咱們是O(logn)地遍歷B+樹,等值判斷,那麼天然就想到了hash索引。遺憾的是,在innodb引擎下,咱們做爲用戶,是沒法創建hash索引。因此,這裏可能會想到使用別的引擎。但其實,innodb在優化的時候,會創建自適應hash索引,因此,咱們能夠認爲最後也是會創建hash索引。那既然會創建hash索引,那是否是用戶名錶就用不着這個hash值,直接對用戶名這個字符串創建索引就行了,反正最後會被優化成hash索引。但優化老是不穩定的,我以爲仍是創建的好。(惟一索引,這樣就不會重複了)

 

須要說明的是,以上尚未通過驗證,只是分享一下這些想法!

相關文章
相關標籤/搜索