rde*_*ges 6 postgresql indexing timestamp postgresql-performance
我有一个名为PostgreSQL的表queries_query,它有很多列.
其中两个列created和user_sid我的应用程序经常在SQL查询中一起使用,以确定给定用户在过去30天内完成了多少查询.在最近30天之前的任何时间查询这些统计数据是非常非常罕见的.
这是我的问题:
我目前通过运行以下方法在这两列上创建了我的多列索引:
CREATE INDEX CONCURRENTLY some_index_name ON queries_query (user_sid, created)
Run Code Online (Sandbox Code Playgroud)
但我想进一步限制索引只关心创建日期在过去30天内的查询.我尝试过以下方法:
CREATE INDEX CONCURRENTLY some_index_name ON queries_query (user_sid, created)
WHERE created >= NOW() - '30 days'::INTERVAL`
Run Code Online (Sandbox Code Playgroud)
但这引发了一个异常,说明我的函数必须是不可变的.
我很乐意让这个工作,以便我可以优化我的索引,并削减Postgres需要执行这些重复查询的资源.
您尝试使用时会遇到异常,now()因为该功能不是IMMUTABLE(显然),我在这里引用手册:
索引定义中使用的所有函数和运算符必须是"不可变的"......
我在这里看到了两种利用(效率更高)部分索引的方法:
CREATE INDEX queries_recent_idx ON queries_query (user_sid, created)
WHERE created > '2013-01-07 00:00'::timestamp;
Run Code Online (Sandbox Code Playgroud)
假设 created实际上定义为timestamp.timestamp为timestamptz列(timestamp with time zone)提供常量是行不通的.转换timestamp为timestamptz(或反之亦然)取决于当前时区设置,并且不是不可变的.使用匹配数据类型的常量.了解带/不带时区的时间戳的基础知识:
在流量较低的小时内删除并重新创建该索引,可能每天或每周都有一个cron作业(或者对你来说足够好).创建索引非常快,尤其是部分索引相对较小.此解决方案也不需要向表中添加任何内容.
假设没有对表的并发访问,可以使用如下函数完成自动索引重新创建:
CREATE OR REPLACE FUNCTION f_index_recreate()
RETURNS void AS
$func$
BEGIN
DROP INDEX IF EXISTS queries_recent_idx;
EXECUTE format('
CREATE INDEX queries_recent_idx
ON queries_query (user_sid, created)
WHERE created > %L::timestamp'
, LOCALTIMESTAMP - interval '30 days'); -- timestamp constant
-- , now() - interval '30 days'); -- alternative for timestamptz
END
$func$ LANGUAGE plpgsql;
Run Code Online (Sandbox Code Playgroud)
呼叫:
SELECT f_index_recreate();
Run Code Online (Sandbox Code Playgroud)
now()(就像你所拥有的)相当于CURRENT_TIMESTAMP并且返回timestamptz.投射到timestamp用now()::timestamp或使用LOCALTIMESTAMP替代.
用Postgres 9.2 - 9.4测试.
SQL小提琴.
如果您必须处理并发访问,请使用CREATE INDEX CONCURRENTLY.但是你不能将这个命令包装成一个函数,因为根据文档:
...常规
CREATE INDEX命令可以在事务块中执行,但CREATE INDEX CONCURRENTLY不能.
因此,有两个单独的交易:
CREATE INDEX CONCURRENTLY queries_recent_idx2 ON queries_query (user_sid, created)
WHERE created > '2013-01-07 00:00'::timestamp; -- your new condition
Run Code Online (Sandbox Code Playgroud)
然后:
DROP INDEX CONCURRENTLY IF EXISTS queries_recent_idx;
Run Code Online (Sandbox Code Playgroud)
(可选)重命名为旧名称:
ALTER INDEX queries_recent_idx2 RENAME TO queries_recent_idx;
Run Code Online (Sandbox Code Playgroud)
archived在表格中添加标签:
ALTER queries_query ADD COLUMN archived boolean NOT NULL DEFAULT FALSE;
Run Code Online (Sandbox Code Playgroud)
UPDATE 您所选择的时间间隔列"退出"旧行并创建索引,如:
CREATE INDEX some_index_name ON queries_query (user_sid, created)
WHERE NOT archived;
Run Code Online (Sandbox Code Playgroud)
为查询添加匹配条件(即使看起来多余),以允许它使用索引.检查EXPLAIN ANALYZE查询规划器是否捕获 - 它应该能够在较新的日期使用索引进行查询.但它不会理解更复杂的条件不完全匹配.
您不必删除并重新创建索引,但是UPDATE表上的内容可能比索引重新创建更昂贵,并且表格略大.
我会选择第一个选项(索引娱乐).事实上,我在几个数据库中使用此解决方案.第二个会导致更高成本的更新.
随着时间的推移,两种解决方案都保持其有用性,随着索引中包含更多过时的行,性能会逐渐恶化.
| 归档时间: |
|
| 查看次数: |
1777 次 |
| 最近记录: |