mysql_lock - taka512/memo GitHub Wiki
mysqlロック
ロックの種類
楽観的ロック
確実性に欠けるが事実上は問題が出ないで”あろう”というレベルのロック制御
update時にエラーとなるロック
ex ポイントシステム
悲観的ロック
更新対象のデータを読み出してから更新を終えるまでの間、他のユーザーがそのデータに触れないようロック
select時にかけるロック
ex ショッピングカートシステム
ロックをかける例(MYSQL)
楽観的ロック
テーブル構造
CREATE TABLE `point_log` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`user_id` int(11) NOT NULL,
`version` int(11) NOT NULL,
`value` int(11) NOT NULL,
`created` datetime NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `user_version` (`user_id`,`version`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
初期データ
insert into point_log(user_id,version,value,created) values(1,1,1,now());
更新のエラーの例
クライアントA
mysql> START TRANSACTION;
mysql> insert into point_log(user_id,version,value,created) values(1,2,1,now());
mysql> commit
クライアントB
mysql> insert into point_log(user_id,version,value,created) values(1,2,1,now());
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
悲観的ロック
テーブル構造
CREATE TABLE `mission_counter` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`mission_id` int(11) NOT NULL,
`count` int(11) NOT NULL,
`created` datetime NOT NULL,
`updated` datetime NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `UNIQ_501E0035BE6CAE90` (`mission_id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8
初期データ
insert into mission_counter(mission_id,count,created,updated) values(1,2,now(),now());
insert into mission_counter(mission_id,count,created,updated) values(2,2,now(),now());
READロック
クライアントA
mysql> START TRANSACTION;
mysql> LOCK TABLES mission_counter READ;
Query OK, 0 rows affected (0.00 sec)
mysql> UNLOCK TABLES;
mysql> COMMIT;
クライアントB
mysql> select * from mission_counter;
+----+------------+---------------------+---------------------+-------+
| id | mission_id | created | updated | count |
+----+------------+---------------------+---------------------+-------+
| 1 | 1 | 2013-02-03 09:23:41 | 2013-02-03 09:23:41 | 2 |
| 2 | 2 | 2013-02-03 09:24:15 | 2013-02-03 09:24:15 | 2 |
+----+------------+---------------------+---------------------+-------+
mysql> insert into mission_counter(mission_id,count,created,updated) values(3,2,now(),now());
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
writeロック
クライアントA
mysql> START TRANSACTION;
mysql> LOCK TABLES mission_counter WRITE;
mysql> UNLOCK TABLES;
mysql> COMMIT;
クライアントB
mysql> select * from mission_counter;
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
mysql> insert into mission_counter(mission_id,count,created,updated) values(3,2,now(),now());
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
select LOCK IN SHARE MODE
共有モードで読み取りを行うというのは、まず最新の有効なデータを読み取り、そして読んだ行に共有モードを設定するという意味です。共有モード ロックは、読み取った行が別の人によって更新されたり、削除されたりする事を防ぎます。また、もし最新データが別のクライアント接続のコミットされていないトランザクションに属していたら、そのトランザクションがコミットされるまで待ちます。先行クエリが親 'Jones' を返すのを確認した後、child テーブルに子レコードを安全に追加し、トランザクションをコミットする事ができます。
クライアントA
mysql> START TRANSACTION;
mysql> SELECT * FROM mission_counter WHERE mission_id = 1 LOCK IN SHARE MODE;
+----+------------+---------------------+---------------------+-------+
| id | mission_id | created | updated | count |
+----+------------+---------------------+---------------------+-------+
| 1 | 1 | 2013-02-03 09:23:41 | 2013-02-03 09:23:41 | 3 |
+----+------------+---------------------+---------------------+-------+
mysql> COMMIT;
クライアントB
mysql> SELECT * FROM mission_counter WHERE mission_id = 1 LOCK IN SHARE MODE;
+----+------------+---------------------+---------------------+-------+
| id | mission_id | created | updated | count |
+----+------------+---------------------+---------------------+-------+
| 1 | 1 | 2013-02-03 09:23:41 | 2013-02-03 09:23:41 | 3 |
+----+------------+---------------------+---------------------+-------+
1 row in set (0.00 sec)
mysql> update mission_counter set count = 4 where id = 1;
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
select FOR UPDATE;
クライアントA
mysql> START TRANSACTION;
Query OK, 0 rows affected (0.00 sec)
mysql> SELECT * FROM mission_counter WHERE mission_id = 1 FOR UPDATE;
+----+------------+---------------------+---------------------+-------+
| id | mission_id | created | updated | count |
+----+------------+---------------------+---------------------+-------+
| 1 | 1 | 2013-02-03 09:23:41 | 2013-02-03 09:23:41 | 2 |
+----+------------+---------------------+---------------------+-------+
mysql> COMMIT;
クライアントB
mysql> SELECT * FROM mission_counter WHERE mission_id = 1 FOR UPDATE;
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
mysql> update mission_counter set count = 2 where id = 1;
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction