【发布时间】:2016-08-08 20:57:50
【问题描述】:
我必须将数据库提取到外部数据库服务器以获取许可软件。 DB 必须是 Postgres,我无法从应用程序更改选择查询(无法更改源代码)。
表(必须是 1 个表)包含大约 6,5M 行,并且在主列(前缀)中具有唯一值。
所有请求都是读取请求,没有插入/更新/删除,每天有约 20 万次选择,峰值为 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;
解释分析显示如下
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,6s(仅测试这部分查询)
单独测试的部分查询:
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
关于数据本身: “前缀”列的前几位数字(前 5 位)相同,其余部分是不同的唯一数字。
Postgres 版本是 9.5 我更改了 Postgres 的以下设置:
random-page-cost = 40
effective_cashe_size = 4GB
shared_buffer = 4GB
work_mem = 1GB
我尝试了几种索引类型(unique、gin、gist、hash),但在所有情况下都没有使用索引(如上所述)并且结果速度是相同的。 我也做过,但没有明显的改进:
vacuum analyze verbose table
请推荐数据库和/或索引配置的设置,以加快此查询的执行时间。
当前硬件是 i5、SSD、Win7 上的 16GB RAM,但我可以选择购买更强大的硬件。 据我了解,对于读取(无插入/更新)占主导地位的情况,更快的 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) 另外,为了使用并行 seq.scan,我安装了 Postgres 9.6 版本。在这种情况下,仅当查询的最后一部分被省略时才使用并行扫描。所以,查询:
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而不是=? -
我无法更改 > 它位于我无权访问的源代码中。使用“=”而不是“like”的查询速度提高了 3 倍,但我无法更改它
-
random-page-cost = 40为什么这么高?在 SSD 上... -
默认是4,我增加看看有什么不同。基本上响应时间与 4 或 8 相同
-
它返回多少行?尝试优化这部分查询。我不知道有什么方法可以优化
const LIKE column之类的条件(不更改查询/数据结构/应用程序逻辑)。
标签: sql postgresql sqlperformance postgresql-performance