基于數據庫的自增主鍵也可以生成趨勢遞增的唯一 ID,且由于唯一ID不與時間戳關聯,所以不會受到時鐘回撥問題的影響。
4.4.1 分庫分表架構
數據庫一般都支持設置自增主鍵的初始值和自增步長,以MySQL為例,自增主鍵的自增步長由auto_increment_increment
變量表示,其默認值為1。MySQL可以使用SET命令設置這個變量值,比如SET @@auto_increment_increment=3
會將自增步長設置為3。
在創建數據表時,MySQL可以為自增主鍵指定初始值。例如,如下建表語句會為test_primary_key表的自增主鍵設置初始值為10:
create table test_primary_key (
id bigint unsigned not null auto_increment,
col tinyint not null,
primary key (id)
) ENGINE=InnoDB AUTO_INCREMENT=10;
接下來向這個表中插入5條數據,然后看看主鍵的增長情況,如圖4-11所示。
可以看到,第一條數據的主鍵為10,之后每個主鍵都自增3。這個數據庫功能可以保證數據庫分庫分表后每個子表的自增主鍵全局唯一。
如圖4-12所示:
假設數據庫被水平拆分為5個子表:依次創建5個子表并分別設置自增主鍵的初始值為1~5,然后設置每個子表的自增主鍵的自增步長也為5。最終,
-
子表1生成的自增主鍵是1,6,11,16,21,…,
-
子表2生成的自增主鍵是2,7,12,17,22,…,
-
其他子表以此類推。
將基于這個思路實現的分庫分表架構應用到唯一ID生成器服務,就會生成趨勢遞增的唯一ID,有效地解決了數據庫單點問題,并在一定程度上提高了數據庫并發吞吐量。
不過,分庫分表架構將自增主鍵的自增步長與分表個數強行綁定,所以系統整體的可擴展性較差,無法對數據庫進行任何擴容操作。也就是說,這個方案只適合不需要擴容的場景。
4.4.2 批量緩存架構
4.2節介紹了基于數據庫的自增主鍵生成單調遞增的唯一ID的方案,當時我們討論過一個關于高可用性的細節問題:如果采用從數據庫中批量獲取ID的方式,則可以大幅提高系統性能,但是任何時刻只能有一個服務實例工作,否則生成的唯一ID將不是單調遞增的,而是趨勢遞增的。這恰好是我們需要的效果。
既然生成的唯一ID是趨勢遞增的,那么唯一ID生成器服務可以有任意多個服務實例。如圖4-13所示:
每個服務實例都從數據庫中批量獲取ID并緩存到本地;同時,為了保證數據庫主從切換不會生成重復的ID,數據庫主從節點采用半同步復制或MGR方式同步最新數據。