WordPress:MySQLの遅いクエリーを検出、最適化する

WordPress、インストール始めのころは速かったが、最近なんだか遅くなって感じることは多いと思います。過剰なプラグインのインストール、重い外部スクリプトの大量読み込み等など、原因は様々ありますが、データベース(MySQL)のクエリーがその一要因となっていることがあります。MySQLの遅いクエリーを検出、最適化することで、Wordpressのパフォーマンス向上を狙います。(2016/09/11作成)

hpgruesen / Pixabay

0.環境

Wordpress Ver.4.6.1

$ mysql --version
mysql  Ver 14.14 Distrib 5.7.15, for Linux (x86_64) using  EditLine wrapper

1.MySQLの SlowQueryLogを「mysqldumpslow」で分析する

$ sudo mysqldumpslow -s t mysql_slow.log > /tmp/analytics.log
代表的なコマンドオプション

-s al → 平均ロックタイムの長い順
-s ar → 平均行数の多い順
-s at → 平均実行時間の長い順
-s c → 総クエリ数の多い順
-s l → 総ロックタイムの長い順
-s r → 総行数の多い順
-s t → 総実行時間の長い順
分析結果(mysqldumpslow):

Count: 312  Time=11.18s (3488s)  Lock=4.45s (1387s)  Rows=497.0 (155075), wordpress[wordpress]@localhost
  SELECT option_name, option_value FROM wp_options WHERE autoload = 'S'

2.スローと検出された「Query」を「EXPLAIN」確認する

mysql> EXPLAIN SELECT option_name, option_value FROM wp_options WHERE autoload = 'S' \G;
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: wp_options
   partitions: NULL
         type: ALL
possible_keys: NULL
          key: NULL
      key_len: NULL
          ref: NULL
         rows: 623
     filtered: 10.00
        Extra: Using where
1 row in set, 1 warning (0.00 sec)

ERROR: 
No query specified
typeの解説 (MySQLリファレンスより)

system:
1 レコードのみで構成されるテーブル(= システムテーブル)。これは、const結合型の特殊なケースである。
const:
テーブルに、一致するレコードが最大で 1 つあり、クエリの開始時に読み取られる。レコードが 1 つしかないため、このレコードのカラムの値はオプティマイザによって定数と見なされる。constテーブルは、1 回しか読み取られないため、非常に高速である。
eq_ref:
前のテーブルのレコードの組み合わせのそれぞれに対して、このテーブルから 1 レコードずつ読み取られる。これは、systemとconst型以外で最適な結合型である。 結合でインデックスのすべての部分が使用され、このインデックスが UNIQUEまたは PRIMARY KEYである場合に使用される。
ref:
前のテーブルのレコードの組み合わせのそれぞれに対して、インデックス値にマッチするすべてのレコードがこのテーブルから読み取られる。refは、インデックスの左端の先頭部分のみが結合で使用される場合、またはインデックスが UNIQUEや PRIMARY KEYではない場合(すなわち、この結合において、インデックス値から1つのレコードをSELECTできない場合)に使用される。
この結合型は、使用されるインデックスと一致するレコードが数レコードしかない場合に適している。
ref_or_null:
refと同様だが、NULLを使用したレコードの補足検索も追加で実行される。 この結合型の最適化は主としてサブクエリを解決する場合に使用される。 下記の例では、MySQLはref_tableで ref_or_nullが使用される。
unique_subquery:
この型は、下記のフォームでINサブクエリの代わりに、refを使用します。
index_subquery:
この結合型はunique_subqueryに似ています。INサブクエリの代わりに使用されますが、下記のサブクエリのフォームでユニークではないインデックスで使用できます。
range:
インデックスを使用して、一定の範囲にあるレコードのみが取り出される。keyカラムに使用されるインデックスが示される。key_len_には使用される最長のインデックス部分が記載される。 この型ではrefカラムがNULLになる。
range は、インデックスを張っているカラムが =、<>、>、>=、<、<=、IS NULL、<=>、BETWEEN、およびIN を使用して定数と比較される場合に使用される。
index:
これは、インデックスツリーのみがスキャンされる点を除いて ALLと同じである。一般にインデックスファイルはデータファイルより小さいため、通常は ALLより高速である。
MySQL は、クエリで 1 インデックスの構成部分であるカラムのみが使用される場合にのみ使用できます。
all:
前のテーブルのレコードの組み合わせのそれぞれに対して、フルテーブルスキャンが実行される。一般に、テーブルが constの指定がない第 1 テーブルの場合には適さず、その他の場合はすべて非常に不適である。
通常は、さらにインデックスを追加することで ALLを回避し、定数値または以前のテーブルのカラム値を基準にレコードを取り出すようにすることができる。

【ポイント】
「all」が選択されている部分が一番コストが高い、インデクスのチューニングが必要である。具体的には、インデクスを張ったほうが良いと思われる部分にインデクスを貼る。

3.インデックス設定

※テーブル「 wp_options 」の「autoload」にINDEXを貼る。

mysql> CREATE INDEX idx01_wp_options ON wp_options(autoload);
Query OK, 623 rows affected (0.41 sec)
Records: 623  Duplicates: 0  Warnings: 0

4.「EXPLAIN」結果(インデックス設定後)

mysql> EXPLAIN SELECT option_name, option_value FROM wp_options WHERE autoload = 'S' \G;
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: wp_options
   partitions: NULL
         type: ref
possible_keys: idx01_wp_options
          key: idx01_wp_options
      key_len: 82
          ref: const
         rows: 1
     filtered: 100.00
        Extra: NULL
1 row in set, 1 warning (0.03 sec)

ERROR: 
No query specified

付録1.MySQLで INDEXが貼られているかを確認する

mysql> SHOW INDEX FROM wp_options \G;
*************************** 1. row ***************************
        Table: wp_options
   Non_unique: 0
     Key_name: PRIMARY
 Seq_in_index: 1
  Column_name: option_id
    Collation: A
  Cardinality: 294
     Sub_part: NULL
       Packed: NULL
         Null: 
   Index_type: BTREE
      Comment: 
Index_comment: 
*************************** 2. row ***************************
        Table: wp_options
   Non_unique: 0
     Key_name: option_name
 Seq_in_index: 1
  Column_name: option_name
    Collation: A
  Cardinality: 294
     Sub_part: NULL
       Packed: NULL
         Null: YES
   Index_type: BTREE
      Comment: 
Index_comment: 
*************************** 3. row ***************************
        Table: wp_options
   Non_unique: 1
     Key_name: idx01_wp_options
 Seq_in_index: 1
  Column_name: autoload
    Collation: A
  Cardinality: 2
     Sub_part: NULL
       Packed: NULL
         Null: YES
   Index_type: BTREE
      Comment: 
Index_comment: 
3 rows in set (0.00 sec)

ERROR: 
No query specified

以上

About yoshimasa

埼玉県さいたま市在住、2男3女のパパです。Linux系の技術情報を中心にまとめています。1978年2月生まれ。