当前位置:首页 > 问答 > 正文

明明在InnoDB里执行了删除操作,结果数据却一点没动,这到底是咋回事啊?

(引用来源:根据MySQL官方文档、常见数据库运维经验以及开发者社区讨论综合而来)

明明在InnoDB里点了执行,提示也说是成功了,回头一查数据,发现要删的那几条还好好地躺在表里,纹丝不动,这种感觉就像你费劲扔出去一个球,结果它绕了一圈又飞回你手里,让人既困惑又上火,这事儿听起来邪门,但其实背后通常就那么几个“坑”,跟InnoDB的设计特性息息相关,根本不是什么灵异事件。

最最常见的一个原因,就是你很可能身处一个数据库事务(Transaction) 当中,而且这个事务还没最终提交(Commit)。(引用来源:数据库事务的ACID特性是InnoDB等关系型数据库的核心基础)你可以把事务想象成一个“打包操作”的过程,在你显式地输入COMMIT;命令之前,你在事务里做的所有修改,比如DELETE(删除)、UPDATE(更新)、INSERT(新增),都只是在一个临时的、属于你当前数据库连接的空间里生效,这个临时空间只有你自己能看到,其他任何连接到这个数据库的用户是看不见的,你查询数据,发现它没变,是因为你查询的也是这个临时视图,这是一种保护机制,确保一系列操作要么全部成功,要么全部失败,不会出现只做了一半的中间状态,解决办法很简单,就是去确认一下你有没有开启事务,如果你用了BEGIN;或者START TRANSACTION;开了头,却忘了用COMMIT;来最终确认,那么你之前所有的删除操作都只是“模拟演练”,同样,如果程序代码里开启了事务但忘了提交,也会出现一模一样的情况,这时候,你要么补上一个COMMIT;让删除生效,要么用一个ROLLBACK;回滚事务,让所有操作当作没发生过。

另一个高频踩坑点在于你的WHERE条件没写对。(引用来源:SQL查询的准确性是数据操作的前提)你可能觉得你的DELETE语句条件写得明明白白,但数据库执行起来却可能跟你预想的不一样,你以为某个字段的值是数字,但它实际存储的是字符串类型,你写了DELETE FROM table WHERE id = 100,但表结构里id是VARCHAR类型,里面存的是"100",在某些比较宽松的SQL模式下,MySQL可能会帮你做隐式类型转换,删掉一条;但在某些严格模式下,它可能一条都匹配不上,结果就是零条记录被删除,你还浑然不觉,更常见的可能是条件逻辑写错了,用了AND when you meant OR,或者不小心多写了个等号,导致条件变得极为苛刻,最终没有记录满足删除要求,数据库会很“听话”地执行你的命令,如果条件不匹配,它就不会删任何东西,然后返回一个“Query OK, 0 rows affected”的消息,如果你没仔细看这个执行结果,光看到“Query OK”就以为成功了,那就被误导了。

第三,有一种情况比较隐蔽,就是可能触发了外键约束(Foreign Key Constraints)。(引用来源:InnoDB支持外键约束以维护数据完整性)假如你要删除的表A的某条记录,它的主键正被另一张表B的外键字段引用着,而你在创建这个外键约束时,又设置了ON DELETE RESTRICT(拒绝删除)或者ON DELETE NO ACTION(无动作,效果类似拒绝)的规则,当你试图删除这条“父记录”时,InnoDB会为了维护数据的完整性和一致性,果断阻止你的删除操作,它不会硬来,而是会报一个错误,明确告诉你违反了外键约束,如果你使用的数据库客户端工具或者程序代码配置为“静默”模式,或者错误被捕获后没有清晰地展示给你,你可能只得到一个操作失败的模糊提示,甚至被忽略,让你误以为命令执行了但没效果,是数据库这个尽职的管家为了保护数据关系,把你的命令给拦下了。

第四,别忘了还有SQL_MODE在作怪。(引用来源:MySQL的sql_mode设置决定了SQL语法和校验的严格程度)MySQL有一个叫做sql_mode的系统变量,它就像一套语法和语义的校验规则,有些比较“宽松”的旧模式,比如不包括STRICT_TRANS_TABLES的模式,可能会对一些模棱两可的操作“睁一只眼闭一只眼”,但在更“严格”的模式下,一些稍微不精确的操作都可能直接报错失败,虽然这通常更直接地表现为报错而非静默失败,但在某些复杂的交互中,模式的差异可能间接导致你认为执行了的操作实际上被某种规则抑制了,检查一下当前的sql_mode设置,确保它符合你的预期,有时也能排除一些奇怪的问题。

虽然概率极低,但理论上也存在极特殊的可能性,比如遇到了MySQL服务器的bug,(引用来源:任何软件都存在出现未知缺陷的可能)或者你连接的数据库根本就不是你以为的那个,或者你的用户权限虽然能执行DELETE命令,但实际上因为某种复杂的权限继承规则,操作被禁止了。

下次再遇到这种“删了个寂寞”的情况,别急着怀疑人生,不妨按照这个顺序排查一下:第一,大声问自己“我提交事务了吗?”;第二,仔仔细细、逐字逐句地检查你的WHERE条件,最好先用SELECT语句验证一下能查出几条记录;第三,看看表之间有没有外键约束在捣乱;第四,确认数据库连接和操作环境没问题,这么一套组合拳下来,十有八九就能找到那个让数据“金身不坏”的真正原因了。

明明在InnoDB里执行了删除操作,结果数据却一点没动,这到底是咋回事啊?