我們介紹一種基於數據庫維護自增 ID 區間,結合內存分配的策略,這也是淘寶的 TDDL 等數據庫中間件使用的主鍵生成策略。
使用這種方式首先在數據庫中創建 sequence 表,其中的每一行,用於記錄某個業務主鍵當前已經被佔用的 ID 區間的最大值。sequence 表的主要字段是 name 和 value,其中 name 是當前業務序列的名稱,value 存儲已經分配出去的 ID 最大值。
CREATE TABLE `sequence_global` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'Id',
`app_name` varchar(64) NOT NULL COMMENT 'sequence name',
`app_value` bigint(32) NOT NULL COMMENT 'sequence current value',
`create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '創建時間',
`update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改時間',
PRIMARY KEY (`id`),
UNIQUE KEY `unique_name` (`app_name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
接下來插入一條行記錄,當需要獲取主鍵時,每臺服務器主機從數據表中取對應的 ID 區間緩存在本地,同時更新 sequence 表中的 value 最大值記錄。
現在我們新建一條記錄,比如設置一條 order 更新的規則,插入一行記錄如下:
INSERT INTO sequence_global(app_name,app_value,create_time,update_time) values('test',200,now(),now());
當服務器在獲取主鍵增長區段時,首先訪問對應數據庫的 sequence 表,更新對應的記錄,佔用一個對應的區間。比如我們這裏設置步長爲 200,原先的 value 值爲 1000,更新後的 value 就變爲了 1200。
取到對應的 ID 區間後,在服務器內部進行分配,涉及的併發問題可以依賴樂觀鎖等機制解決。
有了對應的 ID 增長區間,在本地就可以使用 AtomicInteger 等方式進行 ID 分配。
不同的機器在相同時間內分配出去的 ID 可能不同,這種方式生成的唯一 ID,不保證嚴格的時間序遞增,但是可以保證整體的趨勢遞增,在實際生產中有比較多的應用。
爲了防止單點故障,sequence 表所在的數據庫,通常會配置多個從庫,實現高可用。
參考代碼實現:https://github.com/JMCuixy/dubbo-demo/tree/master/sequence