分佈式系統中我們會對一些數據量大的業務進行分拆,如:用戶表,訂單表。因為數據量巨大一張表無法承接,就會對其進行分庫分表。
但一旦涉及到分庫分表,就會引申出分佈式系統中唯一主鍵ID的生成問題,永不遷移數據和避免熱點的文章中要求需要唯一ID的特性:
- 整個系統ID唯一
- ID是數字類型,而且是趨勢遞增的
- ID簡短,查詢效率快
什麼是遞增?如:第一次生成的ID為12,下一次生成的ID是13,再下一次生成的ID是14。這個就是生成ID遞增。
什麼是趨勢遞增?如:在一段時間內,生成的ID是遞增的趨勢。如:再一段時間內生成的ID在【0,1000】之間,過段時間生成的ID在【1000,2000】之間。但在【0-1000】區間內的時候,ID生成有可能第一次是12,第二次是10,第三次是14。
1.數據庫自增ID
新建一個公共庫,庫裡面新建一個序列表,主鍵id自增,每次請求增加數據都往這個表中插入數據,然後獲取到id,然後使用即可。
優點:方便簡單
缺點:單庫生成自增id,高併發下,會有瓶頸
適用場景:
併發很低,幾百/s,不會出現性能瓶頸
2.UUID
優點:本地生成,不基於任何第三方
缺點:
- 太長,作為數據庫主鍵性能太差,不適合作為主鍵
- 不具有有序性,會導致B+樹索引在寫的時候有過多的隨機寫操作(連續的id可以產生部分順序寫)
- 寫的時候不能產生有順序的 append 操作,而需要進行 insert 操作,將會讀取整個 B+ 樹節點到內存,在插入這條記錄後會將整個節點寫回磁盤,這種操作在記錄佔用空間比較大的情況下,性能下降明顯
適用場景:
隨機生成文件名、編號,生成token等。
3.系統時間+拼接業務字段值
例如:當前時間戳 + 用戶id + 業務含義編碼,併發高的時候,會有重複,此時就不行了,不建議使用。
4.Redis
Redis是單線程的,所以也可以用生成全局唯一的ID。可以用Redis的原子操作 INCR和INCRBY來實現。
優點:
- 不依賴於數據庫,靈活方便,且性能優於數據庫。
- 數字ID天然排序,對分頁或者需要排序的結果很有幫助。
缺點:
- 由於redis是內存的KV數據庫,即使有AOF和RDB,但是依然會存在數據丟失,有可能會造成ID重複。
- 依賴於redis,redis要是不穩定,會影響ID生成。
5.雪花snowflake算法
twitter開源的分佈式id生成算法,把一個64位的long型的id,1個bit是不用的,用其中的41 bit作為毫秒數,用10 bit作為工作機器id,12 bit作為序列號,理論上最多支持1024臺機器每秒生成4096000個序列號。
![分佈式數據庫唯一ID生成方式](http://p2.ttnews.xyz/loading.gif)
- 1 bit:不用
因為二進制裡第一個bit為如果是1,那麼都是負數,但是我們生成的id都是正數,所以第一個bit統一都是0 - 41 bit:表示的是時間戳,單位是ms
41 bit可以表示的數字多達2^41 - 1,也就是可以標識2 ^ 41 - 1個毫秒值,換算成年就是表示69年的時間 - 10 bit:記錄工作機器id
代表的是這個服務最多可以部署在2^10臺機器上哪,也就是1024臺機器,但是10 bit裡5個bit代表機房id,5個bit代表機器id。意思就是最多代表2 ^ 5個機房(32個機房),每個機房裡可以代表2 ^ 5個機器(32臺機器)。 - 12 bit:記錄同一個毫秒內產生的不同id
12 bit可以代表的最大正整數是2 ^ 12 - 1 = 4096,也就是說可以用這個12bit代表的數字來區分同一個毫秒內的4096個不同的id
缺點:
- 依賴機器的時鐘,如果服務器時鐘回撥,會導致重複ID生成
優點:
- 此方案每秒能夠產生409.6萬個ID,性能快
- 時間戳在高位,自增序列在低位,整個ID是趨勢遞增的,按照時間有序遞增
- 靈活度高,可以根據業務需求,調整bit位的劃分,滿足不同的需求
閱讀更多 下頁伴影 的文章