Ano*_*oop 6 sql postgresql query-optimization
有T(user, timestamp,...)100毫升+记录的表(PostgreSQL 9.1).
表单的查询
SELECT *
FROM T
WHERE user='abcd'
ORDER BY timestamp
LIMIT 1
Run Code Online (Sandbox Code Playgroud)
timestamp当有大约100000个用户记录时,使用索引而不是用户索引.
使用时间戳索引总是会得到较差的结果(20秒以上),因为它最终会扫描所有记录.timestamp通过更改要使用的查询来绕过索引ORDER BY DATE(timestamp)将导致查询求助于用户索引并给出小于100毫秒的结果.
为什么postgresql忽略user索引timestamp而是使用索引(时间戳索引需要查看所有记录)?是否有任何postgresql配置参数可以更改,以使查询使用用户名索引本身?
好问题,我刚才解决了这个问题.
您应该查看统计信息中的user='abcd'值数量,如下所示:
SELECT attname, null_frac, ag_width, n_distinct,
most_common_vals, most_common_freqs, histogram_bounds
FROM pg_stats
WHERE table_name='T';
Run Code Online (Sandbox Code Playgroud)
我的猜测是 - 这个值经常发生,你会在most_common_vals输出中找到它.most_common_freqs从中获取相同的元素将获得值的比率,将其乘以总行数(可以从中获取pg_class)以获得估计具有'abcd'值的行数.
Planner假设所有值都具有线性分布.实际上,事情当然是不同的.此外,目前还没有相关的统计数据(尽管正在朝这个方向开展一些工作).
所以,让我们user='abcd'取值,0.001在相应的most_common_freqs条目中有比率(每个问题).这意味着每1000行会出现一次值(假设为线性分布).看来,如果我们以任何方式扫描表格,我们将user='abcd'在大约1000行中击中我们.听起来应该快!计划者"认为"相同并在timestamp列上选择索引.
但事实并非如此.如果我们假设您的表T包含用户活动的日志,并user='abcd'在过去3周休假,那么这意味着我们必须从timestamp索引中读取相当多的行(3周的数据)我们实际上达到了我们想要的排.嗯,你作为DBA知道这一点,但计划者假设线性分布.
您必须欺骗规划人员使用您需要的东西,因为您对数据有更多了解.
使用子查询的OFFSET 0技巧:
SELECT *
FROM
(
SELECT * FROM T WHERE user='abcd' OFFSET 0
)
ORDER BY timestamp
LIMIT 1;
Run Code Online (Sandbox Code Playgroud)
这个技巧可以保护查询不被内联,因此内部部分可以自己执行.
使用CTE(命名子查询):
WITH s AS (
SELECT * FROM T WHERE user='abcd'
)
SELECT *
FROM s
ORDER BY timestamp
LIMIT 1;
Run Code Online (Sandbox Code Playgroud)
每个文件:
WITH查询的一个有用属性是,每次执行父查询时,它们仅被评估一次,即使父查询或兄弟WITH查询多次引用它们也是如此.
使用count(*)了aggrgated查询:
SELECT min(session_id), count(*) -- instead of simply `min(session_id)`
FROM T
WHERE user='abcd'
ORDER BY timestamp
LIMIT 1;
Run Code Online (Sandbox Code Playgroud)
这不是真的适用,但我想提一下.
请考虑升级到9.3.