MySQL如何快速清理大表操作,比如Zabbix监控历史表未定时清理,导致磁盘空间不足,下面讲下Zabbix历史表的清理操作:
表名 | 磁盘空间 | 表注释 |
---|---|---|
history | 198G | 历史表信息 |
history_uint | 439G | 历史表信息(整型) |
history_log | 2.6 | 历史表信息(长整型) |
history_str | 8.6G | 历史表信息(字符串型) |
history_text | 11G | 历史表信息(文本型) |
history_开头的表都大同一致,唯一不同的是保存的数据类型,history_unit保存的数据类型是int即整型类型的,history_str保存的数据类型是str即字符类型的。这个是和采集时设置的数据类型一致的。
因为history表有这么多的类型,那自己写SQL语句等去查询数据时,就需要判断下数据的采集类型,如果查错了表,是没有数据的。SQL查询可以参考以下:
-- 根据 item的id 查询历史明细 值, 一般保留7d, 这个不同的item项保留时间不同
select itemid, FROM_UNIXTIME(clock,'%Y-%m-%d %H:%i:%s') as date, value , ns from history_uint where itemid=95086 order by clock desc limit 10;
select itemid, FROM_UNIXTIME(clock,'%Y-%m-%d %H:%i:%s') as date, value , ns from history_log where itemid=95086 order by clock desc limit 10;
select itemid, FROM_UNIXTIME(clock,'%Y-%m-%d %H:%i:%s') as date, value , ns from history_str where itemid=95086 order by clock desc limit 10;
select itemid, FROM_UNIXTIME(clock,'%Y-%m-%d %H:%i:%s') as date, value , ns from history_text where itemid=95086 order by clock desc limit 10;
select itemid, FROM_UNIXTIME(clock,'%Y-%m-%d %H:%i:%s') as date, value , ns from history_uint where itemid=95086 order by clock desc limit 10;
这里我们要清理的两张表是 history :198G 和 history_uint :439G
当历史表非常大时,直接删除数据,会有几个风险问题,一是DELETE语句会产生较多的binlog文件占用较多的磁盘空间,二是会有主从延迟,且删除时间较长, 这里我们使用RENAME事务的方式来实现秒级修改历史表。RENAME语法参考如下:
RENAME TABLE
tbl_name TO new_tbl_name
[, tbl_name2 TO new_tbl_name2] ...
重命名表重命名一个或多个表。您必须具有原表的 ALTER 和 DROP 特权,以及新表的创建和插入特权。
-- 重命名 history 表
zabbix@localhost [(none)] > use zabbix;
zabbix@localhost [zabbix]> CREATE TABLE `history_new` (
`itemid` bigint(20) unsigned NOT NULL,
`clock` int(11) NOT NULL DEFAULT '0',
`value` double(16,4) NOT NULL DEFAULT '0.0000',
`ns` int(11) NOT NULL DEFAULT '0',
KEY `history_1` (`itemid`,`clock`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
zabbix@localhost [zabbix]> rename table history to history_old, history_new to history;
-- 重命名 history_uint 表
zabbix@localhost [(none)] > CREATE TABLE `history_uint_new` (
`itemid` bigint(20) unsigned NOT NULL,
`clock` int(11) NOT NULL DEFAULT '0',
`value` bigint(20) unsigned NOT NULL DEFAULT '0',
`ns` int(11) NOT NULL DEFAULT '0',
KEY `history_uint_1` (`itemid`,`clock`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
zabbix@localhost [zabbix]> rename table history_uint to history_uint_old, history_uint_new to history_uint;
由于历史表保留了一年多的监控数据,有时候我们可能还需要回溯查看更久的历史监控数据来排查问题,此时如果直接删除就无法追溯了。 这里我们备份下表,备份前确保磁盘空间足够,并压缩备份:
# history 单表备份
root@localhost:~# mysqldump -uroot -p -h127.0.0.1 zabbix history_old --single-transaction --events | gzip > /data/backup/zabbix.history_old.sql.gz
# history_uint 单表备份
root@localhost:~# mysqldump -uroot -p -h127.0.0.1 zabbix history_uint_old --single-transaction --events | gzip > /data/backup/zabbix.history_uint_old.sql.gz
由于备份时间较长,为避免中断,可以使用脚本方式在后台执行。 注意确保磁盘空间足够!
我们知道文件都有文件名与数据,这在 Linux 上被分成两个部分:用户数据 (user data) 与元数据 (metadata)。用户数据,即文件数据块 (data block),数据块是记录文件真实内容的地方;而元数据则是文件的附加属性,如文件大小、创建时间、所有者等信息。在 Linux 中,元数据中的 inode 号(inode 是文件元数据的一部分但其并不包含文件名,inode 号即索引节点号)才是文件的唯一标识而非文件名。文件名仅是为了方便人们的记忆和使用,系统或程序通过 inode 号寻找正确的文件数据块。图 1.展示了程序通过文件名获取文件内容的过程。
在 Linux 系统中查看 inode 号可使用命令 stat 或 ls -i(若是 AIX 系统,则使用命令 istat)
由于硬链接是有着相同 inode 号仅文件名不同的文件,因此硬链接存在以下几点特性:
文件有相同的 inode 及 data block;
只能对已存在的文件进行创建;
不能交叉文件系统进行硬链接的创建;
不能对目录进行创建,只可对文件创建;
删除一个硬链接文件并不影响其他有相同 inode 号的文件。
供助硬链接的特性,我们可以对历史表创建一个硬链接,然后对表执行DROP操作。
root@localhost:/data/mysql/zabbix/# du -shx * | grep history | grep G
2.5G history.ibd
2.6G history_log.ibd
198G history_old.ibd
8.6G history_str.ibd
11G history_text.ibd
4.6G history_uint.ibd
439G history_uint_old.ibd
# 创建硬链接
root@localhost:/data/mysql/zabbix/#ln /data/mysql/zabbix/history_old.ibd /data/mysql/zabbix/history_old.ibd.hdlk
root@localhost:/data/mysql/zabbix/#ln /data/mysql/zabbix/history_uint_old.ibd /data/mysql/zabbix/history_uint_old.ibd.hdlk
root@localhost:/data/mysql/zabbix/# ls -lit | grep -E "history_old*.ibd|history_uint_old*.ibd"
539081974 -rwxr-xr-x 2 mysql mysql 470978396160 May 21 10:48 history_uint_old.ibd
539081974 -rwxr-xr-x 2 mysql mysql 470978396160 May 21 10:48 history_uint_old.ibd.hdlk
539230890 -rwxr-xr-x 2 mysql mysql 211581665280 May 21 10:32 history_old.ibd
539230890 -rwxr-xr-x 2 mysql mysql 211581665280 May 21 10:32 history_old.ibd.hdlk
通过命令我们创建了硬链接,其中history_uint_old.ibd和history_uint_old.ibd.hdlk的Inode(539081974)号一样, history_old.ibd和history_old.ibd.hdlk的Inode(539230890 )号一样。
如果你的数据库是主从架构,那么同时需要在从库也创建硬链接
创建了硬链接之后,接下来我们就可以使用DROP TABLE操作快速删除大表了,如下:
zabbix@localhost [(none)] > use zabbix;
zabbix@localhost [zabbix]> show tables like 'history%';
+-----------------------------+
| Tables_in_zabbix (history%) |
+-----------------------------+
| history |
| history_log |
| history_old |
| history_str |
| history_text |
| history_uint |
| history_uint_old |
+-----------------------------+
7 rows in set (0.00 sec)
zabbix@localhost [zabbix]> drop table zabbix.history_old;
Query OK, 0 rows affected (5.37 sec)
zabbix@localhost [zabbix]> drop table zabbix.history_uint_old;
Query OK, 0 rows affected (11.89 sec)
zabbix@localhost [zabbix]> show tables like 'history%';
+-----------------------------+
| Tables_in_zabbix (history%) |
+-----------------------------+
| history |
| history_log |
| history_str |
| history_text |
| history_uint |
+-----------------------------+
5 rows in set (0.00 sec)
至此,大表已经清理结束,检查下磁盘文件发现原来的那个文件已经删除:
root@localhost:/data/mysql/zabbix/# ls -lit | grep -E "history_old*.ibd|history_uint_old*.ibd"
539081974 -rwxr-xr-x 1 mysql mysql 470978396160 May 21 10:48 history_uint_old.ibd.hdlk
539230890 -rwxr-xr-x 1 mysql mysql 211581665280 May 21 10:32 history_old.ibd.hdlk
这里只是把引用删除了,实质上磁盘物理空间并没有释放,需要执行下面的清理操作。
在生产环境,直接用rm命令来删大文件,会造成磁盘IO开销飙升,CPU负载过高,是会影响其他程序运行的。这里推荐用truncate命令来删,truncate 命令在 coreutils 工具集中。truncate命令对磁盘IO,CPU负载几乎无影响。
root@localhost:~# whereis truncate
truncate: /usr/bin/truncate /usr/share/man/man2/truncate.2.gz /usr/share/man/man1/truncate.1.gz
# 如果系统没有安装,通过以下命令来安装
root@localhost:~# apt-get install coreutils
这里我们先写好脚本,通过执行脚本来自动清理操作,写个循环,每10G执行清理,中间间隔2秒。脚本如下:
root@localhost:/opt# vim truncate_limit.sh
#!/bin/bash
for i in `seq 198 -10 10 `;
do
sleep 2
echo "/usr/bin/truncate -s ${i}G /data/mysql/zabbix/history_old.ibd.hdlk"
/usr/bin/truncate -s ${i}M /data/mysql/zabbix/history_old.ibd.hdlk
echo -n "当前大小:" ; du -shx /data/mysql/zabbix/history_old.ibd.hdlk
echo '==========================================================='
done
rm -f /data/mysql/zabbix/history_old.ibd.hdlk
echo "文件已删除!"
ls -lt /data/mysql/zabbix/history_old.ibd.hdlk
从198G开始,每次缩减10G,停2秒,继续,直到文件只剩10G,最后使用rm命令删除剩余的部分,脚本执行过程日志显示如下
root@localhost:/opt# bash truncate_limit.sh
/usr/bin/truncate -s 198G /data/mysql/zabbix/history_old.ibd.hdlk
当前大小:194G /data/mysql/zabbix/history_old.ibd.hdlk
===========================================================
/usr/bin/truncate -s 188G /data/mysql/zabbix/history_old.ibd.hdlk
当前大小:189G /data/mysql/zabbix/history_old.ibd.hdlk
===========================================================
/usr/bin/truncate -s 178G /data/mysql/zabbix/history_old.ibd.hdlk
当前大小:179G /data/mysql/zabbix/history_old.ibd.hdlk
===========================================================
/usr/bin/truncate -s 168G /data/mysql/zabbix/history_old.ibd.hdlk
当前大小:169G /data/mysql/zabbix/history_old.ibd.hdlk
===========================================================
/usr/bin/truncate -s 158G /data/mysql/zabbix/history_old.ibd.hdlk
当前大小:159G /data/mysql/zabbix/history_old.ibd.hdlk
===========================================================
/usr/bin/truncate -s 148G /data/mysql/zabbix/history_old.ibd.hdlk
当前大小:149G /data/mysql/zabbix/history_old.ibd.hdlk
===========================================================
/usr/bin/truncate -s 138G /data/mysql/zabbix/history_old.ibd.hdlk
当前大小:139G /data/mysql/zabbix/history_old.ibd.hdlk
===========================================================
/usr/bin/truncate -s 128G /data/mysql/zabbix/history_old.ibd.hdlk
当前大小:129G /data/mysql/zabbix/history_old.ibd.hdlk
===========================================================
/usr/bin/truncate -s 118G /data/mysql/zabbix/history_old.ibd.hdlk
当前大小:119G /data/mysql/zabbix/history_old.ibd.hdlk
===========================================================
/usr/bin/truncate -s 108G /data/mysql/zabbix/history_old.ibd.hdlk
当前大小:109G /data/mysql/zabbix/history_old.ibd.hdlk
===========================================================
/usr/bin/truncate -s 98G /data/mysql/zabbix/history_old.ibd.hdlk
当前大小:99G /data/mysql/zabbix/history_old.ibd.hdlk
===========================================================
/usr/bin/truncate -s 88G /data/mysql/zabbix/history_old.ibd.hdlk
当前大小:89G /data/mysql/zabbix/history_old.ibd.hdlk
===========================================================
/usr/bin/truncate -s 78G /data/mysql/zabbix/history_old.ibd.hdlk
当前大小:79G /data/mysql/zabbix/history_old.ibd.hdlk
===========================================================
/usr/bin/truncate -s 68G /data/mysql/zabbix/history_old.ibd.hdlk
当前大小:69G /data/mysql/zabbix/history_old.ibd.hdlk
===========================================================
/usr/bin/truncate -s 58G /data/mysql/zabbix/history_old.ibd.hdlk
当前大小:58G /data/mysql/zabbix/history_old.ibd.hdlk
===========================================================
/usr/bin/truncate -s 48G /data/mysql/zabbix/history_old.ibd.hdlk
当前大小:48G /data/mysql/zabbix/history_old.ibd.hdlk
===========================================================
/usr/bin/truncate -s 38G /data/mysql/zabbix/history_old.ibd.hdlk
当前大小:38G /data/mysql/zabbix/history_old.ibd.hdlk
===========================================================
/usr/bin/truncate -s 28G /data/mysql/zabbix/history_old.ibd.hdlk
当前大小:28G /data/mysql/zabbix/history_old.ibd.hdlk
===========================================================
/usr/bin/truncate -s 18G /data/mysql/zabbix/history_old.ibd.hdlk
当前大小:18G /data/mysql/zabbix/history_old.ibd.hdlk
===========================================================
文件已删除!
ls: cannot access '/data/mysql/zabbix/history_old.ibd.hdlk': No such file or directory
通过truncate命令完成了对物理文件的删除操作,删除期间观察系统IO和CPU,可根据资源情况调整每次清理的大小和时间间隔,这样就不会影响服务器的性能。 按照上面的方法,对剩下的history_old.ibd.hdlk 表执行相同的操作即可。