服了!DELETE 同一行记录也会造成死锁!!

哎,跟大家说一下我踩过的坑,真是令人不安。

我记得去年,当我们的系统搞活动、搞订单量的时候,发生了一些事情。
一次删除多行,并使用单个索引。
结果,数据库大面积关闭,新订单无法录入,客户投诉电话几乎铺天盖地。

为什么会致命?事实上,当头发重叠时,头发的类型就会变得混乱。
想一想,事务A删除了该记录,但在删除之前进行了签名(即逻辑上删除,但物理上没有删除)。
这时,另一个事务B也想删除同一个项目。
当它看到事​​务A有锁时,它只能等待。
当任务A提交后,要“删除”记录时,需要暂时获取另一个锁定的key。
但这一次,事务A因为别的原因想要重新操作这个证书,然后去锁定时间密钥,却又被事务B锁定了,一来二去就卡住了。

这种情况更有可能发生在单个索引中,因为这种情况下锁类型会从记录锁变为临时键锁。
本来想用java记录来模拟删除多线程,但实际却是死锁了。
它清楚地用六位元写成,有一个记录锁和一个临时密钥粘在一起。

您认为可以避免使用 SELECT UPDATE 吗?我尝试过,但没有成功。
它的加锁逻辑与DELETE类似,但它仍然选择没有解决基本问题的锁类型。

我认真查了一下,发现更好的MySQL版本(比如8 .x)已经得到了改进。
在“待删除”状态下,如果事务已经持有锁定记录,它将采取正确的间隙锁,而不是将其升级为临时键锁。
通过这一更改,等待循环消失了,死锁也大大减少了。
但升级时,应仔细考虑兼容性和迁移的成本。

另一种方法是将舒适域调整为RC(已提交读)。
这样,delete不会加临时键锁,死锁少了,但脏读和鬼读又会来,数据一致性几乎不可能。
就看他能不能把生意拿回来了。
还有一种方式就是使用分布式发,比如Redis。
开发中引入它是为了控制并发删除。
我尝试了这个方案,性能确实不错,控制稳定,而且成本也不高。

最后,计划是优化交易。
不要跑太久,也不要留锁太久。
如果发生死锁,捕获异常并重试往往可以解决问题。

总的来说,这个的核心就是单索引和删除加并发。
一旦串行类型转换完毕,就很容易形成await循环。
只有了解了InnoDB的锁定机制,以及索引特性和隔离级别的影响,才能对症下药。
实际使用要看业务情况。
升级版本、调整隔离级别、使用分布式毛发都是需要考虑的事情。
技术是必须观察并始终优化的东西。

如何解决mysql delete表数据后,表空间大小不变的问题?

什么?你提到的数据库碎片问题,我在2 02 2 年之前一直在处理电商系统中的MySQL,确实很烦人。

你看,MySQL的DELETE操作其实是相当“懒”的。
它只会将您要删除的记录标记为“已删除”,并且不会立即释放其占用的空间。
这就像家里有一堆垃圾,只是在上面贴上“要清理”的标签,以为有时间就可以把它处理掉。
于是,新的快递包裹只能在垃圾桶旁边填满,空间变得越来越拥挤。

你提到的两种情况特别有趣。
1 . 带条件删除(例如 DELETE FROM order WHERE status='Cancel')是最常见的。
去年,我在帮助某旅游平台优化数据库时,发现使用三年后,未清理的表中全是“半删除”数据,占用了4 0%的存储空间。
这是因为预订系统每次插入新订单时只能找到标记的废弃车位。
结果,插入速度慢了 5 0%! 2 . 直接删除(例如DELETE FROM user)。
我掉进了这个陷阱。
2 02 3 年处理论坛数据库时,管理员手动删除了 1 0 万个不活动帐户。
结果,表空间并没有减少。
相反,由于交叉引用指向已删除记录的指针,OPTIMIZE 花了整整三个小时才能完成。

说起OPTIMIZE TABLE,这绝对是MySQL的“一键橡皮擦”。
有一个小技巧。
要首先计算有效数据量,请使用 SELECT COUNT() FROM table_name WHERE delete_at IS NULL。
如果它只占总记录的 1 0% 以下,那么自己 TRUNCATE 会更容易。
但对于渐进式删除,比如你提到的网站后台定期清理过期数据,OPTIMIZE是最好的选择。

执行时机尤其重要!去年检查直播平台时,凌晨3 点运行OPTIMIZE,系统监测结果显示主从同步延迟5 分钟左右。
最后补充了数据,结果运营经理骂了我。
现在使用 mysqlcheck -o -A --databases yourdb 命令批量处理表并同时优化最多 3 个表。

另一个琐事:InnoDB 表碎片问题可以使用 ALTER TABLE your_table ENGINE=InnoDB 自动修复。
然而,这比 OPTIMIZE 花费更多的时间,而且我大多只在每年的清洁期间使用它。

去年,在处理高并发新闻网站时,OPTIMIZE LOCAL 参数非常有用。
当时我们使用INSERT INTO messages SELECT FROM old_logs的方式批量迁移数据。
添加LOCAL优化后,日志表写入不受影响。
不过,这仅对MyISAM和Aria引擎有效,不能用于InnoDB。

综上所述,条件DELETE是日常运维的“定时炸弹”,而定期OPTIMIZE则是解药。
但是,特别是在处理大型表时,最好在每次运行之前进行备份。
我的一个朋友在2 02 1 年中遭遇了OPTIMIZE掉电的悲剧,所有数据都乱了……