web-dev-qa-db-ja.com

wp_postmetaに対する単純なSQLクエリが非常に遅い

私はワードプレスのインストールをしており、私はすべての投稿にこのクエリが必要です。

select post_id, meta_key from wp_postmeta where meta_key = 'mykey' and meta_value = 'somevalue'

そのテーブルに3Mの行があり、クエリが完了するまでに約6秒かかります。もっと早くなるべきだと思います。テーブルのインデックスを表示すると、それが返されます。

SHOW INDEX FROM wp_postmeta

Table   Non_unique  Key_name    Seq_in_index    Column_name Collation   Cardinality Sub_part    Packed  Null    Index_type  Comment Index_comment   
wp_postmeta 0   PRIMARY 1   meta_id A   3437260 NULL    NULL        BTREE       
wp_postmeta 1   post_id 1   post_id A   1718630 NULL    NULL        BTREE       
wp_postmeta 1   meta_key    1   meta_key    A   29  191 NULL    YES BTREE   

説明すると、これが返されます。

explain select post_id, meta_key from wp_postmeta where meta_key = 'mykey' and meta_value = 'somevalue'


id  select_type table   type    possible_keys   key key_len ref rows    Extra   
1   SIMPLE  wp_postmeta ref meta_key    meta_key    767 const   597392  Using where

私はmysqlがあまり得意ではないので、それをチェックまたは解決する方法がわかりません。あなたは私に問題がどこにあるかについていくつかの方向性を与えることができますか?

ありがとうございました。

4
superisc

wp_postmetaには非効率なインデックスがあります。公開されている表(Wikipediaを参照)は

CREATE TABLE wp_postmeta (
  meta_id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
  post_id bigint(20) unsigned NOT NULL DEFAULT '0',
  meta_key varchar(255) DEFAULT NULL,
  meta_value longtext,
  PRIMARY KEY (meta_id),
  KEY post_id (post_id),
  KEY meta_key (meta_key)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8;

問題点:

  • AUTO_INCREMENTは何の利点もありません。実際、ほとんどのクエリは遅くなります(auto_inc idを見つけるために二次インデックスを調べなければならないので、次に必要な実際のidについてデータを調べる必要があるためです)。
  • AUTO_INCREMENTは、ディスク上とキャッシュ内の両方で余分な混乱を招きます。
  • PRIMARY KEY(post_id, meta_key)の方がはるかに優れています - クラスター化され、通常のJOINの両方の部分を処理します。
  • BIGINTはやり過ぎですが、他のテーブルを変更しなければ修正できません。
  • VARCHAR(255)はMySQL 5.6ではutf8mb4を伴う問題となる可能性があります。下記の回避策を参照してください。
  • Meta_keyまたはmeta_valueがいつNULLになるのはいつですか?

解決策:

CREATE TABLE wp_postmeta (
    post_id BIGINT UNSIGNED NOT NULL,
    meta_key VARCHAR(255) NOT NULL,
    meta_value LONGTEXT NOT NULL,
    PRIMARY KEY(post_id, meta_key),
    INDEX(meta_key)
    ) ENGINE=InnoDB;

典型的な使い方

JOIN wp_postmeta AS m  ON p.id = m.post_id
WHERE m.meta_key = '...'

ノート:

  • 複合PRIMARY KEYは、目的の行に直接進みます。副次索引では省略せず、複数行でも検索しません。
  • INDEX(meta_key)はあなたが他にどんなクエリを持っているかに依存して、役に立つかもしれないし、そうでないかもしれません。
  • InnoDBは「クラスタリング」に必要です。
  • 今後は、utf8ではなくutf8mb4を使用してください。ただし、すべてのWPテーブルおよび接続パラメータで一貫している必要があります。

エラー "max key length is 767"は、MySQL 5.6でCHARACTER SET utf8mb4を使用しようとしたときに発生する可能性があります。エラーを回避するには、次のいずれかを実行します(それぞれに欠点があります)。

  • 3072バイト制限のために5.7.7にアップグレードしてください - あなたのクラウドはこれを提供しないかもしれません。
  • VARCHARで255を191に変更します。191文字より長いキーはすべて失います(ありそうにないですが)。
  • Utf8への変換 - 絵文字と中国語の一部を失います。
  • 「プレフィックス」インデックスを使用すると、パフォーマンス上の利点が一部失われます。
  • (5.6.3 - 5.7.6のままで)再設定 - 変更する4つのこと:Barracuda + innodb_file_per_table + innodb_large_prefix +動的または圧縮.

潜在的な非互換性

  • meta_idはおそらくどこでも使われていません。 (しかしそれを削除するのは危険です)。
  • これらのインデックスに変更することで、meta_idを維持し、ほとんどの利点を得ることができます。PRIMARY KEY(post_id, meta_key, meta_id), INDEX(meta_id), INDEX(meta_key, post_id)。 (注:PKの最後にmeta_idを付けることで、post_id + meta_keyが一意でなくなる可能性があります。)
  • BIGINTから小さなデータ型に変更すると、他のテーブルも変更されます。

utf8mb4

  • 5.7に移行しても互換性がないはずです。
  • VARCHAR(191)に縮小すると、以前の任意の「255」の制限ではなく、現在の「191」という制限であることをユーザーは理解する必要があります。
  • 「再構成」修正はDBAの問題であり、非互換性の問題ではありません。

コメント

私が主張していることのいくつかがWordPressロードマップにあることを願っています。一方、stackoverflowとdba.stackexchangeは、「なぜWPの実行が非常に遅いのですか」と雑然としています。私はここで与えられた修正がそのような苦情タイプの質問でかなり削減するだろうと思います。

互換性の問題があるにもかかわらず、一部のユーザーはutf8mb4に変更していることに注意してください。それから彼らは困っている。私は彼らが抱えているすべてのMySQL問題に対処しようとしました。

リックジェームズmysqlのブログから撮影: ソース

1
Rick James

あなたがDBのパフォーマンスを「そのまま」探しているのであれば、postメタテーブルはあなたが探したいものを保存するのに間違った場所です。クエリの記述方法は、その使用例に分類法を使用するほうがはるかに優れています。

しかし、DBを再構築するには「うさぎの穴」を過ぎているのであれば、クエリを高速化するのには役に立たないかもしれませんが、キャッシュソリューションを検討する必要があります。必要に応じてcronを使用してキャッシュを生成すると、ユーザーへの影響はほとんどありません。

1
Mark Kaplun