ホームページ  >  記事  >  バックエンド開発  >  高同時実行性、低カーディナリティの複数フィールドの任意の組み合わせクエリの最適化_PHP チュートリアル

高同時実行性、低カーディナリティの複数フィールドの任意の組み合わせクエリの最適化_PHP チュートリアル

WBOY
WBOYオリジナル
2016-07-12 08:57:34965ブラウズ

高同時実行性、低カーディナリティの複数フィールド任意組み合わせクエリの最適化

1. 質問

まず、このタイトルに出てくる「低カーディナリティの複数フィールド任意組み合わせクエリ」とは何を指すのか説明してください。これは、以下の条件を満たすクエリを指します:
1. 検索条件に複数のフィールド条件の組み合わせが含まれている
2. これらのフィールドの組み合わせが不確かである
3. 個々のフィールドの選択性が良くない

多数ある電子商取引の商品表示ページなど、このタイプのクエリの使用シナリオ。ユーザーは、カテゴリ、サプライヤー、ブランド、プロモーション、価格などのクエリ条件のさまざまな組み合わせを入力し、最終的には結果を並べ替えたり、ページネーションしたりする必要があることがよくあります。

この種の問題の問題点は次のとおりです:
1. レコードの数が多い場合、フル テーブル スキャンが実行されると、パフォーマンスが低下し、高い同時アクセスの要件を満たせなくなります。
2. クエリ条件に含まれる単一フィールドの選択性は非常に低く、クエリ効率の問題は単一フィールド インデックスでは解決できません。
3. 通常の Btree マルチフィールド インデックスを構築する場合、ユーザー入力条件の組み合わせが多すぎるため、数百または数千のインデックスが構築される可能性がありますが、これは非現実的であり、保守が困難です。

2. 解決策

この種の問題に対して私が考えることができる解決策は2つあります

2.1 ビットマップインデックス

ビットマップの特徴は、値が等しいすべての行セットのキーとビットマップを保存することです。複数のキーを含むクエリの場合、これらのキーに対応するビットマップに対して AND または演算を実行するだけで済みます。ビットマップのサイズは非常に小さく、ビット AND-OR 演算の効率も非常に高いため、ビットマップはこのタイプのクエリに非常に適しています。
ビットマップインデックスには欠点もあります。レコードを更新するとテーブル全体がロックされるため、同時書き込みが多いシナリオには適していません。もう 1 つの問題は、一般的なリレーショナル データベースの中でビットマップ インデックスをサポートしているのは Oracle だけのようであり、多くの場合オープン ソース データベースを使用したいことです。

2.2 逆インデックス

逆インデックスは、キーと、その値がこのキーと等しい行セットを格納します。行セットは、リスト、ツリー、またはその他の格納形式にすることができます。複数のキーを組み合わせたクエリの場合は、これらのキーの結果に対して集合演算を実行するだけです。
転置インデックスは一般的に全文検索に使用されますが、多くのシステムは Elasticsearch などの構造化データ検索をサポートするためにも使用します。 Elasticsearch は、JSON ドキュメントの高速検索、複合クエリ、並べ替え、集計、分散デプロイメント、その他多くの優れた機能をサポートしています。ただし、次の要素を考慮すると、リレーショナル データベースでソリューションを見つけることを好みます。
- ファジー マッチングには検索エンジンが提供する高度な機能を使用する必要はありません。実際には、完全一致または単純なファジー マッチングが必要です。
- データの量が分散検索クラスターを必要とするほど大きくありません。
- 元のデータはすでにリレーショナル データベースにあるため、データの同期について心配する必要はありません。
- 私はすでにリレーショナル データベースのインターフェイスに基づいたアプリケーションを開発しているので、車輪の再発明はしたくありません。
- リレーショナル データベースの運用と保守管理をマスターしましたが、新しいシステムでどれだけの落とし穴に直面するかわかりません。
- Java と C のパフォーマンスの違いを考慮すると、組み込みのリレーショナル データベース ソリューションのパフォーマンスは、プロの検索エンジンのパフォーマンスに劣らない可能性があります。

3. PostgreSQL のソリューション

ソリューションの範囲をオープンソースのリレーショナル データベースに限定すると、答えは 1 つだけになる可能性があります。それは、PostgreSQL の gin インデックスです。
PostgreSQL の gin インデックスは転置インデックスであり、全文検索だけでなく、int や varchar などの通常のデータ型にも使用されます。
多次元クエリの場合は、次のようなインデックスを構築できます。
1. 等価条件に含まれるすべてのカーディナリティの低いフィールドに対して、一意の複数フィールド ジン インデックスを作成します。
2. 適切な選択性を持つ等価クエリまたは範囲クエリに含まれるフィールドに対して。 , さらに、btree インデックス

を構築するときに、複数フィールドのインデックスでもあるのに、なぜ gin には 1 つの複数フィールドのインデックスを構築する必要があるのに、btree には複数の複数フィールドのインデックスが必要なのかという疑問を持つ人もいるかもしれません。さまざまなクエリの組み合わせを考慮して構築されます。これは、gin 複数フィールド インデックスの各フィールドが同等であり、先頭フィールドがないためです。したがって、一意の gin 複数フィールド インデックスが構築されている限り、btree 複数フィールド インデックスはすべてのクエリの組み合わせをカバーできます。クエリ条件に suoyi 先頭フィールドが含まれていない場合、インデックスは使用できません。

マルチフィールド gin インデックスに内部的に保存される各キーは (列番号、キー データ) の形式であるため、混乱することなく異なるフィールドを区別できます。保存された値は、キーに一致するすべてのレコードの ctid のセットです。レコード数が比較的多い場合、このセットは btree 形式で格納され、圧縮されているため、gin インデックスが占める記憶領域は非常に小さく、同等の btree インデックスの約 20 分の 1 のみです。別のパフォーマンスの向上から派生します。

多次元クエリに含まれる複数のフィールド、複数フィールドの gin インデックスに含まれるフィールドの場合、ctid セットは gin インデックスによってマージされ (和集合または交差を取る)、その後、取得された ctid セットが ctid と結合されます。他のインデックスから取得したセットを BitmapAnd または BitmapOr マージします。 gin インデックス内の ctid セットをマージする効率は、インデックス間で ctid セットをマージする効率よりもはるかに高く、gin インデックスはカーディナリティの低いフィールドをより適切に最適化するため、別のインデックスを構築するよりも gin インデックスの特性を最大限に活用する方が優れています。各フィールドの btree インデックスを作成し、BitmapAnd Or BitmapOr を使用すると、結果セットがより効率的にマージされます。


4. 実際のケース

4.1 元のクエリ

以下のSQLは、あるシステムにおける実際のSQLを簡略化したものです。

  1. gpppur.GB_BEGINDATE <= '2016-02-29 14:36:00' かつ gpppur.GB_ENDDATE > '2016-02-29 14:36:00' THEN 1
  2. WHEN の場合を選択gpppur.PREVIEW_BEGINDT <= '2016-02-29 14:36:00' AND gpppur.PREVIEW_ENDDT > '2016-02-29 14:36:00' THEN 2
  3. ELSE 3 END AS フラグ、
  4. gpppur.*
  5. FROM T_MPS_INFO gpppur
  6. WHERE gpppur.ATTRACT_TP = 0
  7. AND gpppur.COLUMN_ID = 1
  8. AND gpppur.FIELD2 = 1
  9. AND g pppur.STAT US = 1
  10. 注文BY flag ASC,gpppur.PC_SORT_NUM ASC,gpppur.GB_BEGINDATE DESC
  11. LIMIT 0,45
MySQL データベースが使用され、総データ量は 60w、FIELD2+STATUS のマルチフィールド インデックスがあります。
クエリ条件に含まれる 4 つのフィールドの値の分布は次のとおりです:

  1. postgres=# select ATTRACT_TP,count(*) from T_MPS_INFO group by ATTRACT_TP;
  2. attract_tp | count
  3. --- --- ------+--------
  4. 6 | 251
  5. 1 | 143
  6. 10 | 314
  7. 5 | 194333

  8. 7 | 1029


  9. (11行)


  10. postgres=#選択COLUMN_ID、COLUMN_ID による T_MPS_INFO グループのカウント (*);

  11. column_id カウント

  12. -----------+--------

  13. | 285 | 20
  14. 351 | 26
  15. 347 | 2

  16. 228 | 21

  17. | 1

  18. 54 | 10

  19. 2147483647 | 4 | 1

  20. 131 | 10

  21. 243 |

  22. 61 | 40

  23. 350 |
  24. 377 | 2
  25. 260 | 184 | 181
  26. 341 | 199271

  27. 235 |
  28. 294

  29. 352 | 3

  30. 368 1

  31. 199 | 8

  32. 84 |

  33. 361 | 33197 9
  34. 319 |
  35. 244 65
  36. 125
  37. 253 | 49
  38. 121 | 3

  39. 365 | 1

  40. 0 | 1

  41. 217 | 1

  42. 122 | 49
  43. 161 |
    222 | 9

  44. 261 2 | 3816
  45. 57 | 1 9
  46. 97 | 20
  47. 3
  48. 85 | 1
  49. 375 | 641
  50. 1 | 6479

  51. 185 | 10

  52. | 17

  53. (80 行)


  54. postgres=# T_MPS_INFO グループから FIELD2,count(*) を選択します;

  55. フィールド 2 | 2297
  56. |
    6 | 469
  57. 2 | 11452
  58. 4 |
    5 | 200497

  59. 9 | 331979

  60. 0 | 2

  61. 7 | 78

  62. (11 行)

  63. postgres=# T_MPS_INFO グループから STATUS;
  64. ステータスを選択します。
    --- ---+--------

  65. | 2297
  66. ありがとうございます。これらのフィールドの値の分布は非常に不均一です。はい、次の Lua スクリプトを構築して、負荷をシミュレートするためのさまざまな選択ステートメントを生成します。
    qx.lua:

    1. pathtest = string.match(test, "(.*/)") または ""

    2. dofile(pathtest .. "common.lua")

    3. function thread_init(thread_id)
    4. set_vars()
    5. end

    6. 関数イベント(thread_id)

    7. ローカルATTRACT_TP,COLUMN_ID,FIELD2,STATUS

    8. ATTRACT_TP = 0, 10)

    9. COLUMN_ID = sb_rand_uniform(1 , 100)

    10. FIELD2 = sb_rand_uniform(0, 10)

    11. STATUS = sb_rand_uniform(0, 4)


    12. rs = db_query("gpppur.GB_BEGINDATE の場合を選択
  67. WHEN gpppur.PREVIEW_BEGINDT
    ELSE 3 END AS flag,

  68. gpppur.*

  69. FROM T_MPS_INFO gpppur

  70. WHERE gpppur.ATT RACT_TP = "。 .ATTRACT_TP.."

  71. AND gpppur.COLUMN_ID = "..COLUMN_ID.."

  72. AND gpppur.FIELD2 = "..FIELD2.."

  73. AND gpppur.STATUS = "..STATUS.."

  74. ORDER BY flag ASC,gpppur.PC_SORT_NUM ASC,gpppur.GB_BEGINDATE DESC

  75. LIMIT 45")

  76. end
その後sysbenchを使用して実行、結果在32発行時に得られたqpsは64

    [root@rh6375Gt20150507 ~]# sysbench --db-driver=mysql --test=/opt/sysbench-0.5/sysbench/tests/db/qx.lua --mysql-db=test -- mysql-user=mysql --mysql-password=mysql --mysql-host=srdsdevapp69 --num-threads=32 --max-time=5 run
  1. sysbench 0.5: マルチスレッドシステム評価ベンチマーク
  2. Running次のオプションを使用したテスト:
  3. スレッド数: 32
  4. 乱数ジェネレーターのシードは 0 なので無視されます
  5. スレッドが開始されました!
  6. OLTP テスト統計:
  7. 実行されたクエリ:
  8. 読み取り: 825
  9. 書き込み: 0
  10. その他: 0
  11. 合計: 825
  12. トランザクション: 0 (0.00/秒)
  13. 読み取り/書き込みリクエスト: 825 (64.20/秒)
  14. その他の操作:​​ 0 (0.00
  15. 無視されたエラー: 0 (1 秒あたり 0.00)
  16. 再接続: 0 (1 秒あたり 0.00)
  17. 一般統計:
  18. 合計時間: 12.8496 秒
  19. イベント総数: 825
  20. イベント実行にかかった合計時間: 399.6003秒
  21. 応答時間:
  22. 最小: 1.01ミリ秒
  23. 平均: 484.36ミリ秒
  24. 最大: 12602.74ミリ秒
  25. およそ 95 パーセンタイル: 222.79 ミリ秒
  26. スレッドの公平性:
  27. イベント (平均/stddev): 25.7812/24.12
  28. 実行時間 (平均/stddev): 12.4875/0.23

4 .2 強化後の意見

上のページ特定の SQL は、すべての等価評価条件を含む 4 つのフィールド (ATTRACT_TP、COLUMN_ID、FIELD2、STATUS) の組み合わせインデックスを作成することによって変換できますが、この SQL だけがさまざまな評価の組み合わせで生成されることは説明が必要です。 1000 もの異なる SQL の 1 つであり、各 SQL に関連するクエリ フィールドの組み合わせは異なっており、各組み合わせに対して個別に複数のフィールド インデックスを作成することは不可能です。 PostgreSQL の gin インデックスを使用して、MySQL のテーブル定義、インデックス、およびデータベースを非正規に PostgreSQL に移行します。 gin インデックスを追加する前に、事前にテストを行っています。測定されたパフォーマンスは、MySQL の 5 倍 (335/64=5) を達成しました。
  1. [root@rh6375Gt20150507 ~]# sysbench --db-driver=pgsql --test=/opt/sysbench-0.5/sysbench/tests/db/qx.lua --pgsql-db=postgres --pgsql-user= postgres --pgsql-password=postgres --pgsql-host=srdsdevapp69 --num-threads=32 --max-time=5 run
  2. sysbench 0.5: マルチスレッド システム評価ベンチマーク

  3. 次のオプションを使用してテストします:
  4. スレッド数: 32
  5. 乱数生成器のシードは 0 で無視されます


  6. スレッド

  7. OLTP テスト統計:
  8. クエリ実行:
  9. 読み取り: 1948
  10. 書き込み: 0
  11. その他: 0
  12. 合計: 1948
  13. トランザクション: 0 (1秒あたり0.00)
  14. 読み取り/書き込みリクエスト: 1948 (335.52 あたり秒)
  15. その他の操作:​​ 0 (1 秒あたり 0.00)
  16. 無視されたエラー: 0 (1 秒あたり 0.00)
  17. 再接続: 0 (1 秒あたり 0.00)

  18. 一般統計:
  19. 合計時間: 5.8059秒
  20. イベント総数: 1948
  21. イベント実行にかかった合計時間: 172.0538秒
  22. 応答時間:
  23. 分:​​
    平均: 88.32 ミリ秒

  24. 最大: 2885.69ms

  25. 約95 パーセンタイル: 80.01 ミリ秒


  26. スレッドの公平性:

  27. イベント (平均/stddev): 60.8750/27.85

  28. 実行時間 (平均/stddev): 5.3767/0.29
次步、追加ジン

postgres=# 拡張子 btree_gin を作成します;

  1. CREATE EXTENSION
  2. postgres=# gin(attract_tp, column_id, field2, status) を使用して t_mps_info にインデックス idx3 を作成します;
  3. CREATE INDEX
  4. 再度圧縮を実行します。出力された qps は 5412、MySQL の 85 倍 (5412/64=85) です。


[root@rh6375Gt20150507 ~]# sysbench --db-driver=pgsql --test= /opt/sysbench-0.5/sysbench/tests/db/qx.lua --pgsql-db=postgres --pgsql-user=postgres --pgsql-password=postgres --pgsql-host=srdsdevapp69 --num-threads= 32 --max-time=5 run

    sysbench 0.5: マルチスレッド システム評価ベンチマーク


  1. 次のオプションでテストを実行します:

  2. スレッド数: 32

  3. 乱数ジェネレータ シードは0 は無視されます



  4. スレッド


  5. OLTP テスト統計:

  6. 実行されたクエリ:

  7. 読み取り:書く: 0

  8. その他: 0

  9. 合計: 10000

  10. トランザクション: 0 (1秒あたり0.00)

  11. 読み取り/書き込みリクエスト: 10000 (1秒あたり5412.80)

  12. その他の操作:​​ 0 (1秒あたり0.00)

  13. 無視編集エラー: 0 (1 秒あたり 0.00)

  14. 再接続: 0 (1 秒あたり 0.00)


  15. 一般統計:

  16. 合計時間: 1.8475 秒

  17. イベント総数: 10000

  18. 合計イベント実行にかかった時間: 58.2706 秒

  19. 応答時間:

  20. 最小: 0.95 ミリ秒

  21. 平均: 5.83 ミリ秒

  22. 最大: 68.36 ミリ秒

  23. 約95 パーセンタイル: 9.42 ミリ秒


  24. スレッドの公平性:

  25. イベント (平均/stddev): 312.5000/47.80

  26. 実行時間 (平均/stddev): 1.8210/0.02


  27. 4.3补充
  28. 作としてそれに比べて、私たちは MySQL に attract_tp、column_id、field2、status の 4 つのフィールドを含むマルチフィールド インデックスを追加しました。出力される qps は 4000 で、PostgreSQL とは異なります。 (PostgreSQL の計算は不可能です。PostgreSQL のパフォーマンスは MySQL よりもはるかに優れています。これは大家の共用です。私の例では、SQL は計算不可能ですか?)
5. 全体
gin インデックス (同様の gist、spgist インデックスも含む) は PostgreSQL の大きな特徴であり、これは非常に興味深い使用方法に基づいており、Oracle と同じようにビットマップ インデックスを作成できます。また、インデックス検索 (Elasticsearch、Solr など) の方式に基づいて対比を行います。さらに、本人が知っている限り、より良い方式がある場合は、私が知ることができれば幸いです。

www.bkjia.com本当http://www.bkjia.com/PHPjc/1108026.html技術記事高同時実行性の低カーディナリティの複数フィールド任意組み合わせクエリの最適化 1. 質問: まず、このタイトルでいう「低カーディナリティの複数フィールド任意組み合わせクエリ」とは何を指すのか説明してください。これは満足するという意味です...
声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。