概要
今更ですが、MySQLで採番機能(シーケンス)が必要になったので、実装方法について自分用に整理してみました。
環境
MySQL 8.0.18
実装方法
方法1.LAST_INSERT_ID(expr) を使う
公式ドキュメントで紹介されている方法です。
MySQL :: MySQL 8.0 Reference Manual :: 12.15 Information Functions
1. 採番用のテーブルを用意し、ID=0のレコードをINSERTしておく
CREATE TABLE sequence (id INT NOT NULL) ENGINE=InnoDB;
INSERT INTO sequence VALUES (0);
2. LAST_INSERT_ID
を使用し、連番を取得する
UPDATE sequence SET id=LAST_INSERT_ID(id+1);
SELECT LAST_INSERT_ID();
注意点として、
- rollbackすると、同一のIDが再度発行される
- UPDATEで排他ロックがかかる(分離レベル:
REPEATABLE-READ
で検証)
が挙げられます。
方法2.AUTO_INCREMENTを使う
「MySQL で採番テーブル」で紹介されている方法です。
1.採番用のテーブルを用意する
CREATE TABLE seq_ai (
id INT NOT NULL AUTO_INCREMENT PRIMARY KEY
) engine = InnoDB;
2.新規レコードをINSERTし、LAST_INSERT_ID
で連番を取得する
INSERT INTO seq_ai VALUES (NULL);
SELECT LAST_INSERT_ID();
3.採番テーブルにたまるレコードを削除する
DELETE FROM seq_ai WHERE id < :id; -- 2で取得した最新のレコードを残して削除
方法1の注意点1(rollback時の挙動)については、こちらの方法ではrollbackしても同じIDは発行されません(AUTO_INCREMENTのカウンタはrollbackしても戻らない)。
一方で、こちらの方法だと3のDELETE時に排他ロックが発生します(分離レベル:REPEATABLE-READ
で検証)。
方法3.方法1+ストレージエンジンをMyISAMにする
方法1の注意点はどちらもトランザクションに起因する問題だったので、採番用テーブルのみストレージエンジンをMyISAMにします。
CREATE TABLE sequence (id INT NOT NULL) ENGINE=MyISAM;
INSERT INTO sequence VALUES (0);
シーケンスの取得方法は方法1と同じです。
UPDATE sequence SET id=LAST_INSERT_ID(id+1);
SELECT LAST_INSERT_ID();
ストレージエンジンをMyISAMにすることで、
- rollbackしても同じIDが発行されない
- 排他ロックがかからない
という状態を実現できます。