当前位置: 首页 > 科技观察

MySQL的Replace操作导致数据丢失——那些坑你踩过吗?

时间:2023-03-23 10:54:04 科技观察

1。问题是该公司的开发人员在更新数据时使用了replaceinto语句。由于使用不当,导致大量数据丢失。它是如何导致数据丢失的?分析如下。二、问题分析REPLACE的原理官方对REPLACEINTO原理的解释是:REPLACE的工作原理和INSERT完全一样,只是如果表中的旧行对于PRIMARYKEY或UNIQUE索引的新行的值相同,则旧行在插入新行之前被删除。如果新插入行的主键或唯一键在表中已经存在,则删除原记录,插入新行;如果表中不存在,则直接插入地址:https://dev.mysql.com/doc/refman/5.6/en/replace.htmlb。缺失数据的表结构如下:CREATETABLE`active_items`(`id`bigint(20)NOTNULLAUTO_INCREMENTCOMMENT'主键',`ad_id`char(32)NOTNULLDEFAULT''COMMENT'XXX',`ap_id`tinyint(4)NOTNULLDEFAULT'1'COMMENT'XXX',`price`bigint(20)NOTNULLDEFAULT'0'COMMENT'xxx',`rate`int(11)NOTNULLDEFAULT'0'COMMENT'XXX',`cc_price`bigint(20)NOTNULLDEFAULT'0'COMMENT'XXX',`sale_count`int(11)NOTNULLDEFAULT'0'COMMENT'XXX',`prom_count`int(11)NOTNULLDEFAULT'0'COMMENT'XXX',`c1`int(11)NOTNULLDEFAULT'0'COMMENT'XXX',`status`tinyint(4)NOTNULLDEFAULT'0'COMMENT'XXX',`order_num`int(11)NOTNULLDEFAULT'0'COMMENT'XXX',`score`int(11)NOTNULLDEFAULT'0'评论'XXX',`audit_time`int(11)NOTNULLDEFAULT'0'COMMENT'XXX',`prom_modify_time`int(11)NOTNULLDEFAULT'0'COMMENT'XXX',`sales_modify_time`int(11)NOTNULLDEFAULT'0'COMMENT'XXX',`create_time`int(11)NOTNULLDEFAULT'0'COMMENT'创建时间',`modify_time`int(11)NOTNULLDEFAULT'0'COMMENT'最后修改时间',`score_modify_time`int(11)NOTNULLDEFAULT'0'COMMENT'XXX',`brand_id`int(11)NOTNULLDEFAULT'0'COMMENT'XXX',`freight_status`tinyint(1)NOTNULLDEFAULT'0'COMMENT'XXX',`flag`tinyint(4)NOTNULLDEFAULT'0'COMMENT'XXX',PRIMARYKEY(`id`),UNIQUEKEY`idx_ad_id`(`ad_id`)USINGBTREE,...)ENGINE=InnoDBAUTO_INCREMENT=2699DEFAULTCHARSET=utf8COMMENT='XXXXX'执行的替换语句如下(多个):REPLACEINTOactive_items(ad_id,score)VALUES('XXXXXXX',1800);通过查询binlog找到执行记录,部分如下:###UPDATE`items`.`active_items`###WHERE###@1=21926/*LONGINTmeta=0nullable=0is_null=0*/###@2='XXXXXXX'/*STRING(96)meta=65120nullable=0is_null=0*/###@3=1/*TINYINTmeta=0nullable=0is_null=0*/###@4=3900/*LONGINTmeta=0nullable=0is_null=0*/###@5=315/*INTmeta=0nullable=0is_null=0*/###@6=1228/*LONGINTmeta=0nullable=0is_null=0*/###@7=19/*INTmeta=0nullable=0is_null=0*/###@8=0/*INTmeta=0nullable=0is_null=0*/###@9=-6(4294967290)/*INTmeta=0nullable=0is_null=0*/###@10=0/*TINYINTmeta=0nullable=0is_null=0*/###@11=0/*INTmeta=0nullable=0is_null=0*/###@12=8/*INTmeta=0nullable=0is_null=0*/###@13=1489997272/*INTmeta=0nullable=0is_null=0*/###@14=1495765800/*INTmeta=0nullable=0is_null=0*/###@15=1495728000/*INTmeta=0nullable=0is_null=0*/###@16=1489996426/*INTmeta=0nullable=0is_null=0*/###@17=1489997272/*INTmeta=0nullable=0is_null=0*/###@18=1495728725/*INTmeta=0nullable=0is_null=0*/###@19=0/*INTmeta=0nullable=0is_null=0*/###@20=1/*TINYINTmeta=0nullable=0is_null=0*/###@21=0/*TINYINTmeta=0nullable=0is_null=0*/###SET###@1=35313/*LONGINTmeta=0nullable=0is_null=0*/###@2='XXXXXXX'/*STRING(96)meta=65120nullable=0is_null=0*/###@3=1/*TINYINTmeta=0nullable=0is_null=0*/###@4=0/*LONGINTmeta=0nullable=0is_null=0*/###@5=0/*INTmeta=0nullable=0is_null=0*/###@6=0/*LONGINTmeta=0nullable=0is_null=0*/###@7=0/*INTmeta=0nullable=0is_null=0*/###@8=0/*INTmeta=0nullable=0is_null=0*/###@9=0/*INTmeta=0nullable=0is_null=0*/###@10=0/*TINYINTmeta=0nullable=0is_null=0*/###@11=0/*INTmeta=0nullable=0is_null=0*/###@12=3150/*INTmeta=0nullable=0is_null=0*/###@13=0/*INTmeta=0nullable=0is_null=0*/###@14=0/*INTmeta=0nullable=0is_null=0*/###@15=0/*INTmeta=0nullable=0is_null=0*/###@16=0/*INTmeta=0nullable=0is_null=0*/###@17=0/*INTmeta=0nullable=0is_null=0*/###@18=0/*INTmeta=0nullable=0is_null=0*/###@19=0/*INTmeta=0nullable=0is_null=0*/###@20=0/*TINYINTmeta=0nullable=0is_null=0*/###@21=0/*TINYINTmeta=0nullable=0is_null=0*/操作的ad_id已经存在,所以先删除再插入,可以看到除了指定的ad_id、score等字段被修改为默认值,导致原始数据丢失(虽然在日志中被转换为update)c.对比测试接下来我进行了如下测试:左边使用REPLACE语句,右边使用DELETE+INSERT语句,最后的结果是完全一样的。删除原主键id为1的行,新插入行的主键id更新为4,插入没有指定内容的字段c,插入默认值。使用REPLACE更新一行数据,MySQL提示受影响的行数为2总结一下,就是删除了一行,插入了一行。3、数据恢复数据丢失或数据出错后,有几种恢复方式:业务方写脚本恢复通过MySQLbinlog检测误操作sql,生成反向sql进行数据恢复(适用于sql数据量小)通过历史备份文件+增量binlog将数据状态恢复到误操作前时刻4.问题扩展通过从以上分析可知发现REPLACE会删除旧行插入新行,但是binlog是以update的形式记录的,这又带来了一个问题:从库的自增长值比主库小1.测试a。主从一致性:master库:mysql>showcreatetablet\G******************************1.row*****************************表:tCreateTable:CREATETABLE`t`(`id`int(11)NOTNULLAUTO_INCREMENT,`a`int(11)DEFAULT'0',`b`int(11)DEFAULT'0',`c`int(11)DEFAULT'0',PRIMARYKEY(`id`),UNIQUEKEY`a`(`a`))ENGINE=InnoDBAUTO_INCREMENT=4DEFAULTCHARSET=utf81rowinset(0.00sec)来自库:mysql>showcreatetablet\G******************************1.row***************************表:tCreateTable:创建表`t`(`id`int(11)NOTNULLAUTO_INCREMENT,`a`int(11)DEFAULT'0',`b`int(11)DEFAULT'0',`c`int(11)DEFAULT'0',PRIMARYKEY(`id`),UNIQUEKEY`a`(`a`))ENGINE=InnoDBAUTO_INCREMENT=4DEFAULTCHARSET=utf81rowinset(0.00秒)b.主库替换:主库:mysql>replaceintot(a,b)values(1,7);QueryOK,2rowsaffected(0.01sec)mysql>showcreatetablet\G***************************1.row**************************表:tCreateTable:CREATETABLE`t`(`id`int(11)NOTNULLAUTO_INCREMENT,`a`int(11)DEFAULT'0',`b`int(11)DEFAULT'0',`c`int(11)DEFAULT'0',PRIMARYKEY(`id`),UNIQUEKEY`a`(`a`))ENGINE=InnoDBAUTO_INCREMENT=5DEFAULTCHARSET=utf81rowinset(0.00sec)来自库:mysql>showcreatetablet\G******************************1.row*************************表:tCreateTable:创建表`t`(`id`int(11)NOTNULLAUTO_INCREMENT,`a`int(11)默认'0',`b`int(11)DEFAULT'0',`c`int(11)DEFAULT'0',PRIMARYKEY(`id`),UNIQUEKEY`a`(`a`))ENGINE=InnoDBAUTO_INCREMENT=4DEFAULTCHARSET=utf81rowinset(0.00sec)注意此时主从表的AUTO_INCREMENT值是不一样的。C。模拟slave升级master,在从库执行INSERT:mysql>insertintot(a,b,c)values(4,4,4);ERROR1062(23000):Duplicateentry'4'forkey'PRIMARY'mysql>showcreatetablet\G*********************************1。行**************************表:tCreateTable:CREATETABLE`t`(`id`int(11)NOTNULLAUTO_INCREMENT,`a`int(11)DEFAULT'0',`b`int(11)DEFAULT'0',`c`int(11)DEFAULT'0',PRIMARYKEY(`id`),UNIQUEKEY`a`(`a`))ENGINE=InnoDBAUTO_INCREMENT=5DEFAULTCHARSET=utf81rowinset(0.00sec)mysql>insertintot(a,b,c)values(4,4,4);QueryOK,1rowaffected(0.00sec)Insertfromthelibrary有时会报错,主键重复。报错后AUTO_INCREMENT会+1,所以再次执行后可以成功插入。2、结论这个问题在平时不会有丝毫影响,但是:如果主库平时大量使用REPLACE语句,那么从库中AUTO_INCREMENT的值就会降低。主库太落后了。当发生主从切换时,新的主库在再次插入数据时会出现大量重复的主键错误,导致无法插入数据。3.参考文章http://www.cnblogs.com/monian/archive/2014/10/09/4013784.html

最新推荐
猜你喜欢