18m+行のテーブルのサブクエリとmysqlキャッシュ
-
27-09-2019 - |
質問
これは私の最初の投稿であるため、1つのリンクしか投稿できないようですので、下部に言及しているサイトをリストしました。一言で言えば、私の目標は、データベースに結果をより速く返すようにすることです。私は、投稿の下部にある質問を組み立てるのに役立つと考えられるだけの関連情報を含めようとしました。
機械情報
8 processors
model name : Intel(R) Xeon(R) CPU E5440 @ 2.83GHz
cache size : 6144 KB
cpu cores : 4
top - 17:11:48 up 35 days, 22:22, 10 users, load average: 1.35, 4.89, 7.80
Tasks: 329 total, 1 running, 328 sleeping, 0 stopped, 0 zombie
Cpu(s): 0.0%us, 0.0%sy, 0.0%ni, 87.4%id, 12.5%wa, 0.0%hi, 0.0%si, 0.0%st
Mem: 8173980k total, 5374348k used, 2799632k free, 30148k buffers
Swap: 16777208k total, 6385312k used, 10391896k free, 2615836k cached
ただし、256 GBのRAMを持つクラスター内の別のマシンにMySQLインストールを移動することを検討しています
テーブル情報
私のmysqlテーブルはように見えます
CREATE TABLE ClusterMatches
(
id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
cluster_index INT,
matches LONGTEXT,
tfidf FLOAT,
INDEX(cluster_index)
);
約18メートルの列があり、1Mユニークなcluster_indexと6Kユニークな一致があります。私がPHPで生成しているSQLクエリは次のように見えます。
SQLクエリ
$sql_query="SELECT `matches`,sum(`tfidf`) FROM
(SELECT * FROM Test2_ClusterMatches WHERE `cluster_index` in (".$clusters."))
AS result GROUP BY `matches` ORDER BY sum(`tfidf`) DESC LIMIT 0, 10;";
ここで、$クラスターには約3,000のコンマ分離Cluster_indexの文字列が含まれています。このクエリでは、約50,000行を使用し、実行に約15秒かかります。同じクエリが再び実行されると、実行には約1秒かかります。
使用法
- テーブルの内容は静的であると想定できます。
- 同時ユーザーの数が少ない
- 上記のクエリは現在、テーブルで実行される唯一のクエリです
サブクエリ
この投稿に基づいて[StackOverFlow:Cache/MySQLでサブクエリを再利用] [1]とクエリ時間の改善は、サブクエリをインデックス化できると考えています。
mysql> EXPLAIN EXTENDED SELECT `matches`,sum(`tfidf`) FROM
(SELECT * FROM ClusterMatches WHERE `cluster_index` in (1,2,...,3000)
AS result GROUP BY `matches` ORDER BY sum(`tfidf`) ASC LIMIT 0, 10;
+----+-------------+----------------------+-------+---------------+---------------+---------+------+-------+---------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+----------------------+-------+---------------+---------------+---------+------+-------+---------------------------------+
| 1 | PRIMARY | derived2 | ALL | NULL | NULL | NULL | NULL | 48528 | Using temporary; Using filesort |
| 2 | DERIVED | ClusterMatches | range | cluster_index | cluster_index | 5 | NULL | 53689 | Using where |
+----+-------------+----------------------+-------+---------------+---------------+---------+------+-------+---------------------------------+
この古い記事によれば、[mysql:queries and indexes] [2] [2]追加情報で - ここで見るべき悪いものは「一時的な」と「filesortの使用」です。
mysql構成情報
クエリキャッシュは利用可能ですが、サイズが現在ゼロに設定されているため、効果的にオフになります
mysqladmin variables;
+---------------------------------+----------------------+
| Variable_name | Value |
+---------------------------------+----------------------+
| bdb_cache_size | 8384512 |
| binlog_cache_size | 32768 |
| expire_logs_days | 0 |
| have_query_cache | YES |
| flush | OFF |
| flush_time | 0 |
| innodb_additional_mem_pool_size | 1048576 |
| innodb_autoextend_increment | 8 |
| innodb_buffer_pool_awe_mem_mb | 0 |
| innodb_buffer_pool_size | 8388608 |
| join_buffer_size | 131072 |
| key_buffer_size | 8384512 |
| key_cache_age_threshold | 300 |
| key_cache_block_size | 1024 |
| key_cache_division_limit | 100 |
| max_binlog_cache_size | 18446744073709547520 |
| sort_buffer_size | 2097144 |
| table_cache | 64 |
| thread_cache_size | 0 |
| query_cache_limit | 1048576 |
| query_cache_min_res_unit | 4096 |
| query_cache_size | 0 |
| query_cache_type | ON |
| query_cache_wlock_invalidate | OFF |
| read_rnd_buffer_size | 262144 |
+---------------------------------+----------------------+
MySQLデータベースのパフォーマンスターニング]に関するこの記事に基づいて、私が調整する必要がある値はあると信じています
- Table_cache
- key_buffer
- sort_buffer
- read_buffer_size
- record_rnd_buffer(グループごとに条件ごとに注文)
改善のために特定された領域-MySQLクエリの調整
- 一致のデータ型を別のテーブルを指しているインデックスに変更する[MySQLは、テキストやBLOBなどの可変長さフィールドが含まれている場合、実際に動的な行形式を使用します。この場合、ディスクでソートを行う必要があることを意味します。解決策は、これらのデータ型を避けることではなく、そのようなフィールドを関連するテーブルに分割することです。] [4
- 新しいmatch_index feildにインデックスを付けて、グループが
matches
ステートメントに基づいてより速く発生します[「おそらく、選択、グループ化、注文、または参加しているフィールドのインデックスを作成する必要があります。」] [5
ツール
微調整するには、使用する予定です
- 説明] [6] [出力形式]を参照する[7
- ab -apache httpサーバーベンチマークツール] [8
- プロファイリング] [9] with [log data] [10
将来のデータベースサイズ
目標は、1mの一意のcluster_index値を持つことができるシステムを構築することです。1mの一意の一致値、約3,000,000,000のテーブル行を約0.5秒のクエリに応答時間とともに構築することです(必要に応じてRAMを追加し、クラスター全体にデータベースを配布できます)
質問
- MySQLキャッシュにデータベース全体を保持する場合、クエリがディスクに触れないようにRAMに記録セット全体を保持したいと思います。これにより、MemCachedBの必要性がなくなりますか?
- MySQLキャッシュのデータベース全体を永続的に設計されていないため、悪い戦略を維持しようとしていますか? MemcachedbやRedisのようなものがより良いアプローチになるのでしょうか?
- クエリによって作成された一時テーブル「結果」は、クエリが終了すると自動的に破壊されますか?
- InnodbからMyisamに切り替える必要があります[重いデータを読むのに適しているため、Innodbは重いと書くのに適しているため] [11]?
- クエリキャッシュ構成] [12]では、私のキャッシュがゼロとしてオンになっていないようには見えませんが、2回目の実行時にクエリが現在より速く発生するのはなぜですか?
- クエリを再構築して、「一時的な」を使用して「Filesortを使用する」ことを排除できますか?サブクエリの代わりに参加を使用する必要がありますか?
- MySQL [データキャッシュ] [13]のサイズをどのように表示しますか?
- Table_cache、key_buffer、sort_buffer、read_buffer_size、record_rnd_bufferの値のサイズの種類は、出発点として提案しますか?
リンク
- 1:stackoverflow.com/questions/658937/cache-re-use-a-subquery-in-mysql
- 2:databasejournal.com/features/mysql/article.php/10897_1382791_4/optimizing-mysql-quelies-and-indexes.htm
- 3:debianhelp.co.uk/mysqlperformance.htm
- 4:20bits.com/articles/10-tips-for-ptimizing-mysql-queries-that-dontsuck/
- 5:20bits.com/articles/10-tips-for-ptimizing-mysql-queries-that-dontsuck/
- 6:dev.mysql.com/doc/refman/5.0/en/explain.html
- 7:dev.mysql.com/doc/refman/5.0/en/explainoutput.html
- 8:httpd.apache.org/docs/2.2/programs/ab.html
- 9:mtop.sourceforge.net/
- 10:dev.mysql.com/doc/refman/5.0/en/slow-query-log.html
- 11:20bits.com/articles/10-tips-for-ptimizing-mysql-queries-that-dontsuck/
- 12:dev.mysql.com/doc/refman/5.0/en/query-cache-configuration.html
- 13:dev.mysql.com/tech-resources/articles/mysql-query-cache.html
解決
テーブルの変更
この投稿のアドバイスに基づいています クエリごとに注文のインデックスを選択する方法 テーブルは今のように見えます
CREATE TABLE ClusterMatches
(
cluster_index INT UNSIGNED,
match_index INT UNSIGNED,
id INT NOT NULL AUTO_INCREMENT,
tfidf FLOAT,
PRIMARY KEY (match_index,cluster_index,id,tfidf)
);
CREATE TABLE MatchLookup
(
match_index INT UNSIGNED NOT NULL PRIMARY KEY,
image_match TINYTEXT
);
サブクエリを排除します
結果を合計(TFIDF)で並べ替えないクエリは次のように見えます
SELECT match_index, SUM(tfidf) FROM ClusterMatches
WHERE cluster_index in (1,2,3 ... 3000) GROUP BY match_index LIMIT 10;
これは、一時的およびFilesortを使用して排除します
explain extended SELECT match_index, SUM(tfidf) FROM ClusterMatches
WHERE cluster_index in (1,2,3 ... 3000) GROUP BY match_index LIMIT 10;
+----+-------------+----------------------+-------+---------------+---------+---------+------+-------+--------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+----------------------+-------+---------------+---------+---------+------+-------+--------------------------+
| 1 | SIMPLE | ClusterMatches | range | PRIMARY | PRIMARY | 4 | NULL | 14938 | Using where; Using index |
+----+-------------+----------------------+-------+---------------+---------+---------+------+-------+--------------------------+
ソートの問題
ただし、合計(tfdif)で注文を追加した場合
SELECT match_index, SUM(tfidf) AS total FROM ClusterMatches
WHERE cluster_index in (1,2,3 ... 3000) GROUP BY match_index
ORDER BY total DESC LIMIT 0,10;
+-------------+--------------------+
| match_index | total |
+-------------+--------------------+
| 868 | 0.11126546561718 |
| 4182 | 0.0238558370620012 |
| 2162 | 0.0216601379215717 |
| 1406 | 0.0191618576645851 |
| 4239 | 0.0168981291353703 |
| 1437 | 0.0160425212234259 |
| 2599 | 0.0156466849148273 |
| 394 | 0.0155945559963584 |
| 3116 | 0.0151005545631051 |
| 4028 | 0.0149106932803988 |
+-------------+--------------------+
10 rows in set (0.03 sec)
結果はこのスケールで適切に高速ですが、 合計で注文(tfidf)は、一時的なfilesortを使用することを意味します
explain extended SELECT match_index, SUM(tfidf) AS total FROM ClusterMatches
WHERE cluster_index IN (1,2,3 ... 3000) GROUP BY match_index
ORDER BY total DESC LIMIT 0,10;
+----+-------------+----------------------+-------+---------------+---------+---------+------+-------+-----------------------------------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+----------------------+-------+---------------+---------+---------+------+-------+-----------------------------------------------------------+
| 1 | SIMPLE | ClusterMatches | range | PRIMARY | PRIMARY | 4 | NULL | 65369 | Using where; Using index; Using temporary; Using filesort |
+----+-------------+----------------------+-------+---------------+---------+---------+------+-------+-----------------------------------------------------------+
可能な解決策?
一時的またはfilesortを使用しないソリューションを探しています。
SELECT match_index, SUM(tfidf) AS total FROM ClusterMatches
WHERE cluster_index IN (1,2,3 ... 3000) GROUP BY cluster_index, match_index
HAVING total>0.01 ORDER BY cluster_index;
合計のしきい値をハードコードする必要がない場合、アイデアはありますか?