Using <Partial Index> speedup max,min search
时间:2011-02-22
来源:互联网
今天在给数据仓库ETL做优化的时候,发现PostgreSQL一个比较奇怪的问题。
首先介绍一下环境:
有一个表需要被数据仓库的ETL程序抽取,表名暂且叫tbl_test,数据量大概在1亿左右。
修改标记字段为modifytime,允许空,BTREE索引。
创建标记字段为createtime,允许空,无索引。
抽取的时候按照modifytime范围取值进行抽取。
问题来了:
select max(modifytime) from tbl_ups_user_info;
Time: 10563.757 ms
select min(modifytime) from tbl_ups_user_info;
Time: 0.901 ms
差别实在太大了。
执行计划如下:
skyups=> explain analyze select max(modifytime) from tbl_ups_user_info;
QUERY PLAN
------------------------------------------------------------------------------------------------------------------------------------
------------------------------------------------------------
Result (cost=0.05..0.06 rows=1 width=0) (actual time=11801.932..11801.932 rows=1 loops=1)
InitPlan 1 (returns $0)
-> Limit (cost=0.00..0.05 rows=1 width=
(actual time=11801.925..11801.926 rows=1 loops=1)
-> Index Scan Backward using idx_tbl_ups_user_info_modifytime on tbl_ups_user_info (cost=0.00..1644338.43 rows=32003931
width=
(actual time=11801.922..11801.922 rows=1 loops=1)
Filter: (modifytime IS NOT NULL)
Total runtime: 11801.985 ms
(6 rows)
Time: 11803.978 ms
skyups=> explain analyze select min(modifytime) from tbl_ups_user_info;
QUERY PLAN
------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------
Result (cost=0.05..0.06 rows=1 width=0) (actual time=0.067..0.067 rows=1 loops=1)
InitPlan 1 (returns $0)
-> Limit (cost=0.00..0.05 rows=1 width=
(actual time=0.064..0.064 rows=1 loops=1)
-> Index Scan using idx_tbl_ups_user_info_modifytime on tbl_ups_user_info (cost=0.00..1644338.43 rows=32003931 width=
(actual time=0.063..0.063 rows=1 loops=1)
Filter: (modifytime IS NOT NULL)
Total runtime: 0.109 ms
(6 rows)
Time: 0.942 ms
从执行计划上看,预计COST是一致的,但是真实时间差了1万倍以上,分析差别在 Index Scan Backward using idx_tbl_ups_user_info_modifytime ,但是索引是平衡二叉树索引,理论上来说得出结果的时间一致才对。
为了验证,把索引修改为desc排序的索引。
结果还是一样,MAX得到的时间还是要10多秒。
于是想到会不会是空在作怪,默认情况下,空是被认为是最大的。是不是在做max取值时去搜索了空呢?
赶紧来验证一把:
select count(*) from tbl_test where modifytime is null;
9千万条记录.
select count(*) from tbl_test where createtime is null;
0条记录
create index idx_test_2 on tbl_test (createtime) ;
select min(createtime) from tbl_test;
0.9毫秒
select min(createtime) from tbl_test;
0.9毫秒
果然,问题就处在空。
解决这个问题只需要建立partial索引。
如下:
create index idx_test_1 on tbl_test(modifytime) where modifytime is not null;
再次搜索
max(modifytime)和min(modifytime)时间都在1毫秒以内了。
后记:
PostgreSQL 二叉树索引不记录空值,那么应该没有必要再搜索索引之外的其他PAGE才对,这点有点费解。
两次索引的大小如下
第一种情况:
create index idx_test_1 on tbl_test(modifytime);
1023MB;
skyups=> explain select count(*) from tbl_ups_user_info where modifytime is not null;
QUERY PLAN
---------------------------------------------------------------------------------
Aggregate (cost=963192.07..963192.08 rows=1 width=0)
-> Seq Scan on tbl_ups_user_info (cost=0.00..962603.70 rows=235346 width=0)
Filter: (modifytime IS NOT NULL)
(3 rows)
Time: 1.669 ms
skyups=> explain select count(*) from tbl_ups_user_info where modifytime is null;
QUERY PLAN
-----------------------------------------------------------------------------------
Aggregate (cost=1042064.76..1042064.77 rows=1 width=0)
-> Seq Scan on tbl_ups_user_info (cost=0.00..962603.70 rows=31784424 width=0)
Filter: (modifytime IS NULL)
(3 rows)
Time: 0.547 ms
第二种情况:
create index idx_test_1 on tbl_test(modifytime) where modifytime is not null;
4MB;
skyups=> explain select count(*) from tbl_ups_user_info where modifytime is null;
QUERY PLAN
-----------------------------------------------------------------------------------
Aggregate (cost=1042082.04..1042082.05 rows=1 width=0)
-> Seq Scan on tbl_ups_user_info (cost=0.00..962607.48 rows=31789824 width=0)
Filter: (modifytime IS NULL)
(3 rows)
Time: 1.771 ms
skyups=> explain select count(*) from tbl_ups_user_info where modifytime is not null;
QUERY PLAN
--------------------------------------------------------------------------------------------------------------------------
Aggregate (cost=231425.54..231425.55 rows=1 width=0)
-> Index Scan using idx_tbl_ups_user_info_modifytime on tbl_ups_user_info (cost=0.00..230849.98 rows=230224 width=0)
(2 rows)
Time: 0.588 ms
总结:
1. 二叉树索引中记录了modifytime is null的row page 和item号.(注意,在ORACLE中二叉树索引是不包含空的)
这个从建立索引的语法中就可以理解:
CREATE INDEX test2_info_nulls_low ON test2 (info NULLS FIRST);
CREATE INDEX test3_desc_index ON test3 (id DESC NULLS LAST);
2. 当需要使用max和min这类函数时,如果被运算的字段包含较多的空或者没有使用NOT NULL约束,建议在建立索引时使用partial索引。避免而外的PAGE扫描。
首先介绍一下环境:
有一个表需要被数据仓库的ETL程序抽取,表名暂且叫tbl_test,数据量大概在1亿左右。
修改标记字段为modifytime,允许空,BTREE索引。
创建标记字段为createtime,允许空,无索引。
抽取的时候按照modifytime范围取值进行抽取。
问题来了:
select max(modifytime) from tbl_ups_user_info;
Time: 10563.757 ms
select min(modifytime) from tbl_ups_user_info;
Time: 0.901 ms
差别实在太大了。
执行计划如下:
skyups=> explain analyze select max(modifytime) from tbl_ups_user_info;
QUERY PLAN
------------------------------------------------------------------------------------------------------------------------------------
------------------------------------------------------------
Result (cost=0.05..0.06 rows=1 width=0) (actual time=11801.932..11801.932 rows=1 loops=1)
InitPlan 1 (returns $0)
-> Limit (cost=0.00..0.05 rows=1 width=

-> Index Scan Backward using idx_tbl_ups_user_info_modifytime on tbl_ups_user_info (cost=0.00..1644338.43 rows=32003931
width=

Filter: (modifytime IS NOT NULL)
Total runtime: 11801.985 ms
(6 rows)
Time: 11803.978 ms
skyups=> explain analyze select min(modifytime) from tbl_ups_user_info;
QUERY PLAN
------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------
Result (cost=0.05..0.06 rows=1 width=0) (actual time=0.067..0.067 rows=1 loops=1)
InitPlan 1 (returns $0)
-> Limit (cost=0.00..0.05 rows=1 width=

-> Index Scan using idx_tbl_ups_user_info_modifytime on tbl_ups_user_info (cost=0.00..1644338.43 rows=32003931 width=

(actual time=0.063..0.063 rows=1 loops=1)
Filter: (modifytime IS NOT NULL)
Total runtime: 0.109 ms
(6 rows)
Time: 0.942 ms
从执行计划上看,预计COST是一致的,但是真实时间差了1万倍以上,分析差别在 Index Scan Backward using idx_tbl_ups_user_info_modifytime ,但是索引是平衡二叉树索引,理论上来说得出结果的时间一致才对。
为了验证,把索引修改为desc排序的索引。
结果还是一样,MAX得到的时间还是要10多秒。
于是想到会不会是空在作怪,默认情况下,空是被认为是最大的。是不是在做max取值时去搜索了空呢?
赶紧来验证一把:
select count(*) from tbl_test where modifytime is null;
9千万条记录.
select count(*) from tbl_test where createtime is null;
0条记录
create index idx_test_2 on tbl_test (createtime) ;
select min(createtime) from tbl_test;
0.9毫秒
select min(createtime) from tbl_test;
0.9毫秒
果然,问题就处在空。
解决这个问题只需要建立partial索引。
如下:
create index idx_test_1 on tbl_test(modifytime) where modifytime is not null;
再次搜索
max(modifytime)和min(modifytime)时间都在1毫秒以内了。
后记:
PostgreSQL 二叉树索引不记录空值,那么应该没有必要再搜索索引之外的其他PAGE才对,这点有点费解。
两次索引的大小如下
第一种情况:
create index idx_test_1 on tbl_test(modifytime);
1023MB;
skyups=> explain select count(*) from tbl_ups_user_info where modifytime is not null;
QUERY PLAN
---------------------------------------------------------------------------------
Aggregate (cost=963192.07..963192.08 rows=1 width=0)
-> Seq Scan on tbl_ups_user_info (cost=0.00..962603.70 rows=235346 width=0)
Filter: (modifytime IS NOT NULL)
(3 rows)
Time: 1.669 ms
skyups=> explain select count(*) from tbl_ups_user_info where modifytime is null;
QUERY PLAN
-----------------------------------------------------------------------------------
Aggregate (cost=1042064.76..1042064.77 rows=1 width=0)
-> Seq Scan on tbl_ups_user_info (cost=0.00..962603.70 rows=31784424 width=0)
Filter: (modifytime IS NULL)
(3 rows)
Time: 0.547 ms
第二种情况:
create index idx_test_1 on tbl_test(modifytime) where modifytime is not null;
4MB;
skyups=> explain select count(*) from tbl_ups_user_info where modifytime is null;
QUERY PLAN
-----------------------------------------------------------------------------------
Aggregate (cost=1042082.04..1042082.05 rows=1 width=0)
-> Seq Scan on tbl_ups_user_info (cost=0.00..962607.48 rows=31789824 width=0)
Filter: (modifytime IS NULL)
(3 rows)
Time: 1.771 ms
skyups=> explain select count(*) from tbl_ups_user_info where modifytime is not null;
QUERY PLAN
--------------------------------------------------------------------------------------------------------------------------
Aggregate (cost=231425.54..231425.55 rows=1 width=0)
-> Index Scan using idx_tbl_ups_user_info_modifytime on tbl_ups_user_info (cost=0.00..230849.98 rows=230224 width=0)
(2 rows)
Time: 0.588 ms
总结:
1. 二叉树索引中记录了modifytime is null的row page 和item号.(注意,在ORACLE中二叉树索引是不包含空的)
这个从建立索引的语法中就可以理解:
CREATE INDEX test2_info_nulls_low ON test2 (info NULLS FIRST);
CREATE INDEX test3_desc_index ON test3 (id DESC NULLS LAST);
2. 当需要使用max和min这类函数时,如果被运算的字段包含较多的空或者没有使用NOT NULL约束,建议在建立索引时使用partial索引。避免而外的PAGE扫描。
作者: digoal 发布时间: 2011-02-22
索引的使用很重要。
作者: renxiao2003 发布时间: 2011-02-28
相关阅读 更多
热门阅读
-
office 2019专业增强版最新2021版激活秘钥/序列号/激活码推荐 附激活工具
阅读:74
-
如何安装mysql8.0
阅读:31
-
Word快速设置标题样式步骤详解
阅读:28
-
20+道必知必会的Vue面试题(附答案解析)
阅读:37
-
HTML如何制作表单
阅读:22
-
百词斩可以改天数吗?当然可以,4个步骤轻松修改天数!
阅读:31
-
ET文件格式和XLS格式文件之间如何转化?
阅读:24
-
react和vue的区别及优缺点是什么
阅读:121
-
支付宝人脸识别如何关闭?
阅读:21
-
腾讯微云怎么修改照片或视频备份路径?
阅读:28