首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫

MySQL 分表唯一 ID 生成方案:基於 LAST_INSERT_ID 的集中式 ID 服務

     編輯:MySQL 資料庫相關知識

分表(水平拆分)後,各分表若仍使用 AUTO_INCREMENT,主鍵 ID 會重複,無法全局唯一標識記錄。本文介紹一種利用 MySQL LAST_INSERT_ID(expr) 特性,通過單張序列表實現高性能、全局唯一 ID 分配的方案。

核心問題

  • 分表結構相同,各表 id INT AUTO_INCREMENT
  • 插入數據時,無法確定該去哪張分表,也無法保證跨表 ID 不衝突
  • 需要一個「中心化 ID 生成器」在分表之外統一分配 ID

方案架構

1. 序列表設計

CREATE TABLE ticket_mutex (
  name VARCHAR(32) NOT NULL PRIMARY KEY COMMENT '業務標識,如 USER、POST',
  value BIGINT(20) UNSIGNED NOT NULL COMMENT '當前最大ID值'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT '分表ID序列表';

2. 初始化業務序列

INSERT INTO ticket_mutex(name, value) VALUES ('USER', 0), ('POST', 0);

3. 獲取下一個唯一 ID(核心語句)

UPDATE ticket_mutex 
SET value = LAST_INSERT_ID(value + 1) 
WHERE name = 'USER';
SELECT LAST_INSERT_ID();

執行效果演示

操作返回值表中 USER 行 value
首次執行11
第二次22
第 N 次NN

關鍵優勢:單條原子語句完成「遞增 + 取值」,無需事務、無鎖競爭、無額外表鎖,極高併發下仍能保證 ID 單調遞增且無重複。

LAST_INSERT_ID() 兩種用法區別

用法語法返回值來源適用場景
無參數LAST_INSERT_ID()最近一次 INSERT 產生的 AUTO_INCREMENT 值普通自增表插入後取 ID
帶表達式LAST_INSERT_ID(expr)表達式 expr 的計算結果本方案:序列表無自增列,手動維護 value

官方文檔明確:LAST_INSERT_ID(expr) 會將表達式結果存入會話上下文,後續 LAST_INSERT_ID() 直接返回該值,不受並發影響,會話隔離。

完整業務流程

-- 1. 應用層決定分表邏輯(如 user_id % 10)
-- 2. 獲取全局唯一 ID
UPDATE ticket_mutex SET value = LAST_INSERT_ID(value + 1) WHERE name = 'USER';
SET @new_id = LAST_INSERT_ID();

-- 3. 寫入目標分表
INSERT INTO user_@shard_id (id, username, email) 
VALUES (@new_id, 'zhangsan', 'zhangsan@example.com');

擴展:多業務隔離

name 字段區分業務域:USER、POST、ORDER、COMMENT 等互不干擾,單表支撐無限業務線。

對比其他方案

方案性能實現複雜度單點風險ID 連續性
UUID低(索引碎片、空間大)極簡無無序
Snowflake 算法高中(需部署服務/客戶端庫)無趨勢遞增
數據庫自增 + 步長中中(多實例配置步長)有段連續
本方案高(單條 UPDATE)低(純 SQL)有(主庫單點)完全連續

高可用建議

  • 序列表放在主庫,讀寫分離架構下走主庫
  • 配置主從半同步複製(rpl_semi_sync_master=ON)防丟 ID
  • 極高併發可考慮批量分配:value + 100 一次取段,應用層本地消費

適用場景:中小規模分表系統、強依賴數字 ID 連續性的業務(如訂單號)、不想引入額外中間件的團隊。核心優勢:零中間件、純 SQL、原子性強、實現極簡。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved