I recently asked a question关于加快后缀通配符文本查找,例如Pg中的SELECT a, b, c FROM t WHERE a LIKE 'abcde%'
。最后,通过实现以下索引,我可以在每个查询中获得200毫秒到800毫秒。
CREATE INDEX idxa ON t (Lower(a) varchar_pattern_ops);
如果可能的话,我现在有兴趣将查询加速一个数量级;也许在200-800微秒之间。这可以吗?
整个表格大约是1 GB的原始文本(约800万+行),并且可以做得更小,因此它可以很容易地适合内存。我可以在Pg之上实现一个缓存,这是一个随着时间的推移会播种的缓存吗?也许memcached或其他东西。由于大多数缓存都有精确的密钥查找,我如何从缓存中进行通配符搜索?
顺便说一下,作为一个信息点,我确实在Mongodb中加载了整个表格,虽然我对精确搜索a = 'abcdefg'
进行了非常快速的查找,但上面的Mongodb的通配符搜索实际上不如Postgres。< / p>
答案 0 :(得分:4)
你仍然可以挤出更多。
首先,我建议使用数据类型text
而不是varchar
。所以text_pattern_ops
代替varchar_pattern_ops
。但这不会影响性能。
接下来,由于您的列最多包含100个字符,但您只使用前n个(20?)字符,因此我建议使用lower(left(a, 20)
代替lower(a)
的索引要小得多在我的answer to your prequel question。
索引搜索本身执行相同的操作,但服务器必须访问磁盘或RAM中的更多页面。每个RAM或磁盘页面的行数较少,因此每次查找都需要访问更多页面。此外,页面将更快退出缓存等。这对于像您这样的大表来说尤为重要。将可以搜索的字母范围限制为所需的最小值。这给你留下了类似的东西:
CREATE INDEX t_a_lower_left_idx ON t (lower(left(a, 20)) text_pattern_ops);
此外,您可以在查询中使用特殊运算符~>=~
和~<~
,就像我在answer I linked to中演示一样:
SELECT * FROM tbl WHERE lower(a) ~>=~ 'abcde' AND lower(a) ~<~ ('abcdf')
注意第二个表达式中的'f'而不是'e'。问题是:如何根据语言环境'C'得到“下一个”字符?
SELECT chr(ascii('é')+1));
所以你可以:
SELECT * FROM tbl WHERE lower(a) ~>=~ 'abcde'
AND lower(a) ~<~ ('abcd' || chr(ascii('e')+1))
我用一张藏有五十万行的天然桌子进行了测试。产生650行的搜索项在第一次查询时花费4毫秒,在第二次查询时花费3毫秒。 非常很大程度上取决于找到多少行。只产生1行的搜索词在这里需要0.044毫秒。
因此,限制搜索词的最小长度以禁止无论如何会产生太多行的无用查询。最喜欢3或4个字符。
接下来,你可以cluster这样的表:
CLUSTER tbl USING t_a_lower_left_idx
之后,我的测试用例耗时2.5毫秒而不是3毫秒。
当然,所有basic advice for performance optimization都适用。
如果上述内容不够,您可能需要考虑在ramdisk或tmpfs分区(Linux)上创建tablespace并在那里创建索引,甚至将整个表放在那里。我确信您了解数据库的易失性介质的安全隐患。只有在您能够承受丢失所有数据的情况下才能这样做。
CREATE INDEX t_a_lower_left_idx ON t (lower(left(a, 20)) text_pattern_ops)
TABLESPACE indexspace;
如果您的数据库设置正确并且您的计算机有足够的RAM并且表格被大量读取,那么标准缓存算法可能会自动提供大部分性能提升,而且您不会从中获得太多收益。