删除MySQL sourceTable时logTable的SourceId被随机置空问题排查
执行MySQL循环删除操作时出现异常,使用以下存储过程删除sourceTable中6个月前的数据:
drop procedure if exists del_6m_data; delimiter $$ create procedure del_data(table_name text, col_name text, del_limit text) begin set @query = concat('delete from ', table_name, ' where ', col_name, ' < date_sub(current_date, INTERVAL 6 MONTH) limit ', del_limit); prepare query from @query; repeat execute query; until row_count() = 0 end repeat; deallocate prepare query; end $$ delimiter ;
两张表均为InnoDB引擎:
sourceTable存储当日待处理信息,ID为自增int类型;logTable通过SourceId记录相关交互、结果等日志,DDL中未定义SourceId的外键关联及更新/删除规则。
异常现象:删除sourceTable数据时,logTable的SourceId被随机置空。已检查information_schema中的约束、存储过程、更新/删除规则,均未发现异常。logTable表结构如下:
CREATE TABLE `LogTable` ( `id` int NOT NULL AUTO_INCREMENT, `state` int DEFAULT NULL, `statedesc` varchar(255) DEFAULT NULL, `scheduledat` datetime DEFAULT NULL, `countbusyretry` int DEFAULT '0', `countcongestionretry` int DEFAULT '0', `countnoanswerretry` int DEFAULT '0', `countglobal` int DEFAULT '0', `uniqueid` varchar(255) DEFAULT NULL, `originatecalleridnum` varchar(255) DEFAULT NULL, `originatecalleridname` varchar(255) DEFAULT NULL, `calleridnum` varchar(255) DEFAULT NULL, `calleridname` varchar(255) DEFAULT NULL, `starttime` datetime DEFAULT NULL, `responsetime` datetime DEFAULT NULL, `answertime` datetime DEFAULT NULL, `droptime` datetime DEFAULT NULL, `endtime` datetime DEFAULT NULL, `ringtime` int DEFAULT '0', `holdtime` int DEFAULT '0', `talktime` int DEFAULT '0', `followuptime` int DEFAULT '0', `dropreason` varchar(255) DEFAULT NULL, `campaign` varchar(255) DEFAULT NULL, `campaigntype` varchar(255) DEFAULT NULL, `membername` varchar(255) DEFAULT NULL, `reason` varchar(255) DEFAULT NULL, `disposition` varchar(255) DEFAULT NULL, `secondDisposition` varchar(255) DEFAULT NULL, `thirdDisposition` varchar(255) DEFAULT NULL, `dispositionat` datetime DEFAULT NULL, `amd` tinyint(1) DEFAULT '0', `fax` tinyint(1) DEFAULT '0', `blacklist` tinyint(1) DEFAULT '0', `rescheduled` tinyint(1) DEFAULT '0', `rescheduledat` datetime DEFAULT NULL, `callback` tinyint(1) DEFAULT '0', `callbackuniqueid` varchar(255) DEFAULT NULL, `callbackat` datetime DEFAULT NULL, `deleted` varchar(255) DEFAULT NULL, `deletedat` datetime DEFAULT NULL, `recallme` tinyint(1) DEFAULT '0', `agiafterat` datetime DEFAULT NULL, `createdAt` datetime NOT NULL, `updatedAt` datetime NOT NULL, `UserId` int DEFAULT NULL, `QueueId` int DEFAULT NULL, `SourceId` int DEFAULT NULL, `CampaignId` int DEFAULT NULL, `ListId` int DEFAULT NULL, `countnosuchnumberretry` int DEFAULT '0', `countdropretry` int DEFAULT '0', `countabandonedretry` int DEFAULT '0', `countmachineretry` int DEFAULT '0', `countagentrejectretry` int DEFAULT '0', PRIMARY KEY (`id`,`createdAt`), KEY `UserId` (`UserId`), KEY `QueueId` (`QueueId`), KEY `SourceId` (`SourceId`), KEY `CampaignId` (`CampaignId`), KEY `ListId` (`ListId`), KEY `calleridnum` (`calleridnum`), KEY `uniqueid` (`uniqueid`) ) ENGINE=InnoDB AUTO_INCREMENT=43886133 DEFAULT CHARSET=utf8mb3 /*!50100 PARTITION BY RANGE (to_days(`createdAt`)) (PARTITION px0Past VALUES LESS THAN (0) ENGINE = InnoDB, PARTITION px202207 VALUES LESS THAN (738733) ENGINE = InnoDB, PARTITION px202309 VALUES LESS THAN (739159) ENGINE = InnoDB, PARTITION px202310 VALUES LESS THAN (739190) ENGINE = InnoDB, PARTITION px202311 VALUES LESS THAN (739220) ENGINE = InnoDB, PARTITION px202312 VALUES LESS THAN (739251) ENGINE = InnoDB, PARTITION px202401 VALUES LESS THAN (739282) ENGINE = InnoDB, PARTITION px202402 VALUES LESS THAN (739311) ENGINE = InnoDB, PARTITION px9Future VALUES LESS THAN MAXVALUE ENGINE = InnoDB) */
需排查异常原因:是否存在隐藏约束导致此问题?
排除隐藏外键约束:可执行以下SQL再次确认是否存在未显式定义的外键:
SELECT * FROM information_schema.KEY_COLUMN_USAGE WHERE REFERENCED_TABLE_NAME = 'sourceTable' AND TABLE_NAME = 'logTable';从提供的
logTableDDL来看,确实没有定义外键关联,因此隐藏约束导致该问题的可能性极低。优先排查触发器与业务逻辑:
存储过程仅对sourceTable执行删除操作,需检查是否存在针对sourceTable的AFTER DELETE触发器,逻辑中错误将logTable的SourceId置空。执行以下SQL查看触发器:SHOW TRIGGERS LIKE 'sourceTable';同时需排查应用层代码,是否在删除
sourceTable数据时,额外执行了更新logTable的操作,导致SourceId被误置空。分区表与并发操作影响:
logTable是按createdAt分区的大表,若删除操作执行时存在其他批量更新logTable的并发事务,可能因锁冲突、事务隔离级别问题导致数据不一致。可查看数据库错误日志、慢查询日志,排查操作时段是否有异常SQL执行记录。数据类型与索引异常:
SourceId为允许NULL的int类型,需确认应用层在处理关联数据时,是否因查询不到sourceTable的对应ID,错误将logTable的SourceId更新为NULL;同时检查SourceId索引是否存在异常,避免批量操作时更新范围错误。InnoDB事务机制问题:
若删除操作在事务中执行,需检查是否因事务未正确提交、锁等待超时等问题,导致数据更新异常。可在测试环境单独执行存储过程,观察是否会触发logTable的SourceId置空,排除并发操作的干扰。
总结:该异常大概率由未被发现的触发器、应用层逻辑误操作,或并发事务冲突导致,而非隐藏约束。建议优先排查触发器与应用代码,再逐步验证分区表、事务等场景。
内容的提问来源于stack exchange,提问作者chrisiank

