为数据表增加索引后之所以会导致写入(包括插入、更新、删除)操作的速度变慢,其根本原因在于索引本质上是一个独立的、需要与主表数据保持实时同步的“数据结构”。这一机制的核心逻辑涵盖五个方面:因为索引本质上是一个“独立的数据结构”需要被“同步维护”、每次“插入”新数据时都必须向所有索引中“新增”条目、每次“删除”数据时也必须“移除”所有索引中的对应条目、当“更新”被索引的列时更涉及到“删除旧索引”与“添加新索引”的复杂操作、以及索引的数量与复杂度直接导致了“写操作”的成本增加

具体来说,当一条新数据被插入主表时,数据库不仅要完成数据本身的写入,还必须承担一项额外的“维护”工作:数据库需要将新数据中被索引的列的值,分别地、按照预设的排序规则,插入到每一个相关的索引结构中去。这个“插入索引”的过程本身就是一个相对耗时的磁盘操作,并且表的索引越多,这份“额外负担”就越重,从而使得总体的写入性能呈现出明显的下降。

一、问题的“本质”、索引的“双面性”

在数据库性能优化的世界里,索引常被誉为提升查询速度的“银弹”。然而,这颗“银弹”却是一枚具有“双刃剑”效应的硬币,它在为“读”操作带来数量级性能提升的同时,也必然地会对“写”操作征收一笔不菲的“性能税”。

索引的核心价值在于加速“读”操作。一个没有索引的数据表在进行条件查询时,数据库只能进行“全表扫描”,即逐行检查每一条记录直到找到匹配的数据。这种方式在数据量小时尚可接受,但在一个拥有数千万行记录的大表中,一次全表扫描可能是秒级甚至分钟级的操作。一个设计良好的索引就如同书籍的“目录”,它允许数据库通过高效的查找算法快速“定位”到所需数据所在的物理位置,将查询的时间复杂度从“线性”级别降低到“对数”级别,从而实现查询速度的巨大飞跃。

这份极致的“读取”效率的代价,就是在每一次“写入”数据时都需要付出额外的“维护成本”。一个索引并非一个简单的“标记”,它是一个真实存在的、需要占用磁盘空间、并需要被严格维护的独立“数据结构”(最常见的是B+树结构)。“写”操作(包括插入、删除、更新)不仅仅是在修改“主数据表”,更是在同时修改“主数据表”和其身上所附带的“每一个索引”。索引越多,这张“税单”就越长,写入操作的总耗时也就越久。因此,是否要为一个表、一个列添加索引,以及添加什么样的索引,本质上是一场关于“读性能”与“写性能”之间的深刻“权衡取舍”。正如经济学家托马斯·索维尔所言:“世上没有解决方案,只有利弊权衡。

二、“插入”操作的“写放大”效应

让我们首先来剖析最简单的“插入”操作是如何因为索引的存在而变得“昂贵”的。

在一个没有任何索引的“裸表”中,当一条新的记录需要被插入时,数据库所做的工作相对简单:它只需要在数据文件中找到一块足够大的、可用的空白空间,然后将这条新的行数据写入即可。

然而,当这张表拥有了一个或多个索引之后,一次看似简单的“插入”在其在数据库内部会触发一系列复杂得多的“连锁反应”。首先是写入主表数据,这个步骤与无索引时基本相同。其次是同步更新所有索引,这是性能开销的核心来源。对于这张表上所存在的每一个索引,数据库都必须执行一次“索引更新”操作。数据库需要从刚刚插入的那行新数据中提取出与该索引相关的“键值”,然后必须在这个索引的、独立的、通常是巨大的B+树结构中从根节点开始进行一次查找,以定位到这个新的“键值”应该被“插入”的、那个正确的、符合排序规则的“叶子节点”位置。最后数据库将这个新的“索引条目”(包含了键值和指向主表行的物理地址指针)插入到那个叶子节点中。如果这个插入操作导致了叶子节点“分裂”,那么其所引发的、对树状结构的“平衡性”调整将是更进一步的性能开销。

这个“写入数据 -> 查找索引位置 -> 插入索引条目”的过程,其成本会随着“索引数量”的增加而成倍地“叠加”。如果一张表上有5个索引,那么每一次的插入操作就意味着需要进行1次主表的数据写入和5次独立的、复杂的索引树写入。对于那些“写入”极其频繁的“流水日志”类数据表,过多的索引无疑是一场性能的灾难。

三、“删除”操作的“同步清理”

与“插入”操作的逻辑类似,“删除”操作同样需要为索引付出“同步维护”的代价。在一个没有索引的表中,删除一条记录的核心是找到那行数据并将其从数据文件中移除或标记为“已删除”。

而在一个有索引的表中,其过程则要复杂得多。第一步是定位并删除主表数据,如果删除的条件恰好能够利用到某个索引,那么“定位”这一步会非常快。第二步是同步删除所有索引中的条目。在删除了主表的行数据之后,数据库必须再次遍历这张表上的所有索引,并在每一个索引的B+树结构中都找到并删除那个指向刚刚被删除的、那一行数据的“索引条目”。

如果不进行这个“同步清理”的操作,那么这些索引中就会残留下来大量指向“空地址”的“僵尸”索引条目。这不仅会浪费磁盘空间,更会在未来的查询中引入不必要的计算和错误。

四、“更新”操作的“双重打击”

“更新”操作对于索引维护而言,是最复杂、也最能体现其“代价”的场景。我们需要将其分为两种截然不同的情况来讨论。

第一种情况是更新“非索引”列。例如 UPDATE users SET age = 31 WHERE id = 123;,假设age这个列上没有建立索引。在这种情况下,数据库只需要定位到id=123的行并直接地在“原地”修改age字段的值即可。因为所有被索引的列(例如可能存在的namecreate_time列)其值并没有发生任何变化,所以所有的索引结构都无需进行任何的修改。这次操作的成本相对较低。

第二种情况是更新“索引”列,这是性能杀手。例如 UPDATE users SET name = '张三' WHERE id = 123;,假设name这个列上存在一个索引。在数据库的索引维护机制中,一次对“索引列”的“更新”操作,其本质几乎等同于一次“删除旧索引条目”加上一次“插入新索引条目”的、“双倍”成本的操作。数据库首先需要定位到id=123的行,然后更新主表中的name字段。此时数据库必须去name列的索引树中进行一次复杂的维护:它需要根据“”的值找到并删除那个原始的索引条目,然后它需要根据“”的值“张三”在索引树中重新寻找一个的、符合排序规则的位置并插入一个新的索引条目。一个简单的、只修改了一行数据的“更新”指令在底层可能会触发对多个、独立的、分布在磁盘不同位置的“索引”文件进行多次的、复杂的“读-删-写”操作,这种现象被称为“写放大”。

五、平衡的“艺术”、**索引设计**策略

既然索引是一把“双刃剑”,那么在实践中我们该如何进行“权衡”和“优化”,以求在“读性能”和“写性能”之间找到一个最佳的“平衡点”呢?

首先需要深刻理解业务的“读写比”,这是进行所有**索引设计**决策的第一个也是最重要的问题:“对于这张表,其日常的主要负载是‘读’操作还是‘写’操作?” 对于“读多写少”的场景,例如“商品信息”表或用于“数据分析”的报表系统,我们可以也应该为其建立相对完善的、多维度的索引来最大化地提升其核心价值——“快速查询”。而对于“写多读少”的场景,例如用于记录“用户行为”的“日志”表,其索引的创建必须保持极致的“克制”,每一个新增的索引都可能成为其“写入”性能的“瓶颈”。

其次,索引应该是“精准”的,而非“盲目”的。索引应该只为那些在WHERE, JOIN, ORDER BY子句中被频繁使用的列而建立。为一个几乎从未被用于“查询条件”的列建立索引是毫无意义的纯粹的“负资产”。如果一个查询常常需要同时对多个列进行过滤,那么创建一个包含了这多个列的“联合索引”其效率远高于为每一个列都单独地创建一个“独立索引”。如果一个查询所需要返回的所有字段恰好都已经包含在了某个索引之中,那么数据库就无需再去“回”到主表中去读取数据。这种只查询“索引”就能满足所有需求的查询被称为“覆盖索引”,其性能极高。

最后,随着数据的不断“增删改”,索引的内部结构可能会产生“碎片”导致其查询效率下降。数据库管理员需要定期地对索引进行“重建”或“重组”来保持其最佳的性能状态。

常见问答 (FAQ)

Q1: “索引”是不是越多越好?

A1: 绝对不是。索引是“双刃剑”。每一个新增的索引在提升特定“查询”性能的同时,都在增加所有“写入”(插入、更新、删除)操作的“成本”,并占用额外的“磁盘空间”。必须在“读性能”和“写性能”之间做出审慎的“权-衡”。

Q2: 为什么更新一个“没有被索引”的列,速度也可能会变慢?

A2: 这通常与数据库的底层存储机制有关。例如,如果你更新的是一个“变长”字段(如一个长文本),并且更新后的值比原始值长得多,导致当前的数据页无法再容纳下它,此时数据库就可能需要进行一次“行迁移”或“页分裂”的昂贵操作。

Q3: “主键”和“索引”是什么关系?

A3: “主键”是一种约束,它保证了表中每一行数据的“唯一性”。而在绝大多数数据库的实现中,当你为一个表定义一个“主键”时,数据库会自动地为这个主键列创建一个唯一的、通常是“聚集”的“索引”,以确保能够快速地通过主键来定位到唯一的一行数据。

Q4: 我应该如何找到我的数据库中,哪些是“低效”或“未使用”的索引?

A4: 主流的数据库管理系统(如MySQL, PostgreSQL)都提供了系统视图或命令来查询和分析“索引的使用情况统计”。通过查询这些统计信息,你可以清晰地看到哪些索引自上次服务器启动以来从未被任何查询所使用过。这些“零使用”的索引就是最主要的、需要被“清理”的候选对象。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/bicheng/94300.shtml
繁体地址,请注明出处:http://hk.pswp.cn/bicheng/94300.shtml
英文地址,请注明出处:http://en.pswp.cn/bicheng/94300.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

.NET Core 中采用独立数据库的SAAS(多租户)方法

介绍多租户是指一种软件架构,其中软件的单个实例在服务器上运行并为多个租户提供服务。在基于 SAAS 的平台中,租户是指使用该平台开展业务运营的客户。每个租户都拥有独立的数据、用户帐户和配置设置,并且与其他租户隔离。多租户允许有效利用…

运维日常工作100条

这是一份非常详细和实用的“运维日常工作100条”清单。它涵盖了从日常巡检、变更管理、故障处理到安全、优化和文档等运维工作的方方面面,可以作为运维工程师的日常工作指南和检查清单。 运维日常工作100条 一、日常巡检与监控 (20条) 检查核心监控大盘:查看整体业务健康状态…

OpenHarmony子系统介绍

OpenHarmony子系统OpenHarmony子系统1. AI业务子系统2. 方舟运行时子系统3. ArkUI框架子系统4. DFX子系统5. DeviceProfile子系统6. XTS子系统7. 上传下载子系统8. 主题框架子系统9. 事件通知子系统10. 位置服务子系统11. 元能力子系统12. 全局资源调度子系统13. 全球化子系统1…

博士招生 | 英国谢菲尔德大学 招收计算机博士

内容源自“图灵学术博研社”gongzhonghao学校简介谢菲尔德大学(The University of Sheffield)是英国久负盛名的公立研究型大学,也是罗素集团成员之一。在 2026 年 QS 世界大学排名中,谢菲尔德大学位列第92位,其中计算机…

如何理解面向过程和面向对象,举例说明一下?

面向过程和面向对象是两种不同的编程思想,核心区别在于解决问题的视角不同:前者关注 “步骤和过程”,后者关注 “对象和交互”。面向过程的核心思想是把问题拆解成一系列步骤,通过函数实现每个步骤,然后按顺序调用这些…

深入了解评估与微调中使用的Graders:原理、实现与最佳实践

深入了解评估与微调中使用的Graders 在模型评估与微调(Fine-tuning)过程中,Graders(评分器)是衡量模型输出与参考答案之间表现的重要工具。本文将系统介绍Grader的类型、技术实现及如何在实际项目中融入稳定且高质量的…

行缓存(line buffer)在图像卷积中的工作方式

上面这张图配合文字,展示了行缓存(line buffer)在图像卷积中的工作方式:上半部分是一个按行扫描输入的图像块(示例为 99,编号 1–81)。 蓝色表示已被写入行缓存并按队列等待的数据,绿…

【数据分享】中国371个城市的坡度矢量数据和excel数据

今天要说明数据就是中国371个城市的坡度矢量数据和excel数据。数据介绍在城市发展的进程中,地形地貌始终是影响规划决策的关键因素,而坡度作为表征地表倾斜程度的核心指标,更是贯穿于城市建设、生态保护等诸多环节。本文将全面解读中国 371 个…

《WINDOWS 环境下32位汇编语言程序设计》第7章 图形操作(1)

图形设备接口GDI(Graphics Device Interface)是Win32的一个重要组成部分,其作用是允许Windows的应用程序将图形输出到计算机屏幕、打印机或其他输出设备上。GDI实际上是一个函数库,包括直线、画图和字体处理等数百个函数。7.1 GDI…

数据结构-HashMap

在 Java 键值对(Key-Value)集合中,HashMap 是使用频率最高的实现类之一,凭借高效的查找、插入性能,成为日常开发的 “利器”。本文将从 HashMap 的底层原理、核心特点、常用方法到遍历方式、使用注意事项,进…

[系统架构设计师]安全架构设计理论与实践(十八)

[系统架构设计师]安全架构设计理论与实践(十八) 一.信息安全面临的威胁 1.信息系统安全威胁的来源 物理环境,通信链路,网络系统,操作系统,应用系统,管理系统 2.网络与信息安全风险类别 风险类别…

AI适老服务暖人心:AI适老机顶盒破数字鸿沟、毫米波雷达护独居安全,银发生活新保障

银发经济领域长期受限于 “专业照护资源稀缺”“老年人数字适应能力弱”“独居老人安全隐患多” 的困境,而 AI 技术的适老化改造,正让银发服务从 “被动保障” 转向 “主动关怀”,既能帮老年人跨越数字鸿沟,又能为独居老人筑起安全…

Linux应用软件编程---网络编程1(目的、网络协议、网络配置、UDP编程流程)

Linux下的网络编程一、目的不同主机,进程间通信。二、解决的问题1. 主机与主机之间物理层面必须互联互通。 2. 进程与进程在软件层面必须互联互通。物理层面的互联互通流程图如下:其中:IP地址:计算机的软件地址,用来标…

常见开源协议详解:哪些行为被允许?哪些被限制?

常见开源协议详解:哪些行为被允许?哪些被限制? 开源世界的魅力在于共享与合作,但不同的开源协议对分发、修改、再发布以及宣传/推广有不同的要求和限制。很多开发者在 fork 项目、改 README、放到自己仓库并在自媒体传播 时&…

服务器硬盘进行分区和挂载

查看服务器上的硬盘:lsblk -d -o NAME,SIZE,MODEL可以看到我的硬盘是除了vda系统盘以外,还有个vdb。我们查看一下分区:lsblk可以看到:vdb 1T disk (底下没有分区,也没有挂载)我们想要用起来这…

【C初阶】数据在内存中的存储

目录 1. 整数在内存中的存储 2. 大小端字节序 2.1 什么是大小端? 2.2 为什么有大小端? 2.3 练习 2.3.1 练习1 2.3.2 练习2 2.3.3 练习3 2.3.4 练习4 2.3.5 练习5 2.3.6 练习6 3. 浮点数在内存中的存储 3.1 浮点数存储的过程 3.2 浮点数的取…

AI 自动化编程 trae 体验2 帮我分析一个项目

总结: 接手一个项目可以让trae 帮忙分析 上次讲到trae在处理组件引入的时候,经常会碰到版本问题,分析引入了互联网上非本版本或者有bug的代码。主要依赖互联网的资源库。 但是分析一个项目应该是没问题。 这次表现非常好,接手一个…

VMware虚拟机中CentOS 7 报错 ping: www.xxx.com: Name or service not known

1:主要原因是网络配置的问题 2:其实就是下面三张图片中的,物理机虚拟网卡 vmware8 和虚拟机网络编辑器,如果设置静态IP 就是这三个地方的问题最简单的解决办法第一步:还原虚拟机网络点击确认后 ** 第二步给自己的虚拟机设置网络连接方式 选择NAT模式连接…

Java面试-自动装箱与拆箱机制解析

👋 欢迎阅读《Java面试200问》系列博客! 🚀大家好,我是Jinkxs,一名热爱Java、深耕技术一线的开发者。在准备和参与了数十场Java面试后,我深知面试不仅是对知识的考察,更是对理解深度与表达能力的…

《VMware 安装 CentOS 7.9 虚拟机详细教程(含图解步骤)》

目录1.安装前准备1.1 准备VMware软件1.1.1 方式一1.1.2 方式二1.2 准备centos7.9镜像1.2.1 方式一1.2.2 方式二2.安装centos7.91.安装前准备 1.1 准备VMware软件 VMware需要的激活码百度直接搜索vmware workstation17激活码就可以搜索到 1.1.1 方式一 这种方式需要注册官网的…