mysql datetime binlog诡异相差1秒,主从数据不一致深度揭密

  近日发现一个诡异问题,mysql一个时间类似字段有大量的主从值存在相差一秒的情况,具体来说,从机少一秒。

mysql datetime binlog诡异相差1秒,主从数据不一致深度揭密_第1张图片

如图所示,首先排除人为问题,没有人会手动关master上binlog,再手动改数据,再打开binlog,人为造成不一致情况不会出现。binlog格式为mixed,观察master binlog,发现有问题的数据在master机上select出来居然和binlog不一致!!!如下图:

mysql datetime binlog诡异相差1秒,主从数据不一致深度揭密_第2张图片

       前端插入数据方式为jdbc预解析模式,使用了绑定变量,时间(sql.timestamp类型)作为参数传入。简单分析下语句执行流程:execute执行insert之后,只把数据变量发后台,数据库后台sql层把绑定的问号还原成数据生成一条sql,然后插入。这里原始语句是不存在一条完整的insert语句的,问号中的值sql层拼接上去的,innodb引擎层要用这个timestamp值是可能直接到前端发过来的timestamp内存结构的,不需要处理,这两者差一秒很可能是秒以下的精度处理机制不一致,innodb层做了四舍五入,sql层直接丢弃,这种猜想能解释innodb多一秒,下面带着这个猜想到代码中找答案。

先看sql层:

sql层在还原insert语句时,把参数取出拼到thd->query_string,  general_log和binlog都是从这取的sql, 在set_param_datetime函数中设置参数,在这里执行param->set_time 设置时间,这个函数截图如下:

mysql datetime binlog诡异相差1秒,主从数据不一致深度揭密_第3张图片

在进行make_truncated_value_warning 截断精度处理时, MY_MIN(decimals,  DATETIME_MAX_DECIMALS) ,decimals为初始值0,所以MY_MIN(decimals,  DATETIME_MAX_DECIMALS)值为0,所以这里传入make_truncated_value_warning 函数的精度值为0,精度会被直接丢弃不做任何处理,这是sql层做法。假如有个时间值2018-10-12 14:28:29.888,在这之后就变成了2018-10-12 14:28:29。

再看引擎层:

从row_insert_for_mysql一路往回找,最终发现在my_datetime_round 处理精度阶段,里面调用的datetime_add_nanoseconds_with_round函数,如下图:

mysql datetime binlog诡异相差1秒,主从数据不一致深度揭密_第4张图片

简单来说,处理逻辑就是对于时间小数部分(second_part)四舍五入,小于0.5直接return,大于0.5秒的就调date_add_interval加一秒,也就是说sql层和存储层处理这个精度逻辑不一致造成这个问题,这TM是个bug,当然知道了原因要修这个bug很容易。

      运气不错,猜想得到验证,不过如果无的放矢,那要确定原因就有些麻烦了,有时候合理的猜测往往能事半功倍,代码为5.6.28版本代码。

    5.7没跑,应该同样存在,bug出现场景是时间类型是通过发的内存结构到后台,sql层需要拼接还原sql语句时会出现,所有带秒以下精度的场景会有50%的概率出现差一秒。

 

 

 

 

你可能感兴趣的:(源码学习,mysql,timestamp,binlog)