web-dev-qa-db-ja.com

無関係なデータの1,000〜1億行の行テーブルを効果的に処理します

最大1億行のテーブルの読み取り/書き込みパフォーマンスを向上させる一般的なアプローチは何ですか?

テーブルにはcolumnSEGMENT_ID INT NOT NULLがあり、各セグメントには約100.000〜1.000.000行があります。書き込み-SEGMENT_IDのすべての行が一度に挿入され、その後SEGMENT_IDの更新は行われません。読み取り-かなり頻繁に、私はSELECT * FROM table WERE SEGMENT_ID = ?の良いパフォーマンスを必要としています。

最も明白なアプローチは、SEGMENT_IDの新しいテーブルを動的に作成することですが、動的テーブルはORMまたはネイティブSQLクエリフレームワークでのハッキングを意味します。つまり、匂いがするコードで終了します。

シャーディングも使えますよね?データベースは内部で新しいテーブルを作成しますか?

テーブルをSEGMENT_IDでクラスタ化できます。しかし、すべてのセグメント関連データを一度に挿入すると、挿入はクラスター化されますか?

また、Postgresは パーティション分割を使用して非常に大きなテーブルを処理する を提案しています。

たぶん、新しいテーブルを動的に作成したり、シャーディングを構成したりするのを避けるのに役立つ魔法のインデックスがあるのでしょうか?

他のオプションはありますか?

6
VB_

単純な BRIN index を使用する

TIAS。

これは、あなたが説明したとおりの最悪の場合は1億行で、SEGMENT_IDごとに100万行あります。

explain analyze
CREATE TABLE foo AS
  SELECT (x::int%100)::int AS SEGMENT_ID
  FROM generate_series(1,100e6) AS gs(x);

                                                              QUERY PLAN                                                              
--------------------------------------------------------------------------------------------------------------------------------------
 Function Scan on generate_series gs  (cost=0.00..15.00 rows=1000 width=32) (actual time=21740.904..57589.405 rows=100000000 loops=1)
 Planning time: 0.043 ms
 Execution time: 96685.350 ms
(3 rows)

つまり、1.5分でテーブルを作成しました。ここでは、インデックスを追加しています。

CREATE INDEX ON foo
  USING brin (SEGMENT_ID);
VACUUM ANALYZE foo;

次に、さらに100万行追加します。 SEGMENT_ID = 142

explain analyze
INSERT INTO foo(SEGMENT_ID)
  SELECT 142
  FROM generate_series(1,1e6) AS gs(x);

                                                             QUERY PLAN                                                              
-------------------------------------------------------------------------------------------------------------------------------------
 Insert on foo  (cost=0.00..10.00 rows=1000 width=0) (actual time=1489.958..1489.958 rows=0 loops=1)
   ->  Function Scan on generate_series gs  (cost=0.00..10.00 rows=1000 width=0) (actual time=174.690..286.331 rows=1000000 loops=1)
 Planning time: 0.043 ms
 Execution time: 1499.529 ms
(4 rows)

100万行を追加するには1.5秒かかりました。ここで、

explain analyze
SELECT *
  FROM foo
  WHERE SEGMENT_ID=142;

                                                           QUERY PLAN                                                           
--------------------------------------------------------------------------------------------------------------------------------
 Bitmap Heap Scan on foo  (cost=52.00..56.01 rows=1 width=4) (actual time=4.401..140.874 rows=1000000 loops=1)
   Recheck Cond: (segment_id = 142)
   Rows Removed by Index Recheck: 24832
   Heap Blocks: lossy=4535
   ->  Bitmap Index Scan on foo_segment_id_idx  (cost=0.00..52.00 rows=1 width=0) (actual time=1.504..1.504 rows=46080 loops=1)
         Index Cond: (segment_id = 142)
 Planning time: 0.059 ms
 Execution time: 176.902 ms
(8 rows)

100万行の選択には176ミリ秒かかりました。

これは、 "Intel(R)Core(TM)i5-3230M CPU @ 2.60GHz"とシングルSSDを搭載した5年前のx230です。あなたは数百ドルで1つを拾って、それをXubuntuにインストールすることができます。厳密な科学でもありません。 Angularアプリをバックグラウンドでコンパイルしています。

7
Evan Carroll

最大1億行のテーブルの読み取り/書き込みパフォーマンスを向上させるための一般的なアプローチは何ですか?

電話で実行していませんか?つまり、最新のミッドレンジハードウェアでは、真に数億の行は特に大きくありません。これは-うーん、見てみましょう。デュアルソケット、16コア(ここでは、Windowsライセンスの最低限のライセンスを使用しています。ビットは、たとえばAMD EPYCのローエンドと一致します)、128 GB RAM and all SSD Setup、at少なくともSSDが大量にキャッシュされているもの。

つまり、私の古いVM(SQLサーバー、48 GBのメモリ、6コア、約10の専用SSDを使用)は、特に何もせずに6400万行の挿入/削除ジョブを1秒未満で処理しています。

最も明白なアプローチは、SEGMENT_IDの新しいテーブルを作成することです

これは、専門的なデータベースにパーティショニングと呼ばれるものがある1つのことです。ソートグーグルは実際にpostgresにもそれがあると私に伝えます- https://www.postgresql.org/docs/current/static/ddl-partitioning.html -あなたはそれを知っていますか?なんといっても、SQL Serverよりも少しエレガントではないようです(データベースによって透過的に処理されるのではなく、各パーティションにインデックスを作成するようです)。

読み取りまたは書き込みが速くなることはありませんが、WHOLEパーティションを削除すると大幅に高速化できます。動的にここにいる必要はありませんが、ある程度はできます。主なポイントは、サブテーブルを操作することがないため、ORMとクエリは同じままです。

シャーディングも使えますよね?

おそらく何をすべきか-何千億もの行にヒットしたら.

それは本当にパーティション分割ですが、挿入/削除のシナリオで効率的になった場合のみです。それ以外の場合、特に1億は多くないため、答えは実際にはハードウェアです。そして、パーティショニングはORMでうまく機能するほとんど唯一のソリューションです。

本当に、なぜ動的なのですか?事前生成。あ、そして...

SELECT * FROM table WERE SEGMENT_ID =?

パーティションはここでは役に立ちません。さて、ここに問題があります-パーティションはより少ないデータを検索するのに役立ちますが、segment_idを持つインデックスを最初のフィールドとして使用し、これによってフィルタリングすることはまったく同じです。十分なRAMおよびFAST IOは、データを高速で読み取るための唯一のソリューションです。パーティションは、基本的に「1つのパーティションをすばやく削除する」ものです。小さな利益。

2
TomTom