浅谈MySQL如何优雅的做大表删除

随着时间的推移或者业务量的增长,数据库空间使用率也不断的呈稳定上升状态,当数据库空间将要达到瓶颈的时候,可能我们才会发现数据库有那么一两张的超级大表!他们堆积了从业务开始到现在的全部数据,但是90%的数据都是没有业务价值的,这时候该如何处理这些大表?

既然是没有价值的数据,我们通常一般会选择直接删除或者归档后删除两种,对于数据删除的操作方式来说又可分为两大类:

  • 通过truncate直接删除表中全部数据
  • 通过delete删除表中满足条件记录

一、Truncate操作

从逻辑意义上来讲,truncate操作就是删除表中所有记录行,但是又与delete from table_name wehre 1=1这种操作不一样。MySQL为了提高删除整张表数据的性能,truncate操作其本质上其实是先drop table然后在re-create table。也真因如此,truncate操作是一个不可回滚的DDL操作。

1.1 MySQL truncate 都做了哪些操作?

  • truncate操作实际上分为drop、re-create两步
  • drop操作的第一个阶段,是对Buffer pool页面进行清除的过程,将表相关的数据页从flush链中删除,而不需要做flush操作。该步骤的瓶颈点主要在于flush队列的删除操作必须持有对应buffer pool instance的锁并进行遍历搜索,如果buffer pool instance比较大且flush链中需要删除的数据页很多,该操作会导致其他事务在获取buffer pool instance的锁时被阻塞,从而影响数据库的性能
  • drop操作的第二个阶段,是删除ibd磁盘文件的过程。删除数据库物理文件越大I/O资源消耗越大,删除操作耗时越久
  • re-create操作阶段,只要删除表的.frm文件完好无损,在drop table之后就可以按照原表结构信息进行重建,重建后表的auto_increment值会被重置

1.2 如何优化truncate操作带来的资源消耗?

  • 对于truncate操作中的drop表第一阶段,当分配给MySQL实例的innodb_buffer_pool_size超过1GB时,合理的设置innodb_buffer_pool_instances参数,提高并发的同时也变相的减少扫描buffer pool instance时锁资源占用耗时
  • 对于truncate操作中的drop表第二阶段,在删除对应表之前,先对改表的.ibd文件创建一个硬连接,加快MySQL层面的drop操作执行效率,减少对数据库层面的性能损耗。后续手动对操作系统层面我们做的硬连接进行清理

二、Delete操作

2.1 MySQL delete 都做了哪些操作?

  • 根据where条件对删除表进行索引/全表扫描,检查是否符合where条件,该阶段会对扫描中所有行进行加锁。该阶段是最大的资源消耗隐患,若表的数据量大且delete操作无法有效利用索引减少扫描数据量,该步骤对于数据库带来的锁争用、cpu/io资源的消耗都是巨大的
  • 对不能够被where条件匹配的行施加的锁会在条件检查后予以释放,InnoDB仅锁定需要删除的行。这可以有效地降低锁争用,但是我们仍需要关注的一点是,一次性删除大批量的数据,该操作将会产生巨大的binlog事务日志,这对于MySQL自身以及主从架构中的从库都是不友好的,可能带来叫的复制延迟。

2.2 如何优化delete操作?

  • delete全表删除操作需要谨慎,可考虑使用truncate操作
  • delete … where … 中,where过滤条件尽量保证可有效利用索引减少数据扫描量,避免全表扫描
  • 对于大批量数据删除且where条件无索引的情况,delete操作可额外增加自增长主键或者含索引的时间字段,进行分批删除操作,每次删除少量数据,分多批次执行。
  • 对于保留近期数据删除历史数据的经典场景,可创建同结构的xxx_tmp表并通过insert xxx_tmp select …操作将需要的数据保留至tmp表中、然后通过rename操作将当前业务表xxx替换为xxx_bak表,xxx_tmp表替换为当前业务表名xxx,后续手动删除无用的大表xxx_bak

2.3 delete常见的两个场景

2.3.1 delete where条件无有效索引过滤

比较常见的一个场景是,业务上需要删除t1 condition1=xxx的值,condition字段无法有效利用索引,这种情况下我们通常的做法是:

  • 查看当前表结构中可有效利用的索引,尽量是表的自增长主键或者时间索引字段
  • 有效利用自增长主键索引或者时间索引,将delete操作添加索引字段的范围过滤,每次删除少量数据,分多批次执行。具体分批需要根据业务实际进行评估,避免一次性删除大批量数据。
-- 利用自增长主键索引
delete from t1 where condition1=xxx and id >=1 and id < 50000;
delete from t1 where condition1=xxx and id >=50000 and id < 100000;

-- 利用时间索引
delete from t1 where condition1=xxx and create_time >= '2021-01-01 00:00:00' and create_time < '2021-02-01 00:00:00';
delete from t1 where condition1=xxx and create_time >= '2021-02-01 00:00:00' and create_time < '2021-03-01 00:00:00';

2.3.2 保留近期数据删除历史数据

比较常见的一个场景是,需要仅保留t1表近3个月数据,其余历史数据删除,我们通常的做法是:

创建一张t1_tmp表用来临时存储需要保留的数据

create table t1_tmp like t1;

根据有索引的时间字段,分批次的将需要保留的数据写入t1_tmp表中,该步骤需要注意的是,最后一批次时间的操作可暂时不处理

-- 根据实例业务数量进行分批,尽量每批次处理数据量不要太大
insert into t1_tmp select * from t1 where create_time >= '2021-01-01 00:00:00' and create_time < '2021-02-01 00:00:00';
insert into t1_tmp select * from t1 where create_time >= '2021-02-01 00:00:00' and create_time < '2021-03-01 00:00:00';

-- 当前最后一批次数据先不操作
-- insert into t1_tmp select * from t1 where create_time >= '2021-03-01 00:00:00' and create_time < '2021-04-01 00:00:00';

通过rename操作将当前业务表t1替换为t1_bak表,t1_tmp表替换为当前业务表名t1,被删除表若有频繁的DML操作,该步骤会造成短暂的业务访问失败

alter table t1 rename to t1_bak;
alter table t1_tmp rename to t1;

将最后一批次数据写入当前业务表,该步骤的目的是为了减少变更操作流程中的数据丢失

insert into t1 select * from t1_bak where create_time >= '2021-03-01 00:00:00' and create_time < '2021-04-01 00:00:00';

在rename操作步骤中,还有一点我们需要关注的是,变更表主键是自增长还是业务唯一的uuid,若为自增长主键,我们还需要注意修改t1_tmp表的自增长值,保证最终设置值包含变更期间数据写入

alter table t1_tmp auto_increment={t1表当前auto值}+{变更期间预估增长值}

三、Truncate/Delete优劣势对比

操作类型 描述 优势 劣势
Truncate 表的全量删除操作 无需扫描表数据,执行效率高,直接进行物理删除,快速释放空间占用 DDL操作无法进行回滚,无法按条件进行删除
Delete 根据指定条件进行过滤删除操作 可根据指定条件进行过滤删除 删除效率依赖where条件的编写,大表删除会产品大量的binlog且删除效率低,删除操作可能出现较多的碎片空间而不是直接释放空间占用

到此这篇关于浅谈MySQL如何优雅的做大表删除的文章就介绍到这了,更多相关MySQL 大表删除内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Innodb中mysql快速删除2T的大表方法示例

    前言 本文主要给大家介绍了关于Innodb中mysql快速删除2T的大表的相关内容,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍吧 来,先来看小漫画陶冶一下情操 OK,这里就说了.假设,你有一个表erp,如果你直接进行下面的命令 drop table erp 这个时候所有的mysql的相关进程都会停止,直到drop结束,mysql才会恢复执行.出现这个情况的原因就是因为,在drop table的时候,innodb维护了一个全局锁,drop完毕锁就释放了. 这意味着,如果在白天,访

  • MySQL 删除大表的性能问题解决方案

    微博上讨论MySQL在删除大表engine=innodb(30G+)时,如何减少MySQL hang的时间,现做一下简单总结: 当buffer_pool很大的时候(30G+),由于删除表时,会遍历整个buffer pool来清理数据,会导致MySQL hang住,解决的办法是: 1.当innodb_file_per_table=0的时候,以上不是问题,因为采用共享表空间的时候,该表所占用的空间不会被删除,buffer pool中的相关页不会 被discard. 2.当innodb_file_pe

  • MySQL如何优雅的删除大表实例详解

    前言 删除表,大家下意识想到的命令可能是直接使用DROP TABLE "表名",这是初生牛犊的做法,因为当要删除的表达空间到几十G,甚至是几百G的表时候.这样一条命令下去,MySQL可能就直接夯住了,外在表现就是QPS急速下降,客户请求变慢. 解决办法 1.业务低峰时间手动执行删除 这个可能就需要DBA不辞辛劳,大晚上爬起来删表了. 2.先清除数据,最后再删除的方式 譬如1000万条数据,写脚本每次删除20万,睡眠一段时间,继续执行.这样也能做到对用户无感知. 3.对表文件(idb文件

  • mysql 大表批量删除大量数据的实现方法

    问题参考自:https://www.zhihu.com/question/440066129/answer/1685329456 ,mysql中,一张表里有3亿数据,未分表,其中一个字段是企业类型,企业类型是一般企业和个体户,个体户的数据量差不多占50%,根据条件把个体户的行都删掉.请问如何操作?答案为个人原创 假设表的引擎是 Innodb, MySQL 5.7+ 删除一条记录,首先锁住这条记录,数据原有的被废弃,记录头发生变化,主要是打上了删除标记.也就是原有的数据 deleted_flag

  • 浅谈MySQL如何优雅的做大表删除

    随着时间的推移或者业务量的增长,数据库空间使用率也不断的呈稳定上升状态,当数据库空间将要达到瓶颈的时候,可能我们才会发现数据库有那么一两张的超级大表!他们堆积了从业务开始到现在的全部数据,但是90%的数据都是没有业务价值的,这时候该如何处理这些大表? 既然是没有价值的数据,我们通常一般会选择直接删除或者归档后删除两种,对于数据删除的操作方式来说又可分为两大类: 通过truncate直接删除表中全部数据 通过delete删除表中满足条件记录 一.Truncate操作 从逻辑意义上来讲,trunca

  • 浅谈MySQL使用笛卡尔积原理进行多表查询

    MySQL的多表查询(笛卡尔积原理) 先确定数据要用到哪些表. 将多个表先通过笛卡尔积变成一个表. 然后去除不符合逻辑的数据(根据两个表的关系去掉). 最后当做是一个虚拟表一样来加上条件即可. 注意:列名最好使用表别名来区别. 笛卡尔积 Demo: 左,右连接,内,外连接 l 内连接: 要点:返回的是所有匹配的记录. select * from a,b where a.x = b.x ////内连接 l 外连接有左连接和右连接两种. 要点:返回的是所有匹配的记录 外加 每行主表外键值为null的

  • 浅谈MySQL大表优化方案

    背景 阿里云RDS FOR MySQL(MySQL5.7版本)数据库业务表每月新增数据量超过千万,随着数据量持续增加,我们业务出现大表慢查询,在业务高峰期主业务表的慢查询需要几十秒严重影响业务 方案概述 一.数据库设计及索引优化 MySQL数据库本身高度灵活,造成性能不足,严重依赖开发人员的表设计能力以及索引优化能力,在这里给几点优化建议 时间类型转化为时间戳格式,用int类型储存,建索引增加查询效率 建议字段定义not null,null值很难查询优化且占用额外的索引空间 使用TINYINT类

  • 浅谈Mysql大数据分页查询解决方案

    目录 1.简介 2.分页插件使用 3.sql测试与分析 3.1 limit现象分析 3.2 解决之道 4 测试时走过的坑 4.1 百万数据内容都一样 4.2 写sql时,把"77"写成了77: 4.3 一个有趣的现象 总结 1.简介 之前,面阿里的时候,有个面试官问我有没有使用过分页查询,我说有,他说分页查询是有问题的,怎么解决:后来这个问题我没有回答出来:本着学习的态度,今天来解决一下这个问题: 2.分页插件使用 1.pom文件 <dependency> <grou

  • 浅谈mysql 针对单张表的备份与还原

    A.MySQL 备份工具xtrabackup 的安装 1. percona 官方xtrabackup 的二进制版本:二进制版本解压就能用了. 2. 解压xtrabackup & 创建连接 tar -xzvf percona-xtrabackup-2.3.4-Linux-x86_64.tar.gz -C /usr/local/ ln -s /usr/local/percona-xtrabackup-2.3.4 /usr/local/xtrabackup 3. 设置PATH环境变量 export P

  • 浅谈mysql的子查询联合与in的效率

    最近的产品测试发现一个问题,当并发数量小于10时,响应时间可以维持在100毫秒以内.但是当并发数到达30个时,响应时间就超过1秒.这太不能接受了,要求是通过1秒中并发100个. 经过检测发现,时间主要是耗在其中的一个存储过程中.把存储过程的语句一条一条的过一遍也没有发现明显的不合理.因为mysql本身不能提供毫秒级别的时间,google了一个mysql的能提供毫秒的时间函数,再做测试,做了一个定位.发现是其中一条语句,语句是这个样子: select .... from A, B where ..

  • 浅谈mysql的索引设计原则以及常见索引的区别

    索引定义:是一个单独的,存储在磁盘上的数据库结构,其包含着对数据表里所有记录的引用指针. 数据库索引的设计原则: 为了使索引的使用效率更高,在创建索引时,必须考虑在哪些字段上创建索引和创建什么类型的索引. 那么索引设计原则又是怎样的? 1.选择唯一性索引 唯一性索引的值是唯一的,可以更快速的通过该索引来确定某条记录. 例如,学生表中学号是具有唯一性的字段.为该字段建立唯一性索引可以很快的确定某个学生的信息. 如果使用姓名的话,可能存在同名现象,从而降低查询速度. 2.为经常需要排序.分组和联合操

  • 浅谈MySQL 统计行数的 count

    MySQL count() 函数我们并不陌生,用来统计每张表的行数.但如果你的表越来越大,且是 InnoDB 引擎的话,会发现计算的速度会越来越慢.在这篇文章里,会先介绍 count() 实现的原理及原因,然后是 count 不同用法的性能分析,最后给出需要频繁改变并需要统计表行数的解决方案. Count() 的实现 InnoDB 和 MyISAM 是 MySQL 常用的数据引擎,由于两者实现的不同,导致 count() 操作计算的效率也不同. 对于 MyISAM 来说,它把每个表的总行数都存在

  • 浅谈Mysql哪些字段适合建立索引

    1 数据库建立索引常用的规则如下: 1.表的主键.外键必须有索引: 2.数据量超过300的表应该有索引: 3.经常与其他表进行连接的表,在连接字段上应该建立索引: 4.经常出现在Where子句中的字段,特别是大表的字段,应该建立索引: 5.索引应该建在选择性高的字段上: 6.索引应该建在小字段上,对于大的文本字段甚至超长字段,不要建索引: 7.复合索引的建立需要进行仔细分析:尽量考虑用单字段索引代替: A.正确选择复合索引中的主列字段,一般是选择性较好的字段: B .复合索引的几个字段是否经常同

  • 浅谈我是如何用redis做实时订阅推送的

    前阵子开发了公司领劵中心的项目,这个项目是以redis作为关键技术落地的. 先说一下领劵中心的项目吧,这个项目就类似京东app的领劵中心,当然图是截取京东的,公司的就不截了... 其中有一个功能叫做领劵的订阅推送.什么是领劵的订阅推送?就是用户订阅了该劵的推送,在可领取前的一分钟就要把提醒信息推送到用户的app中.本来这个订阅功能应该是消息中心那边做的,但他们说这个短时间内做不了.所以让我这个负责优惠劵的做了-.-!.具体方案就是到具体的推送时间点了,coupon系统调用消息中心的推送接口,把信

随机推荐