MySQL 中删除的数据都去哪儿了?
不知道大家有没有想过下面这件事?
我们平时调用
DELETE
在 MySQL 中删除的数据都去哪儿了?
这还用问吗?当然是被删除了啊
![](https://filescdn.proginn.com/0a8a401b39caffa598906ead0c8a7008/00803dc36b77984c32f3899dbce32b4d.webp)
那么这里又有个新的问题了,如果在 InnoDB 下,多事务并发的情况下,如果事务A删除了 id=1
的数据,同时事务B又去读取 id=1
的数据,如果这条数据真的被删除了,那 MVCC 拿啥数据返回给用户呢?
没错,这就需要了解一下 MySQL 的多版本并发的原理相关的东西,感兴趣的可以去看我之前写的这篇文章。
所以,实际情况中,调用了 DELETE
语句删除的数据并不会真正的被物理删除,这条数据其实还在那,只不过被打上了一个标记,标记已删除。
这其实跟我们日常的操作——软删除,差不多是一个意思
![](https://filescdn.proginn.com/dc6843e5d437199888499f596e27195a/94148c05f43cd73fe225ce53a2fcf938.webp)
在 MySQL 中, UPDATE
和 DELETE
操作本质上是一样的, 都属于更新操作,删除操作只不过是把某行数据中的一个特定的比特位标记为已删除,仅此而已。
那么问题又来了,那这些删除的数据如果一直这么堆下去,那不早晚把硬盘撑爆?
![](https://filescdn.proginn.com/b9656619b8153071cd5a77ea635d6bde/246ace7a2e0e21c6934d95531984e4ed.webp)
如果都玩儿成这样了,那 MySQL 还能像现在这样被大规模的用于生产环境中吗?那 MySQL 到底是怎么玩的?
这就需要提到 Purge 操作了。
Purge操作是啥?
Purge 操作才是真正将数据(已被标记为已删除)物理删除的操作。
![](https://filescdn.proginn.com/e284bfcedf46fd2531fd9f49049c3121/dbb4319a38ff81f31df55269f873bd02.webp)
Purge 操作针对的数据对象,不仅仅是某一行,还有其对应的索引数据和 Undo Log。
好的那么问题又来了。
![](https://filescdn.proginn.com/ca9ec6a40c5849c9c64b6b9fc67e0a5f/b28768205e91b6d5395a65b357d80697.webp)
问题是,Purge 操作什么时候会执行呢?实际上,你可以将执行 Purge 操作的线程(简称 Purge 线程)理解成一个后台周期性执行的线程。
Purge 线程可以有一个,也可以有多个,具体的线程数量可以由 MySQL 的配置项 innodb_purge_threads
来进行配置。当然,我相信你肯定不记得在使用 MySQL 的时候配置过这个,因为 innodb_purge_threads
有个默认值,值为 4
。
![](https://filescdn.proginn.com/fdc931d97c6f1d11b5a8b4c8a9e537e9/82fa96a4d17250a9e06e87d4f36162dd.webp)
InnoDB 会根据 MySQL 中表的数量和 Purge 线程的数量进行分配。
![](https://filescdn.proginn.com/d4eb7180ae68985061ce1b56319f9129/8605adda34e5d17a7c5897fd6fcc5142.webp)
但正是因为有这种特性,Purge 线程的数量才需要根据业务的实际情况来做调整。举个例子,假设 DML 操作都集中在某张表,比如表1上...
你先等等,我打断一下......
![](https://filescdn.proginn.com/15490a13226c2486edf43901b570b687/e1f41670a460d2e253c0d3b6170ab03a.webp)
什么叫 DML 操作?总喜欢搞些复杂的名词...DML(Data Manipulation Language)数据操作语句,实际上就是CRUD增删改查...
与之类似的概念还有DDL(Data Definition Language)数据定义语句,也就是CREATE
、DROP
和ALTER
等等.
以及DCL(Data Control Language)数据控制语句,也就是GRANT
、REVOKE
等等...
继续说回来,虽然 Purge 线程的数量是可配置的,但是也不是你想配多少就配多少的。不然你给它干个 10000
个线程,那不就直接原地 OOM 了吗?
innodb_purge_threads
的最大值为 32,而且并不是我们配了 32 InnoDB 就真的会启动 32 个 Purge 线程,为啥呢?举个很简单的例子,假设此时只有一张表,然后我们配置了 32 个 Purge 线程。
![](https://filescdn.proginn.com/002b8c27e4fbf44a7bc1d25dd0f312d6/b430f882963a216a6b74bcf215a5e80d.webp)
你看着上面这个图问问自己,这「河里」吗?这样不仅浪费了系统的资源,同时还使得不同的 Purge 线程之间发生了数据竞争。不仅如此,Purge 线程还可能跟用户线程产生竞争。
但是当系统中真的有 32 张表的时候,情况又不一样了,一个 Purge 线程对应一张表,线程与线程之间就不会存在数据竞争,并且没有浪费系统资源,还能够提升执行 Purge 操作的性能。
这就是为啥 InnoDB 会根据实际情况来调整 MySQL 中 Purge 线程的数量,所以我们在配置的时候也要按照实际情况来设置。
举个例子,如果你的数据库中,增删改 的操作只集中在某几张表上,则可以考虑将 innodb_purge_threads
设置的稍微低一点。相反,如果 增删改 的操作几乎每张表都有,那么 innodb_purge_threads
就可以设置的大一些。
了解完 Purge 线程本身之后,我们就可以来了解 Purge 线程所针对的对象了。Purge 线程主要清理的对象是 Undo Logs,其次是行记录。
![](https://filescdn.proginn.com/3b0155a6ecca5c2df8af63d4276d8602/07abbf2e2300826c75f814488b149298.webp)
因为 Undo Log 可以分为:
Insert Undo Log Update Undo Log
所以更准确的说法是,Purge 线程清理的对象是 Update Undo Log 和 行记录,因为 Insert Undo Log 会在事务提交之后就会被删除。
我们都知道 InnoDB 的 MVCC 的数据来源是一个一个 Undo Log 形成的单链表,而 Purge 线程就是用于定期清理 Undo Log 的,并且在清理完 删除数据所生成的 Undo Log 的时候,就会把对应的行记录给移除了。
那么问题又来了,Purge 线程每次会读取多少条件 Undo Log 记录呢?
很明显,它不是看当时的心情来决定取多少条的。它是通过配置项 innodb_purge_batch_size
来控制的,默认是 300。然后InnoDB会将这300条 Undo Log 分给innodb_purge_threads
个 Purge 线程。在清理的过程中,Purge 线程还会释放 Undo Log 表空间内的文件。
有道无术,术可成;有术无道,止于术
欢迎大家关注Java之道公众号
好文章,我在看❤️