postgres 把一个表的值转成另一个表的字段名_Postgres索引那些事
本文转自Greenplum中文社区官方网站:greenplum.cn;
最实时最权威的Greenplum技术文档、博客,以及热点新闻,请关注greenplum.cn;
Postgres内部提供了很多种索引的方式,满足不同的索引需求。
传统索引方式(B-Tree)
说到传统,B-Tree索引当仁不让,它也是postgres里的默认索引方式。B-Tree的中文名字是二叉平衡树,每个叶子结点的深度都是一样的,特点是允许在O(logn)的时间内对所存储的内容进行搜索、顺序访问、插入及删除。
B-Tree索引对于唯一值的索引来说是相当之理想的(比如id序号这类数据),它存储的是一对对的键值和指针,指针指向被索引数据所在的heap上的位置。
这里说下什么是heap。Heap(或者叫heap文件,用来区分同名不同义的数据结构heap)存储着postgres表里的所有数据(外部表除外)。heap里边的数据是无序的,这让数据库在向里面添加数据的时候不必考虑排序的问题。
目前postgres支持的索引中,只有B-Tree索引能够提供有序的查询结果(也就是支持Order by和Limit),支持高并发场景(并行scan),支持merge join和包含index scan的nested loop操作。
B-Tree是非常基本且古老的索引,但不代表它功能单一。
有时我们需要更多的功能,比如:表达式。
看下这个例子:
CREATE TABLE customer (name) AS SELECT 'cust' || i
FROM generate_series(1, 1000) AS g(i);
CREATE INDEX i_customer_name ON customer (name);
/*看一下直接对B-Tree索引列做fitler的plan*/
EXPLAIN SELECT * FROM customer WHERE name = 'cust999';
QUERY PLAN
------------------------------------------------
Index Only Scan using i_customer_name on customer ...
Index Cond: (name = 'cust999'::text)
/* 没问题!使用了Index!另外,Index Only Scan的意思是,所有想要的column都在index里已经有了*/
/* 如果对B-Tree索引列在查询时增加一个表达式操作呢?*/
EXPLAIN SELECT * FROM customer WHERE lower(name) = 'cust999';
QUERY PLAN
-------------------------------------------------
Seq Scan on customer (cost=0.00..20.00 rows=5 width=7)
Filter: (lower(name) = 'cust999'::text)
/* 又变回Seq Scan了!? */
Postgres提供了表达式索引,你可以用表的一列或者多列组成的表达式,创建一个索引。
CREATE INDEX i_customer_lower ON customer (lower(name));
EXPLAIN SELECT * FROM customer WHERE lower(name) = 'cust999';
QUERY PLAN
-------------------------------------------------
Bitmap Heap Scan on customer (cost=4.32..9.66 rows=5 width=7)
Recheck Cond: (lower(name) = 'cust999'::text)
-> Bitmap Index Scan on i_customer_lower ...
Index Cond: (lower(name) = 'cust999'::text)
等一下,这个Bitmap Index Scan又是什么意思?
Postgres做涉及到索引的扫描的时候,会先在索引上扫描一遍,得出符合条件的row/block的list,再拿着这个list到表上做真正的取数据操作。但如果一张表上的查询涉及多个索引呢?每个索引都这么来一次岂不是很浪费时间?
Bitmap Index Scan就能很好解决这个问题。多个索引都扫描完毕后,符合每个索引过滤条件的row/block会用同一个bitmap来记录,然后只需要用这个bitmap一次性去heap表上取出数据就好。值得一提的是,Postgres优化器会自动做出是否要使用Bitmap Index Scan的判断,不需要任何人工干预。
下图是bitmap index的示意图:
部分索引
当一个表很大的时候,为每一行都建索引是很耗存储空间的。如果常用的数据只有一小部分的话,完全可以只为这部分数据建立索引,省时省力省空间。
省时:当做Insert和Update操作的时候,额外开销更小!
省力:热数据索引扫描更快!
省空间:索引再也不用耗费大量磁盘了!
让我们看下怎么建立一个部分索引
ALTER TABLE customer ADD COLUMN state CHAR(2);
UPDATE customer SET state = 'AZ' WHERE name LIKE 'cust9__';
CREATE INDEX i_customer_state_az ON customer (state) WHERE state = 'AZ';
/* 一个WHERE语句,只对state = AZ的行进行索引 */
EXPLAIN SELECT * FROM customer WHERE state = 'PA';
QUERY PLAN
-------------------------------------------------
Seq Scan on customer (cost=0.00..17.50 rows=5 width=19)
Filter: (state = 'PA'::bpchar)
/* 当要查询的数据不在部分索引的范围内时,仍然使用Seq Scan */
EXPLAIN SELECT * FROM customer WHERE state = 'AZ';
QUERY PLAN
-------------------------------------------------
Bitmap Heap Scan on customer (cost=4.18..9.51 rows=5 width=19)
Recheck Cond: (state = 'AZ'::bpchar)
-> Bitmap Index Scan on i_customer_state_az ...
Index Cond: (state = 'AZ'::bpchar)
/* 当要查询的数据和部分索引一致时,索引就可以被用上了! */
Postgres这种带条件部分的索引,在mysql里仍不支持[https://dba.stackexchange.com/questions/43/how-to-create-a-conditional-index-in-mysql]。如果想在mysql中创建一个类似上面例子的索引,你需要依据原表创建一个辅助表,并增加3个triggers(update/insert/delete),当对原表有满足“条件”的数据变动时,相应操作也会由trigger在辅助表上触发。然后,对辅助表创建索引。相比postgres,mysql的方式不仅耗时耗空间,对用户来说操作也非常不友好。
非传统索引方式
除了被作为默认索引的B-Tree,Postgres还以插件的方式提供了好几种“新型索引”,用于满足各式各样的应用场景。下面我们逐个认识一下。
BRIN (Block-Range Index)
名字里有个range,顾名思义,这种索引存储的是heap表内每个block中数据的最大值和最小值。
BRIN索引有什么作用和特点呢?
首先,如果一整个block里的数据都非常大或者非常小,不在我们查询的条件范围内,就可以将整个block排除出扫描范围。这种索引对那些分布是有序的数据非常有用,比如insert-only table的id或timestamp列。
其次,因为只存储最大值和最小值,BRIN带来的空间消耗极小。另外,更新数据的时候,索引带来的额外操作只有比较操作,也是相当高效的。
BRIN对于含有多个列的索引是比较合适的选择。
但是,因为索引里存储的信息非常少,查找数据的时候消耗的时间也会很长,当查询的数据范围和一个block的范围值有重叠时,就需要对这个block进行scan,如果有重叠的block很多,其实跟全表扫描的差别也不大了。
GIN (Generalized Inverted Index)
尤其适用于有很多key或value的数据的索引,比如说文档、JSON、整型数组等等。
尤其适用于重复数据很多的场景,这点和B-Tree正好相反。
支持一条索引里存在多个key。而B-Tree每条索引里只能有一个key。比如新插入一条数据“foo bar”,GIN会拆分出两个key:“foo” 和 “bar”。
GIN索引内部数据结构整体上类似于B-Tree。不同的是,叶子结点上存储的不是一个TID,而是很多TID的集合,这是GIN为了存储重复数据做的优化。GIN的叶子结点的数据有三种可能:
1. 当只有一个TID的时候,和B-Tree一样。
2. 当有很多TID的时候,那么存储的是一个列表(posting list)。
3. 当有非常非常多的TID的时候,叶子结点存储的是一个指针,这个指针指向另一棵树(Posting Tree)的根结点。而Posting Tree里面存储了所有符合这一个entry的TIDs(以page为存储单元)。
GIN树结构示意图:
GIN索引的Fast Updates特性:
GIN额外维护着一个列表,当有新的数据进入索引,不会直接进入索引树,而是会先暂存在这个列表里。当然,每个对索引的搜索也都会额外的对这个列表进行扫描。
当做vacuum操作的时候,会把这个列表里的数据插入到索引树上。不过,如果这个列表已经太大了,postgres不会傻傻等待用户进行vacuum,会果断地将它里面的内容插入GIN索引树。
但有意思的是,虽然它叫Fast Updates,但这个特性会让搜索变慢,因为每次搜索都要增加一次额外的列表扫描操作,所以很多用户会把这个特性关掉。
GIN索引的gin_fuzzy_search_limit
GIN索引适用于有大量重复关键字的全文索引,但是,当一个关键字出现次数太多时,返回结果也会很大,太大的结果集对用户是没有实际意义的,而且读取并排序这么多的数据会耗费很长时间。
GIN提供了一个配置项gin_fuzzy_search_limit来控制这种情况下返回结果集的上限。如果这个值非0,那么查询只会返回不超过设置值的一个子结果集,而且是随机的。根据经验,这个值设定为5000-20000比较好。
GIST (Generalized Search Tree)
GIST的数据结构仍然是树,但不再是B-Tree了。
支持地理坐标、range类型(比如ip范围)、hstore(key/value对)、整型数组、pg_trgm。
GIST和range
问题(见上图示意):假设表中有一列是range类型的数据,给出一个range范围,找出表中所有和它有重叠部分的数据。
如果是B-Tree,可以按照range的最大值或最小值进行排序,但仍然避免不了每次查询都要做扫描。GIST的做法,是将范围差不多的数据聚集在一起,形成一个个的“clusters”,每个cluster的最大值和最小值都在根结点上记录,这样就可以按照每个cluster的整体范围,排除掉那些根本不可能有重叠的cluster,大大降低了扫描成本。
GIST树的结构:
索引cluster存储在page上,根结点保存了每个page内部的最小值和最大值。给出查询范围后,只需要先扫描根结点,找出所有可能有范围重叠的page,再对这些page进行相应的扫描即可。
GIST对存储的key的顺序没有严格要求,而且每个key都可以在整棵树上任何一个page上存储,只要能把根结点的相应page的最大值最小值相应更新好就ok。但是,如果使用随机存储的话,最后根结点里每个page的范围就会差不多,性能会变得非常差(几乎等同于全表扫描)。所以,如何选择对key分组的函数就至关重要。另外当一个page增长过大之后,需要用picksplit函数来对page分割,这个函数的好坏对性能也有很大影响。
GIST的用途
GIS (geographic information system)
寻找bounding box的顶点
找到最近的n个邻居
全文检索
整型数组
一句话总结:GIST适合上层结点的范围能“包含”下层结点的类型。
SP-GIST (Space-Partitioned Generalized Search Tree)
虽然和GIST名字相似,但却是完全不同的索引类型,不是平衡树,各个叶子节点的深度可以不同。GIST各个cluster之间是可以有范围重叠的,但是SP-GIST不允许重叠。
举个例子:
这是一个网址数据索引的例子,父节点是各个子节点的共有前缀,一个完整的键值在SP-GIST的树上只存一次,每一部分分别存储在从根结点到该键值叶子节点的路径上。
另外一个常见的使用场景是索引坐标点:将空间分割成不重叠的块,每个子节点再将父节点的空间细分。但是如果是空间里的形状,就无法使用SP-GIST来索引了,因为形状可能有重叠。
索引小知识
你不能删除索引里的一条数据,只能通过vacuum操作将索引里无用的信息删掉。
Index Only Scan并不理会每一条数据是否是可见的,它只会如实把找到的一切符合条件的数据信息返回,可见性的控制是上层程序需要做的事。
什么时候你应该建立索引?
当seq_scan耗时很长的时候!
Explain一下你常用的查询,如果seq_scan的cost很高,可以考虑建立索引。但是相应的,如果一个索引的idx scan很少被用到,就要考虑这个索引是不是还应该继续存在了。毕竟,维护一个索引是耗时耗空间的事情。
如何选择使用哪种索引?
当然,数据类型是第一个考虑要素。在此之外,还可以从以下几点考虑:
索引建立的时间
索引存储空间
数据更新时索引带来的额外开销
访问速度
postgres 把一个表的值转成另一个表的字段名_Postgres索引那些事相关推荐
- postgres 把一个表的值转成另一个表的字段名_用LUT来做一个可动态配置的卷积核...
引言 由于卷积核数据在计算过程中保持不变,更新较慢.这样就可以利用LUT来存储权重并同时进行乘法运算.LUT乘法器的实现很早就已经研究过,本论文正是在此基础上,提出了用于实现可配置的卷积实现方法.基于 ...
- postgres 把一个表的值转成另一个表的字段名_Phoenix系列创建Phoenix映射表
目前,在公司小部分的业务场景中有用到 Phoenix,但也仅限基于 Phoenix 的二级索引机制来进行查询上的优化.虽然使用的频次不大,但偶尔用到时,有些语句的使用方式和注意事项总记不太熟,每次都需 ...
- postgres 把一个表的值转成另一个表的字段名_希望一个数据同步,包治百病
小姐姐味道[ID:xjjdog] 作者:十年架构,日百亿流量经验,与你分享. 哎!这是一个脏活,而且是个高风险的活. 大多数情况下,应用架构设计不好,引入什么新存储,引入什么DDD,治标不治本,都是扯 ...
- 如何把一个数组的值赋给另一个数组
java中的数组 一维数组中把一个数组的值赋给另外一个数组 public class test {public static void main(String[] args) {int[] array ...
- mysql join 去重_MySQL Update inner join数据库去重,以及根据一张表的值更新另一张表...
1 问题来源 这几天在项目中遇到一个问题:由于前期设计不合理,导致后期用户录入数据时,基础数据表中有重复多余数据.如下: 出现两个 2G网络测试手机 这样类似的基础数据,直接后果就是用户在使用这个基础 ...
- 外汇天眼:Apple与MetaQuotes之争!谁是下一个Apple?谁会成下一个MT4/5?
手机巨头Apple 从其应用商店App Store中下架领先的交易软件MetaTrader4(MT4)和 MetaTrader5(MT5)引发了外汇市场热议. 虽然截止发稿,Apple以及交易软件MT ...
- SQL Server修改表名,字段名,索引名
说实话,感觉SQL Server的资料真的好难找(也有可能是很多人在吐槽的CSDN的搜索功能不够强--),我想找个修改表名的方法,结果找了好久,才找到一个可行的,留个纪念,希望也能够帮到你(多个人转发 ...
- mysql怎么把值更新成space,MySQL表的碎片整理和空间回收小结
MySQL表碎片化(Table Fragmentation)的原因 关于MySQL中表碎片化(Table Fragmentation)产生的原因,简单总结一下,MySQL Engine不同,碎片化的原 ...
- 注册表把html设置成桌面,[注册表] 将Windows 10默认应用程序设置页面添加到桌面右键菜单中...
从Windows 10初始版本开始微软就已调整默认程序配置策略,即强制用户设置关联的默认程序不允许软件设置. 这也是很多用户经常在通知栏里看到某某文件格式被重置的原因,因为软件安装后设置的会被微软重置 ...
最新文章
- warnings.filterwarnings(ignore)
- scrapy爬虫程序xpath中文编码报错
- [转]iOS为UILabel添加长按复制功能
- python PIL图像处理-框选
- python 查看当前目录_「Python」打包分发工具setuptools学习
- C++ —— C++引用
- PID控制器改进笔记之一:改进PID控制器之参数动态调整
- Web前端开发工程师必读de设计博客
- sobel prewitt算法 模板加权模糊的解释 + 两类边缘下的二阶导数值
- 【华为云技术分享】《跟唐老师学习云网络》—router路咋走啊
- 为了做服务注册迁移,我提前准备了这些东西,来看看对你有没有用!
- Sentinel流控规则_线程数失败_分布式系统集群限流_线程数隔离_削峰填谷_流量控制_速率控制_服务熔断_服务降级---微服务升级_SpringCloud Alibaba工作笔记0034
- Linux的一些基础命令
- 目标检测中文类别--在图片中添加汉字
- 国产数据库普及风暴有奖征文获奖名单揭晓
- 阿里云、腾讯云----域名DDNS云解析到动态IP
- PHP实现的敏感词过滤方法
- 几个比较好看的几个颜色
- 王者荣耀头像大小怎么调?调整图片尺寸大小工具分享
- JS Array.slice 截取数组的实现方法
热门文章
- linux下批量新增数据,linux下批量插入数据到mysql
- 管状合金电阻和片状合金电阻的区别_合金采样电阻的特点及作用
- linux安装界面意思,为linux安装图形化界面
- android 工厂测试内存,Android性能测试之内存
- java多线程详解 六_java多线程学习-java.util.concurrent详解(六) Exchanger
- oracle插入CLOB类型超过4000个字符报ORA-01704错的解决方法
- www,android18x.com,Android 11 LineageOS 18.1系统
- 安卓团课快进_青年大学习网上主题团课第十季第七期答案
- 带你学 Redis: 基本命令 String 操作(三)
- Job 存储和持久化 (第二部分)