如何正确新增字段

标签: dev | 发表时间:2021-04-24 00:00 | 作者:
出处:http://itindex.net/relian

最近运维执行 DB 变更,给业务表 T_pay 新增字段 F_id,引起 DAO 模块读数据接口 GetBillInfo 出现大量找不到记录的失败。为什么只是新增 MySQL 字段,会引起模块接口失败?


1.原因分析

业务 MySQL 采用了读写分离的方式,写 Master 主机,读 Slave0 从机。这样的好处是读写负载被分布到不同机器,同时可以防止大量读引起 Master 故障。

看 DB 监控发现新增字段时,出现了大量的主备延时。什么是主备延时?

下图是主备同步的流程,Master 生成 binlog 后,dump_tread 线程将 binlog 同步传输给 Slave。Slave 上的 io_thread 线程接收数据并存储为中转日志 relog,最后由 sql_thread 线程将数据重放到引擎存储中。主备延时是从主机执行事务成功到备机 sql_thread 线程重放数据到最新事务 id 的时间差。

1.1.为什么 DB 新增字段会影响主备延时?

运维操作 DDL 变更时选择的策略是从主机变更,那么会先在 Master 进行 DDL 操作,再同步 binlog 到 Slave。

T_pay 表有 300M 大小,主库进行 DDL 时,会产生 mixed 的 binlog。从库 sql_thread 为单线程重放 binlog 日志,这个过程需要重建数据,占用线程时间长,造成 relaylog 堆积,产生延时。

1.2.主备延时会影响 master 上的写操作吗?

出现主备延迟只影响了 Slave0 上的 select,并没影响 Master 上的 insert、update。原因是主备延时不会影响半同步。

业务写 Master 时,会先写 binlog,只要有一个 Slave 返回 ACK,半同步就完成了。Slave 只要更新到 binlog 就返回 ACK 给 Master 了,然后 Slave上 的 sql_thread 进程可以慢慢重放数据。

1.3.出现读数据接口失败原因

用户先付款成功,会在 Master 上 InsertBillInfo 生成一条订单记录;然后在 Slave0 上 GetBillInfo 去读这条订单记录,但由于主备延时,这条订单记录还没有在从机上生成,所以出现找不到记录的失败。


2.如何变更

方案一:DAO 切换为写 Master 读 Master

优势:切换之后实时性更好,不会出现主备延时

劣势:DAO 在读写分离时,可以容忍读故障。例如,索引问题出现慢查询引发 DB 故障,此时只是影响读,业务可以正常运行。读写分离可以对这些场景进行兜底。如果切换为写 Master 读 Master,一旦出现故障,业务会不可用。可以考虑在读 Master 后变更 DB,观察业务情况后再判断是否需要切回 Slave。

风险:需要修改配置切换为读写 Master

方案二:低峰期从主机缓慢变更

优势:DB 变更已开始执行,这种方案不需要代码层面的修改

劣势:每个表之间的变更间隔必须加大,尽量让中间 sleep 的时间追齐前面表变更导致的延迟。不加间隔时大约需要执行 4 小时

风险:出现主备延时,导致销帐延迟

方案三:运维写脚本分开主备变更

优势:可以尽量减少备机延时

劣势:需要运维手写脚本来分开主备变更,属于非标准操作,风险较高

风险:运维手写脚本分开主备变更

方案四:从机读不到时再读 Master 兜底

优势:可以解决一部分主备延时问题

劣势:放大请求,有些订单本来就读不到

方案五:从机获取两个 Slave 的延时,选最优 Slave 读

优势:可以减轻主备延时

劣势:可能还是存在延时问题

方案六:低峰期挂公告停服再变更

优势:不需要开发和运维变更

劣势:MySQL 执行无法并行,执行时间 4 小时+,停服时间过长

风险:影响用户体验

结论

对比以上方案后,选择方案一进行变更。变更当晚又出现主备延时。但因为 GetBillInfo 读订单表已经切换到读 Master,所以不再出现读不到订单的问题。


3、主备延迟来源

在备库上执行 show slave status 命令查看 Seconds_Behind_Master 字段可以知道当前备库的延迟时间。

主备延时包含两部分时间损耗:主机传输 binlog 日志到备库;备机接收完 binlog 和执行完这个事务。在网络正常情况下,第一部分耗时很短,主备延迟主要由第二部分引起,也就是备库 sql_thread 线程消费 relay log 的速度比主库生产要慢。

除了上面业务中出现的场景会导致这种问题,还有哪些场景也会导致主备延迟?

1、备机机器性能比主机差,或者参数配置不同

2、备机负载压力大,比如在上面执行了数据统计分析语句、运维日常导出脚本

3、大事务,在主机上执行 1 min,在备机上可能重放数据也需要 1 min


参考

MySQL DDL 为什么成本高

MySQL 45 讲:MySQL 是这么保证高可用的?

相关 [正确] 推荐:

正确理解ThreadLocal

- - Java - 编程语言 - ITeye博客
转自: http://www.iteye.com/topic/103804. 首先,ThreadLocal 不是用来解决共享对象的多线程访问问题的,一般情况下,通过ThreadLocal.set() 到线程中的对象是该线程自己使用的对象,其他线程是不需要访问的,也访问不到的. 另外,说ThreadLocal使得各线程能够保持各自独立的一个对象,并不是通过ThreadLocal.set()来实现的,而是通过每个线程中的new 对象 的操作来创建的对象,每个线程创建一个,不是什么对象的拷贝或副本.

正确重置MySQL密码

- xxg - 火丁笔记
谁都不想弄丢家门钥匙,但不管多么小心,时间长了,这样的事情总会发生几次. MySQL密码也是一样,把它写在文档上不太安全,记在脑子里又难免会忘记. 如果你忘记了MySQL密码,如何重置它呢. 首先停止MySQL服务,然后使用skip-grant-tables参数启动它:. 此时无需授权就可以进入到MySQL命令行,使用SQL重置MySQL密码:.

正确使用银行卡

- - 雨中发呆
请大家看清楚了,是网上银行汇款. 不是银行柜台上汇!柜台上的手续费比网上银行贵的. 但有一个例外,邮政储蓄要收0.5%. 工行:0.9%,最低0.9元/笔,最高45元/笔. 农行:0.4% ,最低1元/笔,最高20元(柜台手续费是0.5%,最高50元).  跨省:如果对方是银行卡:转账金额的0.06% (也就是万分之六),最低1元/笔,最高12元/笔.

女孩正确的生活方式

- bourne - 佳人
女孩正确的生活方式,关于健康、饮食、运动、爱情、伴侣、承诺、友情、微笑、心态、人生、幸福等方面的建议,为自己心爱的女孩收起来吧. 生理期不吃巧克力,因为会加重痛经. 通过运动而非调整型内衣来塑造曲线. 去年的衣服要进行曝晒后才可以穿. 即使爱美,也不要在耳朵上部的外缘软骨部位穿耳洞. 了解自己的家庭病史,特别是母亲和外婆的病史.

正确理解javascript的this关键字

- BeerBubble - 三水清
javascript有this关键字,this跟javascript的执行上下文密切相关,很多前端开发工程师至今对this关键字还是模棱两可,本文将结合代码讲解下javascript的this关键字. 定义了一个person对象,对象中包含了name、gender属性,还包括了一个getName的方法,其作用是输出person对象的name.

如何正确配置Nginx+PHP

- - 火丁笔记
对很多人而言,配置Nginx+PHP无外乎就是搜索一篇教程,然后拷贝粘贴. 听上去似乎也没什么问题,可惜实际上网络上很多资料本身年久失修,漏洞百出,如果大家不求甚解,一味的拷贝粘贴,早晚有一天会为此付出代价. 假设我们用PHP实现了一个前端控制器,或者直白点说就是统一入口:把PHP请求都发送到同一个文件上,然后在此文件里通过解析「REQUEST_URI」实现路由.

(转)如何正确地处理时间

- - jackyrong
日期和时间在程序中应用广泛,每种程序开发语言都自带处理日期和时间的相关函数,很多开发者把日期和时间存入数据库中,但是,一旦涉及到跨时区的日期和时间的处理时,大多数开发者根本就不明白如何正确地处理日期和时间. 首先,我们来看大部分的程序都是这么创建当前时间并存入数据库的:. 这么做的问题在于,数据库的DateTime类型没有时区(time zone)信息,因此,存入的是本地时间,并且丢掉了时区信息.

如何正确设置job的interval?

- - ITeye博客
首先,先了解interval的定义. 即在job开始执行时根据date function估算job下次执行的时间(NEXT_DATE). 通常,我们采用两种方式来设置job的interval:. 由date function估算出的时间是固定的. 由date function估算出的时间是非固定的.

在Linux进行IO的正确姿势

- - idea's blog
很多C/C++程序虽然在做网络编程, 但大多用别人封装好的库, 对底层不甚了解, 感觉 IO 操作不是很简单吗. 我敢说, 大多数人进行 IO 的姿势都不对, 所谓的 IO, 主要是 read()/write() 两个函数.. 先说错误的 IO 读操作:. 看起来好像很正确的样子, 返回值也判断了, 不仅判断 -1, 还判断 0, 应该姿势正确吧.