Blog

ブログ

【MySQL】ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction が出て困った話

先日MySQLで下記エラーに出会いました。

ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction

対応に手間取ったので、振り返ってみようと思います。

このエラーは何を表している?

データへ書き込むために待機していたトランザクションが、待機時間上限を超えた時に出るエラーのようです。
待機させられている理由は、前のトランザクションによって更新対象がロックされている為とのこと。

ちなみに待機時間の上限は innodb_lock_wait_timeout に定められており、デフォルトは50秒です。

どうやら行ロックを取得したトランザクションがcommitしないまま残ってしまったようで、
該当の行を更新しようとすると、50秒後に上記エラーが出るようになってしまいました。

wait_timeout(非対話型の接続)またはinteractive_timeout(対話型の接続)の時間を経過すると自動で切断されるようですが、
デフォルトは8時間と長いので急ぎ解消するためには、つかみっぱなしのトランザクションをkillする必要があります。

kill対象を判断するのに時間がかかってしまったので、その時調べたことをまとめておこうと思います。

killするスレッドを判断するために情報を集める

検証用にusersテーブルを作成します。MySqlのバージョンは 8.2.0でやっています。

CREATE TABLE users (
 id INT NOT NULL AUTO_INCREMENT ,
 name VARCHAR(255) NOT NULL ,
 email VARCHAR(255) NOT NULL ,
 PRIMARY KEY (id)
 );

INSERT INTO users(name, email)
VALUES ('TARO', 'taro@example.com'),
('JIRO', 'jiro@example.com'),
('SABURO', 'saburo@example.com');

mysql> select * from users;
 +----+--------+--------------------+
 | id | name | email |
 +----+--------+--------------------+
 | 1 | TARO | taro@example.com |
 | 2 | JIRO | jiro@example.com |
 | 3 | SABURO | saburo@example.com |
 +----+--------+--------------------+
 3 rows in set (0.00 sec)

この時点では、スレッドは1つです。

mysql>  show processlist;
 +----+------+-----------+------------+---------+------+-------+------------------+
 | Id | User | Host      | db         | Command | Time | State | Info             |
 +----+------+-----------+------------+---------+------+-------+------------------+
 | 13 | root | localhost | example_db | Query   |    0 | init  | show processlist |
 +----+------+-----------+------------+---------+------+-------+------------------+
 1 row in set, 1 warning (0.00 sec)

テーブルを作った上記のセッションを1つ目として、2つ目のセッションを新しく開始し、レコードの削除をしてcommitしないままクライアントを強制的に閉じます。
次に3つ目のセッションを新たに開始して1レコードのアップデートを行います。

-- 2つ目のセッションを新しく開始し、オートコミットをOFF
mysql> set autocommit = 0;
Query OK, 0 rows affected (0.00 sec)

-- レコードの削除
mysql> DELETE FROM users;
Query OK, 3 rows affected (0.00 sec)

-- commitしないまま切断

 

-- 3つ目のセッションを新しく開始し、オートコミットをOFF
mysql> set autocommit = 0;
Query OK, 0 rows affected (0.00 sec)

-- アップデートすると、50秒後に今回のエラーが発生する
 mysql> UPDATE users set name = 'ichiro' WHERE id = 1;
 ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
 mysql> rollback;

再現できました。

ロックしているトランザクションを終了させないと、usersテーブルを更新出来ない状況になりました。
このまま8時間(デフォルト)経過すれば終了するのですが、8時間は長いので、どうにかkillしたいです。

show processlist

この時点でスレッドは3つ表示されます。

mysql> show processlist;
+----+------+-----------+------------+---------+------+-------+------------------+
| Id | User | Host      | db         | Command | Time | State | Info             |
+----+------+-----------+------------+---------+------+-------+------------------+
| 18 | root | localhost | example_db | Query   |    0 | init  | show processlist |
| 19 | root | localhost | example_db | Sleep   |  126 |       | NULL             |
| 20 | root | localhost | example_db | Sleep   |   53 |       | NULL             |
+----+------+-----------+------------+---------+------+-------+------------------+
3 rows in set, 1 warning (0.00 sec)

ちなみに、重いSQL等で処理が終わっていない場合は「Info」の部分にSQLステートメントが表示されますが、
今回はcommitまたはrallbackがされていないだけなので、Infoはnullになります。


SHOW ENGINE INNODB STATUS

killするならもう少し情報が欲しい、ということで、もう少し集めます。SHOW ENGINE INNODB STATUS で出てくる情報は多岐にわたりますが、トランザクションの情報も含まれています。

mysql> SHOW ENGINE INNODB STATUS;
-- (省略) --

------------
TRANSACTIONS
------------
Trx id counter 1871
Purge done for trx's n:o < 1870 undo n:o < 0 state: running but idle
History list length 0
LIST OF TRANSACTIONS FOR EACH SESSION:
---TRANSACTION 421357384966952, not started
0 lock struct(s), heap size 1128, 0 row lock(s)
---TRANSACTION 421357384965336, not started
0 lock struct(s), heap size 1128, 0 row lock(s)
---TRANSACTION 421357384964528, not started
0 lock struct(s), heap size 1128, 0 row lock(s)
---TRANSACTION 421357384963720, not started
0 lock struct(s), heap size 1128, 0 row lock(s)
---TRANSACTION 1865, ACTIVE 771 sec
2 lock struct(s), heap size 1128, 4 row lock(s), undo log entries 3
MySQL thread id 19, OS thread handle 139881921705728, query id 218 localhost root

-- (省略) --

「TRANSACTIONS」の部分を確認します。

「undo log entries 3」の部分がコミットもロールバックもしていない行数です。

「MySQL thread id 19」がSHOW PROCESSLISTのIdと一致します。Id:19が長時間(771秒)経過しているという情報が取れました。加えて「3行変更しているのにコミットされていない」という情報を取得出来ました。

select * from information_schema.INNODB_TRX

information_schema.INNODB_TRX から現在実行されているすべてのトランザクションに関する情報を取得出来ます。

mysql> select * from information_schema.INNODB_TRX order by trx_id\G
*************************** 1. row ***************************
                    trx_id: 1865
                 trx_state: RUNNING
               trx_started: 2023-11-29 10:17:20
     trx_requested_lock_id: NULL
          trx_wait_started: NULL
                trx_weight: 5
       trx_mysql_thread_id: 19
                 trx_query: NULL
       trx_operation_state: NULL
         trx_tables_in_use: 0
         trx_tables_locked: 1
          trx_lock_structs: 2
     trx_lock_memory_bytes: 1128
           trx_rows_locked: 4
         trx_rows_modified: 3
   trx_concurrency_tickets: 0
       trx_isolation_level: REPEATABLE READ
         trx_unique_checks: 1
    trx_foreign_key_checks: 1
trx_last_foreign_key_error: NULL
 trx_adaptive_hash_latched: 0
 trx_adaptive_hash_timeout: 0
          trx_is_read_only: 0
trx_autocommit_non_locking: 0
       trx_schedule_weight: NULL
*************************** 2. row ***************************
                    trx_id: 421357384966952
                 trx_state: RUNNING
               trx_started: 2023-11-29 10:30:55
     trx_requested_lock_id: NULL
          trx_wait_started: NULL
                trx_weight: 0
       trx_mysql_thread_id: 20
                 trx_query: NULL
       trx_operation_state: NULL
         trx_tables_in_use: 0
         trx_tables_locked: 0
          trx_lock_structs: 0
     trx_lock_memory_bytes: 1128
           trx_rows_locked: 0
         trx_rows_modified: 0
   trx_concurrency_tickets: 0
       trx_isolation_level: REPEATABLE READ
         trx_unique_checks: 1
    trx_foreign_key_checks: 1
trx_last_foreign_key_error: NULL
 trx_adaptive_hash_latched: 0
 trx_adaptive_hash_timeout: 0
          trx_is_read_only: 0
trx_autocommit_non_locking: 0
       trx_schedule_weight: NULL
2 rows in set (0.00 sec)

2行出力されましたが、trx_started (トランザクション開始時間)が早い方が長い間完了していないことになります。

より長時間経過している1行目を見ると、trx_rows_modified(このトランザクションで変更および挿入された行の数)が3で、ここでも3行更新しているが完了していないことが読み取れます。また、trx_mysql_thread_id(MySQL スレッド ID)はSHOW PROCESSLISTのIdと一致するので、これはshow processlistのId:19を指しています。

過去発行したクエリも確認する

先ほどから目を付けているID:19が発行したクエリを見てみます。

まずはMySQL スレッド IDとperformance_schema.threadsからTHREAD_IDを取得します。

mysql> SELECT thread_id FROM performance_schema.threads WHERE processlist_id = 19;
+-----------+
| thread_id |
+-----------+
|        56 |
+-----------+
1 row in set (0.00 sec)

そのTHREAD_IDとperformance_schema.events_statements_historyから、過去のクエリが少し分かります。killして大丈夫かの判断材料になりそうです。

ただし、performance_schema.events_statements_history は最新10件(デフォルト)しか保持していないので、それより前の情報はここからは取得出来ません。

mysql> SELECT THREAD_ID,SQL_TEXT FROM performance_schema.events_statements_history WHERE thread_id = 56 ORDER BY TIMER_START;
+-----------+----------------------------------+
| THREAD_ID | SQL_TEXT                         |
+-----------+----------------------------------+
|        56 | select @@version_comment limit 1 |
|        56 | select $$                        |
|        56 | SELECT DATABASE()                |
|        56 | NULL                             |
|        56 | show databases                   |
|        56 | show tables                      |
|        56 | NULL                             |
|        56 | select * from users              |
|        56 | set autocommit = 0               |
|        56 | DELETE FROM users                |
+-----------+----------------------------------+
10 rows in set (0.00 sec)

情報を集めたので、最後にID:19をkillしてアップデートできるようになることを確認します。

mysql> kill 19;
Query OK, 0 rows affected (0.00 sec)

mysql> show processlist;
+----+------+-----------+------------+---------+------+-------+------------------+
| Id | User | Host      | db         | Command | Time | State | Info             |
+----+------+-----------+------------+---------+------+-------+------------------+
| 18 | root | localhost | example_db | Query   |    0 | init  | show processlist |
| 20 | root | localhost | example_db | Sleep   | 1911 |       | NULL             |
+----+------+-----------+------------+---------+------+-------+------------------+
2 rows in set, 1 warning (0.00 sec)

mysql> UPDATE users set name = 'ichiro' WHERE id = 1;
Query OK, 1 row affected (0.01 sec)
Rows matched: 1  Changed: 1  Warnings: 0

mysql> commit;
Query OK, 0 rows affected (0.01 sec)

-- id1のnameがTAROからichiroに更新されている
mysql> select * from users;
+----+--------+--------------------+
| id | name   | email              |
+----+--------+--------------------+
|  1 | ichiro | taro@example.com   |
|  2 | JIRO   | jiro@example.com   |
|  3 | SABURO | saburo@example.com |
+----+--------+--------------------+
3 rows in set (0.00 sec)

usersテーブルをアップデート出来ました。

 

 

 

今回は【MySQL】ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transactionが発生しkillしないと解決しない場合の調査方法についてまとめてみました。まだまだ知らないことが多いので、今後もMySQLを学んでいきたいと思います。

Continue reading “【MySQL】ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction が出て困った話”

EC-CUBEからMysqlへのSSL接続を設定する

お世話になっております。株式会社Joolenの白井です。

いつもOSS開発部にてEC-CUBEのカスタマイズ開発とかをやっています。ときどき本体へのコントリビュ〜ションなどもやっています(もうちょっと増やしたいです)。

そんなに派手なことはやっておりませんので、ちまちました設定の話をします。

Continue reading “EC-CUBEからMysqlへのSSL接続を設定する”

人の意見を聞くことの大切さ

どうも、モリタです。
こんにちは。

意味をちゃんと調べることなく、生きていく中でなんとなく覚え、なんとなく使っている言葉は結構多いのではないでしょうか。

今日はそんなお話です。

先日、MySQLでテキストが保存されたカラムに対してLike検索することを「全文検索」と呼んでいたら、複数の人から「それは違う」と指摘されて、エンジニア生活10年間、ずっと間違えて使っていたことにショックを受けました。

私はいままで、全文検索の手段としてLike検索を使うのか、全文検索エンジンを使うのか、はたまた転置インデックスを自作するのか、と思っていたのですが、どうやらRDBMSでは転置インデックスを使った検索のことを「全文検索」と言うらしいのです。

(ここで納得してしまった人は最後まで読まないと危険です)

これまで、「全文検索」についてしっかり調べたわけでもなく、エンジニアになって会話の文脈からなんとなく意味を把握し、特に疑問を持たないまま使っていたので、「あー、そーだったのかー」とその時は納得しました。

だがしかし、「全文検索」という日本語からしてLike検索が全文検索ではない、というのがどうもしっくりこなくてモヤモヤが膨らんでいきました。

なのでちゃんと調べてみることにしました。こういうときはウィキペディアが頼りになります。

 

全文検索(ぜんぶんけんさく、英: Full text search)とは、コンピュータにおいて、複数の文書(ファイル)から特定の文字列を検索すること。「ファイル名検索」や「単一ファイル内の文字列検索」と異なり、「複数文書にまたがって、文書に含まれる全文を対象とした検索」という意味で使用される。

 

ん?ちょっとまてよ?

私の認識は間違ってないんじゃないか?

ウィキペディアをもう少し見てみます。

 

全文検索技術
grep型
・・・中略・・・
索引(インデックス)型
・・・中略・・・

 

Likeはgrep型だろうなあ。

 

・・・

 

確信しました。

私の認識は間違っていなかった!!

結局、Likeだろうがインデックス使おうが、全文に対して行う検索は全文検索なんですね!!

ちゃんと調べて良かったー。

 

調べていく中で、「全文検索=インデックス型の全文検索」として扱っている記事が沢山ありました。

言葉は、勘違いや間違いが元で変化していくものだと思うので、多くの人の認識を正とすれば良いと思いますし、日常生活におけるコミュニケーションでは、話が噛み合っていなくてもそれほど大問題になることはないでしょう。(男女関係は別として)

しかしそれがシステム構築となると話は別です。(ビジネスなら何でもそうだと思いますが)

認識の齟齬が重大なバグを生み出すことになるかもしれません。

プロジェクトの全員が同じ認識でいるかどうかを把握するのは難しいと思います。

ただ、少しでもおかしいと思ったら迷わず確認するのが大事だなーと改めて感じました。

そして伝える時は「全文検索」と言う漠然とした言い方ではなく、「likeを使った全文検索」などと具体的に言うように心がけようとも思いました。

 

若い時は人の意見はあまり聞き入れず、自分が正しいと思ったことは貫き通していましたが、年を重ねるごとに、なるべく人の意見を聞くように心がけていました。

それが災いした今回の事件でした。

これからは人の意見を聞かないようにしたいと思います!!!(嘘)

 

ECCUBE3でセッションが切れる?

eccube.CRITICAL: RuntimeException: Failed to start the session (uncaught exception) at /var/www/eccube/vendor/symfony/http-foundation/Session/Storage/NativeSessionStorage.php line 149 {“exception”:”[object] (RuntimeException(code: 0): Failed to start the session at /var/www/eccube/vendor/symfony/http-foundation/Session/Storage/NativeSessionStorage.php:149)”} []

システムエラー画面になってシステムログにはこんなエラーがでていた。

apacheエラーログは以下の通り。

PHP Warning:  session_start(): Failed to decode session object. Session has been destroyed in /var/www/eccube/vendor/symfony/http-foundation/Session/Storage/NativeSessionStorage.php on line 148

セッションオブジェクトをデコードできないってことなので、とりあえずセッションのデータを見てみることに。

結論から言うと、セッションデータがでかすぎて欠落した値がDBに保存されていたことが原因。

eccube3.0.8だったのでセッションはデフォルトでDBに保存される。(3.0.10ではファイルに変更されている模様)

解決策としては

  1. dtb_session.sess_dataの型をblobからmediumblobに変更
  2. 保存先をファイルに変更

が考えられるので今回は1で対応。

因に、my.cnfに「sql_mode=STRICT_TRANS_TABLES」を設定するとblobのままでも欠落した値が挿入されることは無く、

(恐らく値が更新されないので)上記エラーは起きなかった。

そもそも、セッションにCustomerエンティティとそこから紐づくエンティティが保存されるらしく、

それを知らずにリレーションしまくると大変なことになりそう。

(今回はカスタマイズでいろいろリレーションしていた)

紐づくエンティティーでも保存される物とされない物があり良くわからんのですが・・・

引き続き調査は続く。