用單庫自增鍵來生成id了,后期怎么分庫?哎,這個坑大!
星球水友“寫代碼的”提問:
沈老師,我們現在用戶中心是單庫單表,uid使用數據庫自增主鍵,uid被很多業務關聯,不能變化。
現在用戶中心數據量逐步變大,有分庫需求了,如何由單庫升級為多庫,保持歷史uid不變,并且新生成的數據不沖突,有什么好辦法么?
==問題描述完==
應該有不少公司都會利用數據庫“插入數據自動自增id”來作為業務id,這種方法會使得業務與id生成強耦合,導致id生成算法難以升級。
今天和大家一起簡單探討下,id生成要考慮哪些要素。畫外音:別誤會,不是說“自增id”不好,是說它與業務耦合了,難以升級。
一、id生成要考慮的技術點
幾乎所有業務,都會有一個業務唯一標識:
- 用戶標識:uid(user-id)
- 消息標識:mid(msg-id)
- 訂單標識:oid(order-id)
這個標識,在存儲系統里通常是主鍵,主鍵使用聚集索引(clustered-index),即在物理存儲上以這個id排序。于是,對這個id有:唯一性,趨勢遞增性的要求。
畫外音:索引《1分鐘了解不同索引的差異》。
這個標識,也經常被用來做流量負載均衡,數據負載均衡的依據,即這個id必須在統計上必須是完全隨機的。于是,對這個id有:隨機性的要求。
同時,id生成算法升級,理論上對業務系統是透明的。于是,對這個id的生成有:獨立性需求。
為了保證id生成的上述特性,要有一個:
- uint64_t GenID()
的獨立方法(或者獨立接口)來生成id,生成id具體做什么用,該方法不關心,可以是用來做uid,也可以是用來做oid,甚至log-id。
當然,id生成的具體細節,業務也不用關心。即,GenID()的內部實現,可以是利用數據庫的自增id,也可以使用時間遞增,目前行業內最流行的,是仿照snowflake生成分布式id。
這個封裝,屏蔽了id生成的細節,保留方案升級的可能性,是系統設計中,解耦的體現。 如果使用了此類方法生成業務id,數據庫由單庫擴展多庫就很容易了:
- 確定一個路由算法,例如hash取模;
- 將單庫中的數據,通過這個路由算法遷移到多庫中去,以實現單庫數據量的減少;
- 通過這個路由算法尋找數據(讀);
- 通過這個路由算法插入數據(寫);
假如架構設計前期沒有提前考慮獨立的id生成,后期又要實施單庫拆多庫,該怎么辦呢?
二、針對星球水友提到的例子
歷史的坑已經鑄成,沒有解耦id生成方法,而且也沒法批量修改id,該怎么辦呢?
假設由單庫拆分為3庫,可以這么玩:
做一個1主2從數據庫集群,相當于每條數據復制成了3份;
- 將路由算法,設為取模hash算法,%3;
- 第一個庫,%3=0,把余1和余2的uid刪掉;
- 第二個庫,%3=1,把余0和余2的uid刪掉;
- 第三個庫,%3=2,把余0和余1的uid刪掉;
- 將每個庫的自增步長設置為3,這樣每個庫的id生成就不會重復了;
- 升級用戶中心,按照路由算法查詢uid數據;
搞定,拆庫擴容達成:
- 單庫數據量下降為了原來的1/3;
- 讀寫實例個數擴充為了原來的3倍;
- 并且id生成與查詢都不會沖突;
希望這個取巧的方法對你有幫助。
但更希望,大伙提前考慮id生成的唯一性、隨機性、趨勢遞增性、獨立性。
系統性考慮問題,知其然,知其所以然。
【本文為51CTO專欄作者“58沈劍”原創稿件,轉載請聯系原作者】