Percona Toolkit系列之二:pt-archiver

1. pt-archiver是一个主要用于对MySQL表数据进行归档和清除工具。它可以将数据归档到另一张表或者是一个文件中。pt-archiver在清除表数据的过程中并不会影响OLTP事务的查询性能。

它实现的功能包括:归档历史数据,在线删除大批量数据,数据导出和备份,数据远程归档,数据清理等。


基本使用语法为:pt-archiver [OPTIONS] --source DSN --where WHERE


下面列表列出部分options,更加详细的请使用pt-archiver --help进行查看

OPTIONS:

OPTIONS

短操作符

类型

默认值

简要说明

--ask-pass




连接 MySQL 时提示输入密码。

--bulk-delete




批量删除source上的旧数据。

--bulk-insert




使用 LOAD DATA INFILE 插入

--channel


s


使用复制通道连接到服务器时使用的通道名称。

--charset

-A

s


默认字符集。

--[no]check-charset



Yes

确保连接和表字符集相同。

--[no]check-columns



Yes

确保--source 并--dest 具有相同的列。


--check-interval



m

1s


如果--check-slave-lag 给出,这定义了工具每次暂停的时间。 它发现Slave落后的时间。此检查每 100 行执行一次

--check-slave-lag


s


暂停归档,直到指定的 DSN 的从延迟小于--max-lag。可以多次指定此选项以检查多个从。

--columns

-c

a


要存档的列的逗号分隔列表。

--commit-each




提交每组提取和存档的行

--database

-D

s


连接到这个数据库。

--delayed-insert




将 DELAYED 修饰符添加到 INSERT 语句。

将 DELAYED 修饰符添加到 INSERT 或 REPLACE 语句。

--dest


d


DSN 指定要归档到的表

--dry-run




打印查询并退出而不做任何事情。

--file



s


要归档到的文件,用类似DATE_FORMAT()的格式。

%d 一个月中的天,数字(01 ..31 )

%H 小时(00 ..23 )

%I 分钟,数字(00 ..59 )

%m 月份,数字(01 ..12 )

%s 秒(00 ..59 )

%Y 年份,数字,四位数

%D 数据库名

%t 表名

--for-update




将 FOR UPDATE 修饰符添加到 SELECT 语句。

--header




在 顶部打印列标题--file。

--high-priority-select




将 HIGH_PRIORITY 修饰符添加到 SELECT 语句。


--host

-h

s


连接到主机。

--ignore




将 IGNORE 用于 INSERT 语句。

--limit


i

1

每个语句要获取和存档的行数。

--max-lag



m

1s


如果从设备--check-slave-lag 滞后,则暂停归档。

--no-delete




不要删除归档的行。

--output-format


s


用于--file 指定输出格式。

--password

-p



连接时使用的密码。如果密码包含逗号,它们必须用反斜杠转义

--port

-P

i


用于连接的端口号。

--progress


i


每 X 行打印一次进度信息。

--purge




清除而不是归档

--[no]safe-auto-increment



yes

不要归档最大 AUTO_INCREMENT 的行


--slave-user


s


设置用于连接到Slave的用户。

--slave-password



s


设置用于连接到从站的密码。它可以与 –slave-user 一起使用,并且所有从站上的用户密码必须相同。

--set-vars



a


个以逗号分隔的 variable=value 对列表中设置 MySQL 变量。

--socket

-S



用于连接的套接字文件。

--source



d



DSN 指定要从中存档的表(必需)。这个参数是一个 DSN。有关语法,请参阅 DSN 选项。

--statistics




收集和打印计时统计信息。

--txn-size



i

1

指定每个事务的大小(以行数计)。零完全禁用事务。

--user

-u



如果不是当前用户,则用于登录的用户。

--where


s


WHERE 子句来限制要存档的行(必需)。

类型备注:s=string, i=integer, f=float, h/H/a/A=comma-separated list, d=DSN, z=size, m=time


注意以下几点:

--ignore --replace 互斥。

--txn-size 与--commit-each 互斥。

--low-priority-insert --delayed-insert 互斥。

--share-lock --for-update 互斥。

--analyze --optimize 互斥。

--no-ascend --no-delete 互斥。

至少需要指定一个--dest--file --purge


DSN为要操作的数据库和表。

DSN:

DSN

简要说明

a

执行查询时要使用的数据库。

A

默认字符集。

b

如果为 true,则使用 SQL_LOG_BIN 禁用 binlog。

D

数据库。

F

仅从给定文件中读取默认选项

h

连接到主机。

i

要使用的索引。

L

显式启用 LOAD DATA LOCAL INFILE。

m

插件模块名称。

p

连接时使用的密码。如果密码包含逗号,它们必须用反斜杠转义

P

用于连接的端口号。

S

用于连接的套接字文件。

t

表。

u

如果不是当前用户,则用于登录的用户。


2. 工作原理

1) 依据主键与Where,--limit等确定需要归档的数据

2) 归档第N(N>=1)部分数据至目标表或者文件

3) 依据--no-delete进行是否删除原表操作

4) 循环执行2.3步骤,直到无数据需要归档


3. 基本使用样例


目前,我们有数据库test,其中有一张表t_user,结构如下:


CREATE TABLE `t_user` (

`id` int NOT NULL,

`nm` varchar(10) DEFAULT NULL,

`address` varchar(20) DEFAULT NULL,

PRIMARY KEY (`id`)

) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;


表t_user中有数据200000


另有数据库test_bak,其中有一张表t_user_bak,结构同t_user:


CREATE TABLE `t_user_bak` (

`id` int NOT NULL,

`nm` varchar(10) DEFAULT NULL,

`address` varchar(20) DEFAULT NULL,

PRIMARY KEY (`id`)

) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;


3.1 打印查询(--dry-run)

pt-archiver --source h=192.168.246.137,u=root,p=Ygzlpaul*2022,D=test,t=t_user --dest h=192.168.246.137,u=root,p=Ygzlpaul*2022,D=test_bak,t=t_user_bak --where 'nm like "55%"' --progress 50 --no-delete --limit=100 --statistics --commit-each --dry-run



首先,打印查询需要清除数据的执行语句,并确认数据是否准确。

从截图可以看到如下语句,需要在归档时执行:

SELECT /*!40001 SQL_NO_CACHE */ `id`,`nm`,`address`,`p` FROM `test`.`t_user` FORCE INDEX(`PRIMARY`) WHERE (nm like "55%") ORDER BY `id` LIMIT 100

SELECT /*!40001 SQL_NO_CACHE */ `id`,`nm`,`address`,`p` FROM `test`.`t_user` FORCE INDEX(`PRIMARY`) WHERE (nm like "55%") AND ((`id` > ?)) ORDER BY `id` LIMIT 100

INSERT INTO `test_bak`.`t_user_bak`(`id`,`nm`,`address`,`p`) VALUES (?,?,?,?)


3.2 归档,不删除原表数据(逐条事务归档)

pt-archiver --source h=192.168.246.137,u=root,p=Ygzlpaul*2022,D=test,t=t_user --dest h=192.168.246.137,u=root,p=Ygzlpaul*2022,D=test_bak,t=t_user_bak --where 'nm like "55%"' --progress 50 --no-delete --limit=100 --statistics


查看日志,见下图:



下面截图是执行mysqlbinlog后截取的一部分详细信息:



从上面的截图可以明确表明,数据是一条一事务提交的。

3.3 归档,不删除原表数据(逐批事务归档--commit-each

pt-archiver --source h=192.168.246.137,u=root,p=Ygzlpaul*2022,D=test,t=t_user --dest h=192.168.246.137,u=root,p=Ygzlpaul*2022,D=test_bak,t=t_user_bak --where 'nm like "55%"' --progress 50 --no-delete --limit=100 --statistics --commit-each



查看日志,见下图:



从上面的日志截图可以明确表明,数据是以100(--limit)为一个事务进行提交的。




3.4 归档,不删除原表数据(逐批事务归档--txn-size

下面让我们再使用--txn-size来看看是如何工作的

pt-archiver --source h=192.168.246.137,u=root,p=Ygzlpaul*2022,D=test,t=t_user --dest h=192.168.246.137,u=root,p=Ygzlpaul*2022,D=test_bak,t=t_user_bak --where 'nm like "550%"' --progress 50 --no-delete --limit=20 --statistics --txn-size=10



以上数据是以10( --txn-size=10)为一个事务进行提交的


pt-archiver --source h=192.168.246.137,u=root,p=Ygzlpaul*2022,D=test,t=t_user --dest h=192.168.246.137,u=root,p=Ygzlpaul*2022,D=test_bak,t=t_user_bak --where 'nm like "550%"' --progress 50 --no-delete --limit=20 --statistics --txn-size=5



以上数据是以5( --txn-size=5)为一个事务进行提交的

说明--txn-size控制一个事务内提交的数据行数


3.5 归档,不删除原表数据(--bulk-insert

pt-archiver --source h=192.168.246.137,u=root,p=Ygzlpaul*2022,D=test,t=t_user --dest h=192.168.246.137,u=root,p=Ygzlpaul*2022,D=test_bak,t=t_user_bak --where 'nm like "550%"' --no-delete --limit=100 --statistics --bulk-insert


执行报告如下错误:

DBD::mysql::st execute failed: Loading local data is disabled; this must be enabled on both the client and server sides [for Statement "LOAD DATA LOCAL INFILE ? INTO TABLE `test_bak`.`t_user_bak`(`id`,`nm`,`address`,`p`)" with ParamValues: 0='/tmp/qMPmkDZ1mrpt-archiver'] at /bin/pt-archiver line 6875.


解决:需要SET GLOBAL local_infile=ON


再次执行,结果如下图:



查看日志,如下图:



查看binlog,如下图:



一个事务内提交,性能得到提升


3.6 归档到文件

pt-archiver --source h=192.168.246.137,u=root,p=Ygzlpaul*2022,D=test,t=t_user --where 'ID<100' --file '/root/mysqlbak/%Y-%m-%d-%D-%t.sql' --statistics


执行后,数据归档到文件/root/mysqlbak/2022-11-15-test-t_user.sql中,我们简单查看一下文件内容,如下所示:



从文件内容来看,缺少了列名,下面加上列名

pt-archiver --source h=192.168.246.137,u=root,p=Ygzlpaul*2022,D=test,t=t_user --where 'ID >= 120 and ID<130' --file '/root/mysqlbak/%Y-%m-%d-%D-%t.sql' --statistics --header



注意,如果导出的文件已经存在,则不会增加列名。



3.7 清除表数据(逐条事务--purge

如果是仅需要删除数据,则无需指定--dest和file

pt-archiver --source h=192.168.246.137,u=root,p=Ygzlpaul*2022,D=test,t=t_user --where 'ID<300' --purge --statistics

如下图所示,删除了100行数据



binlog日志部分内容如下,显示了都是执行Delete操作



3.8 清除表数据(逐条事务--purge --bulk-delete

pt-archiver --source h=192.168.246.137,u=root,p=Ygzlpaul*2022,D=test,t=t_user --where 'ID<350' --purge --bulk-delete --statistics --txn-size=100 --limit=200


查看日志,如下图:




3.9 清除表数据(--safe-auto-increment

对于表的主键列是AUTO_INCREMENT列的情况,需要特别注意

目前,我们有数据库test,其中有一张表t_dept,结构如下:

CREATE TABLE `t_dept` (

`id` int NOT NULL AUTO_INCREMENT,

`deptnm` varchar(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL,

`address` varchar(20) DEFAULT NULL,

PRIMARY KEY (`id`)

) ENGINE=InnoDB AUTO_INCREMENT=153 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;


表中最大自增列值为152,假设后续未对此自增列做任何其它修改,包括未做手动修改AUTO_INCREMENT的操作,下面做如下数据清理,清理最后2条数据:


在正式清除最后2条数据之前,我们先观察:

pt-archiver --source h=192.168.246.137,u=root,p=Ygzlpaul*2022,D=test,t=t_dept --where 'ID>150' --purge --bulk-delete --statistics --txn-size=100 --limit=200 --dry-run

输出如下:

SELECT /*!40001 SQL_NO_CACHE */ `id`,`deptnm`,`address` FROM `test`.`t_dept` FORCE INDEX(`PRIMARY`) WHERE (ID>150) AND (`id` < '152') ORDER BY `id` LIMIT 200

SELECT /*!40001 SQL_NO_CACHE */ `id`,`deptnm`,`address` FROM `test`.`t_dept` FORCE INDEX(`PRIMARY`) WHERE (ID>150) AND (`id` < '152') AND ((`id` >= ?)) ORDER BY `id` LIMIT 200

DELETE FROM `test`.`t_dept` WHERE (((`id` >= ?))) AND (((`id` <= ?))) AND (ID>150) LIMIT 200


发现删除范围实际上为150与152之间,则只删除1条。我们运行一下清除,看看是否只删除了1条:

pt-archiver --source h=192.168.246.137,u=root,p=Ygzlpaul*2022,D=test,t=t_dept --where 'ID>150' --purge --bulk-delete --statistics --txn-size=100 --limit=200




从上面的输出结果表明,的确仅仅删除了1条。

检查数据表内容

mysql> select count(*) from t_dept where id>150;

+----------+

| count(*) |

+----------+

| 1 |

+----------+

1 row in set (0.01 sec)


mysql> select max(id) from t_dept where id>150;

+---------+

| max(id) |

+---------+

| 152 |

+---------+

1 row in set (0.00 sec)


也表明的确仅仅删除了1条,最大值id=152被保留了下来,这是因为什么呢?

避免AUTO_INCREMENT属性的值在数据库重启后还可以重用


那么如果确认此行的确是需要删除的,请使用--nosafe-auto-increment

继续如下执行:

pt-archiver --source h=192.168.246.137,u=root,p=Ygzlpaul*2022,D=test,t=t_dept --where 'ID>150' --purge --bulk-delete --statistics --txn-size=100 --limit=200 --nosafe-auto-increment




从上面的输出结果表明,又删除了1条。

检查数据表内容


mysql> select * from t_dept where id>150;

Empty set (0.00 sec)


这次将前面保留的id=152的数据也清除了。


3.10 清除表数据(无主键,无唯一键)


目前,我们有数据库test,其中有一张表t_my,结构如下:


CREATE TABLE `t_my` (

`id` int NOT NULL,

`nm` varchar(10) DEFAULT NULL,

`address` varchar(20) DEFAULT NULL

) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;


执行如下命令清除数据:

pt-archiver --source h=192.168.246.137,u=root,p=Ygzlpaul*2022,D=test,t=t_my --where 'ID<300' --purge --statistics


报告错误:

Cannot find an ascendable index in table at /usr/bin/pt-archiver line 3260.


从错误上看,好像是缺失了升序的索引,那么如果我们增加一个升序索引,是否就可以正常工作了呢?


如下增加一个普通索引:

ALTER TABLE `t_my` ADD INDEX `idx_nm` (`nm`) ;


再次清除数据:



执行成功,也成功删除了数据。


回过头来,我们查看一下


pt-archiver --source h=192.168.246.137,u=root,p=Ygzlpaul*2022,D=test,t=t_my --where 'ID<300' --purge --statistics --dry-run


显示如下:

SELECT /*!40001 SQL_NO_CACHE */ `id`,`nm`,`address` FROM `test`.`t_my` FORCE INDEX(`idx_nm`) WHERE (ID<300) ORDER BY `nm` LIMIT 1

SELECT /*!40001 SQL_NO_CACHE */ `id`,`nm`,`address` FROM `test`.`t_my` FORCE INDEX(`idx_nm`) WHERE (ID<300) AND (((? IS NULL OR `nm` >= ?))) ORDER BY `nm` LIMIT 1

DELETE FROM `test`.`t_my` WHERE (`id` = ? AND ((? IS NULL AND `nm` IS NULL) OR (`nm` = ?)) AND ((? IS NULL AND `address` IS NULL) OR (`address` = ?))) LIMIT 1


总结,归档的表最好有主键,可以提升归档性能,也支持仅有普通索引的情况。


注意:以上所用软件的各个版本为:

Mysql:8.0.31

pt-archiver: 3.2.1

展开阅读全文

页面更新:2024-04-23

标签:逗号   截图   语句   索引   事务   密码   数据库   文件   数据   用户

1 2 3 4 5

上滑加载更多 ↓
推荐阅读:
友情链接:
更多:

本站资料均由网友自行发布提供,仅用于学习交流。如有版权问题,请与我联系,QQ:4156828  

© CopyRight 2008-2024 All Rights Reserved. Powered By bs178.com 闽ICP备11008920号-3
闽公网安备35020302034844号

Top