在实际工作中,经常遇到需要给数据库表添加索引的情况。虽然操作是由dba来执行,但开发还是应该了解在线添加索引会引起的性能问题。比如博主最近就遇到了线上添加索引导致业务报警的问题。


问题描述

出于业务需要,给一个表添加普通索引,但这个表有100个分表,因此需要给100个分表都加上。平均每张表大概有500万行的数据量。线上业务流量也比较高。mysql版本为5.7版本,在线添加索引问题不大。跟dba商量好之后,提交sql脚本由dba实施。经实践发现,平均一张表添加索引大概需要3~5分钟。在添加完20多张表之后,发现业务开始报警。查看业务日志,发现有不少接口超时。

根据经验判断,应该是服务端TCP连接满了(见参考资料1),查看监控信息,果然如此,直接使用该数据库表的服务的tcp连接溢出。很显然,应该是数据库操作变慢导致接口响应时间增加,从而导致吞吐量降低,而业务流量保持不变的情况下,大量请求造成堆积,进而导致TCP连接被占满甚至溢出。

同时,即使没有使用该数据库表,使用其他库的服务也出现同样的问题。原因在于两者使用的数据库在同一个实例上,添加索引导致数据库服务器负载增加(实际增加不算多,正常来说应该不影响?),影响到了其他库。

这时候只能让dba将索引添加操作暂停,等待晚上12点之后流量降低再执行。晚上执行时不再报警。

online ddl

通常情况下,对数据量大的表进行ddl操作时,一般都会选在流量低的时候进行。但是在mysql5.6之后,引入了一些新的特性,支持DDL执行期间DML语句的并行操作,提高了数据库的吞吐量。据mysql官方文档介绍,online ddl操作不会加锁,很快就能完成操作。正是基于这一点考虑,所以才直接在白天加。结果就出现了上面的问题。那么什么是online ddl呢?其结构图如下:

Online DDL原理

oneline ddl大致分为3个部分:

  • copy(ALGORITHM=COPY)这部分是offline的,ddl执行时会阻塞dml,中间需要临时表的中转。这也是5.6版本前的DDL执行方法。在innodb中不支持使用inplace的操作都会自动使用copy方式执行,而MyISAM表只能使用copy方式。
  • inplace(ALGORITHM=INPLACE)所有操作在innodb引擎层完成,不需要经过临时表的中转。除上图两种特殊索引创建外,其他以inplace方式执行的操作都是online的,执行期间其他DML操作可以并行,其中又以是否重建表又分为两个部分rebuild和no-rebuild。

rebuild部分涉及表的重建,在原表路径下创建新的.frm和.ibd文件,消耗的IO会较多。期间(原表可以修改)会申请row
log空间记录DDL执行期间的DML操作,这部分操作会在DDL提交阶段应用新的表空间中。no-rebuild部分由于不涉及表的重建,除创建添加索引,会产生部分二级索引的写入操作外,其余操作均只修改元数据项,即只在原表路径下产生.frm文件,不会申请row log,不会消耗过多的IO,速度通常很快。

  • inplace but offline的几种特殊DDL操作,本身是按inplace方式执行,但是执行期间DML语句却不能并行。

如何区分DDL语句是使用了copy方式还是inplace方式,只需要查看语句执行完成输出结果中的 X rows
affected,如果X为0则是inplace(online)方式,如果不为0则是copy(offline)方式。

copy的整体执行过程如下:

  • 1.锁表,期间DML不可并行执行
  • 2.生成临时表以及临时表文件(.frm .ibd)
  • 3.拷贝原表数据到临时表
  • 4.重命名临时表及文件
  • 5.删除原表及文件
  • 6.提交事务,释放锁

inplace(rebuild)的整体执行过程如下:

准备阶段

  • 1.对表加元数据共享升级锁,并升级为排他锁(此时DML不能并行)

  • 2.在原表所在的路径下创建.frm和.ibd临时中转文件(no-rebuild除创建二级索引外只创建.frm文件,其中添加二级索引操作最为特殊,该操作属于no-rebuild不会生成.ibd,但实际上对.ibd文件却做了修改,该操作会在参数tmpdir指定路径下生成临时文件,用于存储索引排序结果,然后再合并到.ibd文件中)

  • 3.申请row log空间,用于存放DDL执行阶段产生的DML操作(no-rebuild不需要)

执行阶段

  • 1.释放排他锁,保留元数据共享升级锁(此时DML可以并行)

  • 2.扫描原表主键以及二级索引的所有数据页,生成 B+ 树,存储到临时文件中

  • 3.将所有对原表的DML操作记录在日志文件row log中

如果只修改元数据部分(no-rebuild),该阶段只是修改.frm文件,不需要其他操作,也不需要申请row log

提交阶段

  • 1.升级元数据共享升级锁,产生排他锁锁表(此时DML不能并行)。

  • 2.重做row log中的内容。(no-rebuild不需要)

  • 3.重命名原表文件,将临时文件改名为原表文件名,删除原表文件

  • 4.提交事务,变更完成。

显式online ddl参数

可以在执行online DDL语句的时候,使用ALGORITHM和LOCK关键字,这两个关键字在DDL语句的最后面,用逗号隔开。

ALGORITHM有如下选项:

  • INPLACE:直接在原表上面执行DDL的操作。
  • COPY:使用临时表。这期间需要多出一倍的磁盘空间来支撑这样的 操作。执行期间,表不允许DML的操作。
  • DEFAULT:默认方式,由MySQL自己选择,优先使用INPLACE的方式。

LOCK有如下选项:

  • SHARE:共享锁,执行DDL的表可以读,但是不可以写。
  • NONE:没有任何限制,执行DDL的表可读可写。
  • EXCLUSIVE:排它锁,执行DDL的表不可以读,也不可以写。
  • DEFAULT:默认值,也就是在DDL语句中不指定LOCK子句的时候使用的默认值,由MySQL自动判断,优先使用NONE的方式。

例句如下,参数间使用逗号隔开:

alter table test add col int,ALGORITHM=INPLACE,LOCK=DEFAULT;

执行DDL操作时,显式参数可以不指定,mysql会自动选择合适的方式去执行,优先使用inplace,none的方式,效果与指定ALGORITHM=DEFAULT,LOCK=DEFAULT一样。但是如果显式指定了这两个参数,则必须按照指定的方式来执行,不支持的话则直接报错。

总结分析

从上面的内容来看,online ddl添加索引正常应该是很快的,但并不是完全不会加锁。在对表元数据加互斥锁的时候,会阻塞dml操作。但从整体来看,这个时间应该是极短的,所以官方才有online ddl操作不会加锁的说法。

上面说到在inplace模式的online ddl操作时,会申请一个缓存空间,用于存放在此期间的dml操作。这个缓存大小由参数innodb_online_alter_log_max_size控制,默认为128mb,支持动态修改。如果更新的表比较大,并且在ddl过程中有大量的写操作,就可能遇到空间不足的情况,会抛出相应的错误。

另外,如果ddl操作的目标表上有未结束的事务或者有锁没有释放,那么在加元数据独占锁(mdl)时就会等待前面的锁释放,这个时候的状态为:waiting for table metadata lock。又因为独占锁的优先权限,后面的DML操作都要排队等待。从而导致db操作阻塞。

最终的结论是:如果表数据量较小,或者加索引的表数量较少,online ddl操作是可以接受的,建议显式指定algorithm和lock参数。但如果数据量较大,或者加索引的表比较多,那么就需要充分考虑上面说到的问题,最好在业务流量低的时候执行。

参考资料

[1].https://www.cnblogs.com/wx170119/p/12068005.html
[2].https://blog.csdn.net/finalkof1983/article/details/88355314

Mysql在线添加索引相关推荐

  1. mysql 中添加索引的三种方法

    在mysql中有多种索引,有普通索引,全文索引,唯一索引,多列索引,小伙伴们可以通过不同的应用场景来进行索引的新建,在此列出三种新建索引的方法 mysql 中添加索引的三种方法 1.1 新建表中添加索 ...

  2. mysql 中添加索引的三种方法(一)

    在mysql中有多种索引,有普通索引,全文索引,唯一索引,多列索引,小伙伴们可以通过不同的应用场景来进行索引的新建,在此列出三种新建索引的方法 mysql 中添加索引的三种方法 1.1 新建表中添加索 ...

  3. mysql千万级大表在线添加索引

    #怎样向有大量数据的表中添加索引 文章目录 前言 一.使用步骤 总结 前言 在日常开发过程中我们可能会遇见这种场景,就是为了优化查询我们需要对某张表中的某个字段添加索引,但表中存在大量数据,而且正式库 ...

  4. 如何向MySQL表添加索引?

    我有一个非常大的MySQL表,大约有150,000行数据. 目前,当我尝试并运行时 SELECT * FROM table WHERE id = '1'; 代码运行正常,因为ID字段是主索引. 但是, ...

  5. mysql怎么添加索引

    mysql 添加索引 mysql 如何创建索引 索引(Index)是帮助MySQL高效获取数据的数据结构.MySQL索引的建立对于MySQL的高效运行是很重要的,索引可以大大提高MySQL的检索速度. ...

  6. mysql中添加索引的命令_mysql添加索引命令

    1.PRIMARY  KEY(主键索引) mysql>ALTER  TABLE  `table_name`  ADD  PRIMARY  KEY (  `column`  ) 2.UNIQUE( ...

  7. mysql数据库添加索引和去重

    1. 数据库添加索引 alter table dir_list add index dir_name(dir_name); alter table dir_list add index file_na ...

  8. MySQL 如何添加索引

    表中添加索引的三种方式 创建表的时候创建索引 隐式创建:使用CREATE TABLE创建表时,在声明有主键约束.唯一性约束.外键约束的字段上,会自动的添加相关的索引. #CREATE TABLE时隐式 ...

  9. mysql 如何添加索引_MySQL如何创建一个好索引?创建索引的5条建议【宇哥带你玩转MySQL 索引篇(三)】...

    MySQL如何创建一个好索引?创建索引的5条建议 过滤效率高的放前面 对于一个多列索引,它的存储顺序是先按第一列进行比较,然后是第二列,第三列...这样.查询时,如果第一列能够排除的越多,那么后面列需 ...

最新文章

  1. Java比较数量怎么比较_java - 如何在Java数量比较字符 - SO中文参考 - www.soinside.com...
  2. 【Python】Python一行代码能做什么,30个实用案例代码详解
  3. RabbitMQ(三):Exchange交换器--fanout
  4. php抓取新浪新闻,新浪新闻采集程序
  5. VuePress 添加百度统计代码
  6. 帧同步和状态同步(二)案例分析
  7. 10 张令人喷饭的程序员漫画
  8. 作者:鲁鸣鸣(1978-),男,博士,中南大学信息科学与工程学院副教授,中国计算机学会会员。...
  9. LeetCode 646. 最长数对链
  10. Python多线程编程基础2:如何创建线程
  11. Linux下的wc命令
  12. 修改 javascript 中alert样式
  13. xmarin.android导航栏,Xamarin.Forms中心标题和透明导航栏 - Android
  14. 给radio添加点击事件
  15. robotframework 接口测试 +RSA 加密
  16. VIIRS-NPP夜光遥感数据下载
  17. mysql卸载不干净无法重新安装mysql
  18. python自定义函数详解_python 自定义函数
  19. win10 安装pytorch gpu 及 解决报错“OSError: [WinError 126] 找不到指定的模块
  20. Maven查看当前生效配置、pom、环境变量等命令(mvn help用法)

热门文章

  1. 类型“XXX”违反了继承安全性规则。派生类型必须与基类型的安全可访问性匹配或者比基类型的安全可访问性低。...
  2. 2008日志清理 server sql_sql2008数据库日志清理
  3. WAAS/EGNOS/MSAS/ GAGAN
  4. WAAS/EGNOS/MSAS/GAGAN
  5. 无源波分和彩光模块_CWDM无源波分技术的应用及光模块的类型
  6. ppt播放影片时出现提示某些文件可能携带病毒,损害您的计算机,我的PPT没病毒—禁用PowerPoint的病毒提示...
  7. 广西玉柴机器股份有限公司IBM建议方案
  8. 让你的Mac桌面变得与众不同其实很简单
  9. 求教:会员管理系统,如何增加积分制和相应的等级制
  10. 什么是供应商管理?供应商管理办法及流程介绍