暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

聊聊数据库的部分索引

白鳝的洞穴 2024-06-19
107

二十多年前给用户做数据库优化的时候,经常会遇到一种场景,一些数据,INSERT的时候状态是“N”,经过处理后改为“F”。在这张表中,可能有几百万条状态为“F”的数据,只有几百条状态为“N”的数据。那时候对数据库索引的理解不太透彻,通过理论上认为值域范围较多的字段索引才有效,因此很多这样的字段上都不创建索引。后来了解了B树索引的原理,发现如果我们总是读取“N”的数据,那么索引是十分有效的。只不过这条索引仅对值为“N”的查询有效,如果要查值为“F”的数据,那么走全表扫描效率要高得多。

实际上这条索引仅仅是为值为“N”的记录创建的,其他索引叶节点是没有任何价值的。因此当时就想如果能仅仅为N值的行创建索引就好了。索引是数据库最为关键的数据结构,可以实现数据的快速定位。不过索引是需要定期维护的,否则索引的碎片化会比较严重,影响写操作的性能。大表索引的维护是个比较麻烦的事情。数据库索引不会将数据库中的每一行都视为单独的叶节点,而是使用“值”作为叶并在其中记录行的地址或者主键,部分索引仅为满足特定条件的“值”创建节点。

PostgreSQL、MongoDB等都支持部分索引。Oracle则支持一种比较特殊的部分索引,分区表基于分区的部分索引。MySQL 8.0开始支持了一种十分特殊的部分索引,就是为某个列的前面N个字节创建索引。部分索引的主要目的是减小索引的大小,提高索引扫描的效率。

PG的部分索引(Partial Index)的存储结构也是B-TREE的。部分索引也称为过滤索引,它只覆盖表数据的一个子集,是带有 WHERE 子句的索引。

部分索引有助于加快查询速度,同时减少索引的大小,这些索引需要更少的存储空间,它们更易于维护,扫描速度更快。如果您通常使用具有常量值的 WHERE 条件,则部分索引很有用。在PostgreSQL中,我们可以通过Partial索引获得更好的效果。

create index idx_partial_status on t_order (status) where status=’001’;    

这个索引中只有status=’001’的数据,记录数只占整张表的万分之一,因此索引十分小。访问的效率也十分高。

MySQL的Innodb引擎是不支持和PG类似的部分索引的,不过MySQL 8.0开始也支持一种称为部分索引的索引,这种索引可以看做是一种特殊的函数索引:

Create index idx_partial_mysql on t_order (status(2));

如果某个字段的最后几个字节对于查询来说关系不大 ,那么我们可以通过前面几个字节来进行行的定位,则可以使用这种不分索引。

实际上Oracle从12C开始也支持partial索引了,不过Oracle的partial索引与PG的不大相同。Oracle支持对分区表创建部分索引,从而减轻索引创建和维护的成本,提高索引扫描的效率。我们想象一种场景,有一些分区表,一些历史数据很少通过索引字段访问,哪怕偶尔访问一次,也能忍受分区扫描的延时,那么我们的索引只需要针对比较新的分区去做维护。对于LOCAL INDEX,我们可以通过分区级的维护来实现,而对于全局索引,就束手无策了。Oracle的Partial Index就是用来解决这个问题的。Oracle通过对分区的indexing on|off字句来设定某个分区是否需要维护索引。

CREATE TABLE T12 (

order_id NUMBER(12),

order_date DATE CONSTRAINT order_date_nn NOT NULL,

order_mode VARCHAR2(8),

customer_id NUMBER(6) CONSTRAINT order_customer_id_nn NOT NULL,

order_status NUMBER(2),   

order_total NUMBER(8,2),

sales_rep_id NUMBER(6),

promotion_id NUMBER(6),

CONSTRAINT order_mode_lov CHECK (order_mode in

('direct','online')),          
CONSTRAINT order_total_min CHECK (order_total >= 0)

INDEXING OFF

PARTITION BY RANGE (ORDER_DATE)

 (PARTITION ord_p1 VALUES LESS THAN (TO_DATE('01-MAR-1999','DD-MON-YYYY')) INDEXING ON,

PARTITION ord_p2 VALUES LESS THAN (TO_DATE('01-JUL-1999','DD-MON-YYYY')) INDEXING OFF,

PARTITION ord_p3 VALUES LESS THAN (TO_DATE('01-OCT-1999','DD-MON-YYYY')) INDEXING ON,

PARTITION ord_p4 VALUES LESS THAN (TO_DATE('01-MAR-2000','DD-MON-YYYY')),

PARTITION ord_p5 VALUES LESS THAN (TO_DATE('01-MAR-2010','DD-MON-YYYY')));


或者:

ALTER TABLE T1_comp MODIFY DEFAULT ATTRIBUTES FOR PARTITION ordp3_comp INDEXING OFF;

PG和Oracle的部分索引在设计理念上是不同的 ,都完美地解决了数据库应用的某个问题。如果结合在一起才是一个真正完美的解决方案。去年ORACLE PAB大会时,O记让我从国产开源数据库中找一些值得O记学习的功能提交给他们的产品经理。我推荐的功能中就包含了PG的部分索引,他们也欣然接受了,认为Oracle需要这个功能。

              

文章转载自白鳝的洞穴,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论