天天看點

用單庫自增鍵來生成id了,後期怎麼分庫?哎,這個坑大!

星球水友“寫代碼的”提問:

沈老師,我們現在使用者中心是單庫單表,uid使用資料庫自增主鍵,uid被很多業務關聯,不能變化。 現在使用者中心資料量逐漸變大,有分庫需求了,如何由單庫更新為多庫,保持曆史uid不變,并且新生成的資料不沖突,有什麼好辦法麼? ==問題描述完== 應該有不少公司都會利用資料庫“插入資料自動自增id”來作為業務id,這種方法會使得業務與id生成強耦合,導緻id生成算法難以更新。

今天和大家一起簡單探讨下,id生成要考慮哪些要素。畫外音:别誤會,不是說“自增id”不好,是說它與業務耦合了,難以更新。

一、id生成要考慮的技術點 幾乎所有業務,都會有一個業務唯一辨別:

  • 使用者辨別:uid(user-id)
  • 消息辨別:mid(msg-id)
  • 訂單辨別:oid(order-id)

這個辨別,在存儲系統裡通常是主鍵,主鍵使用聚集索引(clustered-index),即在實體存儲上以這個id排序。于是,對這個id有:唯一性,趨勢遞增性的要求。

這個辨別,也經常被用來做流量負載均衡,資料負載均衡的依據,即這個id必須在統計上必須是完全随機的。于是,對這個id有:随機性的要求。

同時,id生成算法更新,理論上對業務系統是透明的。于是,對這個id的生成有:獨立性需求。 為了保證id生成的上述特性,要有一個:uint64_t GenID()的獨立方法(或者獨立接口)來生成id,生成id具體做什麼用,該方法不關心,可以是用來做uid,也可以是用來做oid,甚至log-id。 當然,id生成的具體細節,業務也不用關心。即,GenID()的内部實作,可以是利用資料庫的自增id,也可以使用時間遞增,目前行業内最流行的,是仿照snowflake生成分布式id。 這個封裝,屏蔽了id生成的細節,保留方案更新的可能性,是系統設計中,解耦的展現。 如果使用了此類方法生成業務id,資料庫由單庫擴充多庫就很容易了:(1)确定一個路由算法,例如hash取模;(2)将單庫中的資料,通過這個路由算法遷移到多庫中去,以實作單庫資料量的減少;(3)通過這個路由算法尋找資料(讀);(4)通過這個路由算法插入資料(寫);

假如架構設計前期沒有提前考慮獨立的id生成,後期又要實施單庫拆多庫,該怎麼辦呢? 二、針對星球水友提到的例子

曆史的坑已經鑄成,沒有解耦id生成方法,而且也沒法批量修改id,該怎麼辦呢?

假設由單庫拆分為3庫,可以這麼玩:(1)做一個1主2從資料庫叢集,相當于每條資料複制成了3份;(2)将路由算法,設為取模hash算法,%3;(3)第一個庫,%3=0,把餘1和餘2的uid删掉;(4)第二個庫,%3=1,把餘0和餘2的uid删掉;(5)第三個庫,%3=2,把餘0和餘1的uid删掉;(6)将每個庫的自增步長設定為3,這樣每個庫的id生成就不會重複了;(7)更新使用者中心,按照路由算法查詢uid資料; 搞定,拆庫擴容達成:(1)單庫資料量下降為了原來的1/3;(2)讀寫執行個體個數擴充為了原來的3倍;(3)并且id生成與查詢都不會沖突; 希望這個取巧的方法對你有幫助。但更希望,大夥提前考慮id生成的唯一性、随機性、趨勢遞增性、獨立性。

系統性考慮問題,知其然,知其是以然。

歡迎大家繼續提問,有問必答。

本文轉自“架構師之路”公衆号,58沈劍提供。