分表分庫架構下,單表 AUTO_INCREMENT 無法保證全域唯一。需要一個全域 ID 生成器,支援高併發、無重複、效能佳。
CREATE TABLE auto_id (
idname VARCHAR(20) NOT NULL DEFAULT '',
id BIGINT(20) NOT NULL DEFAULT 0 COMMENT '',
PRIMARY KEY (idname)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
DELIMITER //
DROP PROCEDURE IF EXISTS get_increment_id;
CREATE PROCEDURE get_increment_id(
IN idname_in VARCHAR(20),
IN small_in BIGINT,
OUT id_out BIGINT
)
BEGIN
DECLARE oldid BIGINT;
START TRANSACTION;
SELECT id INTO oldid FROM auto_id WHERE idname = idname_in FOR UPDATE;
IF oldid IS NULL THEN
INSERT INTO auto_id (idname, id) VALUES (idname_in, small_in);
SET id_out = small_in;
ELSE
UPDATE auto_id SET id = id + 1 WHERE idname = idname_in;
SET id_out = oldid + 1;
END IF;
COMMIT;
END //
DELIMITER ;
SELECT ... FOR UPDATE 給目標行加排他鎖,其他併發請求阻塞等待,直到事務提交,保證同一 idname 同一時間只有一個進程獲取 ID。
$sql = "CALL get_increment_id('{$key}', {$small}, @id)";
$db->query($sql);
$ret = $db->query("SELECT @id");
$id = $ret[0]['@id'];
CREATE TABLE test (
id INT(11) NOT NULL AUTO_INCREMENT COMMENT 'id',
PRIMARY KEY (id)
) ENGINE=MyISAM AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
UPDATE test SET id = LAST_INSERT_ID(id + 1);
SELECT LAST_INSERT_ID();
LAST_INSERT_ID() 為連接級別,不受其他連接影響| 維度 | 方案一(序號表) | 方案二(LAST_INSERT_ID) |
|---|---|---|
| 彈性 | 單表管理多種 ID 類型 | 每類型一張表 |
| 引擎需求 | InnoDB(支援行鎖) | MyISAM/InnoDB 均可 |
| 擴展性 | 易於加入業務邏輯 | 單純遞增 |
| 效能 | 極佳(單行鎖) | 極佳(無鎖競爭) |
根據業務複雜度選擇:簡單場景用方案二,需統一管理多類型 ID 用方案一。