MySQL——关于删除/purge/删除加锁/删除大量数据/truncatedelete

2023-10-14 15:30

本文主要是介绍MySQL——关于删除/purge/删除加锁/删除大量数据/truncatedelete,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

本篇介绍MySQL执行删除命令时的一些机制,包括:

  • MySQL如何删除一行数据?

  • 解释为什么删除数据后表文件大小不变?

  • purge线程

  • 为什么建议逻辑删除数据而非物理删除?为什么建议自增主键?

  • 为什么建议删除数据的语句条件上加索引?

  • 如何删除大量数据?truncate与delete/drop的区别?

MySQL如何删除一行数据?

InnoDB里的数据都是用B+树的结构组织的;

如图,要删掉R4这个记录,InnoDB引擎只会把R4这个记录标记为"删除";如果之后要再插入一个ID在300和600之间的记录时,可能会复用这个位置;但是,磁盘文件的大小并不会缩小

InnoDB的数据是按页存储的,如果删掉了一个数据页上的所有记录,那么整个页都可以被复用;

数据"空洞"导致页合并

  • 如果相邻的两个数据页利用率都很小,系统就会把这两个页上的数据合到其中一个页上,另外一个数据页就被标记为可复用;

  • 或者一次删除了整个页上的所有数据,这一整个数据页也会被标记为可复用;

为什么删除数据后表文件大小不变?

无论是删除记录还是整个页数据删除,磁盘上的文件不会变小,因为delete只是标记为已删除,而不是真正的物理删除,即表空间数据回收;

delete命令其实只是把记录的位置或者数据页标记为了mark del,在后台purge执行回收后,被删除数据部分对应的磁盘空间标记为"可用",可以被后续写入操作使用,但磁盘文件的大小是不会变的;也就是说,通过delete命令是不能回收表物理空间的;

purge线程

本节介绍跟删除相关的purge线程相关的知识;

为什么MySQL InnoDB需要Purge操作?

明确这个问题的答案,首先还得从InnoDB的多版本并发控制(MVCC)开始;

"快照"是InnoDB在实现MVCC时用到的一致性读视图,用于支持RC(Read Committed,读提交)和RR(Repeatable Read,可重复读)隔离级别的实现;它没有物理结构,作用是事务执行期间用来定义“我能看到什么数据”;

InnoDB 利用了“所有数据都有多个版本”的这个特性,实现了“秒级创建快照”的能力;

但是这里"快照"并不是说把此刻的整库数据拷贝一份,而是类似git,对于每一行数据,仅记录其在某一次事务中的增量更新,因此大部分的数据都是没有更新的,从而不会像"全量物理拷贝"那样占用巨大的内存;

也就是说,对于数据表中的一行记录,在数据库的不断更新下,可能存在多个数据版本 (row),而每个数据版本有自己的 row trx_id;如下图所示,就是一条记录被多个事务连续更新后的过程;

由图可知:语句所在的事务ID与语句更新结果的数据版本的row trx_id一一对应;

实际上,历史版本数据的值并不会物理存在,而是在每次需要的时候根据当前版本和 undo log 计算出来的;

在事务不断的执行过程中,undo log占用的空间会不断地扩张;

另外,对于更新和删除操作,InnoDB并不是真正的删除原来的记录,而是设置原记录的delete mark标志位为1,也就是说原数据依然存于数据页上;那么可以标记为mark delete的数据都有哪些类型呢?包括主键记录、二级索引记录

因此为了清除数据Page和Undo Log膨胀的问题,需要引入purge机制进行回收

purge流程简介

purge的主要任务是将数据库中已经mark del的数据删除,另外也会批量回收undo pages;而数据库的数据页很多,要清除被删除的数据,不可能遍历所有的数据页

由于所有的变更都有undo log, 因此,执行purge时,MySQL从undo作为切入点,在清理过期的undo的同时,也将数据页中的被删除的记录一并清除

一个关于删除数据后磁盘空间再次利用的实验

1.初始化表t1; session1插入一定数量的数据;在 session2中观察到表的t1.ibd大小在增加;

2. session1删除t1所有记录;执行后稍等等片刻(等purge线程自动清理数据、master线程将数据从缓存落盘),这时候在 session2中观察到t1.ibd文件体积一点也没有减少;

3. session1重新执行少量的插入操作;在 session2中观察到t1.ibd文件体积并没有再次增长;

原因:purge线程将上述实验中被删除数据部分对应的磁盘空间标记为可用,可以被后续写入操作使用,这样就不用再次分配磁盘空间了;

本章参考:

阿里云-MySQL Innodb Purge简介

MySQL purge线程 相关参数

为什么建议逻辑删除数据而非物理删除?为什么建议自增主键?

逻辑删除的一个天然的好处是方便数据的恢复和归档查询;此外,另一个是避免删完数据导致的页利用率降低,低于阈值时会产生的相邻叶子节点的页合并;

使用自增主键的好处就是避免频繁的"页分裂";结合B+数的结构,叶子节点是有序的,如果数据是按照索引递增顺序插入的,页写满申请新页时,是不移动原有页面的任何记录的,因此索引是紧凑的;注意,这里的"按照索引递增顺序插入"并不是说一定要自然数值连续,而是大小关系连续即可,如 1 2 4 7 9;这也是为什么推荐使用自增主键的原因;

如图,传统B+树页面分裂是按照原页面中50%的数据量进行分裂,随机插入就可能导致原先的数据页放不下了,造成索引的数据页分裂,从而导致分裂后的数据页产生了"空洞";

出现数据空洞时,一般使用重建表DDL语句来优化表存储空间;

为什么建议删除数据的语句条件上加索引?

从性能和加锁来看delete操作;

一般来说,DELETE的加锁和SELECT FOR UPDATE 或 UPDATE 并没有太大的差异;

因为,在MySQL数据库中,执行DELETE语句其实并没有直接删除记录,而是在记录上打上一个删除标记,然后通过后台的一个叫做purge的线程来清理;从这一点来看,DELETE和UPDATE确实是非常相像;事实上,DELETE和UPDATE的加锁也几乎是一样的;

前面有文章分析过全表扫描的加锁方式和更新记录时加锁的规则,可知:如果更新条件可以走索引,则间隙锁会加在条件所在的索引位置的前后间隙;如果查询条件没走索引走全表扫描,则对全表所有行之间加间隙锁

MySQL delete语句的加锁分析总结可参考:

  • 《MySQL DELETE 删除语句加锁分析》

  • 《mysql delete语句 MySQL死锁系列-常见加锁场景分析》

因此建议删除语句的条件尽量走索引查询,或者先查出这条记录的主键ID,再根据主键ID(唯一索引)条件删除;

如何删除大量数据?

根据where条件删除数据前,会先扫描数据检查是否符合where条件,该阶段会对扫描中所有数据行及行的间隙加锁;若表的数据量大且delete操作无法有效利用索引减少扫描数据量,该步骤对于数据库带来的锁争用、CPU/IO资源的消耗都是巨大的

单次删除大量数据属于大事务,由于一次修改的表记录太多,无论是产生的binlog日志的数量,还是加锁的范围,都会比较大;较大的加锁范围很容易出现事务执行超时的情况,或阻塞其他更新操作

此外,大事务的执行时间长,会导致主从延迟不断增加;在一些读写分离(写主库读从库)的场景下,导致读库读不到最新的数据引发业务问题;

下面介绍删除大量数据时,常用的方案及建议:

方案:分批次删除

减小单次删除的数据的数量,拆成分批次执行,减小批次执行间隔,控制执行的速度;此方案一定程度缓解主从延迟不断增加的问题,减小事务涉及的记录行数,减小事务的执行时间,避免一次锁住太多数据阻塞后序SQL命令;

方案:新建表迁移数据

批量删除大量数据的场景一般是做历史数据归档,释放表空间;考虑到删除数据的成本和数据迁移的成本,例如1000W数据表中清理时间小于某个时刻的100W条数据,则可以通过以下步骤操作来减少锁表的时间:

  1. 选择不需要删除的数据,并把它们迁移插入到一张相同结构的空表里;

  1. 重命名原始表,并给新表命名为原始表的原始表名;

  1. 删掉原始表;

此外,建议

  • 建议在delete的SQL语句中使用limit,防止一次删除全部满足条件的大量的记录;且limit后面的数量也不能太大,要选择一个合适的量;

  • 如果delete的查询条件不走索引,建议先查出满足条件的记录的主键,然后根据主键删除记录

  • 为了临时提高删除速度,在不影响业务的情况下,如凌晨业务低谷期,可以临时删除部分索引;因为MySQL官方手册提到:删除数据的速度和索引数量是成正比的;因此可以先删除这个表中的其他索引,即除了delete的where条件命中的索引,等删除完数据后再重新加上原来的索引;

truncate与delete的区别?

truncate都做了哪些操作?

truncate操作实际上分为drop、re-create两步:

  • drop操作的第一个阶段,是对buffer pool页面进行清除的过程,将表相关的数据页从flush链中删除;该操作会导致其他事务在获取buffer pool instance的锁时被阻塞,从而影响数据库性能;

  • drop操作的第二个阶段,是删除ibd磁盘文件的过程;删除数据库物理文件越大,则占用I/O资源消耗越大,删除操作耗时越久;

  • re-create操作阶段,只要被删除的表的.frm文件完好无损,在drop table之后就可以按照原表结构信息进行重建,重建后表的auto_increment值会被重置;

truncate与delete的区别?

  • 关于删除的数据范围:

truncate不带任何条件,属于表级别删除;

而delete可以根据where条件删除多条满足where条件的数据;

  • 关于回滚:

truncate操作是一个不可回滚的ddl操作;MySQL为了提高删除整张表数据的性能,truncate操作其本质上其实是先drop table一次性删除所有数据,然后再re-create table,因此不可回滚;

delete语句是一个可回滚的dml语句;delete语句可以通过where条件对要删除的记录进行选择,数据一行一行的删除,binlog日志会记录每行数据的删除记录,事务提交前可通过undolog回滚,通过undolog恢复数据;

  • 对自增主键的影响:

如果一个表中有自增字段,使用TRUNCATE TABLE和没有WHERE子句的DELETE删除所有记录后,这个自增字段将起始值恢复成1;

否则,delete语句不会重置自增主键的起点;

  • 对表空间的影响:

truncate对表数据直接进行一次性的物理删除;当表被truncate后,这个表和索引所占用的空间会恢复到初始大小;

delete操作不会减少表和索引所占用的空间,会产生数据空洞导致空间利用率低,可能引发页合并;

  • 执行效率:

truncate执行效率高;无需逐行扫描表数据删除,而是直接一次性进行物理删除,快速释放空间占用;

delete效率依赖where条件的编写;大表删除会产品大量的binlog且删除效率低,删除操作可能出现较多的碎片空间而不是直接释放空间占用;

其他:对于由FOREIGN KEY约束引用的表,不能使用TRUNCATE TABLE,而应使用不带WHERE子句的DELETE语句;由于TRUNCATE TABLE不记录在日志中,所以它不能激活触发器;

这篇关于MySQL——关于删除/purge/删除加锁/删除大量数据/truncatedelete的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/211392

相关文章

大模型研发全揭秘:客服工单数据标注的完整攻略

在人工智能(AI)领域,数据标注是模型训练过程中至关重要的一步。无论你是新手还是有经验的从业者,掌握数据标注的技术细节和常见问题的解决方案都能为你的AI项目增添不少价值。在电信运营商的客服系统中,工单数据是客户问题和解决方案的重要记录。通过对这些工单数据进行有效标注,不仅能够帮助提升客服自动化系统的智能化水平,还能优化客户服务流程,提高客户满意度。本文将详细介绍如何在电信运营商客服工单的背景下进行

SQL中的外键约束

外键约束用于表示两张表中的指标连接关系。外键约束的作用主要有以下三点: 1.确保子表中的某个字段(外键)只能引用父表中的有效记录2.主表中的列被删除时,子表中的关联列也会被删除3.主表中的列更新时,子表中的关联元素也会被更新 子表中的元素指向主表 以下是一个外键约束的实例展示

基于MySQL Binlog的Elasticsearch数据同步实践

一、为什么要做 随着马蜂窝的逐渐发展,我们的业务数据越来越多,单纯使用 MySQL 已经不能满足我们的数据查询需求,例如对于商品、订单等数据的多维度检索。 使用 Elasticsearch 存储业务数据可以很好的解决我们业务中的搜索需求。而数据进行异构存储后,随之而来的就是数据同步的问题。 二、现有方法及问题 对于数据同步,我们目前的解决方案是建立数据中间表。把需要检索的业务数据,统一放到一张M

关于数据埋点,你需要了解这些基本知识

产品汪每天都在和数据打交道,你知道数据来自哪里吗? 移动app端内的用户行为数据大多来自埋点,了解一些埋点知识,能和数据分析师、技术侃大山,参与到前期的数据采集,更重要是让最终的埋点数据能为我所用,否则可怜巴巴等上几个月是常有的事。   埋点类型 根据埋点方式,可以区分为: 手动埋点半自动埋点全自动埋点 秉承“任何事物都有两面性”的道理:自动程度高的,能解决通用统计,便于统一化管理,但个性化定

如何去写一手好SQL

MySQL性能 最大数据量 抛开数据量和并发数,谈性能都是耍流氓。MySQL没有限制单表最大记录数,它取决于操作系统对文件大小的限制。 《阿里巴巴Java开发手册》提出单表行数超过500万行或者单表容量超过2GB,才推荐分库分表。性能由综合因素决定,抛开业务复杂度,影响程度依次是硬件配置、MySQL配置、数据表设计、索引优化。500万这个值仅供参考,并非铁律。 博主曾经操作过超过4亿行数据

使用SecondaryNameNode恢复NameNode的数据

1)需求: NameNode进程挂了并且存储的数据也丢失了,如何恢复NameNode 此种方式恢复的数据可能存在小部分数据的丢失。 2)故障模拟 (1)kill -9 NameNode进程 [lytfly@hadoop102 current]$ kill -9 19886 (2)删除NameNode存储的数据(/opt/module/hadoop-3.1.4/data/tmp/dfs/na

异构存储(冷热数据分离)

异构存储主要解决不同的数据,存储在不同类型的硬盘中,达到最佳性能的问题。 异构存储Shell操作 (1)查看当前有哪些存储策略可以用 [lytfly@hadoop102 hadoop-3.1.4]$ hdfs storagepolicies -listPolicies (2)为指定路径(数据存储目录)设置指定的存储策略 hdfs storagepolicies -setStoragePo

Hadoop集群数据均衡之磁盘间数据均衡

生产环境,由于硬盘空间不足,往往需要增加一块硬盘。刚加载的硬盘没有数据时,可以执行磁盘数据均衡命令。(Hadoop3.x新特性) plan后面带的节点的名字必须是已经存在的,并且是需要均衡的节点。 如果节点不存在,会报如下错误: 如果节点只有一个硬盘的话,不会创建均衡计划: (1)生成均衡计划 hdfs diskbalancer -plan hadoop102 (2)执行均衡计划 hd

性能分析之MySQL索引实战案例

文章目录 一、前言二、准备三、MySQL索引优化四、MySQL 索引知识回顾五、总结 一、前言 在上一讲性能工具之 JProfiler 简单登录案例分析实战中已经发现SQL没有建立索引问题,本文将一起从代码层去分析为什么没有建立索引? 开源ERP项目地址:https://gitee.com/jishenghua/JSH_ERP 二、准备 打开IDEA找到登录请求资源路径位置

MySQL数据库宕机,启动不起来,教你一招搞定!

作者介绍:老苏,10余年DBA工作运维经验,擅长Oracle、MySQL、PG、Mongodb数据库运维(如安装迁移,性能优化、故障应急处理等)公众号:老苏畅谈运维欢迎关注本人公众号,更多精彩与您分享。 MySQL数据库宕机,数据页损坏问题,启动不起来,该如何排查和解决,本文将为你说明具体的排查过程。 查看MySQL error日志 查看 MySQL error日志,排查哪个表(表空间