MySQL 8.0 前無原生 CREATE SEQUENCE (8.0+ 已支持),傳統方案通過**單行表 + LAST_INSERT_ID() 函數**實現線程安全、高性能的序列號生成,邏輯等同於 Oracle SEQUENCE。
LAST_INSERT_ID(expr) 將函數返回值設置為 expr,並更新該連接的「最後插入 ID」上下文變量。後續同連接調用 SELECT LAST_INSERT_ID() 即可獲取該值,**不受並發影響**,屬於連接級別隔離。
CREATE TABLE emp_seq (
seq BIGINT NOT NULL
) ENGINE=InnoDB;
INSERT INTO emp_seq VALUES (0); -- 起始值
-- 步驟 1: 更新並設置 LAST_INSERT_ID
UPDATE emp_seq SET seq = LAST_INSERT_ID(seq + 1);
-- 步驟 2: 獲取新值
SELECT LAST_INSERT_ID();
也可合併為單條語句 (需客戶端支持多結果集) 或封裝為存儲過程:
DELIMITER //
CREATE PROCEDURE next_val(OUT next_id BIGINT)
BEGIN
UPDATE emp_seq SET seq = LAST_INSERT_ID(seq + 1);
SELECT LAST_INSERT_ID() INTO next_id;
END //
DELIMITER ;
-- 調用
CALL next_val(@next_id);
SELECT @next_id;
public long nextSeqValue() {
String sql = "UPDATE emp_seq SET seq = LAST_INSERT_ID(seq + 1); SELECT LAST_INSERT_ID();";
try (Connection con = ds.getConnection();
Statement stmt = con.createStatement()) {
// 需要 allowMultiQueries=true (連接串參數)
boolean hasResults = stmt.execute(sql);
if (hasResults) {
try (ResultSet rs = stmt.getResultSet()) {
if (rs.next()) return rs.getLong(1);
}
}
// 兼容不支持多查詢的驅動:分兩次 execute
return -1;
} catch (SQLException e) {
throw new RuntimeException("獲取序列失敗", e);
}
}
連接串關鍵參數:jdbc:mysql://host:3306/db?allowMultiQueries=true 允許單次 execute 多條語句。
| 特性 | Oracle SEQUENCE | MySQL 模擬表 |
|---|---|---|
| 語法 | CREATE SEQUENCE emp_seq START WITH 1 INCREMENT BY 1 | 單行表 + UPDATE/LAST_INSERT_ID |
| 獲取值 | emp_seq.NEXTVAL | SELECT LAST_INSERT_ID() (緊隨 UPDATE 後) |
| 緩存 | CACHE 20 可配置 | 無緩存 (每次 UPDATE),可通過 Redis/應用層緩存優化 |
| 持久化 | 數據庫字典 | 普通表,受事務/備份保護 |
| 並發性能 | 極高 (內存緩存) | 依賴行鎖,高並發下有鎖爭用 |
UPDATE seq = LAST_INSERT_ID(seq + 1000) 取 1000 號,本地原子計數器分配,減少數據庫交互 1000 倍。emp_seq_0...emp_seq_9),按 hash(id) % 10 路由,分散熱點。CREATE SEQUENCE,支持 CACHE、NEXTVAL 函數、無縫遷移。單行表 + LAST_INSERT_ID 是 MySQL 5.x/5.7 時代標準、可靠的序列實現,核心優勢在於**連接級隔離性**保證並發正確性。高並發場景建議引入應用層批量取號或遷移至 MySQL 8.0 原生序列。