4

実稼働環境よりも開発サーバーで実行する時間が短いクエリがあります (データベースは同じです)。製品サーバーははるかに効率的です (64 GB RAM、12 コアなど)。

クエリは次のとおりです。

SELECT `u`.`id`,
       `u`.`user_login`,
       `u`.`last_name`,
       `u`.`first_name`,
       `r`.`referrals`,
       `pr`.`worker`,
       `rep`.`repurchase`
FROM `ci_users` `u`
LEFT JOIN
  (SELECT `referrer_id`,
          COUNT(user_id) referrals
   FROM ci_referrers
   GROUP BY referrer_id) AS `r` ON `r`.`referrer_id` = `u`.`id`
LEFT JOIN
  (SELECT `user_id`,
          `expire`,
          SUM(`quantity`) worker
   FROM ci_product_11111111111111111
   GROUP BY `user_id`) AS `pr` ON `pr`.`user_id` = `u`.`id`
AND (`pr`.`expire` > '2015-12-10 09:23:45'
     OR `pr`.`expire` IS NULL)
LEFT JOIN `ci_settings` `rep` ON `u`.`id` = `rep`.`id`
ORDER BY `id` ASC LIMIT 100,
                        150;

開発サーバーで次の結果を説明します。

   +----+-------------+------------------------------+--------+---------------+-------------+---------+-----------+-------+---------------------------------+
| id | select_type | table                        | type   | possible_keys | key         | key_len | ref       | rows  | Extra                           |
+----+-------------+------------------------------+--------+---------------+-------------+---------+-----------+-------+---------------------------------+
|  1 | PRIMARY     | u                            | index  | NULL          | PRIMARY     | 4       | NULL      |     1 | NULL                            |
|  1 | PRIMARY     | <derived2>                   | ref    | <auto_key0>   | <auto_key0> | 5       | dev1.u.id |    10 | NULL                            |
|  1 | PRIMARY     | <derived3>                   | ref    | <auto_key1>   | <auto_key1> | 5       | dev1.u.id |    15 | Using where                     |
|  1 | PRIMARY     | rep                          | eq_ref | PRIMARY       | PRIMARY     | 4       | dev1.u.id |     1 | NULL                            |
|  3 | DERIVED     | ci_product_11111111111111111 | ALL    | NULL          | NULL        | NULL    | NULL      | 30296 | Using temporary; Using filesort |
|  2 | DERIVED     | ci_referrers                 | ALL    | NULL          | NULL        | NULL    | NULL      | 11503 | Using temporary; Using filesort |
+----+-------------+------------------------------+--------+---------------+-------------+---------+-----------+-------+---------------------------------+

そして、これはprodからのものです:

+----+-------------+------------------------------+--------+---------------+---------+---------+--------------+-------+---------------------------------+
| id | select_type | table                        | type   | possible_keys | key     | key_len | ref          | rows  | Extra                           |
+----+-------------+------------------------------+--------+---------------+---------+---------+--------------+-------+---------------------------------+
|  1 | PRIMARY     | u                            | ALL    | NULL          | NULL    | NULL    | NULL         | 10990 |                                 |
|  1 | PRIMARY     | <derived2>                   | ALL    | NULL          | NULL    | NULL    | NULL         |  2628 |                                 |
|  1 | PRIMARY     | <derived3>                   | ALL    | NULL          | NULL    | NULL    | NULL         |  8830 |                                 |
|  1 | PRIMARY     | rep                          | eq_ref | PRIMARY       | PRIMARY | 4       | prod123.u.id |     1 |                                 |
|  3 | DERIVED     | ci_product_11111111111111111 | ALL    | NULL          | NULL    | NULL    | NULL         | 28427 | Using temporary; Using filesort |
|  2 | DERIVED     | ci_referrers                 | ALL    | NULL          | NULL    | NULL    | NULL         | 11837 | Using temporary; Using filesort |
+----+-------------+------------------------------+--------+---------------+---------+---------+--------------+-------+---------------------------------+

prod サーバーでのプロファイリング結果は、次のような結果を示しました。

............................................
| statistics                     | 0.000030 |
| preparing                      | 0.000026 |
| Creating tmp table             | 0.000037 |
| executing                      | 0.000008 |
| Copying to tmp table           | 5.170296 |
| Sorting result                 | 0.001223 |
| Sending data                   | 0.000133 |
| Waiting for query cache lock   | 0.000005 |
............................................

しばらくグーグルで調べた後、一時テーブルを RAM に移動することにしました。

/etc/fstab:

tmpfs /var/tmpfs tmpfs rw,uid=110,gid=115,size=16G,nr_inodes=10k,mode=0700 0 0

ディレクトリ ルール:

drwxrwxrwt  2 mysql mysql   40 Dec 15 13:57 tmpfs

/etc/mysql/my.cnf (値でたくさん遊んだ):

[client]
port        = 3306
socket      = /var/run/mysqld/mysqld.sock

[mysqld_safe]
socket      = /var/run/mysqld/mysqld.sock
nice        = 0

[mysqld]
user        = mysql
pid-file    = /var/run/mysqld/mysqld.pid
socket      = /var/run/mysqld/mysqld.sock
port        = 3306
basedir     = /usr
datadir     = /var/lib/mysql
tmpdir      = /var/tmpfs
lc-messages-dir = /usr/share/mysql
skip-external-locking
bind-address        = 127.0.0.1
key_buffer      = 16000M
max_allowed_packet  = 16M
thread_stack        = 192K
thread_cache_size       = 150
myisam-recover         = BACKUP
tmp_table_size         = 512M
max_heap_table_size    = 1024M
max_connections        = 100000
table_cache            = 1024
innodb_thread_concurrency = 0
innodb_read_io_threads = 64
innodb_write_io_threads = 64
query_cache_limit   = 1000M
query_cache_size        = 10000M
log_error = /var/log/mysql/error.log
expire_logs_days    = 10
max_binlog_size         = 100M

[mysqldump]
quick
quote-names
max_allowed_packet  = 16M

[mysql]

[isamchk]
key_buffer      = 16M

そして、それは機能しません。実行時間は変わらず、約 5 秒です。2つの質問に答えてください:

  1. tmpfs 構成の何が問題になっていますか?
  2. サーバーで Explain が異なるのはなぜですか。このクエリを最適化するにはどうすればよいですか? (tmpfs を使用していなくても、最後の 'order by' を削除すると、クエリがはるかに速く完了することがわかりました)。

前もって感謝します。

4

2 に答える 2

2

サーバーで Explain が異なるのはなぜですか。このクエリを最適化するにはどうすればよいですか? (tmpfs を使用しなくても、最後の 'order by' を削除すると、クエリがはるかに速く完了することがわかりました)。

「データベースは同じ」と言いますが、説明の出力からは、おそらく「スキーマは同じ」という意味です。本番スキーマにはさらに多くのデータがあるように見えますか? MySQL は、データ量、インデックス サイズなどに基づいてクエリの処理方法を最適化します。これにより、(最高レベルで) なぜこのような劇的な違いが見られるのかが説明されます。

説明する出力の列は「行」です。dev では 2 つの派生テーブルが非常に小さかったことに注意してください。どうやら ( #mysqlfreenode IRC で確認することができます)、MySQL は派生テーブルのインデックスを dev で作成していましたが、本番環境では作成しないことを選択したようです (おそらく、もっと多くのレコードがあったためでしょうか?)。

tmpfs 構成の何が問題になっていますか?

何もない。tmp_table_size:) MySQL は、一時データをディスクに書き込む前に、メモリ内のデータ量が特定のサイズ ( ) に達するまで、メモリ内に一時テーブルを作成します。MySQL がこれを行うことを信頼できます。メモリ内に一時ファイルシステムを作成し、そこに MySQL を指定するという複雑さとオーバーヘッドをすべて作成する必要はありません... InnoDBの重要なinnodb_buffer_pool_size変数は です。チューニング。

Percona による多くの (IMHO) 優れたものを含め、オンラインにはたくさんのドキュメントがあります。(私は彼らと提携していませんが、彼らと仕事をしたことがあります。彼らとサポート契約を結ぶ余裕があれば、そうしてください。彼らは本当に彼らのことを知っています。)

私は MySQL のチューニングの専門家ではないので、あなたが選択したオプションについてコメントするつもりはありません。ただし、Percona チームに見てもらい、チューニングする前に何週間も費やしたと言うだけです「それは素晴らしいが、あなたはこれを見逃していて、あれは間違っていた」と言ってください - そして結果として顕著な改善がありました!

最後に、インデックス、スキーマ、およびクエリが主要なものであることを指摘します。2 つのサブクエリがあります。まず、それらを除外して、それが役立つかどうかを確認します。クエリを適切に調整するには、dev で利用できる代表的なデータ サンプルが必要です。(過去にこれに読み取り専用のレプリケーションサーバーを使用しました。)クエリが何をしようとしているのかを完全には理解していませんが、これらのテーブルを結合して全体的な結果をグループ化できるようです.

明らかなことが欠けている場合 (おそらく!)、これらのサブクエリでデータのテーブルを個別に維持することを検討します。INSERTDBA がトランザクション的に安全な方法で後でそのようなキャッシュ ロジックをより簡単に追加できると指摘して以来、私は常にデフォルトで SP を使用して s を処理してきました。したがって、テーブルに挿入するときci_*は、データのテーブルも更新しますCOUNT()(サブクエリを除外できない場合)。これにより、すべてが適切にインデックス付けされた一連の結合になります。

于 2015-12-16T15:37:40.170 に答える
1

説明は、prod ではクエリが u、derived1、derived2 テーブルのインデックスを使用しないのに対し、dev では使用することを示しています。その結果、スキャンされた行数は本番環境で大幅に高くなります。2 つの派生テーブルのインデックス名は、これらがmysql v5.6.5 から利用できるマテリアライズド派生テーブルの最適化戦略を利用して、オンザフライで mysql によって作成されたことを示唆しています。prod サーバーからの Explain にはそのような最適化が存在しないため、prod サーバーには以前のバージョンの mysql が含まれている可能性があります。

@Satevg がコメントで提供したように、開発環境と製品環境には次の mysql バージョンがあります。

開発者: debian 7、Mysql 5.6.28。製品: debian 8、Mysql 5.5.44

このmysqlバージョンの微妙な違いが速度の違いを説明している可能性があります.devサーバーはマテリアライゼーション最適化戦略を利用できますが、製品-v5.5のみ-は利用できないためです。

于 2015-12-17T15:34:36.730 に答える