笔者最近工作中遇见一个性能瓶颈问题,MySQL表,每天大概新增776万条记录,存储周期为7天,超过7天的数据需要在新增记录前老化。连续运行9天以后,删除一天的数据大概需要3个半小时(环境:128G, 32核,4T硬盘),而这是不能接受的。当然如果要整个表删除,毋庸置疑,用

TRUNCATE TABLE就好。

最初的方案(因为未预料到删除会如此慢),代码如下(最简单和朴素的方法):

delete from table_name where cnt_date <= target_date

后经过研究,最终实现了飞一般的速度删除770多万条数据,单张表总数据量在4600万上下,优化过程的方案层层递进,详细记录如下:

  • 批量删除(每次限定一定数量),然后循环删除直到全部数据删除完毕;同时key_buffer_size 由默认的8M提高到512M

运行效果:删除时间大概从3个半小时提高到了3小时

(1)通过limit(具体size 酌情设置)限制一次删除的数据量,然后判断数据是否删除完,附源码如下(Python实现):

def delete_expired_data(mysqlconn, day):
mysqlcur = mysqlconn.cursor()
delete_sql = "DELETE from table_name where cnt_date<='%s' limit 50000" % day
query_sql = "select srcip from table_name where cnt_date <= '%s' limit 1" % day
try:
df = pd.read_sql(query_sql, mysqlconn)
while True:
if df is None or df.empty:
break
mysqlcur.execute(delete_sql)
mysqlconn.commit() df = pd.read_sql(query_sql, mysqlconn)
except:
mysqlconn.rollback()

(2)增加key_buffer_size

mysqlcur.execute("SET GLOBAL key_buffer_size = 536870912")

key_buffer_size是global变量,详情参见Mysql官方文档: https://dev.mysql.com/doc/refman/5.7/en/server-configuration.html

  • DELETE QUICK + OPTIMIZE TABLE

适用场景:MyISAM Tables

Why: MyISAM删除的数据维护在一个链表中,这些空间和行的位置接下来会被Insert的数据复用。 直接的delete后,mysql会合并索引块,涉及大量内存的拷贝移动;而OPTIMIZE TABLE直接重建索引,及直接把数据块情况,再重新搞一份。

运行效果:删除时间大3个半小时提高到了1小时40分

具体代码如下:

def delete_expired_data(mysqlconn, day):
mysqlcur = mysqlconn.cursor()
delete_sql = "DELETE QUICK from table_name where cnt_date<='%s' limit 50000" % day
query_sql = "select srcip from table_name where cnt_date <= '%s' limit 1" % day
optimize_sql = "OPTIMIZE TABLE g_visit_relation_asset"
try:
df = pd.read_sql(query_sql, mysqlconn)
while True:
if df is None or df.empty:
break
mysqlcur.execute(delete_sql)
mysqlconn.commit() df = pd.read_sql(query_sql, mysqlconn)
mysqlcur.execute(optimize_sql)
mysqlconn.commit()
except:
mysqlconn.rollback()
  • 表分区,删除直接删除过期日期所在的分区(最终方案—秒杀)

MySQL表分区有几种方式,包括RANGE、KEY、LIST、HASH,具体参见官方文档。因为这里的应用场景日期在变化,所以不适合才用RANGE固定分区名称,而HASH分区更适宜

(1)分区表定义,SQL语句如下:

ALTER TABLE table_name PARTITION BY HASH(TO_DAYS(cnt_date)) PARTITIONS 7;

TO_DAYS将日期(必须为日期类型,否则会报错:Constant, random or timezone-dependent expressions in (sub)partitioning function are not allowed)转换为天数(按一年的天数计算),然后HASH;分区建立7个。实际上,就是 days MOD 7 。


(2)查询出需要老化的日期所在的分区,SQL语句如下:

"explain partitions select * from g_visit_relation_asset where cnt_date = '%s'" % expired_day

(3)OPTIMIZE or REBUILD partition,SQL语句如下:

"ALTER TABLE g_visit_relation_asset OPTIMIZE PARTITION '%s'" % partition

完整代码如下【Python实现】,循环删除小于指定日期的数据:

def clear_partition_data(mysqlconn, day):
mysqlcur = mysqlconn.cursor()
expired_day = day
query_partition_sql = "explain partitions select * from table_name where cnt_date = '%s'" % expired_day
# OPTIMIZE or REBUILD after truncate partition
try:
while True:
df = pd.read_sql(query_partition_sql, mysqlconn)
if df is None or df.empty:
break
partition = df.loc[0, 'partitions']
if partition is not None:
clear_partition_sql = "alter table table_name TRUNCATE PARTITION %s" % partition
mysqlcur.execute(clear_partition_sql)
mysqlconn.commit() optimize_partition_sql = "ALTER TABLE table_name OPTIMIZE PARTITION %s" % partition
mysqlcur.execute(optimize_partition_sql)
mysqlconn.commit() expired_day = (expired_day - timedelta(days = 1)).strftime("%Y-%m-%d")
df = pd.read_sql(query_partition_sql, mysqlconn)
except:
mysqlconn.rollback()
  •  其它

如果删除的数据超过表数据的百分之50,建议拷贝所需数据到临时表,然后删除原表,再重命名临时表为原表,附MySQL如下:

   INSERT INTO New
SELECT * FROM Main
WHERE ...; -- just the rows you want to keep
RENAME TABLE main TO Old, New TO Main;
DROP TABLE Old; -- Space freed up here

参考:

1)https://dev.mysql.com/doc/refman/5.7/en/alter-table-partition-operations.html 具体分区说明

2)http://mysql.rjweb.org/doc.php/deletebig#solutions  删除大数据的解决方案

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

************************************************************************

精力有限,想法太多,专注做好一件事就行

  • 我只是一个程序猿。5年内把代码写好,技术博客字字推敲,坚持零拷贝和原创
  • 写博客的意义在于打磨文笔,训练逻辑条理性,加深对知识的系统性理解;如果恰好又对别人有点帮助,那真是一件令人开心的事

************************************************************************

MySQL 快速删除大量数据(千万级别)的几种实践方案的更多相关文章

  1. MySQL 快速删除大量数据

    千万级数据量 方案1. 直接使用delete 因delete执行速度与索引量成正比,若表中索引量较多,使用delete会耗费数小时甚至数天的时间   方案2. (1)创建临时表,表结构与原表结构相同 ...

  2. oracle 快速删除大批量数据方法(全部删除,条件删除,删除大量重复记录)

    oracle 快速删除大批量数据方法(全部删除,条件删除,删除大量重复记录) 分类: ORACLE 数据库 2011-05-24 16:39 8427人阅读 评论(2) 收藏 举报 oracledel ...

  3. mysql进阶(十五) mysql批量删除大量数据

    mysql批量删除大量数据 假设有一个表(syslogs)有1000万条记录,需要在业务不停止的情况下删除其中statusid=1的所有记录,差不多有600万条, 直接执行 DELETE FROM s ...

  4. MySQL 中删除的数据都去哪儿了?

    不知道大家有没有想过下面这件事? 我们平时调用 DELETE 在 MySQL 中删除的数据都去哪儿了? 这还用问吗?当然是被删除了啊 那么这里又有个新的问题了,如果在 InnoDB 下,多事务并发的情 ...

  5. MySQL 快速导入大量数据 资料收集

    一.LOAD DATA INFILE http://dev.mysql.com/doc/refman/5.5/en/load-data.html 二. 当数据量较大时,如上百万甚至上千万记录时,向My ...

  6. 【MySQL】MySQL快速插入大量数据

    起源 在公司优化SQL遇到一个索引的问题,晚上回家想继续验证,无奈没有较多数据的表,于是,想造一些随机的数据,用于验证. 于是 于是动手写.由于自己不是MySQL能手,写得也不好.最后,插入的速度也不 ...

  7. MySQL中删除重复数据的简单方法,mysql删除重复数据

    MYSQL里有五百万数据,但大多是重复的,真实的就180万,于是想怎样把这些重复的数据搞出来,在网上找了一圈,好多是用NOT IN这样的代码,这样效率很低,自己琢磨组合了一下,找到一个高效的处理方式, ...

  8. 针对mysql delete删除表数据后占用空间不变小的问题

    开发环境 Yii1版本 MySQL PHP5.6.27 前言 物流规则匹配日志表记录订单匹配规则相关日志信息,方便管理员维护和查阅不匹配的订单,四个月时间,该日志表数据就有174G,当前,这么大的数据 ...

  9. KingbaseES例程之快速删除表数据

    概述 快速删除表中的数据 delete语句删除数据 表中的数据被删除了,但是这个数据在硬盘上的真实存储空间不会被释放. 这种删除缺点是:删除效率比较低. 这种删除优点是:支持删除部分数据,支持回滚. ...

随机推荐

  1. GIT 仓库的搭建

    1.安装并配置必要的依赖关系 在CentOS 7(和RedHat / Oracle / Scientific Linux 7)上,以下命令还将在系统防火墙中打开HTTP和SSH访问. yum inst ...

  2. JNI_day01

    C语言简介 system()是提供C语言调用系统命令的函数 C语言基本数据类型 C语言中的整数类型:char/short/int/long C中使用ASCII保存字符,所以char所占用的字节数为1 ...

  3. App接口设计之token的php实现

    为了保证移动端和服务端数据传输相对安全,需要对接口进行加密传输. 一.ttoken的设计目的:  因为APP端没有和PC端一样的session机制,所以无法判断用户是否登陆,以及无法保持用户状态,所以 ...

  4. EIGRP-15-其他和高级的EIGRP特性-1-路由器ID

    与很多协议一样, EIGRP也使用了路由器ID (RTD)的概念,用一个4字节的编号来标识某个路由器实例.每个地址家族实例拥有自已独立的RID.工程师可以在一台路由器上,为多个EIGRP进程和地址家族 ...

  5. Uint47 calculator【map数组+快速积+各种取余公式】

    Uint47 calculator 题目链接(点击) In the distant space, there is a technologically advanced planet. One day ...

  6. MFC exe文件生成的图标更改方法

    MFC exe文件生成的图标更改方法 https://blog.csdn.net/txwtech/article/details/92980545

  7. 无法解析的外部符号 "public: virtual struct CRuntimeClass * _

    SetupPropertyPage.obj : error LNK2001: 无法解析的外部符号 "public: virtual struct CRuntimeClass * __this ...

  8. SSH免密登录详解

    SSH免密登录详解 SSH(Security Shell)安全外壳协议,是较为可靠的,专为远程登录会话和其他网络服务提供安全保证的协议. ​ 对于传统的网络服务程序(例如,FTP,Telnet等)来说 ...

  9. 【Spring注解驱动开发】使用InitializingBean和DisposableBean来管理bean的生命周期,你真的了解吗?

    写在前面 在<[Spring注解驱动开发]如何使用@Bean注解指定初始化和销毁的方法?看这一篇就够了!!>一文中,我们讲述了如何使用@Bean注解来指定bean初始化和销毁的方法.具体的 ...

  10. ArrayList类的使用

    ArrayList常用类方法 (1)添加元素 public boolean add(E element) 在集合末端添加一个元素 public void add(int index,E element ...