首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 數據庫知識 >  >> MySQL 資料庫 >> MySQL 綜合教學

MySQL REPLACE INTO 與 LAST_INSERT_ID() 實現高效能全域 ID 生成器

     編輯:MySQL 綜合教學

核心思路:單表單行,REPLACE 觸發自增

利用 MySQL REPLACE INTO 特性:唯一鍵衝突時先刪除舊行再插入新行。配合 LAST_INSERT_ID() 獲取自增值,實現無鎖、高效能的 ID 生成。

基礎版:Tickets64 表

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;

-- 獲取 ID
REPLACE INTO `tickets64` (`stub`) VALUES ('a');
SELECT LAST_INSERT_ID();

執行後表永遠只有 1 行,id 持續遞增:1, 2, 3... 原子操作,無並發衝突。

進階版:多業務序列共用一張表

表結構

CREATE TABLE `sequence` (
  `name` VARCHAR(50) NOT NULL,
  `id` BIGINT UNSIGNED NOT NULL DEFAULT '0',
  PRIMARY KEY (`name`)
) ENGINE=InnoDB;

-- 初始化
INSERT INTO `sequence` (`name`) VALUES ('users'), ('photos'), ('albums'), ('comments');

關鍵差異:id 非自增、非主鍵,name 為主鍵。

獲取指定業務 ID

UPDATE `sequence` SET `id` = LAST_INSERT_ID(`id` + 1) WHERE `name` = 'photos';
SELECT LAST_INSERT_ID();
  • LAST_INSERT_ID(expr) 將函數返回值設為 expr
  • 更新返回影響行數,函數返回新 ID,單條 SQL 完成「取號+持久化」

動態建立新序列(無需預先 INSERT)

INSERT INTO `sequence` (`name`) VALUES('new_business')
  ON DUPLICATE KEY UPDATE `id` = LAST_INSERT_ID(`id` + 1);
SELECT LAST_INSERT_ID();
  • 首次執行:INSERT 成功,id=0,LAST_INSERT_ID() 返回 0(無自增欄位)
  • 後續執行:觸發 UPDATE,返回遞增 ID
  • 起始值為 0 而非 1,業務層可 +1 處理

高可用擴展:多節點無協調

參考 Flicker 奇偶數思路,擴展至 N 節點:

  • 節點 i 設定初始值 i(0-based 連續)
  • auto_increment_increment = N(步長)
  • 客戶端輪詢或隨機選節點獲取 ID

優於 Flicker:不限於 2 節點、InnoDB 支援事務、單表管理所有序列。

效能與注意事項

  • QPS:單表單行更新,InnoDB 行鎖極輕,單節點輕鬆支撐 10k+ QPS
  • 持久化:每次 UPDATE 即落盤,重啟不丟號(但可能有間隙)
  • 間隙處理:事務回滾、伺服器崩潰會導致 ID 不連續,業務通常可接受
  • 熱點行:高並發下單行鎖競爭,可考慮分段序列(如 users_1, users_2...)分散熱點

總結

這一方案不依賴外部中間件、程式碼極簡、擴展靈活,適合中小規模、已有 MySQL 基礎設施的系統。大規模高並發建議遷移至 Snowflake 等無協調演算法。

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