我用Google搜索,制作了15个不同订单的不同索引(包括interval_end, ticker, interval_start DESC
)和ASC/DESC
,最终唯一使用的是idx_onemin_intervalstart
。我相信<=
运算符正在杀死我。
我已经阅读了所有关于索引的PostgreSQL手册,但我仍然感到困惑,这不是一个例子。
explain analyze
SELECT open
FROM onemin_interval
WHERE ticker = 'QQQ'
AND interval_end <= 1326810600000
ORDER BY interval_start DESC
LIMIT 19960;
表public.onemin_interval
Column | Type | Modifiers
---------------------+---------------+-----------
interval_start | numeric(13,0) |
interval_end | numeric(13,0) |
open | numeric(10,2) |
close | numeric(10,2) |
high | numeric(10,2) |
low | numeric(10,2) |
volume_for_interval | bigint |
ticker | character(10) |
humantimeopen | character(23) |
humantimeclose | character(23) |
adlval | bigint |
"idx_onemin_intervalend" btree (interval_end)
"idx_onemin_intervalend_intervalstart" btree (interval_end, interval_start)
"idx_onemin_intervalstart" btree (interval_start DESC)
"idx_onemin_ticker" btree (ticker)
"idx_onemin_ticker_intervalend" btree (ticker, interval_end)
Limit (cost=0.00..10295.29 rows=19960 width=20) (actual time=581.856..1731.352 rows=19960 loops=1)
-> Index Scan Backward using idx_onemin_intervalstart on onemin_interval (cost=0.00..36843.32 rows=71430 width=20) (actual time=581.842..1621.713 rows=19960 loops=1)
Filter: ((interval_end <= 1326810600000::numeric) AND (ticker = 'QQQ'::bpchar))
Total runtime: 1791.594 ms
(4 rows)
我为测试目的添加了大量索引,并运行ANALYZE onemin_interval
。查询几乎与以前相同:
explain analyze
SELECT open
FROM onemin_interval
WHERE ticker = 'QQQ'
AND interval_end <= 1327698068642
ORDER BY interval_start DESC
LIMIT 19960;
Limit (cost=0.00..5849.68 rows=19960 width=16) (actual time=0.088..394.596 rows=19960 loops=1)
-> Index Scan using test11 on onemin_interval (cost=0.00..21748.74 rows=74210 width=16) (actual time=0.079..298.848 rows=19960 loops=1)
Filter: ((interval_end <= 1327698068642::bigint) AND (ticker = 'QQQ'::text))
Total runtime: 1442.898 ms
(4 rows)
表public.onemin_interval
Column | Type | Modifiers
--------------------+---------------+-----------
interval_start | bigint |
interval_end | bigint |
open | numeric |
close | numeric |
high | numeric |
low | numeric |
volume_for_interval | bigint |
ticker | text |
humantimeopen | character(23) |
humantimeclose | character(23) |
adlval | bigint |
"idx_onemin_intervalend" btree (interval_end)
"idx_onemin_intervalend_intervalstart" btree (interval_end, interval_start)
"idx_onemin_intervalstart" btree (interval_start)
"idx_onemin_ticker" btree (ticker)
"idx_onemin_ticker_intervalend" btree (ticker, interval_end)
"test1" btree (interval_end DESC)
"test10" btree (ticker, interval_end DESC, interval_start DESC)
"test11" btree (interval_start DESC)
"test12" btree (interval_start DESC, interval_end DESC, ticker)
"test13" btree (interval_start DESC, ticker, interval_end DESC)
"test14" btree (ticker, interval_start DESC, interval_end DESC)
"test15" btree (interval_end DESC, interval_start DESC, ticker)
"test3" btree (interval_end)
"test4" btree (interval_end DESC, ticker)
"test5" btree (interval_end, ticker)
"test6" btree (ticker, interval_end DESC)
"test7" btree (ticker, interval_end)
"test8" btree (interval_end, ticker)
"test9" btree (interval_end DESC, ticker, interval_start DESC)
答案 0 :(得分:0)
首先,类型character(n)几乎总是一个糟糕的选择。如果您没有充分的理由并知道自己在做什么,请改用text
类型。 如果您实际需要强制执行最大长度,请使用CONSTRAINT。列ticker
的示例:
ALTER TABLE onemin_interval
ADD CONSTRAINT onemin_interval_ticker_len CHECK (length(ticker) < 11);
显然你有像'QQQ'这样的值,所以10的长度似乎是随意的。这将减少表格大小(可能显着)并加快......一切。
另外,请考虑使用bigint
代替numeric(13,0)
。不管怎样你都不存储小数位数,这可能是优秀的设计。 numeric(13,0)
需要13个字节(5 + 4x2),bigint需要8个字节,并且对它的操作在各方面都要快。
最后:bigint
列也可以是integer
吗? (如果范围-2147483648到+2147483647在整个表的使用寿命期间足够大。)
为什么索引idx_onemin_ticker_intervalend
没有使用?
我会尝试几件事:
如果您无法将数据类型更改为text
,请使用显式转换为character(10)
来测试查询:
WHERE ticker = 'QQQ'::character(10)
较旧版本的PostgreSQL可能无法使用character(10)
的索引作为三个字符('QQQ'
)的未填充字符串。但是,在我的测试中,PostgreSQL 8.4和9.1中都使用了索引 。
尝试颠倒多列索引中列的顺序。
idx_onemin_ticker_intervalend btree (interval_end DESC, ticker)
Order of the columns in a multi-column index is relevant。虽然,在您的情况下,如果一切都按顺序排列,它应该无关,因为您在两列上都包含条件。我还是会测试一下。
查询规划器似乎期望条件ticker = 'QQQ' AND interval_end <= 1326810600000
不是非常有选择性的 - 事实上,它们似乎是非常有选择性的(结果是4行)。尝试提升两列的default_statistics_target,如下所示:
ALTER TABLE onemin_ticker ALTER COLUMN interval_end SET STATISTICS 1000;
ALTER TABLE onemin_ticker ALTER COLUMN ticker SET STATISTICS 1000;
ANALYZE onemin_ticker;
甚至更多,最多10000(你有420k行)。默认值为100.这通常只对数据分配不均衡有所帮助,但对于庞大的表格,它应该很容易获得回报。
performance optimization in the PostgreSQL Wiki的一般建议始终适用。
最后,请注意,此类查询永远不会快速消失。排序70k行需要花费时间 - 并且引擎必须在应用LIMIT 19960
之前对所有 70k行进行排序。
正如我从您的更新中看到的那样,查询计划程序使用索引test11 (interval_start DESC)
- 这表示排序是查询中最昂贵的操作。
当您在排序之前按多个条件选择时,我没有看到索引如何进一步帮助您的方式。您的数据无法预先排序和预选 - 除了如果 WHERE
子句稳定,那么您可以创建一个部分索引,如:
CREATE INDEX onemin_interval_interval_start_part_idx
ON onemin_interval (interval_start DESC)
WHERE ticker = 'QQQ'::character(10)
AND interval_end <= 1326810600000
...导致减肥效果。但我不认为你那么幸运? 如果其中一个标准是稳定的,那么它会有很多帮助,所以你可以在那个标准上有一个部分索引。