数据库内核月报 - 2015 / 05-PgSQL · 社区动态 · 9.5 新功能BRIN索引

本文涉及的产品
云原生数据库 PolarDB 分布式版,标准版 2核8GB
云数据库 RDS SQL Server,基础系列 2核4GB
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
简介:

虽然PG 9.4发布不过半年时间,下一个大版本9.5却已经进入人们的视野。按目前的情况,2015年上半年可能发布beta版本,下半年正式发布PG 9.5。9.5里面最令人瞩目的一个新功能恐怕是BRIN索引了。下面这个commit加入了对BRIN索引的支持:

commit: 7516f5259411c02ae89e49084452dc342aadb2ae
author: Alvaro Herrera alvherre@alvh.no-ip.org
date: Fri, 7 Nov 2014 16:38:14 -0300
BRIN: Block Range Indexes

BRIN is a new index access method intended to accelerate scans of very
large tables, without the maintenance overhead of btrees or other
traditional indexes. They work by maintaining “summary” data about
block ranges. Bitmap index scans work by reading each summary tuple and
comparing them with the query quals; all pages in the range are returned
in a lossy TID bitmap if the quals are consistent with the values in the
summary tuple, otherwise not. Normal index scans are not supported
because these indexes do not store TIDs.

BRIN即Block Range Indexes,顾名思义,就是对数据块区段所做的索引。其实它的设计思路很简单,就是通过扫描整个表,记录下每个固定区段(例如第1到128号数据块)所含数据被索引字段的最大值和最小值,依次存入索引空间。当处理某个查询,需要找到符合查询条件的记录时,可以使用BRIN索引,跳过与查询条件不符合的区段,加速查找。下面我们分析一下这种新型索引的使用方法和内核实现。

使用

下面我们创建一个有一百万记录的表,然后为其建立BRIN索引,再在表上做查询:

postgres=# create table t AS SELECT generate_series(1,100000000) AS id;
SELECT 100000000
postgres=# \timing
Timing is on.
postgres=# create index idx_brin on t using brin(id);
CREATE INDEX
Time: 72766.822 ms

postgres=# explain analyze select * from t where id = 507654;
                                                     QUERY PLAN
-----------------------------------------------------------------------------------------
 Bitmap Heap Scan on t  (cost=52.01..56.02 rows=1 width=4) (actual time=26.046..41.431 rows=1 loops=1)
   Recheck Cond: (id = 507654)
   Rows Removed by Index Recheck: 28927
   Heap Blocks: lossy=128
   ->  Bitmap Index Scan on idx_brin  (cost=0.00..52.01 rows=1 width=0) (actual time=6.408..6.408 rows=1280 loops=1)
         Index Cond: (id = 507654)
 Planning time: 8.265 ms
 Execution time: 42.575 ms
(8 rows)

Time: 67.897 ms

可见,使用BRIN避免了全表扫描,执行时间为67ms左右。下面我们对比一下,如果不使用BRIN,耗时多少(注意,我们做下面操作之前,清空了操作系统pagecache,并重启了PG):

postgres=# drop index idx_brin;
DROP INDEX

postgres=# explain analyze select * from t where id = 507654;
                                              QUERY PLAN
----------------------------------------------------------------------------------------
 Seq Scan on t  (cost=0.00..1692478.00 rows=1 width=4) (actual time=194.665..35124.454 rows=1 loops=1)
   Filter: (id = 507654)
   Rows Removed by Filter: 99999999
 Planning time: 6.345 ms
 Execution time: 35125.633 ms
(5 rows)

执行时间35s左右!原因在于,使用BRIN索引时,实际只读取了一个区段里的数据块,而全表扫描时,则读取所有数据块。

实现

存储结构

BRIN索引的存储结构如下图所示:

存储结构

BRIN索引由一组相同结构的索引块组成,每个索引块含有固定数目的索引记录,每条记录里面含有一个指向最值块的指针。最值块里面的每条记录存放了区段最大和最小值,及其对应的数据区段起始块的块号。要想定位某个数据块对应的BRIN索引记录,可以按下面的公式,计算索引块号和索引记录的位置:

索引块号 =(数据块号 / 每个区段包含的块数) / 每个索引块含有的索引记录数
索引记录的位置 = (数据块号 / 每个区段包含的块数)% 每个索引块含有的索引记录数

例如,如果一条数据记录所在数据块块号为1000,而在缺省情况下BRIN索引每个区段包含的块数为128(可以在创建索引时,通过WITH (pages_per_range = xxx)子句来修改),而每个索引块的索引记录数固定(约为8K/6),这样可以很容易按照公式找到对应索引记录。而由索引记录里面存放的指针,可以读取到对应最值块和最值记录。

索引构建

BRIN索引的构建过程比较简单,需要对原表做一次全表扫描,每扫描完一个区段,相应的索引块和最值块也构建完成。值得注意的是,对于最后一个区段,如果所含的块数不足,则不会为其构建最值记录;而是等到数据块数达到一个完整区块时,才为其计算最值。这样的设计,有利于提高从表尾部大规模插入数据时的性能。

查询操作

使用BRIN索引处理查询时,PG会从头开始,检查所有的索引记录,并用索引记录指向的最值记录与查询条件相比较,从而判断对应区段是否可能包含符合查询条件的数据;最后得到所有相关区段列表,再顺序读取这些区段中的数据块进行比较,返回实际符合查询条件的数据记录。

插入操作

BRIN索引插入操作的接口函数为brininsert,在一个数据记录被插入到数据块后被调用。其过程主要是利用数据块号,按照上面提到的定位过程,找到对应的最值记录,然后比较最值和插入的数据记录值。如果最大值小于该数据记录值,或最小值大于数据记录值,则更新最大或最小值。值得注意的是,插入操作如果要更新索引或最值记录,是要锁定整个块的,这样多个并行插入对索引的修改是很容易冲突的,就是说BRIN索引会一定程度上降低并行插入的性能。

删除和更新操作

BRIN索引的所有接口函数可以在pg_am.h中找到。但令人疑惑的是,只有插入(insert)的接口函数,没有针对删除(delete)或更新(update)的函数。其实,和PG其他类型的索引类似,BRIN索引也是不需要执行删除操作的。删除一条数据记录时,BRIN索引不做修改。即使在对一个表做VACUUM操作时,也同样不需修改BRIN索引(注意,对于其他索引如B树索引,在VACUUM操作时会修改索引删除无效索引记录)。另一方面,对于更新操作,相当于一次删除加一次插入操作,由于不需要对索引做删除操作,实际只做了一次索引插入。

小结

通过上面分析,不难看出,这种BRIN索引适用于下面的场景:

  1. 非常大的表,如果创建B树索引,会占用较大空间;采用BRIN,不失为一种时间换空间的方法(BRIN比B树索引处理查询时会慢一些);
  2. 经常大批量尾部插入的表,这些表如果创建了B树索引,会引起索引尾部更新的互斥;而使用BRIN索引,尾部数据块的索引记录只在满了一个区段时才进行一次插入,减少了互斥的情况。

BRIN是PG 9.5一个令人期待的新功能,对于BRIN将给PG带来的性能、易用性方面的变化,我们拭目以待。

目录
相关文章
|
2月前
|
前端开发 Java 关系型数据库
基于ssm的社区物业管理系统,附源码+数据库+论文+任务书
社区物业管理系统采用B/S架构,基于Java语言开发,使用MySQL数据库。系统涵盖个人中心、用户管理、楼盘管理、收费管理、停车登记、报修与投诉管理等功能模块,方便管理员及用户操作。前端采用Vue、HTML、JavaScript等技术,后端使用SSM框架。系统支持远程安装调试,确保顺利运行。提供演示视频和详细文档截图,帮助用户快速上手。
102 17
|
2月前
|
存储 缓存 数据库
数据库索引采用B+树不采用B树的原因?
● B+树更便于遍历:由于B+树的数据都存储在叶子结点中,分支结点均为索引,方便扫库,只需要扫一遍叶子结点即可,但是B树因为其分支结点同样存储着数据,我们要找到具体的数据,需要进行一次中序遍历按序来扫,所以B+树更加适合在区间查询的情况,所以通常B+树用于数据库索引。 ● B+树的磁盘读写代价更低:B+树在内部节点上不包含数据信息,因此在内存页中能够存放更多的key。 数据存放的更加紧密,具有更好的空间局部性。因此访问叶子节点上关联的数据也具有更好的缓存命中率。 ● B+树的查询效率更加稳定:由于非终结点并不是最终指向文件内容的结点,而只是叶子结点中关键字的索引。所以任何关键字的查找必须走一条
|
6月前
|
数据库 索引
深入探索数据库索引技术:回表与索引下推解析
【10月更文挑战第15天】在数据库查询优化的领域中,回表和索引下推是两个核心概念,它们对于提高查询性能至关重要。本文将详细解释这两个术语,并探讨它们在数据库操作中的作用和影响。
121 3
|
6月前
|
数据库 索引
深入理解数据库索引技术:回表与索引下推详解
【10月更文挑战第23天】 在数据库查询性能优化中,索引的使用是提升查询效率的关键。然而,并非所有的索引都能直接加速查询。本文将深入探讨两个重要的数据库索引技术:回表和索引下推,解释它们的概念、工作原理以及对性能的影响。
225 3
|
3月前
|
关系型数据库 分布式数据库 数据库
喜报|PolarDB开源社区荣获“2024数据库国内活跃开源项目”奖
喜报|PolarDB开源社区荣获“2024数据库国内活跃开源项目”奖
|
5月前
|
存储 缓存 数据库
数据库索引采用B+树不采用B树的原因?
B+树优化了数据存储和查询效率,数据仅存于叶子节点,便于区间查询和遍历,磁盘读写成本低,查询效率稳定,特别适合数据库索引及范围查询。
88 6
|
6月前
|
存储 缓存 数据库
数据库索引采用B+树不采用B树的原因
B+树相较于B树,在数据存储、磁盘读写、查询效率及范围查询方面更具优势。数据仅存于叶子节点,便于高效遍历和区间查询;内部节点不含数据,提高缓存命中率;查询路径固定,效率稳定;特别适合数据库索引使用。
81 1
|
7月前
|
存储 关系型数据库 MySQL
Mysql(4)—数据库索引
数据库索引是用于提高数据检索效率的数据结构,类似于书籍中的索引。它允许用户快速找到数据,而无需扫描整个表。MySQL中的索引可以显著提升查询速度,使数据库操作更加高效。索引的发展经历了从无索引、简单索引到B-树、哈希索引、位图索引、全文索引等多个阶段。
131 3
Mysql(4)—数据库索引
|
6月前
|
数据库 索引
数据库索引
数据库索引 1、索引:建立在表一列或多列的辅助对象,目的是加快访问表的数据。 2、索引的优点: (1)、创建唯一性索引,可以确保数据的唯一性; (2)、大大加快数据检索速度; (3)、加速表与表之间的连接; (4)、在查询过程中,使用优化隐藏器,提高系统性能。 3、索引的缺点: (1)、创建和维护索引需要耗费时间,随数据量增加而增加; (2)、索引占用物理空间; (3)、对表的数据进行增删改时,索引需要动态维护,降低了数据的维护速度。
91 2
|
6月前
|
监控 关系型数据库 MySQL
数据库优化:MySQL索引策略与查询性能调优实战
【10月更文挑战第27天】本文深入探讨了MySQL的索引策略和查询性能调优技巧。通过介绍B-Tree索引、哈希索引和全文索引等不同类型,以及如何创建和维护索引,结合实战案例分析查询执行计划,帮助读者掌握提升查询性能的方法。定期优化索引和调整查询语句是提高数据库性能的关键。
934 1