MySQL 并發生成流水號
背景
在業務系統中,生成唯一的流水號是非常常見的問題之一。每個業務系統都需要有一個自己的流水號規則,這些規則往往是業務體系架構的一部分。當多個并發客戶端同時在請求系統時,如何保證流水號的唯一性是一項非常重要的任務。
傳統解決方案
傳統的解決方案是使用數據庫中的 AUTO_INCREMENT 列。為確保唯一性,可以在數據庫中創建一個自增長的列,每次插入新記錄時,AUTO_INCREMENT 列都會自動加1。但是,當多個客戶端同時訪問數據庫時,容易發生競爭條件,進而導致生成的流水號出現重復。
新方案:利用序列化鎖
為解決競爭條件,在 mysql 中,可以使用序列化鎖(SERIALIZABLE)來確保流水號的唯一性。具體實現方法如下:
1. 創建一個表 SEQ_NUM,其中存儲了當前可用的流水號,表結構如下:
CREATE TABLE SEQ_NUM ( id BIGINT(20) NOT NULL AUTO_INCREMENT, seq_num VARCHAR(20) NOT NULL, PRIMARY KEY (id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
2. 在 SEQ_NUM 表中插入初始數據:
INSERT INTO SEQ_NUM(seq_num) VALUES ('000000');
3. 在業務代碼中,使用如下 SQL 語句操作 SEQ_NUM 表來獲取唯一的流水號:
BEGIN; SELECT seq_num FROM SEQ_NUM FOR UPDATE; UPDATE SEQ_NUM SET seq_num = LPAD(seq_num+1, 6, '0'); COMMIT;
解析
1. 在執行 SELECT 語句時,使用了 FOR UPDATE 關鍵字,該語句會獲取 SEQ_NUM 表的排它鎖,進而確保獲取的 seq_num 值不會被其他事務修改。
2. 更新 SEQ_NUM 表的 seq_num 時,使用了 LPAD 函數將值格式化為 6 位數。這種方式可以靈活地根據業務需求修改。
3. 使用 MySQL 的事務機制確保了整個更新過程的原子性,確保了流水號的唯一性。
總結
通過使用序列化鎖機制,我們可以實現并發環境下的流水號唯一性。除此之外,我們還可以使用其他技術如 Redis、ZooKeeper 等來解決流水號唯一性問題。