我需要将数据库提取到外部数据库服务器上,用于授权软件。 数据库必须是Postgres,而且我不能更改应用程序中的选择查询(无法更改源代码)。
表格(必须为1个表格)包含大约650万行,并在主列(前缀)中具有唯一值。
所有请求均为读取请求,没有插入/更新/删除操作,每天有大约200,000个选择操作,峰值为15 TPS。
选择查询为:
SELECT prefix, changeprefix, deletelast, outgroup, tariff FROM table
WHERE '00436641997142' LIKE prefix
AND company = 0 and ((current_time between timefrom and timeto) or (timefrom is null and timeto is null)) and (strpos("Day", cast(to_char(now(), 'ID') as varchar)) > 0 or "Day" is null )
ORDER BY position('%' in prefix) ASC, char_length(prefix) DESC
LIMIT 1;
Explain analyze 显示如下
Limit (cost=406433.75..406433.75 rows=1 width=113) (actual time=1721.360..1721.361 rows=1 loops=1)
-> Sort (cost=406433.75..406436.72 rows=1188 width=113) (actual time=1721.358..1721.358 rows=1 loops=1)
Sort Key: ("position"((prefix)::text, '%'::text)), (char_length(prefix)) DESC
Sort Method: quicksort Memory: 25kB
-> Seq Scan on table (cost=0.00..406427.81 rows=1188 width=113) (actual time=1621.159..1721.345 rows=1 loops=1)
Filter: ((company = 0) AND ('00381691997142'::text ~~ (prefix)::text) AND ((strpos(("Day")::text, (to_char(now(), 'ID'::text))::text) > 0) OR ("Day" IS NULL)) AND (((('now'::cstring)::time with time zone >= (timefrom)::time with time zone) AN (...)
Rows Removed by Filter: 6417130
Planning time: 0.165 ms
Execution time: 1721.404 ms`
查询中最慢的部分是:
SELECT prefix, changeprefix, deletelast, outgroup, tariff FROM table
WHERE '00436641997142' LIKE prefix
这段代码生成了1.6秒的执行时间(仅测试此查询的这一部分)
单独测试的查询部分:
Seq Scan on table (cost=0.00..181819.07 rows=32086 width=113) (actual time=1488.359..1580.607 rows=1 loops=1)
Filter: ('004366491997142'::text ~~ (prefix)::text)
Rows Removed by Filter: 6417130
Planning time: 0.061 ms
Execution time: 1580.637 ms
关于数据本身: 列"prefix"具有相同的前几位数字(前5位),其余是不同的,唯一的。
Postgres版本为9.5 我已更改以下Postgres设置:
random-page-cost = 40
effective_cashe_size = 4GB
shared_buffer = 4GB
work_mem = 1GB
我尝试了几种索引类型(unique、gin、gist、hash),但在所有情况下,索引都没有被使用(如上所述的explain语句),结果速度相同。 我也做了一些尝试,但没有明显的改进:
vacuum analyze verbose table
请推荐数据库和/或索引配置的设置,以加快此查询的执行时间。
当前硬件为i5、SSD、16GB RAM在Win7上,但我有购买更强大硬件的选项。据我所知,在读取(无插入/更新)占主导地位的情况下,更快的CPU核心比核心数量或磁盘速度更重要,请确认。
附加1: 添加了9个索引后,索引仍未被使用。
附加2: 1)我发现不使用索引的原因是查询中的单词顺序问题。如果查询如下:
SELECT prefix, changeprefix, deletelast, outgroup, tariff FROM table WHERE prefix like '00436641997142%'
AND company = 0 and
((current_time between timefrom and timeto) or (timefrom is null and timeto is null)) and (strpos("Day", cast(to_char(now(), 'ID') as varchar)) > 0 or "Day" is null )
ORDER BY position('%' in prefix) ASC, char_length(prefix) DESC LIMIT 1
它使用索引。
注意区别:
... WHERE '00436641997142%' like prefix ...
正确使用索引的查询:
... WHERE prefix like '00436641997142%' ...
由于我无法更改查询本身,有什么方法可以克服这个问题吗?我可以更改数据和Postgres设置,但无法更改查询本身。
2)此外,我安装了Postgres 9.6版本,以使用并行seq.scan。在这种情况下,只有当查询的最后一部分被省略时才会使用并行扫描。因此,查询如下:
SELECT prefix, changeprefix, deletelast, outgroup, tariff FROM table WHERE '00436641997142' LIKE prefix
AND company = 0 and
((current_time between timefrom and timeto) or (timefrom is null and timeto is null))
ORDER BY position('%' in prefix) ASC, char_length(prefix) DESC LIMIT 1
使用并行模式。
有什么办法可以强制执行原始查询(我无法更改查询):
SELECT prefix, changeprefix, deletelast, outgroup, tariff FROM erm_table WHERE '00436641997142' LIKE prefix
AND company = 0 and
((current_time between timefrom and timeto) or (timefrom is null and timeto is null)) and (strpos("Day", cast(to_char(now(), 'ID') as varchar)) > 0 or "Day" is null )
ORDER BY position('%' in prefix) ASC, char_length(prefix) DESC LIMIT 1
如何使用并行顺序扫描?
like
而不是=
有什么原因吗? - Abelistorandom-page-cost = 40
为什么这么高?在 SSD 上... - wildplasserconst LIKE column
这样的条件(而不改变查询/数据结构/应用程序逻辑)。 - Abelisto