pt-archiver 归档慢问题分析

Posted 渔夫数据库笔记

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了pt-archiver 归档慢问题分析相关的知识,希望对你有一定的参考价值。

1.问题发现

最近在给一些大表配置了归档任务后,发现一些表的归档比其他表慢很多,下面具体分析下原因。

2.问题原因

通过监控数据库中归档相关连接,发现归档程序在源库中执行的select 操作耗时很长,相关sql类似如下

在使用pt-archiver 进行归档时,如果我们不指定i参数(归档时使用该参数指定索引进行select查询),默认情况下 pt-archiver 在源端 select 和 delete时会用主键索引进行升序查询(下一次查询根据上次查询的主键最大值,作为本次范围查询的最小值)。如果表的主键是多列组成的复合主键,那么会导致where 条件过于复杂,影响语句执行效率。这时我们可以指定 --ascend-first 指定指使用复合索引最左前缀。

通常情况下使用主键索引来归档是没有问题的,比如上面的例子中归档999天之前的数据(id_times是时间字段),如果我们用的是自增主键,那么通常主键较小的记录,时间值也会较小,对于这样的表。通过主键归档通常不会有问题。(因为最小主键时间值也最小,所以扫描很少记录就能满足要求)但是如果表中的数据并不像我们想的这样,比如主键和时间字段没有成正比关系。那么根据主键去过滤满足小于某个时间条件的N条记录可能就需要扫描太多行,导致归档效率低下。

还是以我们上面的表为例子,看看表中的数据情况

这样的数据分布情况就导致我们根据主键索引归档效率极低。

3.问题处理

   那么怎么解决该问题呢?可以给id_times 字段加上索引,pt-archiver 归档时指定使用 id_times 字段索引。 这时源库select 和 delete 时都会使用 id_times 字段上的索引,效率就会高很多。

pt-archiver --source A=utf8mb4,h=源主机地址,P=3306,u=data_archiver_rw,p='xxxxx',D=test_db,t=test_table,i=idx_create_time --dest h=目的主机地址,P=3306,u=data_archiver_rw,p='xxxxx',D=test_db,t=test_table  --charset=utf8 --where 'create_time < DATE_SUB(CURDATE(),INTERVAL 15 DAY)' --progress 100000 --limit=1000 --txn-size 1000 --bulk-insert --bulk-delete --statistics --no-check-charset

这时源库的select和delete 类似如下:

#这里看到where 条件中时间字段指定了两次小于 DATE_SUB(),这个是因为在pt-archiver的where 条件中指定了两次(同事想通过这个来控制每次删除多久的数据)

SELECT /*!40001 SQL_NO_CACHE */ `p_key`,`id_cars`,`id_times`,`id_orders`,`face_counters`,`car_state`,`photo_path`,`id_city`,`min_score`,`avg_score` FROM `test_db`.`test_table` FORCE INDEX(`time`) WHERE (id_times < DATE_SUB(CURDATE(),INTERVAL 552 DAY) and id_times < DATE_SUB(CURDATE(),INTERVAL 550 DAY)) AND ((('2020-11-12 23:59:59' IS NULL OR `id_times` >= '2020-11-12 23:59:59'))) ORDER BY `id_times` LIMIT 1000;
#这里跟使用主键索引一样,本次访问的1000条记录中,最大的时间值,会作为下次最小的时间值,尽量减少不必要的扫描

DELETE FROM `test_db`.`test_table` WHERE (((('2020-11-12 23:59:53' IS NULL OR `id_times` >= '2020-11-12 23:59:53')))) AND (((('2020-11-12 23:59:59' IS NULL OR `id_times` <= '2020-11-12 23:59:59')))) AND (id_times < DATE_SUB(CURDATE(),INTERVAL 552 DAY) and id_times < DATE_SUB(CURDATE(),INTERVAL 550 DAY)) LIMIT 1000



默认情况下使用主键升序查询,源库select 和 delete如下(使用主键升序查询,还会有select max(id) 查询)

SELECT /*!40001 SQL_NO_CACHE */ `id`,`uid`,`city_code`,`awake_time`,`update_time` FROM `test_db`.`test_table` FORCE INDEX(`PRIMARY`) WHERE (awake_time < DATE_SUB(CURDATE(),INTERVAL 61 DAY) and awake_time < DATE_SUB(CURDATE(),INTERVAL 61 DAY)) AND (`id` < '1420865800') AND ((`id` >= '1208412567')) ORDER BY `id` LIMIT 1000

DELETE FROM `test_db`.`test_table` WHERE (((`id` >= '1208411550'))) AND (((`id` <= '1208412567'))) AND (awake_time < DATE_SUB(CURDATE(),INTERVAL 61 DAY) and awake_time < DATE_SUB(CURDATE(),INTERVAL 61 DAY)) LIMIT 1000

以上是关于pt-archiver 归档慢问题分析的主要内容,如果未能解决你的问题,请参考以下文章

pt-archiver 归档数据

pt-archiver数据归档

如何对MySQL中的大表进行数据归档

pt-archiver归档工具的使用详解

pt-archiver归档数据 源库和目标库是否会出现不一致

percona-toolkit 之 pt-archiver