2011年 5月 24日
今回は、MySQL-5.0.45のInnoDBで連番を管理するテーブルのパフォーマンス測定をしていたのですが、その際に少し変わったデッドロック問題に遭遇しましたので、そのあたりをネタとして書いてみたいと思います。
まずは、今回使用したデータベースのスキーマは下記のようなものです。
CREATE TABLE num (
id bigint unsigned NOT NULL default '0'
) Engine=InnoDB;
AUTO_INCREMENTは使用していません。
そこに1レコードだけ登録します。
INSERT INTO num (id) values (1);
そして実際連番を取得する際には、
UPDATE num SET id = LAST_INSERT_ID(id+1);
といったクエリを発行しインクリメントしていき、最新のidはSELECTするのではなくUPDATE時のMySQL応答パケットに含まれるmysql_insertidを参照します。
上記のような内容をベンチマークテストのために同時接続を増やしながらテストしていると、350を超えたあたりで
ERROR 1213 (40001): Deadlock found when trying to get lock
というエラーが発生するという事態に遭遇しました(350という具体的な数値はハードウェアの性能などで変動すると思います)。ちなみに試験環境のOSやMySQLのバージョンは簡単ですが下記のような感じです。
- MySQL-5.0.45
- Linux-2.6.22
まずはWebで検索してみる
筆者の英語力不足という説もありますが、なかなか「コレ!」というものが見つけられませんでした。
パラメータやSQL文等を色々試してみる
- 1カラムしかないのが逆に良くないのかと思い、主キーカラムを追加しWhere句で指定 → 変化なし
- autocommitなのが良くないのかと思い、start transaction(またはbegin)とcommitを発行する → 変化なし
- トランザクション分離レベルをserializableに変えてみる → 変化なし
- innodb_table_locksパラメータを0にしてみる → 変化なし
他にもいくつか試しました。また、それらを組み合わせてみたりもしましたが解決しません。なので、ソースを読んでみることにします。
mixi Engineers’ Blog » MySQLのInnoDBでのデッドロック
結論からいいますと、サーバーのハードのスペックにもよりますが、今回の検証用環境では多少の増加は出来ましたが、さらに負荷を上昇させていくとロック待ち時間が増加し、その結果innodb_lock_wait_timeoutに引っかかり始めました。じゃあinnodb_lock_wait_timeoutも増加させればいいじゃないか、と考えもしましたが、ただ実際にはそれによりパフォーマンスが向上するわけではなく、「90秒や120秒待てばデッドロック扱いもタイムアウト扱いもせずSQLが完了します」というのは、MySQL的にはエラーでなくとも実際のシステム的にはエラーも同然ですのであまり意味がないのではないかと判断しました。というわけで、現在mysqldを動作させる一般的なハードウェア上では200という数字はそれなりに適切な値なのではないかと思いました。
ちなみにMyISAMではこのような問題は起こらず、パフォーマンスも良好な結果でした。システムの要件にもよりますが、MyISAMに変更しても問題ない場合はそちらも検討されるのも良いかと思います。
デッドロックになるはずのないSQL文でデッドロックだとエラーメッセージが返却された場合にこのような例もあったなということを思い出していただけると幸いです。
————-
MySQL5.1や5.5を使えばこの問題は起きないという噂です
9ヶ月前 |
固定リンク | 2011年 5月 24日 |
