分表後,各表自增 ID 獨立遞增,全域唯一性失效。本文對比三種主流解決方案,並提供 PHP 實作範例,助你根據併發量與業務選型。
CREATE TABLE `create_id` (
`id` BIGINT(20) NOT NULL AUTO_INCREMENT PRIMARY KEY
) ENGINE=MyISAM;
插入資料前先向此表寫入一筆空記錄,取得 LAST_INSERT_ID() 作為全域 ID:
function get_AI_ID() {
$sql = "INSERT INTO create_id (id) VALUES ('')";
$this->db->query($sql);
return $this->db->insertID();
}
| 優點 | 缺點 |
|---|---|
| 實作簡單、ACID 保證、無額外元件 | 高併發下 AUTO_INCREMENT 表鎖/行鎖成為瓶頸 |
innodb_autoinc_lock_mode = 0 -- Traditional:每次插入表鎖
innodb_autoinc_lock_mode = 1 -- Consecutive(預設):可預測筆數用批量鎖,否則表鎖
innodb_autoinc_lock_mode = 2 -- Interleaved:無鎖,來一個處理一個,併發最高
高併發建議設為 2,但可能導致自增值不連續(有間隙)。MyISAM 僅支援 Traditional 模式。
function get_next_autoincrement($timeout = 60) {
// 分散式鎖防止並發初始化
if (!get_lock("serial:lock", $timeout)) return 0;
$id = $redis->incr("serial");
if ($id > 1) { release_lock("serial:lock"); return $id; }
// 首次初始化:從 DB 取得最大 ID
$maxId = $db->query("SELECT MAX(id) FROM user_posts")->fetchColumn();
$id = $maxId + 1;
$redis->set("serial", $id);
release_lock("serial:lock");
return $id;
}
優點:極高併發效能、單機/叢集皆適用。缺點:引入 Redis 依賴、需處理網路故障與持久化一致性。
class IdQueue {
private $redis;
public function __construct() { $this->redis = new Redis(); $this->redis->connect('127.0.0.1', 6379); }
public function pushBatch(array $ids) { foreach ($ids as $id) $this->redis->lPush('id_queue', $id); }
public function pop() { return $this->redis->lPop('id_queue'); }
}
// 初始化:預生成 20 個 1000~4000 間的靚號
$ids = []; while (count($ids) < 20) { $n = rand(1000,4000); if (!in_array($n,$ids)) $ids[]=$n; }
$queue = new IdQueue(); $queue->pushBatch($ids);
// 業務使用
$id = $queue->pop();
if ($id) { $sql = "INSERT INTO user_posts(id,...) VALUES($id,...)"; }
優點:可規劃「靚號」銷售、ID 帶業務含義、DB 寫入零鎖競爭。缺點:需監控隊列長度自動補充、ID 耗盡風險。
innodb_autoinc_lock_mode=2避免直接使用分表後的各表自增 ID 作為全域主鍵,必須引入全域 ID 生成機制。