業務增長到單表/單庫瓶頸時,Sharding 勢在必行。若 Sharding Key 需全域唯一(如訂單號、商品 ID),ID 生成策略直接決定系統擴展性與可用性。本文從 DBA 視角,剖析 4 類主流方案優劣。
建立序列表 SEQUENCE(name PK, gid),每次取 ID:
UPDATE sequence SET gid = @new_id WHERE name = 'users' AND gid < @new_id;
AND gid < @new_id 保證並發下只增不減function next_id_db($name) {
for ($i=0; $i<100; $i++) {
$last = $dao->get_seq_id($name);
$id = $last + 1;
if ($dao->set_seq_id($name, $id)) return $id;
}
return false;
}
SETNX (Redis) / add (Memcached) 實現分散式鎖INCR 生成 ID → 寫回 DB 序列表 → 釋放鎖$redis->setnx("sequence_{$name}_flag", time()); // 加鎖
$id = $redis->incr("sequence_{$name}"); // 生成 ID
if ($id === 1 || $id === false) { // Redis 重啟丟失
$last = $dao->get_seq_id($name);
$redis->set("sequence_{$name}", $last);
$id = $redis->incr("sequence_{$name}");
}
$dao->set_seq_id($name, $id); // 持久化
$redis->delete("sequence_{$name}_flag"); // 釋放鎖
function next_id($name) {
try { return $this->next_id_redis($name); }
catch (Exception $e) { return $this->next_id_db($name); }
}
結合方案一二優點,Redis 正常時高效能,故障時自動降級 DB CAS,可用性大幅提升。
CREATE TABLE Tickets64 (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
stub CHAR(1) NOT NULL DEFAULT '',
PRIMARY KEY (id), UNIQUE KEY stub (stub)
) ENGINE=MyISAM;
REPLACE INTO Tickets64 (stub) VALUES ('a');
SELECT LAST_INSERT_ID(); -- 獲取新 ID
每次 REPLACE 刪除舊行插入新行,stub 唯一鍵保證表永遠只有一行,ID 單調遞增。
-- Server 1 auto_increment_increment = 2 auto_increment_offset = 1 -- 產生 1,3,5,7... -- Server 2 auto_increment_increment = 2 auto_increment_offset = 2 -- 產生 2,4,6,8...
客戶端輪詢兩台獲取 ID,單點故障自動切換。
| 方案 | 效能 | 可用性 | 複雜度 | ID 有序 | 適用場景 |
|---|---|---|---|---|---|
| DB CAS | 低 | 低(單點) | 低 | 是 | 低並發、簡單業務 |
| Redis 鎖 | 高 | 中(需哨兵/集群) | 高 | 是 | 高並發、有 Redis 運維能力 |
| Redis+DB 降級 | 高 | 高 | 高 | 是 | 核心業務、強一致要求 |
| Flicker 雙主 | 中 | 高 | 中 | 是(奇偶) | 傳統架構、不想引入新中間件 |
SEQUENCE、Oracle SEQUENCE、MySQL 8.0 AUTO_INCREMENT 持久化改進選型建議:新架構首選 Snowflake 或類似演算法;存量 MySQL 架構可用 Flicker 或 Redis+DB 降級平滑過渡。