千万级数据表添加索引

435 阅读3分钟

故事背景

有一个订单业务需要查询cid字段,但是由于cid字段没加索引查询很慢,所以需要添加索引。但是查询订单表发现已经有7000万+数据,如果直接加索引不知道多久,也有可能有锁表的风险,锁表了就会影响业务流程,这种DDL操作还是要小心谨慎为妙~

请教大佬

因为没怎么做过这种千万级数据表添加索引的操作,所以想问问技术群的大佬们有没有什么好的想法。

大佬一:直接加上,选在业务量少的时候操作

回答:可能要半个小时以上,又不太想停服务

大佬二:gpt

那我们看看GPT有什么答案~

image.png

好像gpt没理解到我的需求,再问一遍

image.png

看了一下操作起来都比较复杂,没有简单一点的......

大佬三:onlineDDL

这个听起来挺靠谱的,mark一下。一开始我以为onlineDDL是一个在线操作DDL的工具,通过查资料才发现这是MYSQL对DDL语句的优化。

数据准备

我想把正式环境的7000W数据导出到测试环境,在测试环境做模拟操作,用Navicat的转储SQL文件(结构和数据),因为不是DDL语句所以不会锁表,但是在导出的过程中发现速度比较慢,导了1个多小时才2千万数据,后面就想直接用这2千万数据测试了。

但是问题又来了,导入数据更慢,导了一个小时才导了200万....查了一下那个SQL文件 2千万居然有20G,快把我的C盘都占满了。感觉这种大数据表用mysqldump导出比较快,因为导出SQL文件是create table和insert xxx的格式,比较慢。转换思路,用回我之前用存储过程加了1E条数据的测试表测试,发现在添加索引的时候修改和插入的时候都不会锁表。

翻车

alter table table_name add index(index_name)

等我在现网数据库执行添加索引的语句的时候,发现居然锁表了,怎么跟我测试环境测试的时候不一样,是因为数据问题还是什么问题呢?不过还好准备了兜底操作,如果锁表了直接kill掉

SELECT * FROM information_schema.processlist WHERE command != 'Sleep';

kill pid

说好的onlineddl不锁表呢?后面看到有人写后面加个lock = none 可以不缩表,试了下还是不行。

后面发现现网这个ddl语句的状态不对,测试环境的是alter table状态,现网是waiting for table metadata lock,看起来是等待锁的状态,但是看了下只有select语句。查阅资料后发现select的长事务也会锁表

598452062ae8f03ab1cd3d56bf8dc34.png

然后我就把select语句全kill掉,再执行DDL语句,发现正常了~系统能正常插入数据,查询也没什么问题。

最后还有一个小插曲...执行了15分钟左右,又发现卡了...正在执行的语句有三条

alter table table_name add index(index_name) 状态 waiting for table metadata lock

select * from xxxx 状态 正常

insert into table xxxx 状态 waiting for table metadata lock

过了一会儿,锁都释放了。说明添加索引操作结束的时候也会需要metadata独占锁。

总结

onlineDDL挺牛逼的,如果在开始和结束添加索引的时候需要metadata独占锁,这个时候不能有表的操作(包括读),如果没拿到metadata锁就会等待,从而insert update等语句都会卡住。如果拿到metadata锁后就可以执行insert/update语句不锁表,执行完的时候也要释放独占锁,这个时候也不能有表的操作。另外7000万数据如果不锁表,添加一个普通索引大概需要15分钟左右。