为什么在SQL中向有大量索引的表插入数据会非常缓慢?

千婷同学_8006

千婷同学_8006

2026-09-02

152人浏览

原创

根本原因是每行insert都需同步更新所有索引、生成wal并触发b-tree分裂或gin重构,属acid刚性要求;而create index是离线批量构建,绕过逐行开销。

为什么在sql中向有大量索引的表插入数据会非常缓慢?

因为每插入一行,数据库必须同步更新所有相关索引——这不是配置问题,而是 ACID 语义的刚性要求。

INSERT 时每个索引都在做“实时维护”

插入不是只写数据页,而是对每一行执行 N+1 次写操作:1 次主键/聚簇索引 + N 次二级索引。这些操作必须原子、一致、可回滚:

  • UNIQUE 索引要先查重,再插入,两次 B+ 树查找 + 一次写入
  • FULLTEXT 或 GIN 索引需分词、生成倒排项、合并 posting list,开销呈倍数增长
  • 多列复合索引(如 (a, b, c, d))比较和分裂成本随键长上升,不是线性而是近似平方级
  • 低区分度字段(如 status 只有 'pending'/'done'/'error')导致大量键值挤在少数叶子页,加剧页锁争用与分裂频率

WAL 和缓冲区压力被指数放大

索引越多,WAL 日志体积越大、刷盘越频繁,buffer pool 压力也越重:

Co.dev
Co.dev

Co.dev 是一款通过文本描述生成全栈 Web 应用的 AI 应用构建工具。

下载
  • 每次索引更新都触发 WAL 记录,5 个索引 ≈ 5 倍 WAL 写入量,IO:WALWrite 等待事件飙升
  • InnoDB 的 innodb_buffer_pool_size 若小于活跃索引+数据总大小,就会频繁淘汰页、刷脏页、重加载——等于用内存模拟磁盘行为
  • PostgreSQL 中 shared_buffers 不足时,DataFileRead 等待明显,尤其在插入同时触发 vacuum 或 autovacuum 时

哪些索引最拖后腿?别盲目删,要看类型

不是所有索引对写入的影响相同。以下三类在批量插入中性能衰减最剧烈,且原因各异:

  • GIN 索引:默认 fastupdate = on 会堆积 pending list,虽写入看似快,但后续 vacuum 或查询时集中爆发;设为 off 则每次插入都走完整 Entry Tree 路径,更慢
  • 含大字段的索引:如对 JSONB 或 TEXT 字段建全文索引,单行可能生成数十个倒排项,每个都要独立定位并写入
  • 冗余复合索引:比如已有 (user_id, created_at),又额外建了 (user_id) —— 后者不仅不提升查询(前缀已覆盖),还白增一次索引维护

真正有效的缓解手段不是“调优”,而是“绕过”

优化器不会帮你跳过索引更新,所以高吞吐写入场景下,核心思路是把“在线更新”换成“离线构建”:

  • 大批量导入前,用 DROP INDEX CONCURRENTLY(PG)或 ALTER INDEX ... DISABLE(SQL Server)临时移除非关键索引;MySQL 8.0+ 可改用 ALTER INDEX idx_name INVISIBLE 避免 DDL 锁
  • 务必禁用非业务强依赖的触发器:SHOW TRIGGERS LIKE 'table_name' 查出后,用 session 变量或条件逻辑临时跳过
  • 对日志类、报表类表,考虑设为 UNLOGGED(PG)或切换至 simple recovery model(SQL Server),但要接受崩溃丢失风险
  • 用 LOAD DATA INFILE(MySQL)或 COPY(PG)替代 INSERT INTO ... VALUES,它们跳过 SQL 解析层,直接构造元组

最容易被忽略的一点:索引是否被查询真正用到,比“看起来有用”重要得多。长期未被 idx_scan 触发的索引,删掉它对写入的帮助,远大于保留它对某条半年没跑过的报表 SQL 的潜在好处。

相关文章

PHP速学视频免费教程(入门到精通)
PHP速学视频免费教程(入门到精通)

PHP怎么学习?PHP怎么入门?PHP在哪学?PHP怎么学才快?不用担心,这里为大家提供了PHP速学教程(入门到精通),有需要的小伙伴保存下载就能学习啦!

下载

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

相关专题

更多
数据分析工具有哪些
数据分析工具有哪些

数据分析工具有Excel、SQL、Python、R、Tableau、Power BI、SAS、SPSS和MATLAB等。详细介绍:1、Excel,具有强大的计算和数据处理功能;2、SQL,可以进行数据查询、过滤、排序、聚合等操作;3、Python,拥有丰富的数据分析库;4、R,拥有丰富的统计分析库和图形库;5、Tableau,提供了直观易用的用户界面等等。

2023.10.12

4503

8

SQL中distinct的用法
SQL中distinct的用法

SQL中distinct的语法是“SELECT DISTINCT column1, column2,...,FROM table_name;”。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

2023.10.27

931

4

SQL中months_between使用方法
SQL中months_between使用方法

在SQL中,MONTHS_BETWEEN 是一个常见的函数,用于计算两个日期之间的月份差。想了解更多SQL的相关内容,可以阅读本专题下面的文章。

2024.02.23

1189

5

SQL出现5120错误解决方法
SQL出现5120错误解决方法

SQL Server错误5120是由于没有足够的权限来访问或操作指定的数据库或文件引起的。想了解更多sql错误的相关内容,可以阅读本专题下面的文章。

2024.03.06

6481

10

sql procedure语法错误解决方法
sql procedure语法错误解决方法

sql procedure语法错误解决办法:1、仔细检查错误消息;2、检查语法规则;3、检查括号和引号;4、检查变量和参数;5、检查关键字和函数;6、逐步调试;7、参考文档和示例。想了解更多语法错误的相关内容,可以阅读本专题下面的文章。

2024.03.06

3243

4

oracle数据库运行sql方法
oracle数据库运行sql方法

运行sql步骤包括:打开sql plus工具并连接到数据库。在提示符下输入sql语句。按enter键运行该语句。查看结果,错误消息或退出sql plus。想了解更多oracle数据库的相关内容,可以阅读本专题下面的文章。

2024.04.07

6440

11

sql中where的含义
sql中where的含义

sql中where子句用于从表中过滤数据,它基于指定条件选择特定的行。想了解更多where的相关内容,可以阅读本专题下面的文章。

2024.04.29

8801

6

sql中删除表的语句是什么
sql中删除表的语句是什么

sql中用于删除表的语句是drop table。语法为drop table table_name;该语句将永久删除指定表的表和数据。想了解更多sql的相关内容,可以阅读本专题下面的文章。

2024.04.29

1190

5

sql中删除一列的命令是什么
sql中删除一列的命令是什么

在sql中,使用alter table语句可以删除一列,语法为:alter table table_name drop column column_name。想了解更多sql的相关内容,可以阅读本专题下面的文章。

2024.04.29

1052

5

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
热门推荐
/
最新课程
phpStudy极速入门视频教程
phpStudy极速入门视频教程

共6课时 | 54.7万人学习

独孤九贱(4)_PHP视频教程
独孤九贱(4)_PHP视频教程

共89课时 | 133.9万人学习