使用函数索引优化PostgreSQL?
在进行一些性能调整时,我遇到了Instagram工程团队的以下帖子: 在我们的一些表上,我们需要索引相当长的字符串(例如,64个字符的base64令牌),在这些字符串上创建索引最终会复制大量数据。对于这些,Postgres的功能索引功能非常有用:使用函数索引优化PostgreSQL?,postgresql,Postgresql,在进行一些性能调整时,我遇到了Instagram工程团队的以下帖子: 在我们的一些表上,我们需要索引相当长的字符串(例如,64个字符的base64令牌),在这些字符串上创建索引最终会复制大量数据。对于这些,Postgres的功能索引功能非常有用: CREATE INDEX CONCURRENTLY on tokens (substr(token), 0, 8) 虽然将有多行与该前缀匹配,但让Postgre与这些前缀匹配,然后进行向下筛选很快,结果索引的大小是对整个字符串进行索引时大小的1/1
CREATE INDEX CONCURRENTLY on tokens (substr(token), 0, 8)
虽然将有多行与该前缀匹配,但让Postgre与这些前缀匹配,然后进行向下筛选很快,结果索引的大小是对整个字符串进行索引时大小的1/10
这看起来是个好主意,所以我尝试了一下——我们有很多项都是由校验和键控的
我们的结果不好。我想知道是否还有其他人运气好
首先,这篇博文看起来是错误的:
CREATE INDEX CONCURRENTLY on tokens (substr(token), 0, 8)
那不应该是
CREATE INDEX CONCURRENTLY on tokens (substr(token, 0, 8));
我们的一个字段基于40个字符的散列。所以我试着:
CREATE INDEX __speed_idx_test_8 on foo (substr(bar, 0, 8));
CREATE INDEX __speed_idx_test_20 on foo (substr(bar, 0, 20));
查询计划器不会使用它
所以我试着:
CREATE INDEX __speed_idx_test_8 on foo (substr(bar, 0, 8));
CREATE INDEX __speed_idx_test_20 on foo (substr(bar, 0, 20));
查询计划器仍然不会使用它
然后我试着:
CREATE INDEX __speed_idx_test_40 on foo (substr(bar, 0, 40));
尽管如此,规划者还是不会使用它
如果我们尝试禁用seq扫描会怎么样
set enable_seqscan=false;
没有
让我们回到原始索引
CREATE INDEX __speed_idx_original on foo (bar);
set enable_seqscan = True;
这就行了
然后我想——也许我需要在查询中使用函数来使用函数索引。因此,我尝试更改查询:
旧的:
新的
这起作用了
是否有人知道,如果不添加额外的搜索条件,是否有可能实现此功能?我不想这样做,但看看文件大小和速度的改进。。。哇
如果你想知道“解释-分析”的输出是什么
-- seq scan
Seq Scan on foo (cost=10000000000.00..10000073130.77 rows=1 width=1921) (actual time=373.785..1563.551 rows=1 loops=1)
Filter: (hash = 'eae1d1728963f107fa7d8136bcf7c72572896e1d'::bpchar)
Rows Removed by Filter: 450252
Total runtime: 1563.687 ms
-- index scan
Index Scan using __speed_idx_original on foo (cost=0.00..16.53 rows=1 width=1920) (actual time=0.060..0.061 rows=1 loops=1)
Index Cond: (hash = 'eae1d1728963f107fa7d8136bcf7c72572896e1d'::bpchar)
Total runtime: 1.501 m
-- index scan with substring function
Index Scan using __speed_idx_test_8 on foo (cost=0.00..16.37 rows=1 width=1913) (actual time=0.134..0.134 rows=0 loops=1)
Index Cond: (substr((hash)::text, 0, 8) = 'eae1d172'::text)
Filter: (hash = 'eae1d1728963f107fa7d8136bcf7c72572896e1d'::bpchar)
Total runtime: 0.216 ms
它仅在使用WHERE子句中的函数时有效。函数签名向查询计划器提示,从函数返回的标量值包含在索引中。
这只适用于不可变函数。Volatile函数(在每次调用时不返回相同结果的函数,如rand())不能使用此方法编制索引。谢谢。这正是我所想/害怕的。没有办法让PostgreSQL自动意识到这是一个前缀索引。(当然有,但这涉及到为PostgreSQL添加一项功能——请随意资助这项工作!)。因此,您需要修改查询以利用表达式索引。