0

約 30 のテーブルを持つ複雑なデータベースがあります。1 つのテーブルには 500,000 を超える行があり、別のテーブルには 15,000 を超える行があり、今日まで 1 つのデータベースだけに実装することにするまで、両方を別のデータベースで使用していました。

今日まで、500,000 行のテーブルは MySQL データベースにあり、15,000 行のテーブルは PostgreSQL にありました。頻繁に使用される 1 ページで、これは PHP ベンチマークの結果でした。

getSimilarAvaiable - 0.0287 s
getUnavaiable - 0.27 s
ProcessDataOfUnavaiable - 1.4701 s
Process - 1.8622 s
TotalPageTime - 3.631 s

すべてを PostgreSQL に移行し、同じ SQL コードを変更せずに使用した後、同じページの結果は次のようになりました。

getSimilarAvaiable - 2.7465 s
getUnavaiableCars - 9.0763 s
ProcesseDataOfUnavaiableCars - 1.4167 s
ProcessCars - 1.7207 s
TotalPageTime - 14.9602 s

MySQL、同じインデックス、すべてにすべて同じものを入れましたが、なぜこの大きな違いがあるのか​​ わかりません。これを最適化するにはどうすればよいですか?

編集:よりよく説明されました。

500.00 テーブルは、次の構造で構成されています。

id - bigint (primary key)
plate- varchar(10) Unique key
manufacturer - varchar(30)
vin - varchar(30)

主なクエリは次のようなものです。

SELECT plate, vin, 1 as n, substr(plate,1,2) as l 
FROM imtt_vin WHERE substr(plate,1,1) >= 'A' and substr(plate,1,1) <= 'Z' AND
(manufacturer ILIKE '%".self::$Manufacturer."%') AND vin LIKE ?
UNION
SELECT plate, vin, 3 as n, substr(plate,4,2) as l 
FROM imtt_vin WHERE substr(plate,4,1) >= 'A' and substr(plate,4,1) <= 'Z' AND
(manufacturer ILIKE '%".self::$Manufacturer."%') AND vin LIKE ?
UNION
SELECT plate, vin, 2 as n, substr(plate,7,2) as l 
FROM imtt_vin WHERE substr(plate,7,1) >= 'A' and substr(plate,7,1) <= 'Z' AND 
(manufacturer ILIKE '%".self::$Manufacturer."%') AND vin LIKE ?
ORDER BY n, l, plate;

EDIT2:複雑な単一のクエリでテストし、15 秒から 8/9 秒に短縮しました。それでも私には多すぎます。

4

6 に答える 6

4

EXPLAIN yourquery (mysql の場合) と EXPLAIN ANALYZE yourquery (postgres の場合) を投稿する必要があります。それがなければ、関連することは何も言えません。

また、SELECT pg_relation_size('imtt_vin')

たとえば、「?」の値は何ですか? このクエリで ?

SELECT plate, vin, 1 as n, substr(plate,1,2) as l 
FROM imtt_vin WHERE substr(plate,1,1) >= 'A' and substr(plate,1,1) <= 'Z' AND
(manufacturer ILIKE '%".self::$Manufacturer."%') AND vin LIKE ?

あなたが働いているナンバープレートについては知りませんが、この部分:

WHERE substr(plate,1,1) >= 'A' and substr(plate,1,1) <= 'Z'

おそらくデータベース内のすべての行を選択するため、その唯一の目的は CPU サイクルを消費することです。substr() の呼び出しを避けるために、少なくとも次のように (および他のすべてを) 書き直すことができます。

WHERE substr(plate,1,1) BETWEEN 'A' AND 'Z'

もちろん、役に立たない場合は条件を削除します。

次に、次のようになります。

manufacturer ILIKE '%".self::$Manufacturer."%'

悪いデータベース設計: 世界には 500.000 の自動車メーカーがありますか? おそらくそうではありません。製造元を別のテーブルに配置し、外部キーを使用する必要があります。これにより、このインデックス付けできない状態がインデックス付け可能な状態に変わります。

残りについては、EXPLAIN / EXPLAIN ANALYZE を投稿してください。

于 2011-05-02T17:14:30.410 に答える
3

MySQL で MyISAM を使用していた場合、パフォーマンスの違いは理論的に説明できます (データベースの設計と実行されたクエリに関してはあまり明らかにされていないため)。2 つの RDBMS 間のクロス パフォーマンスについては、この比較ページ(MyISAM セクションに固定) を参照することをお勧めします。

于 2011-04-16T22:42:40.060 に答える
3

デフォルトでは、MySQL はより多くのメモリを使用します。def install で 256MB 以上使用するように割り当てられていると思います。正確な数はわかりません。PostgreSQL はデフォルトで 32MB 程度を使用するように設定されています。構成ファイルでそれぞれを最大 1 GB の RAM に増やしてから、ベンチマークを実行して、私たちに戻ってきてください。

于 2011-04-17T15:08:40.673 に答える
2

クエリ

(
SELECT 1 AS n, left(plate, 2) AS l, plate, vin
FROM   imtt_vin
WHERE  left(plate, 1) BETWEEN 'A' AND 'Z'
AND    manufacturer ILIKE '%".self::$Manufacturer."%'
AND    vin LIKE ?   -- You probably mean: vin = ?
ORDER  BY l, plate
)

UNION ALL
(
SELECT 3 AS n, substr(plate, 4, 2) AS l, plate, vin
FROM   imtt_vin
WHERE  substr(plate, 4, 1) BETWEEN 'A' AND 'Z'
AND    manufacturer ILIKE '%".self::$Manufacturer."%'
AND    vin LIKE ?
ORDER  BY l, plate
)

UNION  ALL ...
  • を使用しUNION ALLます。UNION重複を折りたたむために使用されますが、これは明らかにここでは当てはまらず、より高価になります。
  • 先頭の ORDER BY アイテムはnであるため、クエリの個々のレッグを並べ替える方がおそらく効率的です。そのためには、余分な括弧のセットが必要です。
  • left (plate, 2)よりも少し速いですsubstr(plate, 1, 2)。先頭の部分文字列 (最初のSELECT) に対してのみ機能します。

索引

デフォルトのB ツリー インデックスは、左アンカーLIKEに対してのみ機能します。ただし、トリグラム GiST または GIN インデックスは、左固定されていないパターンに使用できます。追加のモジュールが必要ですpg_trgmCREATE EXTENSIONPostgreSQL 9.1 以降では、データベースごとに 1 回インストールします。古いバージョンのマニュアルを参照してください。

CREATE EXTENSION pg_trgm;

続ける情報はあまりありませんが、基本的な部分 GIN インデックスは驚くほどうまくいくはずです:

CREATE INDEX imtt_vin_partial_gist_idx ON imtt_vin
USING  gin (manufacturer gin_trgm_ops)
WHERE  left(plate, 1) BETWEEN 'A' AND 'Z';

CREATE INDEX imtt_vin_partial_gist_idx ON imtt_vin
USING  gin (manufacturer gin_trgm_ops)
WHERE  substr(plate, 4, 1) BETWEEN 'A' AND 'Z';

-- more ...
  • vinおそらくそこに等値演算子が必要なので、インデックスには含めませんでし=た。
  • クエリ プランナーがインデックスが適用可能であることを理解できるように、部分インデックスの述語をクエリで (多かれ少なかれ) 繰り返す必要があります。
  • トリグラム インデックスは、大文字と小文字を区別しない一致で機能します。
  • EXPLAIN ANALYZEインデックスが実際に使用されているかどうかをテストします。そうであれば、クエリ時間は秒単位ではなくミリ秒単位である必要があります。
  • 速度は、インデックスのメンテナンスのための書き込み操作に (わずかな) コストがかかります。通常、インデックスはディスク上のテーブルのサイズの数倍です。
  • MySQL ではこれを行うことはできません。
于 2012-10-18T23:57:24.733 に答える
2

Postgres データベースの統計を更新していないようです。統計が不適切な場合、データベースは十分に機能しません。

于 2011-04-16T22:47:59.947 に答える
1

あなたはまだ十分な情報を提供していません.どのようなインデックスを持っているか、遅いクエリのEXPLAIN ANALYZE出力など.

サンプルクエリの最適化に関するいくつかの考え:

1: 通常、UTF-8 文字列関数はあまり高速ではありません。文字列関数を高速化したい場合はbytea、この列に varchar の代わりに型を使用します (または、データベース全体のエンコーディングを に変更しますSQL_ASCIIが、これはお勧めできません)。

2: クエリが与えられた場合、データベースはおそらくテーブル内のすべての行を調べて、それぞれに対してこれらの文字列関数を計算する必要があります。

一致する数がわからないため、インデックスは役に立たないかもしれませんが、関数インデックスが役立つ場合があります。

 CREATE INDEX imtt_vin_plate_1 ON imtt_vin (substr(plate,1,1));
 CREATE INDEX imtt_vin_plate_4 ON imtt_vin (substr(plate,4,1));
 CREATE INDEX imtt_vin_plate_7 ON imtt_vin (substr(plate,7,1));

3: 重複した出力を許容できる場合は、UNION ALL代わりにUNIONクエリで使用します。これにより、より大きな結果セットでの処理が節約されます。

4: 避けるLIKE/ILIKEできる限り.

于 2011-04-17T22:55:23.523 に答える