次の表があります。
id | bigint | not null default nextval('shares_id_seq'::regclass)
poolid | text | not null
blockheight | bigint | not null
networkdifficulty | double precision | not null
miner | text | not null
worker | text |
ipaddress | text | not null
created | timestamp without time zone | not null
useragent | text |
payoutinfo | text |
difficulty | double precision | not null default 0
Indexes:
"shares_pkey" PRIMARY KEY, btree (id)
"idx_shares_pool_block" btree (poolid, blockheight)
"idx_shares_pool_created" btree (poolid, created)
"idx_shares_pool_miner" btree (poolid, miner)
"idx_shares_pool_miner_diff" btree (poolid, miner, difficulty)
次のクエリに非常に時間がかかる理由を理解できません。
explain analyze SELECT SUM(difficulty) FROM shares WHERE poolid = 'xmr1' AND miner = '4BCeEPhodgPMbPWFN1dPwhWXdRX8q4mhhdZdA1dtSMLTLCEYvAj9QXjXAfF7CugEbmfBhgkqHbdgK9b2wKA6nqRZQCgvCDm';
QUERY PLAN
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Aggregate (cost=4150712.54..4150712.55 rows=1 width=8) (actual time=25490.101..25490.101 rows=1 loops=1)
-> Bitmap Heap Scan on shares (cost=389414.64..4143195.97 rows=3006629 width=8) (actual time=2499.409..24815.011 rows=7445802 loops=1)
Recheck Cond: ((poolid = 'xmr1'::text) AND (miner = '4BCeEPhodgPMbPWFN1dPwhWXdRX8q4mhhdZdA1dtSMLTLCEYvAj9QXjXAfF7CugEbmfBhgkqHbdgK9b2wKA6nqRZQCgvCDm'::text))
Rows Removed by Index Recheck: 55232916
Heap Blocks: exact=149273 lossy=2641988
-> Bitmap Index Scan on idx_shares_pool_miner (cost=0.00..388662.98 rows=3006629 width=0) (actual time=2449.977..2449.977 rows=7445802 loops=1)
Index Cond: ((poolid = 'xmr1'::text) AND (miner = '4BCeEPhodgPMbPWFN1dPwhWXdRX8q4mhhdZdA1dtSMLTLCEYvAj9QXjXAfF7CugEbmfBhgkqHbdgK9b2wKA6nqRZQCgvCDm'::text))
Planning time: 0.256 ms
Execution time: 25490.137 ms
(9 rows)
このシナリオ用にインデックスidx_shares_pool_miner_diffを特別に作成しましたが、まだ使用されていません。 MVCCにより、PostgreSQLはインデックスを使用できませんか?
更新:
ランニング vacuum analyze shares
推奨されるように、PostgreSQLは前述のインデックスを使用します。
Aggregate (cost=546165.94..546165.95 rows=1 width=8) (actual time=2489.446..2489.447 rows=1 loops=1)
-> Index Only Scan using idx_shares_pool_miner_diff on shares (cost=0.69..537874.79 rows=3316458 width=8) (actual time=0.041..1924.396 rows=7461785 loops=1)
Index Cond: ((poolid = 'xmr1'::text) AND (miner = '4BCeEPhodgPMbPWFN1dPwhWXdRX8q4mhhdZdA1dtSMLTLCEYvAj9QXjXAfF7CugEbmfBhgkqHbdgK9b2wKA6nqRZQCgvCDm'::text))
Heap Fetches: 16575
Planning time: 0.122 ms
Execution time: 2489.477 ms
(6 rows)
並列ワーカーを使用しなくても、それが可能な限り速いと思います。
インデックスのみのスキャンに関するドキュメントを読んだ後、sharesテーブルへの頻繁な書き込みアクティビティ(> 200挿入/秒)が原因でパフォーマンスが低下し、多くのヒープフェッチが発生しているようです。興味深いことに、ヒープフェッチの数は明らかに増加しているだけで、決して減少していません。
テーブルがINSERTのみの場合、合計を(はるかに)速くする方法があります。
単調に増加する値(例ではid
またはcreated
のような)の列があると想定して、 MATERIALZED VIEW
(最近の)指定されたしきい値よりも古い合計を事前計算します。次に、最近の追加の合計を追加します。
CREATE MATERIALIZED VIEW shares_summed AS
SELECT poolid, miner, SUM(difficulty) AS sum_diff
FROM shares
GROUP BY poolid, miner
ORDER BY poolid, miner; -- optional, but to optimize some more
WHERE created < '2018-01-01 0:0';
(poolid, miner)
の組み合わせが多い場合は、UNIQUE
インデックスを追加します。 CONCURRENTLY
の更新にも必要です。以下を参照してください。
そのテーブルから行をフェッチしても、コストはほとんどかかりません。次に、最近の追加のみを追加します。
SELECT sum(sum_diff) AS total_sum -- takes care of possible missing rows
FROM (
SELECT sum_diff
FROM shares_summed
WHERE poolid = 'xmr1'
AND miner = '4BCeEPhod...'
UNION ALL
SELECT SUM(difficulty)
FROM shares
WHERE poolid = 'xmr1'
AND miner = '4BCeEPhod...'
AND created >= '2018-01-01 0:0'
) sub;
時々しきい値を調整するだけで、 REFRESH
MVになります。 CONCURRENTLY
オプションを使用して、同時実行の問題を回避するのが最適です多数のINSERT
s検討してください:
値を覚えて、それに応じてクエリを調整します。別のテーブルに格納する場合があります。
さらに高速にするには、 パーティションテーブル を作成し、毎月(または何でも)後に計算済みの合計で別のパーティションを追加します。これを自動化するために毎月のcronジョブを簡単にスケジュールできます。その場合、しきい値は常に月の初日程度になります。
SELECT sum(sum_diff) AS total_sum
FROM (
SELECT SUM(difficulty) AS sum_diff
FROM shares_summed_master -- includes all partitions
WHERE poolid = 'xmr1'
AND miner = '4BCeEPhod...'
UNION ALL
SELECT SUM(difficulty)
FROM shares
WHERE poolid = 'xmr1'
AND miner = '4BCeEPhod...'
AND created >= date_trunc('month', now()) -- careful, current time zone affects it
) sub;
重複がないこと、および事前に計算された合計が最新であることを確認してください。
Postgres 10を使用して、テーブルのパーティション分割が大幅に改善および簡略化されました。最新のポイントリリースを実行してください。Postgres10.2でいくつかのコーナーケースのバグが修正されました。上記のマニュアルへのリンクはバージョン9.6用です。 現在のバージョンはこちら 。