安全快速地删除 MySQL 大表数据并释放空间

本文详细描述了一个在MySQL数据库中高效删除旧数据并保持低延迟,同时不影响正常业务操作的需求。通过创建关联表、导出数据、文件处理、批量删除以及监控从库延迟等步骤,确保数据清理过程的顺利进行,最终释放空间并分析表结构。

一、需求

  1. 按业务逻辑删除大量表数据
  2. 操作不卡库,不能影响正常业务操作
  3. 操作不能造成 60 秒以上的复制延迟
  4. 满足以上条件的前提下,尽快删除数据并释放所占空间

表结构如下:

create table `space_visit_av` (
  `userid` bigint(20) not null comment '用户id',
  `avid` bigint(20) not null comment '作品id',
  `touserid` bigint(20) not null comment '被访问用户d',
  `createtime` timestamp not null default current_timestamp comment '创建时间',
  `updatetime` timestamp not null default current_timestamp on update current_timestamp comment '收藏时间',
  primary key (`userid`,`avid`),
  key `index_1` (`touserid`,`updatetime`) using btree,
  key `index_2` (`avid`,`updatetime`) using btree,
  key `idx_updatetime` (`updatetime`)
) engine=innodb default charset=utf8 comment='用户访问作品表';

表中现有约 50 亿条数据,只保留 2023-10-01 以后的数据(约占总量的 1/10),其它删除。

二、实现

1. 主库按原表创建删除关联表,只保留原表的主键
mysql -uwxy -p123456 -h10.10.10.1 -P18251 -Dspace -e "
create table del (
  userid bigint(20) not null comment '用户id',
  avid bigint(20) not null comment '作品id',
  primary key (userid,avid));"
2. 导出需要删除数据的主键到文件
-- 在从库执行查询
select userid, avid into outfile '/data/del.txt' from space_visit_av where updatetime < '2023-10-01';
3. 将文件分割成 10 万行一个的小文件
cd /data
split -l 100000 -d -a 6 del.txt

# 删除原文件
rm del.txt
4. 遍历文件执行删除
# 后台执行
nohup ~/del.sh > ~/del.log 2>&1 &

del.sh 脚本文件内容如下:

#!/bin/bash
source ~/.bashrc

dir="/data/"
ls $dir | while read line
do
    file=${dir}${line}

    # 表关联删除数据
    mysql -wxy -p123456 -h10.10.10.1 -P18251 -Dspace --local-infile -e "
        delete from del;
        load data local infile '$file' into table del;
        analyze table del; analyze table space_visit_av;
        delete t1 from space_visit_av t1, del t2 where t1.userid=t2.userid and t1.avid=t2.avid;" -vvv

    echo ${line}
    
    # 取得所有从库的延迟秒数
    s1=`mysql -wxy -p123456 -h10.10.10.2 -P18251 -e "show slave status\G" 2>/dev/null | egrep 'Seconds_Behind_Master' | awk -F": " '{print $2}'`
    s2=`mysql -wxy -p123456 -h10.10.10.3 -P18251 -e "show slave status\G" 2>/dev/null | egrep 'Seconds_Behind_Master' | awk -F": " '{print $2}'`
    s3=`mysql -wxy -p123456 -h10.10.10.4 -P18251 -e "show slave status\G" 2>/dev/null | egrep 'Seconds_Behind_Master' | awk -F": " '{print $2}'`

    # 只有所有从库延迟小于等于 1 秒时继续执行删除,否则等待从库追赶
    while ((s1 > 1)) || ((s2 > 1)) || ((s3 > 1))
    do
        sleep 1;
        s1=`mysql -wxy -p123456 -h10.10.10.2 -P18251 -e "show slave status\G" 2>/dev/null | egrep 'Seconds_Behind_Master' | awk -F": " '{print $2}'`
        s2=`mysql -wxy -p123456 -h10.10.10.3 -P18251 -e "show slave status\G" 2>/dev/null | egrep 'Seconds_Behind_Master' | awk -F": " '{print $2}'`
        s3=`mysql -wxy -p123456 -h10.10.10.4 -P18251 -e "show slave status\G" 2>/dev/null | egrep 'Seconds_Behind_Master' | awk -F": " '{print $2}'`

        echo "$s1 $s2 $s3"
    done

done

# 删除完成后,分析原表,删除关联表
mysql -wxy -p123456 -h10.10.10.1 -P18251 -Dspace -e "
    analyze table space_visit_av;
    drop table del;"

如果只是简单的按时间字段删除历史数据,可以不用导出文件而是直接利用 limit 子句进行删除,例如:

#!/bin/bash
source ~/.bashrc

matched=""

while [ -z "$matched" ]; do
    r=`mysql -wxy -p123456 -h10.10.10.1 -P18251 -e "
         delete from test.user_task_record 
          where TaskDate < 20231201 
          limit 10000;" -vv 2>/dev/null`

    matched=$(echo "$r" | grep "Query OK, 0 rows affected")
	
    s1=`mysql -uwxy -p123456 -h10.10.10.2 -P18251 -e "show slave status\G" 2>/dev/null | egrep 'Seconds_Behind_Master' | awk -F": " '{print $2}'`
    s2=`mysql -uwxy -p123456 -h10.10.10.3 -P18251 -e "show slave status\G" 2>/dev/null | egrep 'Seconds_Behind_Master' | awk -F": " '{print $2}'`
    s3=`mysql -uwxy -p123456 -h10.10.10.4 -P18251 -e "show slave status\G" 2>/dev/null | egrep 'Seconds_Behind_Master' | awk -F": " '{print $2}'`

    while ((s1 > 1)) || ((s2 > 1)) || ((s3 > 1))
    do
        sleep 1;
        s1=`mysql -uwxy -p123456 -h10.10.10.2 -P18251 -e "show slave status\G" 2>/dev/null | egrep 'Seconds_Behind_Master' | awk -F": " '{print $2}'`
        s2=`mysql -uwxy -p123456 -h10.10.10.3 -P18251 -e "show slave status\G" 2>/dev/null | egrep 'Seconds_Behind_Master' | awk -F": " '{print $2}'`
        s3=`mysql -uwxy -p123456 -h10.10.10.4 -P18251 -e "show slave status\G" 2>/dev/null | egrep 'Seconds_Behind_Master' | awk -F": " '{print $2}'`

        echo "$s1 $s2 $s3"

    done
done

where 条件中使用的时间字段最好有索引,否则大表执行 delete 会非常慢。联机给大表加索引也可以使用第6步引入的 pt-online-schema-
change 工具,在不卡库、复制不延迟的约束下执行操作。

5. 所有从库分析表
mysql -wxy -p123456 -h10.10.10.2 -P18251 -Dspace -e "analyze table space_visit_av;"
mysql -wxy -p123456 -h10.10.10.3 -P18251 -Dspace -e "analyze table space_visit_av;"
mysql -wxy -p123456 -h10.10.10.4 -P18251 -Dspace -e "analyze table space_visit_av;"
6. 使用 pt-online-schema-change 释放删除数据所占空间
# 后台执行
nohup ~/shrink.sh > ~/shrink.log 2>&1 &

shrink.sh 脚本文件内容如下:

#!/bin/bash
source ~/.bashrc

# 延迟复制实例停止复制,避免 pt-online-schema-change 陷入等待
mysql -uwxy -p123456 -h172.18.10.5 -P18251 -e "stop slave;" -vvv 2>/dev/null

sleep 60

# 连接主库执行
pt-online-schema-change \
--host="10.10.10.1" \
--port=18251 \
--user="wxy" \
--password="123456" \
--charset="utf8mb4" \
--chunk-size=10000 \
--recursion-method="processlist" \
--check-interval=1s \
--max-lag=10s \
--nocheck-replication-filters \
--critical-load="Threads_running=512" \
--max-load="Threads_running=256" \
D="space",t="space_visit_av" \
--progress=time,30 \
--execute

sleep 60

# 从库执行表分析
mysql -uwxy -p123456 -h10.10.10.2 -P18251 -Dspace -e "analyze table space_visit_av;" -vvv 2>/dev/null
mysql -uwxy -p123456 -h10.10.10.3 -P18251 -Dspace -e "analyze table space_visit_av;" -vvv 2>/dev/null
mysql -uwxy -p123456 -h10.10.10.4 -P18251 -Dspace -e "analyze table space_visit_av;" -vvv 2>/dev/null

# 延迟复制实例开启复制
mysql -uwxy -p123456 -h10.10.10.5 -P18251 -e "start slave;" -vvv 2>/dev/null

接下来我将给各位同学划分一张学习计划表!

学习计划

那么问题又来了,作为萌新小白,我应该先学什么,再学什么?
既然你都问的这么直白了,我就告诉你,零基础应该从什么开始学起:

阶段一:初级网络安全工程师

接下来我将给大家安排一个为期1个月的网络安全初级计划,当你学完后,你基本可以从事一份网络安全相关的工作,比如渗透测试、Web渗透、安全服务、安全分析等岗位;其中,如果你等保模块学的好,还可以从事等保工程师。

综合薪资区间6k~15k

1、网络安全理论知识(2天)
①了解行业相关背景,前景,确定发展方向。
②学习网络安全相关法律法规。
③网络安全运营的概念。
④等保简介、等保规定、流程和规范。(非常重要)

2、渗透测试基础(1周)
①渗透测试的流程、分类、标准
②信息收集技术:主动/被动信息搜集、Nmap工具、Google Hacking
③漏洞扫描、漏洞利用、原理,利用方法、工具(MSF)、绕过IDS和反病毒侦察
④主机攻防演练:MS17-010、MS08-067、MS10-046、MS12-20等

3、操作系统基础(1周)
①Windows系统常见功能和命令
②Kali Linux系统常见功能和命令
③操作系统安全(系统入侵排查/系统加固基础)

4、计算机网络基础(1周)
①计算机网络基础、协议和架构
②网络通信原理、OSI模型、数据转发流程
③常见协议解析(HTTP、TCP/IP、ARP等)
④网络攻击技术与网络安全防御技术
⑤Web漏洞原理与防御:主动/被动攻击、DDOS攻击、CVE漏洞复现

5、数据库基础操作(2天)
①数据库基础
②SQL语言基础
③数据库安全加固

6、Web渗透(1周)
①HTML、CSS和JavaScript简介
②OWASP Top10
③Web漏洞扫描工具
④Web渗透工具:Nmap、BurpSuite、SQLMap、其他(菜刀、漏扫等)

那么,到此为止,已经耗时1个月左右。你已经成功成为了一名“脚本小子”。那么你还想接着往下探索吗?

阶段二:中级or高级网络安全工程师(看自己能力)

综合薪资区间15k~30k

7、脚本编程学习(4周)
在网络安全领域。是否具备编程能力是“脚本小子”和真正网络安全工程师的本质区别。在实际的渗透测试过程中,面对复杂多变的网络环境,当常用工具不能满足实际需求的时候,往往需要对现有工具进行扩展,或者编写符合我们要求的工具、自动化脚本,这个时候就需要具备一定的编程能力。在分秒必争的CTF竞赛中,想要高效地使用自制的脚本工具来实现各种目的,更是需要拥有编程能力。

零基础入门的同学,我建议选择脚本语言Python/PHP/Go/Java中的一种,对常用库进行编程学习
搭建开发环境和选择IDE,PHP环境推荐Wamp和XAMPP,IDE强烈推荐Sublime;

Python编程学习,学习内容包含:语法、正则、文件、 网络、多线程等常用库,推荐《Python核心编程》,没必要看完

用Python编写漏洞的exp,然后写一个简单的网络爬虫

PHP基本语法学习并书写一个简单的博客系统

熟悉MVC架构,并试着学习一个PHP框架或者Python框架 (可选)

了解Bootstrap的布局或者CSS。

阶段三:顶级网络安全工程师

如果你对网络安全入门感兴趣,那么你需要的话可以点击这里👉网络安全重磅福利:入门&进阶全套282G学习资源包免费分享!

学习资料分享

当然,只给予计划不给予学习资料的行为无异于耍流氓,这里给大家整理了一份【282G】的网络安全工程师从入门到精通的学习资料包,可点击下方二维码链接领取哦。

<think>我们正在处理用户关于MySQL删除数据释放磁盘空间的问题。用户使用了"mysqldeletetabledatareleasespaceoptimize"这样的关键词。根据MySQL的知识,当我们使用DELETE语句删除中的数据时,这些数据不会立即从磁盘上物理删除,而是被标记为删除,所占用的空间也不会立即释放。这是因为InnoDB存储引擎使用了一种称为“MVCC”(多版本发控制)的机制,需要保留旧数据版本以供其他事务可能使用。此外,这些被删除的行所占用的空间会被保留,以便将来插入新数据时可以重用这些空间。但是,如果删除量的数据可能会变得“碎片化”,即数据在物理存储上不是连续的,这可能会影响性能。此时,我们可以通过一些操作来释放空间或整理碎片。常见的释放空间的方法有:1.使用OPTIMIZETABLE命令:该命令会重建释放未使用的空间。对于InnoDB,它相当于执行`ALTERTABLE...FORCE`,这会重建重新生成索引,从而整理碎片释放空间。2.使用ALTERTABLE命令:例如`ALTERTABLEtable_nameENGINE=InnoDB;`,这也会重建释放未使用的空间。需要注意的是,OPTIMIZETABLE在执行过程中会锁定,因此在高负载时段需要谨慎使用。另外,对于InnoDB,OPTIMIZETABLE操作在MySQL5.6.17及更高版本中是在线进行的(不会阻塞DML操作),但仍有短暂的锁时间(在重建的开始和结束时)。另外,如果使用的是独立空间(innodb_file_per_table=ON),那么当删除数据后,通过重建可以释放空间给操作系统。但如果使用的是共享空间(innodb_file_per_table=OFF),则释放空间不会归还给操作系统,而是留给InnoDB存储引擎内部重用。用户的问题中提到了“deletetabledata”,所以我们可以假设用户已经删除数据,现在想要释放磁盘空间。根据以上分析,我们可以给出如下建议:步骤1:确认是否使用了独立空间(innodb_file_per_table)因为只有独立空间才能将释放空间归还给操作系统。可以通过以下命令查看:```sqlSHOWVARIABLESLIKE'innodb_file_per_table';```如果值为ON,则说明每个有独立的空间文件(.ibd),执行OPTIMIZETABLE后空间可以释放给操作系统。步骤2:执行OPTIMIZETABLE```sqlOPTIMIZETABLEyour_table_name;```或者,使用ALTERTABLE:```sqlALTERTABLEyour_table_nameENGINE=InnoDB;```注意:在MySQL5.7及更高版本中,OPTIMIZETABLE对于InnoDB实际上就是执行ALTERTABLE...FORCE,所以两者效果相同。步骤3:对于使用MyISAM引擎的,OPTIMIZETABLE同样有效,而且会释放空间给操作系统。但是,用户的问题中提到了MySQL的版本,我们注意到用户提供的引用中提到了MySQL8.0的相关内容。因此,我们也可以提及MySQL8.0中的新特性。引用[2]中提到MySQL8.0.16开始支持对通用空间的加密,但与我们当前的问题关系不。引用[1]提到了一个移除未使用选项的变更,也与问题无关。因此,我们主要依据MySQL的通用知识。另外,如果用户删除了整个数据,也可以考虑使用TRUNCATETABLE。TRUNCATETABLE会删除整个数据重建,因此会立即释放空间(对于独立空间)。但是TRUNCATE操作是DDL,不能回滚,且会重置自增列的值。因此,如果用户需要删除整个数据释放空间,TRUNCATETABLE是一个更好的选择。总结:-如果只是删除部分数据,使用DELETE后,可以通过OPTIMIZETABLE或ALTERTABLE来释放空间(在独立空间下)。-如果是删除整个数据,可以考虑使用TRUNCATETABLE。最后,生成相关问题。</think>### MySQL删除数据释放磁盘空间的方法 在MySQL中,使用`DELETE`语句删除数据后,磁盘空间不会立即释放,因为InnoDB引擎会将已删除数据标记为"可复用"而非立即回收空间。以下是释放空间的几种有效方法: #### 1. **使用`OPTIMIZE TABLE`命令**(推荐) 这是最直接的释放空间方法: ```sql OPTIMIZE TABLE your_table_name; ``` - **作用**:重建重组物理存储,释放未使用的空间 - **要求**:需启用`innodb_file_per_table`(独立空间),MySQL 5.6+默认开启 - **耗时**:对可能较慢(会锁) #### 2. **重建结构** 通过`ALTER TABLE`重建: ```sql ALTER TABLE your_table_name ENGINE=InnoDB; ``` - 效果等同于`OPTIMIZE TABLE` - 在MySQL 5.5+中支持在线DDL(减少锁时间) #### 3. **使用`TRUNCATE TABLE`** 如果需删除全部数据: ```sql TRUNCATE TABLE your_table_name; ``` - **优势**:直接释放空间(无需额外操作) - **注意**:会重置自增ID,且不可回滚 #### 4. **配置空间回收** 确保启用独立空间: ```sql -- 检查配置 SHOW VARIABLES LIKE 'innodb_file_per_table'; -- 永久启用(需重启) SET GLOBAL innodb_file_per_table=ON; ``` > 只有启用独立空间时,释放空间才会归还操作系统[^1]。 --- ### 操作建议 1. **优先选择`OPTIMIZE TABLE`**:适合删除部分数据后的空间回收 2. **操作**: - 在业务低峰期执行 - 使用`pt-online-schema-change`工具(避免锁) 3. **监控空间**: ```sql -- 查看碎片率 SELECT table_name, data_free/1024/1024 AS '碎片空间(MB)' FROM information_schema.tables WHERE data_free > 0; ``` > **注意**:共享空间(`ibdata1`)中删除数据后,空间不会释放给操作系统,只能通过重建整个实例回收[^2]。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值