行数(少なくとも15億行)に関して非常に大きなテーブルをクエリする必要があるオーディオフィンガープリント問題に取り組んでいますが、サイズは比較的良好(23G)であり、合計で約50K〜100K行を取得します。複数のクエリを使用する(20〜50クエリ)。
テーブルには、3つの列、ハッシュ、および2つのint値があります。制約は一切ありません。ハッシュ列には多くの衝突/重複があります。 show create tableの出力は次のとおりです
CREATE TABLE `fingerprints` (
`hash` binary(10) NOT NULL,
`int1` mediumint(8) unsigned NOT NULL,
`int2` mediumint(8) unsigned NOT NULL,
KEY `hash` (`hash`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
クエリは単純です。ここに例を示します。
select int1 ,int2 from fingerprints where hash in (UNHEX("1ff99335cce004f2765d"),UNHEX("14c4b93ed575982ed2e4"),UNHEX("41044b0cf21dc8ac8f9b"),UNHEX("a791403ca116b4da53dd"),UNHEX("d9f91514b900c25fa095"),UNHEX("3349f906deae6cd32883"),UNHEX("221c0e3e2bc243fb0fe5").... more here);
私はさまざまなハードウェア仕様を試しました(AWSを1つのマシン/インスタンスのみで使用)。 my.cnf構成は異なりますが、大幅なパフォーマンスの向上はありません。
この操作の目標速度しきい値(合計クエリ時間)は5秒です。しかし、私が平均して得た最高のものは、単一のクエリのみで3秒です(20クエリある場合、合計操作時間は1分です)。
最後の注意:クエリのプロファイリング時に、SHOWプロファイルコマンドは、最も遅い部分が(データの送信)状態であったことを示します。結果セットが大きい場合、クエリは遅くなります(つまり、1万行の取得には約6秒かかりますが、1000行の取得には2秒かかります)。
質問:
私のセットアップ:
編集:
SHOW INDEXの出力:
+--------------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment | Visible | Expression |
+--------------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
| fingerprints | 1 | hash | 1 | hash | A | NULL | NULL | NULL | | BTREE | | | YES | NULL |
+--------------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
EXPLAIN QUERY出力(クエリの例)
+----+-------------+--------------+------------+-------+---------------+------+---------+------+------+----------+-----------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+--------------+------------+-------+---------------+------+---------+------+------+----------+-----------------------+
| 1 | SIMPLE | fingerprints | NULL | range | hash | hash | 10 | NULL | 4912 | 100.00 | Using index condition |
+----+-------------+--------------+------------+-------+---------------+------+---------+------+------+----------+-----------------------+
次のクエリを実行することで、この遅い問題を解決できました。
alter table fingerprints order by hash;
ハッシュ列に多くの繰り返しがあります(34mの一意のハッシュのみがあります)。私が状況を正しく理解している場合、順序付けにより、私のユースケースでは読み取りがずっと連続的になりました(ハッシュから*を選択*テーブルから*を選択)。
SHOW INDEXの出力を確認すると、カーディナリティ値はNULLです。コマンドによる順序を実行した後、カーディナリティは今=一意のハッシュの数= 34mです。それは理にかなっています。これが根本的な問題だと思います。参照してください: https://stackoverflow.com/questions/6521673/is-null-cardinality-in-an-index-a-problem-mysql-5-x
約60秒かかったジョブは、今では350ミリ秒しかかかりません。
(UNHEXing
は問題の重要な部分ではありません。)
本当の問題は、ハッシュのランダム性です。ディスク上の多くの場所をジャンプすることにつながります。クエリを分析してみましょう。
IN
リストは、INDEX(hash)
全体に散在する値のリストです。.MYI
_ファイルにあります)をドリルダウンすることにより、各値が検索されます。 _key_buffer_size
_の値は何ですか? `SHOW TABLE STATUS LIKE 'fingerprints'\Gの結果は何ですか?fingerprints.MYD
_へのランダムディスクアクセス(シーク、BTreeなし)になります。 (レコードはFIXED
長さ17バイトのようです。)何をすべきか?
ケース1:Data_length + Index_length <RAM size:key_buffer_sizeをIndex_lengthより少し大きくします。徐々に両方のキャッシュがインデックスとデータで満たされ、I/Oはなくなります。
ケース2:その合計がRAMよりもわずかに大きい:キャッシュの1つを選択して、十分に大きくします。
ケース3:合計がRAMよりもはるかに大きい:より多くのRAMを取得するまで、大量のI/Oが発生します。
Data_lengthとIndex_lengthはほぼ等しいと思います。 availableRAM半分に分割します-key_buffer_sizeには半分、残りはデータキャッシングに使用します。
さらに2つのアイデアがあります。
2番目のステップでintをフェッチするのではなく、KEY(hash, int1, int2)
を使用します。これは、BTreeルックアップのみが必要であることを意味します。データはリーフノードに配置されます。このアプローチでは、_key_buffer_size
_をavailableRAMの 'most'に設定できます。そのSELECT
はデータを操作せず、インデックスのみを操作します。
InnoDBに切り替えます。ブロックは1KBではなく16KBです。これはかもしれない物事をより速くします。ただし、ディスクフットプリントは2〜3倍になります。ここでも、3列のインデックスを使用しますが、_key_buffer_size
_を20Mに縮小し、_innodb_buffer_pool_size
_をRAMの70%に増やします。
その他の注意事項:
「データの送信」では何もわかりません。一般的に、プロファイリングは役に立ちません。
SSDはHDDよりもはるかに高速に動作します。
I/Oバインドされているようです。
I/Oバウンドかどうかに関係なく、合計クエリ時間は、検索されるハッシュの数にほぼ比例します。 (これは私の解剖から推測できます。)
MEMORYはMyISAMよりも大幅に高速または低速になる可能性は低いです。また、データを永続化する必要がある場合、MEMORYは揮発性であるため、面倒です。
圧縮するのに6バイトしかないので、圧縮は役に立たないと予測します。 (そして、ハッシュ自体はおそらく圧縮可能ではありません。)
プロバイダーがIOPを制限している場合、それは問題です。インデックスが完全にキャッシュされている場合(そして、RAMを不必要に消費するほど大きくない場合)、IOPはデータブロックのフェッチです。 3バイトのキーは約70%大きくなります。十分な大きさのkey_bufferがRAMに収まりますか?もしそうなら、そのアプローチは最適かもしれません。