ロックとは
ロックとは、データにアクセスしている間はほかのユーザーが読み書きできないようにするために、アクセスを制御して、一時的に待ってもらう操作のこと。
例えば、座席の予約システムのようなものを作る場合、予約操作をしている最中にほかのユーザーがそれを書き換えると、席がダブルブッキングしてしまう。
そのようなものを作る際は、操作中は別のユーザーが変更しないようにロックをかける。
ロックの範囲と種類
ロックの範囲
ロックの範囲はテーブル全体かレコードかを選択できる。テーブル全体にロックをかけたときは、そのテーブル全体w他のユーザーが一時的に利用できなくなる。
レコードにロックをかけたときはそのレコード以外は利用できる。
共有ロックと排他ロック
誰かが操作をしている最中に、ほかのユーザーに「何をさせたくないのか」という観点から、2 つのロックの方式がある。
共有ロック (読み取りロック)
「自分が読み込んでいる最中だから、書き込まないで欲しい」という考え方。他人の読み取りをロックするのではなく、自分が読み取っている最中であることを宣言して、相手の操作を制限するロックとなる。
共有ロックをかけると、ロックをかけたユーザー以外は読み込むことはできても書き換えることはできなくなる。
排他ロック (書き込みロック)
「自分が書き込んでいる最中だから、書き込まないのはもちろん、読み込まないで欲しい」というロック。
他人の書き込みをロックするという意味ではなく、自分が書き込み中なので、ほかのユーザーは書き込みも読み取りもしないで欲しいと制限をかけるロックとなる。
排他ロックをかけると、ロックをかけたユーザー以外はすべての操作ができなくなる。
共有ロックがかかっているときに、他のユーザーがさらに共有ロックをかけることは可能だが、排他ロックをかけることはできない。
また、排他ロックがかかっている場合は、そこからさらに共有ロックや排他ロックをかけることはできない。
ロック待ちになる状況
他のコネクションが行を更新するときに、行ロックがかかる。
実際にやってみる。
まずは Connection A でトランザクションを開始し、UPDATE を行う。
# Connection A
mysql> START TRANSACTION;
Query OK, 0 rows affected (0.00 sec)
mysql> UPDATE zaiko SET stock = 800 WHERE idzai = 8010;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0
その際に、Connection B で同様に UPDATE を行おうとすると、以下のまま停止する形になる。
# Connection B
mysql> START TRANSACTION;
Query OK, 0 rows affected (0.00 sec)
mysql> UPDATE zaiko SET stock = 900 WHERE idzai = 8010;
Connection A で COMMIT すると Connection B の UPDATE の待ち状態が解除され、クエリを進めることができる。
# Connection A
mysql> COMMIT;
Query OK, 0 rows affected (0.03 sec)
# Connection B
mysql> UPDATE zaiko SET stock = 900 WHERE idzai = 8010;
Query OK, 1 row affected (27.33 sec)
Rows matched: 1 Changed: 1 Warnings: 0
mysql> COMMIT;
Query OK, 0 rows affected (0.00 sec)
デッドロック
ロックがかかっている場合、ほかのユーザーはそのロックが解除されるまで待つが、その際に別のテーブルにロックをかけながら待つ可能性がある。
その場合、まれに互いにロックが解除されるまで待ち続けて、八方塞がりになることがあり、これをデッドロックと呼ぶ。
MySQL をはじめとする RDBMS にはデッドロックを検知する仕組みがあり、デッドロックが発生したときは両方を失敗させることでデッドロックを解決する。
とはいえ、データベースを操作するときはデッドロックがかからないように工夫することが必要であり、例えばロックの範囲を狭めることを検討する。