如果我添加一个额外的ORDER子句,则Postgres会进行位图扫描,并且性能会惊人地下降(4秒对0.06毫秒)。该应用程序变得无法使用。但是,我只是要求它排序一小部分结果,这些结果会被索引。
应如何修改查询,以便Postgres使用索引而不是位图扫描?因为应该使用索引,所以表中有1亿条记录。
慢速查询,位图扫描:
EXPLAIN ANALYZE
SELECT
valtr_id,
from_id,
to_id,
from_balance,
to_balance,
block_num
FROM value_transfer v
WHERE v.block_num<=2435013 AND v.to_id = 22479
ORDER BY block_num desc,valtr_id desc
LIMIT 1
Limit (cost=1235402.27..1235402.27 rows=1 width=32) (actual time=4665.595..4665.596 rows=1 loops=1)
-> Sort (cost=1235402.27..1238237.41 rows=1134056 width=32) (actual time=4665.594..4665.594 rows=1 loops=1)
Sort Key: block_num DESC, valtr_id DESC
Sort Method: top-N heapsort Memory: 25kB
-> Bitmap Heap Scan on value_transfer v (cost=21229.61..1229731.99 rows=1134056 width=32) (actual time=268.917..4170.374 rows=1102867 loops=1)
Recheck Cond: (to_id = 22479)
Rows Removed by Index Recheck: 9412580
Filter: (block_num <= 2435013)
Heap Blocks: exact=32392 lossy=132879
-> Bitmap Index Scan on vt_to_id_idx (cost=0.00..20946.10 rows=1134071 width=0) (actual time=254.870..254.870 rows=1102867 loops=1)
Index Cond: (to_id = 22479)
Planning time: 0.290 ms
Execution time: 4665.634 ms
(13 rows)
现在,如果仅删除一个ORDER条件,查询速度将提高几个数量级。
没有ORDER by valtr_id DESC
,我的表现如下:
EXPLAIN ANALYZE
SELECT
valtr_id,
from_id,
to_id,
from_balance,
to_balance,
block_num
FROM value_transfer v
WHERE v.block_num<=2435013 AND v.to_id = 22479
ORDER BY block_num desc
LIMIT 1
Limit (cost=0.57..2.46 rows=1 width=32) (actual time=0.028..0.028 rows=1 loops=1)
-> Index Scan using idx_2 on value_transfer v (cost=0.57..2148650.88 rows=1134056 width=32) (actual time=0.027..0.027 rows=1 loops=1)
Index Cond: ((to_id = 22479) AND (block_num <= 2435013))
Planning time: 0.310 ms
Execution time: 0.060 ms
(5 rows)
如何告诉Postgres首先使用INDEX,然后才使用-对结果进行排序?
我的表是这样定义的:
CREATE TABLE value_transfer (
valtr_id BIGSERIAL PRIMARY KEY,
tx_id BIGINT REFERENCES transaction(tx_id) ON DELETE CASCADE ON UPDATE CASCADE,
block_id INT REFERENCES block(block_id) ON DELETE CASCADE ON UPDATE CASCADE,
block_num INT NOT NULL,
from_id INT NOT NULL,
to_id INT NOT NULL,
value NUMERIC DEFAULT 0,
from_balance NUMERIC DEFAULT 0,
to_balance NUMERIC DEFAULT 0,
kind CHAR NOT NULL,
depth INT DEFAULT 0,
error TEXT NOT NULL
);
我在测试期间创建了许多不同的索引:
indexname | indexdef
---------------------+-----------------------------------------------------------------------------------------
value_transfer_pkey | CREATE UNIQUE INDEX value_transfer_pkey ON public.value_transfer USING btree (valtr_id)
vt_block_id_idx | CREATE INDEX vt_block_id_idx ON public.value_transfer USING btree (block_id)
vt_block_num_idx | CREATE INDEX vt_block_num_idx ON public.value_transfer USING btree (block_num)
vt_from_id_idx | CREATE INDEX vt_from_id_idx ON public.value_transfer USING btree (from_id)
vt_to_id_idx | CREATE INDEX vt_to_id_idx ON public.value_transfer USING btree (to_id)
vt_tx_from_idx | CREATE INDEX vt_tx_from_idx ON public.value_transfer USING btree (tx_id)
idx_1 | CREATE INDEX idx_1 ON public.value_transfer USING btree (from_id, block_num DESC)
idx_2 | CREATE INDEX idx_2 ON public.value_transfer USING btree (to_id, block_num DESC)
idx_1_rev | CREATE INDEX idx_1_rev ON public.value_transfer USING btree (block_num DESC, from_id)
idx_2_rev | CREATE INDEX idx_2_rev ON public.value_transfer USING btree (block_num DESC, to_id)
valtr_ordered_idx | CREATE INDEX valtr_ordered_idx ON public.value_transfer USING btree (valtr_id)
(11 rows)