コンテンツにスキップ

100万件程度のデータを用意。
0.1 秒以下を指標とする。

スロークエリの抽出

スロークエリ設定確認

https://gihyo.jp/dev/serial/01/mysql-road-construction-news/0007
https://ptune.jp/tech/how-to-check-mysql-slow-query/

show variables like 'slow_query%'

出力例

Variable_name Value
slow_query_log ON
slow_query_log_file /var/lib/mysql/dcde79b8befd-slow.log
show variables like 'long%';

+-----------------+-----------+ | Variable_name | Value | +-----------------+-----------+ | long_query_time | 10.000000 | +-----------------+-----------+

※1秒

設定内容は、「my.cnf」でも確認可能

スロークエリ検出有効設定を ON

※MySQL を再起動すると、再び OFF に戻る

set global slow_query_log=1;

スロークエリ基準時間設定

以下、「1秒以下以上のクエリを検出する」という設定

set global long_query_time=1;

set global long_query_time=0.4;

※「show variables like 'long%'」で見ても変わっていない場合、一度コンソールを開きなおす

スロークエリの出力ログを設定

set global slow_query_log_file ='/usr/local/var/mysql/slow_query.log';

set global slow_query_log_file ='/var/lib/mysql/slow_query.log'

スロークエリ

select sleep(15)

ログ出力例

# cat /var/lib/mysql/dcde79b8befd-slow.log
mysqld, Version: 5.7.35 (MySQL Community Server (GPL)). started with:
Tcp port: 3306  Unix socket: /var/run/mysqld/mysqld.sock
Time                 Id Command    Argument
# Time: 2021-10-04T04:07:59.774545Z
# User@Host: root[root] @ myapp01-php_1.bigbang-shared-network [172.18.0.7]  Id:   115
# Query_time: 0.001368  Lock_time: 0.000000 Rows_sent: 0  Rows_examined: 0
use asims;
SET timestamp=1633320479;
# administrator command: Prepare;
# Time: 2021-10-04T04:07:59.822910Z
# User@Host: root[root] @ myapp01-php_1.bigbang-shared-network [172.18.0.7]  Id:   114
# Query_time: 0.002219  Lock_time: 0.000000 Rows_sent: 0  Rows_examined: 0
SET timestamp=1633320479;
# administrator command: Prepare;
# Time: 2021-10-04T04:07:59.826203Z
# User@Host: root[root] @ myapp01-php_1.bigbang-shared-network [172.18.0.7]  Id:   114
# Query_time: 0.002824  Lock_time: 0.000036 Rows_sent: 18  Rows_examined: 1116
SET timestamp=1633320479;
select `table_name`, `logical_table_name`, `column_name`, `logical_column_name`, `is_editable_required_attribute` from `table_column_definitions` where `id` in (select `table_column_definition_id` from `by_function_table_column_definitions` where `target_table_type` = '28') and `table_column_definitions`.`deleted_at` is null order by `table_name` desc, `sort_order` asc;

実行計画

explain

「explain」を付けて実行

explain
select 
 `table_name`, `logical_table_name`, `column_name`, `logical_column_name`, `is_editable_required_attribute`
 from `table_column_definitions` where `id` in (select `table_column_definition_id` from `by_function_table_column_definitions` where `target_table_type` = '28') 
and `table_column_definitions`.`deleted_at` is null order by `table_name` desc, `sort_order` asc;

出力例

id select_type table partitions type possible_keys key key_len ref rows filtered Extra
1 SIMPLE table_column_definitions « NULL » ALL PRIMARY « NULL » « NULL » « NULL » 247 10 Using where; Using filesort
1 SIMPLE by_function_table_column_definitions « NULL » ref by_function_table_column_definitions_index_1 by_function_table_column_definitions_index_1 8 asims.table_column_definitions.id 3 10 Using index condition; Using where; FirstMatch(table_column_definitions)

key - 効いている index ref - 対象のカラム。(cons が最速) row - updateの場合、その行数となる?

type

const・・・PRIMARY KEYまたはUNIQUEインデックスのルックアップによるアクセス。最速。
eq_ref・・・JOINにおいてPRIARY KEYまたはUNIQUE KEYが利用される時のアクセスタイプ。constと似ているがJOINで用いられるところが違う。
ref・・・ユニーク(PRIMARY or UNIQUE)でないインデックスを使って等価検索(WHERE key = value)を行った時に使われるアクセスタイプ。
range・・・インデックスを用いた範囲検索。
index・・・フルインデックススキャン。インデックス全体をスキャンする必要があるのでとても遅い。
ALL・・・フルテーブルスキャン。インデックスがまったく利用されていないことを示す。OLTP系の処理では改善必須。

注意点

index が効く範囲と、update の範囲が異なる事がある。 (デッドロックの範囲に注意する)

テーブルロックについて

https://yoku0825.blogspot.com/2012/07/deadlock-found-when-trying-to-get-lock.html
https://qiita.com/dorarep/items/d7d767e1bceb2d5649a7

update 分を発行した時、業ロックが発生する。

indexで絞れる範囲まで絞ってから、indexで絞りきれなかったrows全てにロックをかけてる。

colomn1 インデックスなし colomn2 インデックスあり

update  TABLE1
   set  column1 = 1
 where  column1 = 200
   and  column2 = 300

この場合、column2 にしかインデックスが効いていない。そのため、column1 の全範囲がロック対象となる。 ※インデックスもロック制御となる(?)

update  TABLE1
   set  column1 = 1
 where  column1 = 200

この場合、全行がロック対象となる。

※デッドロックが頻発する場合、テーブルインデックスを見直す(デッドロックが発生した SQL はログに出力され、メールで飛ぶ。)

レンジロック(insert によるロック)

insert 文を発行する時、id の空き番を予約する。
この時、同様の insert 文が複数並列で動作した時、デッドロックが発生する事がある。

同様の insert 文が同時に3つ発行された場合、2つ目までは処理できるが、3つ目はエラー(デッドロック)となる。
(MySQL の仕様。7では改善されてるっぽい? MySQL 5 は悲痛な叫びがいろいろ聞こえるが、7はほとんど見られない。ユーザ数の違いによるもの?)

index を細かく張ることで回避できる。 (同時に、「同一荷主の同一販売チャンネルの出荷指示No」と、同時に実行される可能性が極め低い単位まで、index の粒度を上げる事で回避できる)

デッドロック発生のエラーメッセージ例

critical 9999900001 A system error has occurred. :[message]SQLSTATE[40001]: Serialization failure: 1213 Deadlock found when trying to get lock; try restarting transaction (SQL:          update  shipment_record_details

[trace]#0 /application/vendor/laravel/framework/src/Illuminate/Database/Connection.php(631): Illuminate\Database\Connection->runQueryCallback('  ...', Array, Object(Closure))
#1 /application/vendor/laravel/framework/src/Illuminate/Database/Connection.php(496): Illuminate\Database\Connection->run('               ...', Array, Object(Closure))
#2 /application/vendor/laravel/framework/src/Illuminate/Database/Connection.php(429): Illuminate\Database\Connection->affectingStatement('    ...', Array)
#3 /application/vendor/laravel/framework/src/Illuminate/Database/DatabaseManager.php(367): Illuminate\Database\Connection->update(' ...', Array)
#4 /application/vendor/laravel/framework/src/Illuminate/Support/Facades/Facade.php(261): Illuminate\Database\DatabaseManager->__call('update', Array)

ロック待ちのタイムアウト時間の確認/設定

確認

デフォルトは 50秒

mysql> SHOW VARIABLES LIKE 'innodb_lock_wait_timeout';

設定

mysql> SET innodb_lock_wait_timeout=5;

MySQLのインデックス作成方法 - 効いてないと思ったらexplainで確認する

https://style.potepan.com/articles/17616.html

インデックスの種類

MySQLではPRIMARY、UNIQUE、INDEX、FULLTEXT、SPATIALの5種類のインデックスが設定可能です。

SQL内の「ADD INDEX」の部分を「ADD UNIQUE」などに差し替えることでインデックス種類の指定ができます。

PRIMARYは、プライマリキーのインデックスです。プライマリキー指定をすると自動的に設定されます。

UNIQUEは、ユニークキー、外部キー用のインデックスです。

INDEXは一般のインデックスです。基本は「ADD INDEX」でインデックス指定すると良いでしょう。

FULLTEXTは、全文検索用のインデックスです。不定長のtextカラムに設定すると、全文検索の速度が大幅に改善されますが、インデックスに使用する領域を多く使うという欠点もあります。

SPATIALは、空間データ用のインデックスです。空間データとは、都市や山などの立体情報や町や市などの範囲情報で、これらを格納するのに向いている空間データ型に対するのが空間インデックスです。MySQLでは、空間データのソリューション開発に関わる 250 以上の企業、機関、および大学の国際的なコンソーシアムOpen Geospatial Consortium (OGC) の仕様に沿って、空間データが実装されています。

インデックスの再構築方法

照合順序の修正をおこなった場合など、インデックスを再設定するには、単純にインデックスをドロップしてから再作成します。