大表新增字段
1. 问题
如果直接插入字段,会对这张表加元数据锁,导致整张表被锁住,所有请求会阻塞,可能会导致服务器 OOM 问题,甚至会导致雪崩。
2. 解决
2.1. MySQL 5.6 之前
2.1.1. 主从切换
- MySQL 主库 A,从库 B;
- 关闭主从同步,负载均衡让 A 全部承载读写操作;
- B 新增字段,新增完成之后,恢复主从复制;从库 B 追赶主库 A 的数据;
- 追赶完成后,从库 B 升级为主库,进行主从切换,此后由主库 B 承载读写操作;
- 从库 A 进行字段新增,新增完成后;
- 切换回原有的主从结构。
- 此过程看起开复杂,实际一点也不简单,存在巨大的风险;
- 主从结构在这个过程中遭到破坏,数据库读写压力需要预估;
- 操作复杂。
2.1.2. 幽灵表
- 在原表的基础上复制出一张新的表结构(幽灵表);
- 在幽灵表中新增字段;
- 将原表数据同步到幽灵表;
- 增量同步,binlog 监听、触发器
- 全量同步,批量分块
- 同步完成后,通过原子操作修改表名
- 数据同步过程对数据库造成压力较难预估;
- 更改表名的操作会阻塞一段时间的请求。
2.1.3. 拓展表(JOIN)
新建一张表,在新表中添加字段,不会阻塞原表,新表和原表进行主键的连接查询,风险极低。
- 在应用层通过
JOIN进行联表查询、保证一致性等操作,对性能可能会造成影响; - 新表中添加字段可能还会导致本文所论述的问题;
2.1.4. 拓展字段
建表时添加拓展字段,或JSON类型字段;
- 维护难,需写好注释以及相关文档。
2.1.5. 压力产品
代码较难实现且风险较大,跟产品沟通好,放在其他存储介质中。
2.2. MySQL 5.6 之后
2.2.1. Online DDL
Online DDL是 MySQL 5.6 之后的新特性,可以在不大幅度影响业务读写的情况下,动态改变表结构。
- 准备阶段:短暂持有元数据锁;
- 防止其他 DDL 改动
- 执行阶段:执行 DDL 语句;
COPY:原表的基础上复制出新表,原表加锁,阻塞写操作,写操作存储在日志缓存,读操作变慢(竞争 IO);INPLACE(5.7 默认):原表基础上直接新增字段,row_log 记录增删改,查询仍然可以正常进行;INSTANT(8.0+):只修改表的元数据信息而不会动物理信息,查询数据时动态生成默认值;
- 第三阶段:短暂持有元数据锁
COPY:原子操作,删除原表,把日志的缓存同步到新表中;INPLACE:设置元数据字段指针指向位置,同步缓存中的写操作;INSTANT:只需确认一致性。
评论