我有一个包含8000万行的只读表:
Column | Type | Modifiers | Storage | Stats target | Description
-------------+------------------------+-----------+----------+--------------+-------------
id | character(11) | not null | extended | |
gender | character(1) | | extended | |
postal_code | character varying(10) | | extended | |
operator | character varying(5) | | extended | |
Indexes:
"categorised_phones_pkey" PRIMARY KEY, btree (id)
"operator_idx" btree (operator)
"postal_code_trgm_idx" gin (postal_code gin_trgm_ops)
id
是主键,包含唯一的手机号码。表行如下所示:
id | gender | postal_code | operator
----------------+--------------+----------------+------------
09567849087 | m | 7414776788 | mtn
09565649846 | f | 1268398732 | mci
09568831245 | f | 7412556443 | mtn
09469774390 | m | 5488312790 | mci
此查询第一次需要大约65秒,下次需要大约8秒:
select operator,count(*) from categorised_phones where postal_code like '1%' group by operator;
输出如下:
operator | count
----------+---------
mci | 4050314
mtn | 6235778
explain alanyze
的输出:
HashAggregate (cost=1364980.61..1364980.63 rows=2 width=10) (actual time=8257.026..8257.026 rows=2 loops=1)
Group Key: operator
-> Bitmap Heap Scan on categorised_phones (cost=95969.17..1312915.34 rows=10413054 width=2) (actual time=1140.803..6332.534 rows=10286092 loops=1)
Recheck Cond: ((postal_code)::text ~~ '1%'::text)
Rows Removed by Index Recheck: 25105697
Heap Blocks: exact=50449 lossy=237243
-> Bitmap Index Scan on postal_code_trgm_idx (cost=0.00..93365.90 rows=10413054 width=0) (actual time=1129.270..1129.270 rows=10287127 loops=1)
Index Cond: ((postal_code)::text ~~ '1%'::text)
Planning time: 0.540 ms
Execution time: 8257.392 ms
如何更快地进行此查询?
任何想法都会非常感激。
P.S:
我正在使用PostgreSQL 9.6.1
更新
我刚刚更新了这个问题。我已停用Parallel Query
并且结果已更改。
答案 0 :(得分:1)
对于涉及形式LIKE '%start'
的比较的查询,以及遵循PostgreSQL自己的建议,您可以使用以下索引:
CREATE INDEX postal_code_idx ON categorised_phones (postal_code varchar_pattern_ops) ;
使用该索引和一些模拟数据,您的执行计划很可能如下所示:
| QUERY PLAN | | :------------------------------------------------------------------------------------------------------------------------------------- | | HashAggregate (cost=2368.65..2368.67 rows=2 width=12) (actual time=18.093..18.094 rows=2 loops=1) | | Group Key: operator | | -> Bitmap Heap Scan on categorised_phones (cost=536.79..2265.83 rows=20564 width=4) (actual time=2.564..12.061 rows=22171 loops=1) | | Filter: ((postal_code)::text ~~ '1%'::text) | | Heap Blocks: exact=1455 | | -> Bitmap Index Scan on postal_code_idx (cost=0.00..531.65 rows=21923 width=0) (actual time=2.386..2.386 rows=22171 loops=1) | | Index Cond: (((postal_code)::text ~>=~ '1'::text) AND ((postal_code)::text ~<~ '2'::text)) | | Planning time: 0.119 ms | | Execution time: 18.122 ms |
您可以在 dbfiddle here
查看如果您使用LIKE 'start%'
和LIKE '%middle%'
进行两个查询,则应添加此索引,但保留已存在的索引。对于第二种匹配,Trigram索引可能证明是有用的。
<强>为什么吗
来自PostgreSQL documentation on operator classes:
运算符类
text_pattern_ops
,varchar_pattern_ops
和bpchar_pattern_ops
分别支持类型text,varchar和char上的B树索引。与默认运算符类的不同之处在于,这些值严格按字符进行比较,而不是根据特定于语言环境的排序规则进行比较。这使得当数据库不使用标准&#34; C&#34;语言环境。
来自PostgreSQL documentation on Index Types
如果模式是常量并且锚定到字符串的开头,优化器也可以使用B树索引来处理涉及模式匹配运算符
LIKE
和~
的查询 - 例如,col LIKE 'foo%'
或col ~ '^foo'
,但不是LIKE '%bar'
。但是,如果您的数据库不使用C语言环境,则需要使用特殊的运算符类创建索引,以支持模式匹配查询的索引;见下面的第11.9节。也可以对ILIKE
和~*
使用B树索引,但前提是模式以非字母字符开头,即不受大写/小写转换影响的字符。 / p>
<强>更新强>
如果执行的查询总是涉及修复(且相对较小)的LIKE 'x%'
个表达式,请考虑使用partial indexes
。
例如,对于LIKE '1%'
,您具有以下索引和以下查询计划(它显示了大约3倍的改进):
CREATE INDEX idx_1 ON categorised_phones (operator) WHERE postal_code LIKE '1%';
VACUUM categorised_phones ;
| QUERY PLAN | | :-------------------------------------------------------------------------------------------------------------------------------------------- | | GroupAggregate (cost=0.29..658.74 rows=3 width=12) (actual time=3.235..6.493 rows=2 loops=1) | | Group Key: operator | | -> Index Only Scan using idx_1 on categorised_phones (cost=0.29..554.10 rows=20921 width=4) (actual time=0.028..3.266 rows=22290 loops=1) | | Heap Fetches: 0 | | Planning time: 0.293 ms | | Execution time: 6.517 ms |