10

私は現在、MySQL テーブルの SELECTS の速度を改善しようとしていますが、それを改善する方法についての提案をいただければ幸いです。

テーブルには 3 億件を超えるレコードがあり、テーブルにはタグ、日付、値の構造があります。主キーは、タグと日付を組み合わせたキーです。テーブルには、約 600 個の一意のタグの情報が含まれています。ほとんどのタグには平均で約 400,000 行が含まれていますが、2,000 行から 1,100 万行を超える行の範囲に及ぶ場合があります。

テーブルに対して実行されるクエリは次のとおりです。

  SELECT date,
         value 
    FROM table 
   WHERE tag = "a" 
     AND date BETWEEN 'x' and 'y' 
ORDER BY date

....そして、INSERTSがあったとしてもごくわずかです。

タグごとにデータをさまざまな数のパーティションに分割しようとしましたが、速度はほとんど向上していないようです。

4

8 に答える 8

4

ここで私の答えを読むのに時間がかかります:(あなたと同様のボリュームがあります)

0.02 秒で 5 億行、1,500 万行の範囲スキャン。

MySQL と NoSQL: 適切なものを選択するのを手伝ってください

次に、次のようにテーブル エンジンを innodb に修正します。

create table tag_date_value
(
tag_id smallint unsigned not null, -- i prefer ints to chars
tag_date datetime not null, -- can we make this date vs datetime ?
value int unsigned not null default 0, -- or whatever datatype you require
primary key (tag_id, tag_date) -- clustered composite PK
)
engine=innodb;

代わりに、以下を主キーと見なすことができます。

primary key (tag_id, tag_date, value) -- added value save some I/O

ただし、値がLARGE varchar型ではない場合のみ!

以前のようにクエリ:

select
 tag_date, 
 value
from
 tag_date_value
where
 tag_id = 1 and
 tag_date between 'x' and 'y'
order by
 tag_date;

お役に立てれば :)

編集

言い忘れていましたが、alter table を使用してエンジン タイプを mysiam から innodb に変更するのではなく、データを csv ファイルにダンプし、新しく作成された空の innodb テーブルに再インポートします。

エクスポート プロセス中にデータを注文していることに注意してください。クラスター化インデックスが重要です。

書き出す

select * into outfile 'tag_dat_value_001.dat' 
fields terminated by '|' optionally enclosed by '"'
lines terminated by '\r\n'
from
 tag_date_value
where
 tag_id between 1 and 50
order by
 tag_id, tag_date;

select * into outfile 'tag_dat_value_002.dat' 
fields terminated by '|' optionally enclosed by '"'
lines terminated by '\r\n'
from
 tag_date_value
where
 tag_id between 51 and 100
order by
 tag_id, tag_date;

-- etc...

輸入

正しい順序でテーブルにインポートし直してください!

start transaction;

load data infile 'tag_dat_value_001.dat' 
into table tag_date_value
fields terminated by '|' optionally enclosed by '"'
lines terminated by '\r\n'
(
tag_id,
tag_date,
value
);

commit;

-- etc...
于 2011-03-10T22:33:04.710 に答える
1

クエリはいくつかのことを要求しています。行数が多いため、データの外観によって最適なアプローチが変わる可能性があります。

   SELECT date, value 
   FROM table 
   WHERE tag = "a" 
     AND date BETWEEN 'x' and 'y' 
   ORDER BY date

この選択クエリを遅くする可能性があることがいくつかあります。

  1. 並べ替える (並べ替える) 必要がある非常に大きな結果セット。
  2. 非常に大きな結果セット。タグと日付がインデックスにある場合 (それで十分だと仮定しましょう)、すべての結果行はインデックスを離れて値フィールドを検索する必要があります。これは、本の各章の最初の文を必要とするようなものだと考えてください。章名だけを知りたい場合は簡単です。目次から取得できますが、最初の文が必要なため、実際の章に移動する必要があります。場合によっては、オプティマイザーは最初の文を取得するために、本全体をめくる (クエリ プラン用語でのテーブル スキャン) だけを選択する場合があります。
  3. 最初に間違った where 句でフィルタリングしています。インデックスが order タグの date... にある場合、タグは (大部分のクエリで) 2 つの列の中でより厳密である必要があります。したがって、基本的には、日付よりも多くのタグ (または、通常の日付範囲の日付よりも多いタグ) がない限り、日付はインデックスの 2 つの列の最初にある必要があります。

いくつかの推奨事項:

  1. ほとんどの場合、データが古すぎて気にしない場合は、そのデータの一部を切り捨てることができるかどうかを検討してください。
  2. 現在のインデックスで遊んでみてください。つまり、その中のアイテムの順序を変更してください。
  3. 現在のインデックスを廃止し、カバリング インデックスに置き換えます (3 つのフィールドがすべて含まれています)。
  4. いくつかの EXPLAIN を実行して、インデックスがまったく使用されていることを確認してください。
  5. 他のデータ ストア (mongo db?) に切り替えるか、このモンスター テーブルができるだけ多くのメモリに保持されるようにします。
于 2011-03-10T21:59:06.287 に答える
1

日付フィールドのカーディナリティ (つまり、そのフィールドに表示される異なる値の数) は? 日付 BETWEEN 'x' AND 'y' が WHERE 句の tag = 'a' 部分よりも限定的である場合は、(tag, date) の代わりに主キー (date, tag) を作成して、日付を使用できるようにしてください。インデックス値として。

また、WHERE 句で「x」と「y」を指定する方法にも注意してください。MySQL が各日付フィールドをキャストして、比較する値の日付以外の暗黙の型に一致させる状況がいくつかあります。

于 2011-01-23T19:52:41.813 に答える
1

上記のように、最初にタグと日付の周りにいくつかのインデックスをスローします。

alter table table add index (tag, date);

次に、クエリをメイン クエリに分割し、メイン クエリに入ったときに結果を絞り込むサブ選択を行います。

SELECT date, value
FROM table
WHERE date BETWEEN 'x' and 'y'
AND tag IN ( SELECT tag FROM table WHERE tag = 'a' )
ORDER BY date
于 2011-03-09T16:41:27.133 に答える
0

value列はパフォーマンスの問題の一番下にあると思います。これはインデックスの一部ではないため、テーブルにアクセスできます。さらに、ORDER BY はインデックスの一部であり、順序付けする必要があるため、パフォーマンスに深刻な影響を与える可能性は低いと思います。

valueパーティション化によってクエリの実行時間が実際には短縮されないという事実によって、列に対する私の疑いを主張します。クエリを実行せずに実行してvalue、さらに結果と EXPLAIN を教えていただけますか? 行ごとに本当に必要ですか、それはどのような列ですか?

乾杯!

于 2011-01-23T19:00:18.783 に答える
0

必要な日付だけを一時テーブルに挿入し、タグと順序付けのために一時テーブルを選択して終了してみてください。

CREATE temporary table foo
SELECT date, value 
FROM table 
WHERE date BETWEEN 'x' and 'y' ;

ALTER TABLE foo ADD INDEX index( tag );

SELECT date, value 
FROM foo 
WHERE tag = "a" 
ORDER BY date;

それがうまくいかない場合は、代わりにタグ選択から foo を作成してみてください。

CREATE temporary table foo
SELECT date, value 
FROM table 
WHERE tag = "a";    

ALTER TABLE foo ADD INDEX index( date );

SELECT date, value 
FROM foo 
WHERE date BETWEEN 'x' and 'y' 
ORDER BY date;
于 2011-02-09T19:47:02.540 に答える
0

さらに改善する唯一のチャンスは、3 つの列 (タグ、データ、値) をすべて含むインデックスをカバーすることだと思います。これにより、テーブルへのアクセスが回避されます。

パーティショニングがそれを助けることができるとは思わない。

于 2011-01-23T18:26:54.857 に答える
0

(tag, date)インデックスを追加すると役立つと思います:

alter table table add index (tag, date);

このクエリの説明の結果を投稿してください (EXPLAIN SELECT 日付、値 FROM ......)

于 2011-01-23T18:28:39.060 に答える