关于PostgreSQL的GiST索引之四
3. hstore的GiST索引
hstore的GiST索引实现和全文检索的tsvector的GiST索引的实现差不过,都是签名文件索引。
3.1 GiST索引项的存储
1)所有索引项都采用签名向量压缩
2)hstore中的所有键和所有值都进行哈希设置bit位得到一个签名向量
3)签名向量的大小为128bit
3.2 性能推论
1)索引尺寸比较小(因为签名向量的大小是固定的而且一般比原始数据小)
2)每次查询要扫描的索引页很多(20%~40%的索引页)
3)每次键查询(?操作符)至少要recheck 1.6%(2/128)以上的数据行
每条记录包含的键值对越多,需要recheck的数据行越多
4)每次键值查询(@>操作符)至少要recheck 0.02%((2/128) * (2/128))以上的数据行
每条记录包含的键值对越多,需要recheck的数据行越多
假设每条记录平均包含10条键值对,需要recheck的数据行估算如下
键查询(?操作符)匹配:7.5%的数据行
点击(此处)折叠或打开
- testdb=# with recursive tx1(n,sign_bits) as
- testdb-# (select 1 n,1.0 sign_bits
- testdb(# union all
- testdb(# select n+1, sign_bits + 1 - sign_bits/128 from tx1 where n=10
- testdb(# )
- testdb-# select n,sign_bits,sign_bits/128 scan_probability from tx1 where n in(10);
- n | sign_bits | scan_probability
- ----+------------------------+------------------------
- 10 | 9.65566251563533320389 | 0.07543486340340104066
- (1 row)
由此可见对键查询(多个键的?&查询另当别论),hstore的GiST索引的查询效率是比较低的。不过,从另外一个角度考虑,通常的应用场景下,不同的key值数本来就不会很多,就不是索引的用武之地。
键值查询(@>操作符)匹配:0.57%(0.57%=0.07543486340340104066*0.07543486340340104066)
但是这只是理论上的平均值,由于签名向量的冲突,个别查询的效率可能会很低。比如,如果大部分数据记录都包含某个key,而查询这个键值对的时候,碰巧它的值在签名向量中对应的bit位和这个很常用的key相同,那么大部分数据记录都要进行recheck。
3.3 相关代码
contrib/hstore/hstore_gist.c
点击(此处)折叠或打开
- /* bigint defines */
- #define BITBYTE 8
- #define SIGLENINT 4 /* >122 => key will toast, so very */
- #define SIGLEN ( sizeof(int)*SIGLENINT )
- #define SIGLENBIT (SIGLEN*BITBYTE)
- typedef char BITVEC[SIGLEN];
- typedef char *BITVECP;
- ...
- Datum
- ghstore_compress(PG_FUNCTION_ARGS)
- {
- GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
- GISTENTRY *retval = entry;
- if (entry->leafkey)
- {
- GISTTYPE *res = (GISTTYPE *) palloc0(CALCGTSIZE(0));
- HStore *val = DatumGetHStoreP(entry->key);
- HEntry *hsent = ARRPTR(val);
- char *ptr = STRPTR(val);
- int count = HS_COUNT(val);
- int i;
- SET_VARSIZE(res, CALCGTSIZE(0));
- //遍历所有KV对
- for (i = 0; i count; ++i)
- {
- int h;
- //对key做哈希
- h = crc32_sz((char *) HS_KEY(hsent, ptr, i), HS_KEYLEN(hsent, i));
- HASH(GETSIGN(res), h);
- if (!HS_VALISNULL(hsent, i))
- {
- //对value做哈希
- h = crc32_sz((char *) HS_VAL(hsent, ptr, i), HS_VALLEN(hsent, i));
- HASH(GETSIGN(res), h);
- }
- }
- retval = (GISTENTRY *) palloc(sizeof(GISTENTRY));
- gistentryinit(*retval, PointerGetDatum(res),
- entry->rel, entry->page,
- entry->offset,
- FALSE);
- }
- else if (!ISALLTRUE(DatumGetPointer(entry->key)))
- {
- int32 i;
- GISTTYPE *res;
- BITVECP sign = GETSIGN(DatumGetPointer(entry->key));
- LOOPBYTE
- {
- if ((sign[i] & 0xff) != 0xff)
- PG_RETURN_POINTER(retval);
- }
- res = (GISTTYPE *) palloc(CALCGTSIZE(ALLISTRUE));
- SET_VARSIZE(res, CALCGTSIZE(ALLISTRUE));
- res->flag = ALLISTRUE;
- retval = (GISTENTRY *) palloc(sizeof(GISTENTRY));
- gistentryinit(*retval, PointerGetDatum(res),
- entry->rel, entry->page,
- entry->offset,
- FALSE);
- }
- PG_RETURN_POINTER(retval);
- }
3.4 性能验证
1)环境
CentOS 6.5
PostgreSQL 9.4.0
2)测试数据准备
点击(此处)折叠或打开
- testdb=# create extension hstore;
- CREATE EXTENSION
- Time: 235.391 ms
- testdb=# create table tbhs(id serial,hs hstore);
- CREATE TABLE
- Time: 17.459 ms
- testdb=# insert into tbhs select id,hstore(id::text, md5(id::text)) from generate_series(1,100000) id;
- INSERT 0 100000
- Time: 970.613 ms
- testdb=# select pg_relation_size('tbhs');
- pg_relation_size
- ------------------
- 8445952
- (1 row)
- Time: 0.643 ms
- testdb=# select * from tbhs limit 5;
- id | hs
- ----+-----------------------------------------
- 1 | "1"=>"c4ca4238a0b923820dcc509a6f75849b"
- 2 | "2"=>"c81e728d9d4c2f636f067f89cc14862c"
- 3 | "3"=>"eccbc87e4b5ce2fe28308fd9f2a7baf3"
- 4 | "4"=>"a87ff679a2f3e71d9181a67b7542122c"
- 5 | "5"=>"e4da3b7fbbce2345d7772b0674a318d5"
- (5 rows)
- Time: 1.147 ms
注)上面每个记录只有一个键值对,且每个key都是唯一的,这样的数据模型就不适合使用hstore存储,本文故意这么做只是为了验证性能。
2)无索引的查询
点击(此处)折叠或打开
- testdb=# explain (analyze,buffers) select * from tbhs where hs ? '10';
- QUERY PLAN
- -----------------------------------------------------------------------------------------------------
- Seq Scan on tbhs (cost=0.00..2281.00 rows=100 width=53) (actual time=0.025..31.587 rows=1 loops=1)
- Filter: (hs ? '10'::text)
- Rows Removed by Filter: 99999
- Buffers: shared hit=1031
- Planning time: 0.055 ms
- Execution time: 31.619 ms
- (6 rows)
- Time: 32.150 ms
3)建立GiST索引再查询
点击(此处)折叠或打开
- testdb=# create index tbhs_idx_gist on tbhs using gist(hs);
- CREATE INDEX
- Time: 6243.003 ms
- testdb=# analyze tbhs;
- ANALYZE
- Time: 77.956 ms
- testdb=# select pg_relation_size('tbhs_idx_gist'),pg_relation_size('tbhs_idx_gist')/8192 index_pages;
- pg_relation_size | index_pages
- ------------------+-------------
- 4825088 | 589
- (1 row)
- Time: 0.456 ms
?查询扫描了45%(265/589)的索引页,Recheck了1.6%的数据行。
点击(此处)折叠或打开
- testdb=# explain (analyze,buffers) select * from tbhs where hs ? '10';
- QUERY PLAN
- ---------------------------------------------------------------------------------------------------------------------------
- Bitmap Heap Scan on tbhs (cost=5.06..302.42 rows=100 width=53) (actual time=3.003..4.637 rows=1 loops=1)
- Recheck Cond: (hs ? '10'::text)
- Rows Removed by Index Recheck: 1558
- Heap Blocks: exact=863
- Buffers: shared hit=1128
- -> Bitmap Index Scan on tbhs_idx_gist (cost=0.00..5.03 rows=100 width=0) (actual time=2.869..2.869 rows=1559 loops=1)
- Index Cond: (hs ? '10'::text)
- Buffers: shared hit=265
- Planning time: 0.153 ms
- Execution time: 5.073 ms
- (10 rows)
- Time: 6.209 ms
@>查询扫描了19%(114/589)的索引页,Recheck了0.012%的数据行。
点击(此处)折叠或打开
- testdb=# explain (analyze,buffers) select * from tbhs where hs @> '1=>c4ca4238a0b923820dcc509a6f75849b';
- QUERY PLAN
- -------------------------------------------------------------------------------------------------------------------------
- Bitmap Heap Scan on tbhs (cost=5.06..302.42 rows=100 width=53) (actual time=2.123..2.205 rows=1 loops=1)
- Recheck Cond: (hs @> '"1"=>"c4ca4238a0b923820dcc509a6f75849b"'::hstore)
- Rows Removed by Index Recheck: 11
- Heap Blocks: exact=12
- Buffers: shared hit=126
- -> Bitmap Index Scan on tbhs_idx_gist (cost=0.00..5.03 rows=100 width=0) (actual time=2.097..2.097 rows=12 loops=1)
- Index Cond: (hs @> '"1"=>"c4ca4238a0b923820dcc509a6f75849b"'::hstore)
- Buffers: shared hit=114
- Planning time: 0.407 ms
- Execution time: 2.294 ms
- (10 rows)
- Time: 3.332 ms
4)建立GIN索引再查询
建立GIN索引对比一下。
点击(此处)折叠或打开
- testdb=# drop index tbhs_idx_gist;
- DROP INDEX
- Time: 7.036 ms
- testdb=# create index tbhs_idx_gin on tbhs using gin(hs);
- CREATE INDEX
- Time: 2244.241 ms
- testdb=# analyze tbhs;
- ANALYZE
- Time: 64.608 ms
- testdb=# select pg_relation_size('tbhs_idx_gin'),pg_relation_size('tbhs_idx_gin')/8192 index_pages;
- pg_relation_size | index_pages
- ------------------+-------------
- 17629184 | 2152
- (1 row)
- Time: 0.641 ms
- testdb=# explain (analyze,buffers) select * from tbhs where hs ? '10';
- QUERY PLAN
- ------------------------------------------------------------------------------------------------------------------------
- Bitmap Heap Scan on tbhs (cost=16.77..314.14 rows=100 width=53) (actual time=0.096..0.097 rows=1 loops=1)
- Recheck Cond: (hs ? '10'::text)
- Heap Blocks: exact=1
- Buffers: shared hit=5
- -> Bitmap Index Scan on tbhs_idx_gin (cost=0.00..16.75 rows=100 width=0) (actual time=0.086..0.086 rows=1 loops=1)
- Index Cond: (hs ? '10'::text)
- Buffers: shared hit=4
- Planning time: 0.349 ms
- Execution time: 0.144 ms
- (9 rows)
- Time: 1.014 ms
- testdb=# explain (analyze,buffers) select * from tbhs where hs @> '1=>c4ca4238a0b923820dcc509a6f75849b';
- QUERY PLAN
- ------------------------------------------------------------------------------------------------------------------------
- Bitmap Heap Scan on tbhs (cost=28.77..326.14 rows=100 width=53) (actual time=0.070..0.071 rows=1 loops=1)
- Recheck Cond: (hs @> '"1"=>"c4ca4238a0b923820dcc509a6f75849b"'::hstore)
- Heap Blocks: exact=1
- Buffers: shared hit=8
- -> Bitmap Index Scan on tbhs_idx_gin (cost=0.00..28.75 rows=100 width=0) (actual time=0.047..0.047 rows=1 loops=1)
- Index Cond: (hs @> '"1"=>"c4ca4238a0b923820dcc509a6f75849b"'::hstore)
- Buffers: shared hit=7
- Planning time: 0.131 ms
- Execution time: 0.105 ms
- (9 rows)
- Time: 0.661 ms
GIN索引的查询效率相当不错。GIN的缺点是:索引比较大,对更新速度有一定影响。
3.5 参考
http://blog.chinaunix.net/uid-20726500-id-4884626.html
http://blog.chinaunix.net/uid-20726500-id-4884681.html
http://blog.chinaunix.net/uid-20726500-id-4886571.html
http://blog.chinaunix.net/uid-259788-id-4279627.html
关于PostgreSQL的GiST索引之四相关推荐
- PostgreSQL 10.1 手册_部分 II. SQL 语言_第 12 章 全文搜索_12.9. GIN 和 GiST 索引类型
12.9. GIN 和 GiST 索引类型 有两种索引可以被用来加速全文搜索.注意全文搜索并非一定需要索引,但是在一个定期会被搜索的列上,通常需要有一个索引. CREATE INDEX name ON ...
- postgreSQL源码分析——索引的建立与使用——GIST索引(3)
2021SC@SDUSC 本篇博客主要讲解GiST索引查询的相关函数,并结合具体实例来介绍GIST的使用过程 目录 GIST索引查询 GISTSearchHeapItem GISTSearchItem ...
- postgreSQL源码分析——索引的建立与使用——GIST索引(2)
2021SC@SDUSC 本篇博客主要讲解GiST索引创建以及删除的相关函数 这里写目录标题 GIST创建 相关数据结构 GISTBuildState GISTInsertStack gistbuil ...
- postgreSQL源码分析——索引的建立与使用——GIST索引(1)
2021SC@SDUSC 这一篇博客主要讲解GIST索引的相关的介绍,组织结构以及原理的讲解. 目录 GIST 简介 介绍 扩展性 实现 typedef struct GISTSTATE GIST的索 ...
- Postgresql杂谈 09—Postgresql中的Gist索引的深入学习
本文,我们进一步学习下Gist索引.Gist是Generalized Search Tree的意思,意思是通用搜索树,底层结构也是一种平衡树,它是一套索引模板,可以支持用户实现自定义的索引.相比于BT ...
- PostgreSQL中的索引—5(GiST)上
在之前的文章中,我们讨论了PostgreSQL索引引擎.访问方法的接口,以及两种访问方法:哈希索引和B树.在本文中,我们将描述GiST索引. GiST GiST是"广义搜索树"的缩 ...
- PostgreSQL中的索引—5(GiST)下
接上一篇 目录 "btree_gist"扩展 用于全文搜索的RD树 RD-trees 示例 内部构件 属性 其他数据类型 "btree_gist"扩展 让我们把 ...
- PostgreSQL索引详解5——Gist索引
1.概述 Gist(Generalized Search Tree),即通用搜索树.和btree一样,也是平衡的搜索树. 和btree不同的是,btree索引常常用来进行例如大于.小于.等于这些操作中 ...
- PostgreSql索引(B-tree索引 Hash索引 GiST索引 SP-GiST索引 GIN 索引 BRIN 索引)
索引 语法: CREATE INDEX test1_id_index ON test1 (id); 索引的名字test1_id_index可以自由选择,但我们最好选择一个能让我们想起该索引用途的名字. ...
最新文章
- SQL Server 监控统计阻塞脚本信息
- [ARM异常]-SPIs(共享中断)routing到指定CPU的方法
- 新鲜出炉的电信诈骗经历
- WebSocket webshop后台服务器的一些全局数据结构
- LazyInitializationException的四种解决方案–第1部分
- leetcode310. 最小高度树(bfs)
- 电脑时间校对器_笔记本电脑如何保养?华为教你五招轻松延长使用时间
- mac 命令失效问题 命令不存在 : command not found
- Ubantu 安装SSH
- qq数据泄露_用这个开源项目来解决你团队里猪队友泄露公司敏感信息的问题
- jsp注册里密码强弱怎么弄_JavaScript注册时密码强度校验代码
- 最新新游社iApp源码+后台对接的hybbs内核
- 我心中的你是春天的样子
- 基于Tensorflow的MINIST手写体识别
- Python TKinter下拉日历控件
- [redis]知识回顾之redis主从+哨兵搭建简要记录
- 育碧信条:AI 在手,天下我有
- 笔记本计算机盖,怎么让笔记本盖子合上不待机-笔记本盖子合上 电脑继续运行的方法 - 河东软件园...
- Java好学吗?零基础入门Java,三个就业方向实现月入过万!
- 手把手教你写一个安卓app
热门文章
- Hadoop之回收站
- iOS 使用UI控件的外观协议UIAppearance进行设置默认UI控件样式
- Git(创建版本库)
- Python删除文件及进行文件夹压缩
- Bias vs. Variance(1)--diagnosing bias vs. variance
- AutoFac使用方法总结:Part III
- MVC通过ViewBag动态生成Html输出到View
- 如何成为一名优秀的web前端工程师[转]
- [ASP.NET] Session的了解
- javascript随机生成GUID