LAST_INSERT_ID() 是獲取剛插入行自增主鍵值的標準函數,廣泛用於主從表關聯寫入、返回新建資源 ID 等場景。本文深入剖析其連線級隔離機制、批量插入行為、與 MAX(id) 的本質區別、分佈式場景下的替代方案及常見誤用。
last_insert_id 變數,互不干擾。連線池複用時,必須同一連線內完成 INSERT 與 SELECT LAST_INSERT_ID()。INSERT、REPLACE、LOAD DATA 等寫入語句更新該值;SELECT、UPDATE(不涉及自增)、DELETE 不改變它。LAST_INSERT_ID(expr) 也可手動設定值供後續使用。-- 插入單行
INSERT INTO orders (cust_id, amount) VALUES (1001, 99.9);
SELECT LAST_INSERT_ID(); -- 返回剛生成的 order_id
-- 應用層模式(PHP PDO 示例)
$pdo->exec("INSERT INTO orders (cust_id, amount) VALUES (1001, 99.9)");
$orderId = $pdo->lastInsertId(); -- PDO 封裝 LAST_INSERT_ID()
-- 手動設定(無自增表、分佈式 ID 生成)
INSERT INTO t (id, data) VALUES (LAST_INSERT_ID(123456789), 'foo');
SELECT LAST_INSERT_ID(); -- 123456789
INSERT INTO t (name) VALUES ('a'), ('b'), ('c'); -- 假設自增從 100 開始
SELECT LAST_INSERT_ID(); -- 返回 100(第一行的值),而非 102
原因:設計為返回「第一行生成的自增值」,便於主從複制時從庫重放相同邏輯產生相同 ID 序列。若需獲取所有生成 ID,需自行計算:LAST_INSERT_ID() + ROW_COUNT() - 1(連續無間隙前提下)。
| 維度 | LAST_INSERT_ID() | SELECT MAX(id) |
|---|---|---|
| 併發安全性 | ✅ 連線隔離,無競態 | ❌ 並發插入時可能取到他人 ID |
| 效能 | O(1) 讀取會話變數 | O(log N) 索引掃描/全表掃描 |
| 事務回滾行為 | 回滾後值不變(下次插入仍遞增) | 回滾後 MAX 不變,但下次插入可能復用 ID(取決於引擎) |
| 分佈式適用 | 配合 LAST_INSERT_ID(expr) 可傳遞外部 ID | 無法保證全局唯一 |
CREATE PROCEDURE create_order(OUT new_id BIGINT)
BEGIN
INSERT INTO orders ...;
SET new_id = LAST_INSERT_ID();
END;
BEFORE INSERT 觸發器中 LAST_INSERT_ID() 為 0(尚未生成),AFTER INSERT 中可獲取正確值。COMMIT/ROLLBACK,下一個借用者繼承舊 last_insert_id。務必在連線重置回調執行 ROLLBACK 或 SELECT LAST_INSERT_ID() 清理。LAST_INSERT_ID() → 插從表,全程同一連線、同一事務。LAST_INSERT_ID() 返回該值(需表無自增或手動 LAST_INSERT_ID(id))。LAST_INSERT_ID() 增強:支援作為窗口函數參數、更精確的複制語義。UPDATE seq SET val=LAST_INSERT_ID(val+1) 獲取分段 ID,減少鎖競爭。LAST_INSERT_ID() 看似簡單,實則承載著「連線隔離、複制一致性、應用層獲取 ID」三重關鍵語義。正確使用它,是構建高併發寫入服務的基石之一。