当前位置: 首页 > article >正文

深入理解索引(二)

1.引言

在数据库和数据结构中,索引(Index)是一种用于提高数据检索速度的重要机制。本文将详细深入介绍索引。

2. 为什么要使用索引

大家在使用索引之前一定要搞清楚使用索引的目的,因为索引的不当使用可能不但起不到正向作用,反而会带来灾难性后果。一般来说使用索引的目的有以下几种:

  1. 加速数据检索
    索引可以显著减少查询所需的时间,尤其是在处理大量数据时。例如,在关系数据库中,索引可以加快 SELECT 查询的执行速度。
  2. 提高查询效率
    对于复杂的查询(如包含 JOIN、WHERE、ORDER BY、GROUP BY 等子句的查询),索引可以极大地提高查询效率。
  3. 唯一性约束
    某些类型的索引(如主键索引和唯一索引)可以确保数据的唯一性,防止重复数据的插入。
  4. 强制数据完整性
    通过索引,数据库可以更容易地实施数据完整性约束(如外键约束),确保数据之间的关联关系正确无误。
  5. 加快排序操作
    如果索引包含排序信息(如 B-树索引),那么 ORDER BY 查询可以更快地执行,因为数据已经按索引顺序排列。
  6. 支持快速查找特定值
    索引允许数据库系统快速定位特定值,而不需要扫描整个数据集。
  7. 优化连接操作
    在关系数据库中,索引可以优化表之间的连接操作(如 JOIN),尤其是当连接条件被索引覆盖时。
  8. 提高数据更新性能(在某些情况下)
    虽然索引会增加插入、更新和删除操作的开销,但在某些情况下(如批量更新),索引可以加快这些操作的速度,因为数据库系统可以利用索引来快速定位需要更新的记录。

3. 选择适合的索引类型

3.1 考虑查询类型

3.1.1 等值查询
  1. B - 树索引:如果经常进行等值查询(如查询工资等于某一具体数值的员工记录),B - 树索引是一个很好的选择。它可以通过从根节点到叶子节点的快速定位来找到对应的记录。例如,在一个员工信息表中,经常根据员工编号查找员工详细信息,对员工编号列建立 B - 树索引能高效地满足查询需求。
  2. 哈希索引:对于等值查询,哈希索引的性能也非常出色。在内存数据库或者对查询速度要求极高的场景下,如果主要是精确匹配查询,哈希索引可以在近乎常数时间内返回结果。比如,在一个高速缓存系统中,通过缓存键的精确匹配查询来获取缓存值,哈希索引能提供快速的查找。
3.1.2 范围查询
  1. B - 树索引:B - 树索引非常适合范围查询(如查询年龄在某个区间内的用户)。由于其索引键值是有序排列的,数据库可以沿着索引叶子节点顺序读取数据,减少磁盘 I/O 的随机访问。例如,在销售数据表中,查询销售额在一定区间内的订单记录,对销售额列建立 B - 树索引能有效提高查询效率。
  2. 位图索引(在一定条件下):对于低基数(不同取值较少)列的范围查询,位图索引可以通过位运算来合并查询条件,也能有较好的表现。例如,在一个用户状态(活跃 / 不活跃)列上的范围查询,利用位图索引可以快速定位符合条件的用户记录。
3.1.3 全文查询
  1. 全文索引:当需要对文本内容进行查询,如查找包含特定关键词的文档或者模糊查询时,全文索引是必不可少的。它可以对文本中的词汇单元进行分析和索引,支持多种文本搜索方式。例如,在一个内容管理系统中,用户搜索文章内容中的特定词汇,全文索引能够提供高效的搜索功能。

3.2 考虑数据列的特点

3.2.1 低基数列
  1. 位图索引:如果列的取值范围较小且重复值较多(如性别、状态等列),位图索引是一个不错的选择。它通过位图表示数据分布,在查询这类列时可以快速定位记录。例如,在一个电商用户表中,对于用户会员等级(只有普通、银卡、金卡等几种级别)列,位图索引能够高效地查询不同等级的用户。
  2. 不建议 B - 树索引(特殊情况除外):对于低基数列,B - 树索引可能不是最优的,因为索引的维护成本相对较高,且在查询时可能不会带来显著的性能提升,除非该列经常用于范围查询且与其他条件组合查询。
3.2.2 高基数列
  1. B - 树索引:当列的取值非常多(如身份证号码、产品序列号等),B - 树索引通常更合适。例如,在一个产品库存表中,产品序列号是唯一标识产品的高基数列,对该列建立 B - 树索引可以方便地通过产品序列号查询产品的库存信息。
  2. 哈希索引(有条件使用):在等值查询为主且对范围查询没有要求的情况下,对于高基数列也可以考虑哈希索引。但要注意哈希冲突问题以及哈希索引不支持范围查询等限制。
3.2.3 包含函数操作的列
  1. 函数索引:如果查询经常涉及对列的函数操作(如对日期列查询某一年的记录,需要提取年份函数),则应该建立函数索引。这样在查询时可以直接利用索引,而不需要每次都对列进行函数计算。例如,在一个财务报表表中,对日期列建立提取季度的函数索引,方便查询每个季度的财务数据。

3.3 考虑数据更新频率

3.3.1 更新频繁的数据列
  1. 谨慎使用索引:对于数据更新频繁的列,要谨慎使用索引。因为每次数据更新都可能导致索引的更新,增加系统的负担。例如,在一个实时交易记录表中,交易金额列可能频繁更新,如果对该列建立过多索引,会导致插入、更新和删除操作的性能下降。
  2. 哈希索引(特殊情况):哈希索引在更新操作方面有一定的特殊性。虽然它在等值查询上高效,但在数据更新时也需要重新计算哈希值并调整哈希桶。如果更新操作涉及哈希冲突的调整,可能会比较复杂,不过在某些场景下(如内存数据库的快速更新和查找平衡),仍然可以考虑使用。
3.3.2 相对稳定的数据列
  1. 可以积极使用索引:对于相对稳定的数据列,如系统配置参数表、数据字典表等,应该积极考虑使用索引来提高查询性能。例如,在一个软件系统的配置参数表中,参数名称列建立 B - 树索引后,在系统读取配置参数时可以快速定位,而不用担心频繁更新导致的索引维护问题。

3.4 考虑数据库的存储和性能要求

3.4.1 存储资源有限的情况
  1. 位图索引优势:如果存储资源有限,对于低基数列,位图索引可以在节省存储空间的同时提供较好的查询性能。因为它不需要像 B - 树索引那样存储每个索引键值和对应的行标识符,而是通过位图来表示数据分布。
3.4.2 性能要求极高的情况
  1. 哈希索引或 B - 树索引(根据查询类型):在对性能要求极高的场景下,如高并发的实时查询系统,需要根据主要的查询类型来选择。如果是等值查询为主,哈希索引可能更合适;如果涉及范围查询等多种查询类型,B - 树索引可能是更好的选择。同时,要考虑数据库的缓存机制和硬件性能(如内存大小、磁盘 I/O 速度等)对索引性能的影响。

4. 选择索引类型时,如何权衡空间和时间的开销

4.1 B - 树索引

4.1.1 时间开销
  1. 查询性能优势:B - 树索引在范围查询和等值查询方面都有较好的时间性能。对于范围查询,由于其键值是有序存储的,在查询一个区间内的值时,数据库可以沿着索引的叶子节点顺序读取数据,减少磁盘 I/O 的随机访问次数,从而提高查询效率。例如,在一个销售记录表中查询销售额在一定区间内的订单,B - 树索引能够快速定位到起始位置,然后顺序读取符合条件的记录。在等值查询时,也能通过从根节点到叶子节点的快速路径定位到目标记录。
  2. 更新性能影响:但 B - 树索引在数据更新操作(插入、删除和更新索引列)时,会产生一定的时间开销。当插入新数据时,可能会导致索引节点的分裂或合并操作,以保持树的平衡性。删除数据时也可能引起节点的调整。这些操作需要额外的时间来重新组织索引结构。例如,在一个高并发的在线交易系统中,如果频繁插入新订单记录,B - 树索引的维护可能会导致一定的性能延迟。
4.1.2 空间开销

B - 树索引需要存储索引键值、对应的行标识符(ROWID)以及用于维护树结构的节点信息。随着表中数据量的增加和索引列的增多,索引的大小会逐渐增大。例如,对于一个包含大量列的员工信息表,对多个列建立 B - 树索引,可能会占用相当可观的磁盘空间。

4.2 位图索引

4.2.1 时间开销
  1. 查询性能优势(低基数列):对于低基数(不同取值较少)列的查询,位图索引在时间性能上表现出色。通过位运算可以快速合并查询条件,定位到符合条件的记录。例如,在一个用户状态(活跃 / 不活跃)列上进行查询,位图索引可以快速扫描位图来确定状态为活跃的用户记录。然而,在高并发的更新环境下,位图索引的更新操作会比较复杂,因为插入或删除数据可能需要更新多个位图,这可能会导致锁竞争等问题,增加时间开销。
4.2.2 空间开销

位图索引对于低基数列可以节省空间。它不需要像 B - 树索引那样为每个键值存储 ROWID 等信息,而是通过位图来表示数据分布。但如果列的基数较高,位图索引的空间开销可能会急剧增加,因为每个不同的取值都需要一个单独的位图来表示。例如,对于一个取值范围很广的产品序列号列,使用位图索引可能会占用大量的空间。

4.3 哈希索引

4.3.1 时间开销
  1. 查询性能优势(等值查询):哈希索引在等值查询时时间性能极佳,几乎可以在常数时间内返回结果。例如,在一个内存数据库的键 - 值存储系统中,通过键的精确匹配查询,哈希索引可以快速定位到对应的存储位置。但哈希索引不支持范围查询,在这种情况下,可能需要进行全表扫描,这会带来巨大的时间开销。另外,哈希索引在处理哈希冲突时也会产生时间开销,尤其是当冲突严重时,在哈希桶内的查找时间会增加。
4.3.2 空间开销

哈希索引的空间开销主要取决于哈希桶的大小和数据量。为了减少哈希冲突,可能需要较大的哈希桶,但这会增加空间占用。同时,哈希索引也需要存储键 - 行映射关系,随着数据量的增加,空间开销也会相应增加。

4.4 函数索引

4.4.1 时间开销
  1. 查询性能优势(函数操作相关查询):函数索引在查询经常涉及对列的函数操作时,可以显著提高时间性能。例如,在一个日期列上建立提取月份的函数索引,查询某个月份的记录时,无需每次对日期列进行函数计算,直接利用索引即可快速定位。但在更新索引列时,由于需要重新计算函数值来更新索引,会增加时间开销。如果函数计算比较复杂,这种时间开销会更加明显。
4.4.2 空间开销

函数索引的空间开销类似于 B - 树索引,需要存储函数计算后的结果、对应的 ROWID 以及索引结构信息。其空间大小会随着数据量和函数的复杂程度而变化。

4.5 权衡策略

4.5.1 以时间性能为主的场景

如果应用场景对查询响应时间要求极高,且主要是等值查询,在空间允许的情况下,可以优先考虑哈希索引。如果涉及范围查询,B - 树索引可能是更好的选择。对于文本搜索,全文索引能够提供较好的时间性能,但要注意其更新成本。在数据更新操作相对较少的情况下,为了优化查询时间,可以积极建立合适的索引。

4.5.2 以空间性能为主的场景

当存储资源有限时,对于低基数列,位图索引是一个较好的空间 - 时间权衡选择。如果对空间占用要求非常严格,且查询性能可以适当牺牲,可以减少不必要的索引创建。例如,在一个嵌入式数据库系统中,由于存储设备容量有限,可能会谨慎使用索引,仅对最关键的查询列建立索引。

4.5.3 综合权衡场景

在大多数实际应用中,需要综合考虑时间和空间开销。可以通过性能测试和监控来确定最优的索引策略。例如,在一个企业级数据库应用中,先对主要的查询操作进行分析,根据数据列的特点和更新频率,初步选择可能合适的索引类型。然后,通过模拟实际负载进行性能测试,观察时间和空间的占用情况,对索引类型和数量进行调整,以达到最佳的平衡。

5. 索引的优化

5.1 定期评估索引的使用情况

5.1.1 查询索引使用统计信息:

在 Oracle 中,可以使用数据字典视图来查看索引的使用情况。例如,V$OBJECT_USAGE视图提供了有关索引是否被使用的信息。通过查询这个视图,可以了解哪些索引在实际的查询操作中被使用,哪些没有被使用。
示例查询语句如下:

SELECT * FROM V$OBJECT_USAGE WHERE INDEX_NAME = 'your_index_name';
5.1.2 识别未使用的索引:

如果发现某些索引长时间未被使用,那么这些索引可能是优化的对象。未使用的索引会占用磁盘空间,并且在数据更新时还会增加不必要的维护开销。
不过,需要注意的是,仅仅因为某个索引在一段时间内未被查询统计信息记录为使用,并不一定意味着它完全没有价值。可能是因为应用程序的查询模式尚未触发对该索引的使用,或者是查询优化器在某些情况下选择不使用该索引。

5.2 优化索引列的选择

5.2.1 选择高选择性列:

高选择性列是指那些在表中不同值的数量与总行数的比例较高的列。例如,在一个包含员工信息的表中,员工编号(假设是唯一的)列就是高选择性列。
对于高选择性列建立索引,在进行等值查询(如查询特定员工编号的员工信息)时,能够更精准地定位到目标数据行,减少查询时需要扫描的数据量,从而提高查询效率。

5.2.2 避免对低选择性列过度索引:

低选择性列是指那些取值重复率高的列,如性别列(只有男和女两种取值)。对于这样的列,除非有特殊的查询需求(如频繁查询某一性别的所有记录,且数据量巨大),否则过度建立索引可能不会带来显著的查询性能提升,反而会增加数据更新时的索引维护成本。

5.2.3 考虑组合索引的列顺序:

当创建组合索引时,列的顺序至关重要。应该将最常用于过滤数据的列放在组合索引的最前面。
例如,在一个订单表中,有客户编号(customer_id)和订单日期(order_date)两个列。如果大多数查询首先是根据客户编号进行筛选,然后才考虑订单日期,那么组合索引应该按照(customer_id, order_date)的顺序创建。这样,在查询时可以更有效地利用索引,先通过客户编号快速定位到一部分数据,再在这部分数据中根据订单日期进一步筛选。

5.3 处理索引碎片

5.3.1 识别索引碎片程度:

可以使用 Oracle 提供的工具或查询来评估索引的碎片程度。例如,通过查询数据字典视图或者使用 Oracle 的分析工具(如 DBMS_STATS)来获取索引的物理存储信息,包括索引块的使用情况、叶子节点的填充程度等。
如果发现索引块的空闲空间过多,或者叶子节点的数据分布不均匀,就可能存在索引碎片问题。

5.3.2 重建或重组索引:

当索引碎片严重影响性能时,可以采取重建或重组索引的措施。
重建索引(ALTER INDEX index_name REBUILD;)会重新创建索引结构,将数据重新组织,消除碎片。重组索引(如使用ALTER INDEX index_name COALESCE;对于 B - 树索引)是一种相对温和的方式,它主要是将索引的相邻空闲空间合并,在一定程度上减少碎片,同时对系统的影响相对较小。不过,重组索引可能无法完全消除碎片,对于严重碎片化的索引,重建可能是更有效的方法。

5.4 优化索引参数设置(针对 B - 树索引)

5.4.1 调整索引块大小(PCTFREE 和 PCTUSED):

PCTFREE 参数用于指定在索引块中为将来的更新操作(如插入新的索引键值)预留的空闲空间百分比。较高的 PCTFREE 值可以减少更新索引时的块分裂操作,但会增加索引的存储空间占用。
PCTUSED 参数用于确定当索引块的使用空间低于多少百分比时,可以重新插入数据。合理设置这两个参数可以优化索引块的空间利用效率,减少索引结构的频繁调整。

5.4.2 考虑索引的存储参数(如 TABLESPACE):

根据数据的访问频率和重要性,将索引存储在合适的表空间(TABLESPACE)中。例如,对于频繁访问的索引,可以将其存储在性能较好的表空间(如存储在高速磁盘设备上的表空间),以提高查询响应速度。

码字不易,宝贵经验分享不易,请各位支持原创,转载注明出处,多多关注作者,后续不定期分享DB基本知识和排障案例及经验、性能调优等。


http://www.kler.cn/a/408088.html

相关文章:

  • vue-office:word(.docx)、pdf、excel(.xlsx,.xls)格式文件预览
  • RabbitMQ和RocketMQ相关面试题
  • 医院信息化与智能化系统(22)
  • flutter 专题十一 Fair原理篇Fair逻辑动态化架构设计与实现
  • 密码学11
  • MySQL 中的锁
  • ubuntu搭建k8s环境详细教程
  • 「Mac玩转仓颉内测版28」基础篇8 - 元组类型详解
  • linux ollama安装没有sudo权限
  • Android 不同情况下使用 runOnUiThread
  • H.265流媒体播放器EasyPlayer.js H5流媒体播放器关于如何查看手机端的日志信息并保存下来
  • IIFE - 立即执行函数
  • Flutter:AnimatedIcon图标动画,自定义Icon通过延时Interval,实现交错式动画
  • C# 委托与事件
  • Linux——进程间通信之管道
  • docker 容器运行Ruoyi-cloud
  • 二.LoadBalancer负载均衡服务调用(1)
  • python代码制作数据集的测试和数据质量检测思路
  • SQL注入--DNSlog外带注入--理论
  • Android开发实战班 - 应用架构 - 单向数据流(Unidirectional Data Flow, UDF)
  • Java 8 Stream API 在数据转换中的应用 —— 将列表转换为映射
  • 怎么只提取视频中的声音?从视频中提取纯音频技巧
  • 【StarRocks】starrocks 3.2.12 【share-nothing】 多Be集群容器化部署
  • 【AI系统】GPU 架构与 CUDA 关系
  • 简单版面试
  • 大语言模型---LoRA中损失值的计算