分布式系統中我們一般會對一些資料量大的業務進行拆分,如:訂單表,使用者表。因為資料量巨大一張表無法承載。就會對其進行分庫分表。
但一旦涉及到分庫分表,就會引申出分布式系統中唯一主鍵ID的生成問題,永不遷移資料和避免熱點的文章中要求需要唯一ID的特性。
1. 背景
在複雜分布式系統中,往往需要對大量的資料和消息進行唯一辨別。如在美團點評的金融、支付、餐飲、酒店、貓眼電影等産品的系統中,資料日漸增長,對資料分庫分表後需要有一個唯一ID來辨別一條資料或消息,資料庫的自增ID顯然不能滿足需求;特别一點的如訂單、騎手、優惠券也都需要有唯一ID做辨別。此時一個能夠生成全局唯一ID的系統是非常必要的。
(1)業務系統對分布式ID有哪些訴求
全局唯一性:不能出現重複的ID号,既然是唯一辨別,這是最基本的要求;
趨勢遞增:在MySQL InnoDB引擎中使用的是聚集索引,由于多數RDBMS使用B-tree的資料結構來存儲索引資料,在主鍵的選擇上面我們應該盡量使用有序的主鍵保證寫入性能;
單調遞增:保證下一個ID一定大于上一個ID,例如事務版本号、IM增量消息、排序等特殊需求;
資訊安全:如果ID是連續的,惡意使用者的扒取工作就非常容易做了,直接按照順序下載下傳指定URL即可;如果是訂單号就更危險了,競争對手可以直接知道我們一天的單量。是以在一些應用場景下,會需要ID無規則、不規則;
性能要求:高可用。如平均延遲和TP999延遲都要盡可能低;可用性5個9;高QPS;
分布式id生成算法的有很多種,Twitter的SnowFlake就是其中經典的一種。
本文介紹的是基于Twitter的SnowFlake的一種優化版本,預設支援的運作環境: php-fpm、shell-cli、php -S 127.0.0.1:80 模式下生成全局唯一ID
Twitter版SnowFlake
SnowFlake算法(簡稱雪花算法)生成64位的二進制正整數,然後轉換成10進制的數。64位二進制數由如下部分組成:

優化後SnowFlake版本
基于Twitter的雪花算法改造,分布式全局唯一ID生成器, 組成<毫秒級時間戳+機器ip+程序id+序列号>
長度最長為64位bit,各bit位含義如下:
-
不用。二進制中最高位為1的都是負數,但是我們生成的id一般都使用整數,是以這個最高位固定是01位
-
用來記錄時間戳(毫秒)41位
- 41位可以表示$2^{41}-1$個數字,
- 如果隻用來表示正整數(計算機中正數包含0),可以表示的數值範圍是:0 至 $2^{41}-1$,減1是因為可表示的數值範圍是從0開始算的,而不是1。
- 也就是說41位可以表示$2^{41}-1$個毫秒的值,轉化成機關年則是$(2^{41}-1) / (1000 * 60 * 60 * 24 * 365) = 69$年
-
機器IP低10位,可以支援最多1023個機器節點10位
-
目前處理程序辨別,10位的長度最多支援1023個機器程序10位
-
計數序列号,序列号即序列自增id,可以支援同一節點的同一程序同一毫秒生成4個ID序号2位
優點
1、此方案每秒能夠産生409.6萬個ID,性能快
2、時間戳在高位,自增序列在低位,整個ID是趨勢遞增的,按照時間有序遞增
3、靈活度高,可以根據業務需求,調整bit位的劃分,滿足不同的需求
缺點
1、依賴機器的時鐘,如果伺服器時鐘回撥,會導緻重複ID生成
使用
通過composer包引用,引用到項目中
use WeberGiles\Favorites\Snowflake;
//生成一個全局唯一ID
echo Snowflake::uniqueId();