トランザクション
読んでいて首をひねる個所が複数あった。
まぁ「何でselectを一つかける前にトランザクション開始したんだ」「rollbackまで一度に書いてから貼れ」っていうのは置いておこう。
現象について
なぜselect一つを実行しただけでテーブルのロックがかかったのかと考えると、どう考えても「ファントムリード対策(トランザクション内部で同じセレクトを発行したのに違う結果が返ってくる可能性があるのでそれを予防する)」が発動していると考えられる。
DBのトランザクションってそこまで考慮してくれたっけ?と思って「トランザクションレベル」で検索すると、PostgreSQLのマニュアルがヒットした。
「リピータブルリード 」か「シリアライザブル」を指定してある場合、ファントムリードが起こらないとされているのでロックはかかるようだ。(バージョンによって若干表記が異なる。)
デフォルトがリピータブルリードらしいので、selectだけでもテーブルロックは発生するようだ。
文中で「明示的ロック」についてのPostgreSQLのマニュアルが引かれているが、それはトランザクションが行ったテーブルロックなので、トランザクションの問題としてはトランザクション分離レベルについて言及するのが正しい。
MySQLでもロックはかかるようなのだが、新しいマニュアルは英語だけだったのでまじめに読んでいない。だが「レコード単位のロックをする」と書かれているようにも見える。
MySQL :: MySQL 4.1 リファレンスマニュアル :: 7.5.9.1 InnoDB と SET ... TRANSACTION ISOLATION LEVEL ...
MySQL :: MySQL 8.0 Reference Manual :: 17.5.1.34 Replication and Transactions
対策について
普通は「selectを一回発行するだけならトランザクションはいらない」「beginからrollbackまでまとめて書いて実行する」のどちらかになると思う。
あるいは「トランザクションレベルを下げる」である。最低レベルの「リードアンコミッティド」で機能的には十分だろうし、それなら多分ロックされない。
その語の記述について
MySQL では
MySQL :: MySQL 5.6 リファレンスマニュアル :: 13.3.5.1 テーブルロックとトランザクションの通信
という項を引いているが、マニュアルを見るところが違う。この件で参照するのはここだ。
MySQL :: MySQL 5.6 リファレンスマニュアル :: 13.3.1 START TRANSACTION、COMMIT、および ROLLBACK 構文
table lockは明示的にテーブルをロックする命令で、rollbackはトランザクションを巻き戻す命令。系統が違う。
13.3.5.1冒頭の「LOCK TABLES
はトランザクションセーフではないため、テーブルをロックしようとする前に、アクティブなトランザクションをすべて暗黙的にコミットします。」という冒頭の文章を読んで気が付いてほしいところである。
気が付いていないという事はトランザクション中にテーブルロックを明示的に使ってしまって、コミットしてはいけないデータをコミットしてしまうという事故を将来起こす可能性があるので危険である。
逆に「セッションが (たとえば、START TRANSACTION
で) トランザクションを開始した場合は、暗黙的な UNLOCK TABLES
が実行され、既存のロックが解放されます」という記述もあった。
引用されている個所は「トランザクションによって発行されたテーブルロックはロールバックで解除されるが、トランザクション中でユーザーが明示的に行ったテーブルロックはトランザクションの対象にならないので解除されない」と書かれているだけだろう。