关于pt-archiver和自增主键的那些事
来源:脚本之家
时间:2023-02-16 19:26:37 243浏览 收藏
来到golang学习网的大家,相信都是编程学习爱好者,希望在这里学习数据库相关编程知识。下面本篇文章就来带大家聊聊《关于pt-archiver和自增主键的那些事》,介绍一下pt-archiver自增、主键,希望对大家的知识积累有所帮助,助力实战开发!
本文Percona Blog 的译文,原文移步文章末尾的 阅读原文。
MySQL 中删除大表之前可以使用 pt-archiver 批量删除所有记录。这样助于避免在某些情况下您的服务器可能会意外的情况,比如磁盘 IO 满导致数据库hang或者影响正常 SQL 慢查。
笔者最近遇到一个案例 ,有客户反馈 "使用 pt-archiver 删除数据时,最后一行数据未被删除。这个是不是bug?"
分析
在解决客户的问题之前,我们需要解释为什么在删除大表之前使用 pt-archiver 当我们在 MySQL 中删除一个表时, MySQL 系统会做如下动作:
删除表数据/索引 (ibd) 和定义 (frm) 文件。
删除触发器。
通过删除要删除的表来更新表定义缓存。
扫描 InnoDB 缓冲池以查找关联页面以使其无效。--内存到的表会遇到系统hang。
需要注意的是,DROP 是一个 DDL 语句,它需要持有元数据锁 (MDL) 才能完成,这样会导致所有其他线程必须等待DDL完成,清除表相关的大量数据页会对缓冲池产生额外的压力。
最后,table_definition_cache 操作需要 LOCK_open mutex 来清理,这会导致所有其他线程等待直到删除完成。
为了降低此操作的严重性,我们可以使用 pt-archiver 通过批量的形式删除大量数据,从而显着降低表大小。一旦我们从大表中删除了记录,DROP 操作就会快速进行而不会对系统性能产生影响。
社区成员注意到此行为,在 pt-archiver 完成后,该表仍有一行待处理。
# Created table mysql> CREATE TABLE `tt1` ( `id` int(11) NOT NULL AUTO_INCREMENT, `a` char(5) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB # Poured random test data into it mysql> call populate('test','att1',10000,'N'); # Purged data using pt-archiver [root@centos_2 ~]# pt-archiver --source=h=localhost,D=test,t=tt1 --purge --where "1=1" # Verifying count (expected 0, got 1) mysql> select count(*) from test.tt1; +----------+ | count(*) | +----------+ | 1 | +----------+ 1 row in set (0.00 sec)
当我们使用带有 --no-delete 参数的 pt-archiver 进行数据归档时,也会发生同样的情况。我们的工具 pt-archiver 似乎没有将最大值复制到目标表。
将表从 tt1 迁移到 tt2 [root@centos_2 ~]# pt-archiver --source=h=localhost,D=test,t=tt1 --dest=h=localhost,D=test,t=tt2 --no-delete --where "1=1" mysql> select count(*) from tt2; +----------+ | count(*) | +----------+ | 5008 | +----------+ 1 row in set (0.00 sec) mysql> select count(*) from tt1; +----------+ | count(*) | +----------+ | 5009 | +----------+ 1 row in set (0.00 sec)
解析
通读 pt-archiver 文档,有一个选项 –[no]safe-auto-increment 描述了用法:“不要使用 max AUTO_INCREMENT 归档行。”
这意味着,选项 –safe-auto-increment(默认)添加了一个额外的 WHERE 子句,以防止 pt-archiver 在提升单列 AUTO_INCREMENT 时删除最新的行,如下面的代码部分所示:
https://github.com/percona/percona-toolkit/blob/3.x/bin/pt-archiver#L6449 if ( $o->get('safe-auto-increment') && $sel_stmt->{index} && scalar(@{$src->{info}->{keys}->{$sel_stmt->{index}}->{cols}}) == 1 && $src->{info}->{is_autoinc}->{ $src->{info}->{keys}->{$sel_stmt->{index}}->{cols}->[0] } ) { my $col = $q->quote($sel_stmt->{scols}->[0]); my ($val) = $dbh->selectrow_array("SELECT MAX($col) FROM $src->{db_tbl}"); $first_sql .= " AND ($col quote_val($val) . ")"; }
让我们通过空运行输出看看这两个命令之间的区别:
# With --no-safe-auto-increment [root@centos_2 ~]# pt-archiver --source=h=localhost,D=test,t=tt1 --dest=h=localhost,D=test,t=tt2 --no-delete --where "1=1" --no-safe-auto-increment --dry-run SELECT /*!40001 SQL_NO_CACHE */ `id`,`a` FROM `test`.`tt1` FORCE INDEX(`PRIMARY`) WHERE (1=1) ORDER BY `id` LIMIT 1 SELECT /*!40001 SQL_NO_CACHE */ `id`,`a` FROM `test`.`tt1` FORCE INDEX(`PRIMARY`) WHERE (1=1) AND ((`id` > ?)) ORDER BY `id` LIMIT 1 INSERT INTO `test`.`tt2`(`id`,`a`) VALUES (?,?)
# Without --no-safe-auto-increment (default) [root@centos_2 ~]# pt-archiver --source=h=localhost,D=test,t=tt1 --dest=h=localhost,D=test,t=tt2 --no-delete --where "1=1" --dry-run SELECT /*!40001 SQL_NO_CACHE */ `id`,`a` FROM `test`.`tt1` FORCE INDEX(`PRIMARY`) WHERE (1=1) AND (`id` ORDER BY `id` LIMIT 1 SELECT /*!40001 SQL_NO_CACHE */ `id`,`a` FROM `test`.`tt1` FORCE INDEX(`PRIMARY`) WHERE (1=1) AND (`id` AND ((`id` > ?)) ORDER BY `id` LIMIT 1 INSERT INTO `test`.`tt2`(`id`,`a`) VALUES (?,?)
注意到上面的附加子句 "AND ( id
如果服务器重新启动,–no-safe-auto-increment 的这个选项可以防止重新使用 AUTO_INCREMENT 值。请注意,额外的 WHERE 子句包含自归档或清除作业开始时自增列的最大值。如果在 pt-archiver 运行时插入新行,pt-archiver 将看不到它们。
好吧,现在我们知道了为什么没有删除干净的“原因”,但为什么呢?AUTO_INCREMENT 的安全问题是什么?
AUTO_INCREMENT 计数器存储在内存中,当 MySQL 8.0之前的版本 重新启动(崩溃或其他)时,计数器将重置为最大值。如果发生这种情况并且表正在接受写入,则 AUTO_INCREMENT 值将更改。
# deleting everything from table mysql> delete from tt1; ... mysql> show table status like 'tt1'\G *************************** 1. row *************************** Name: tt1 Engine: InnoDB ... Auto_increment: 10019 ... # Restarting MySQL [root@centos_2 ~]# systemctl restart mysql # Verifying auto-increment counter [root@centos_2 ~]# mysql test -e "show table status like 'tt1'\G" *************************** 1. row *************************** Name: tt1 Engine: InnoDB ... Auto_increment: 1 ...
上面的测试结果告诉我们: 这里的问题实际上并不在于 pt-archiver,而在于参数选项。在处理 AUTO_INCREMENT 列时使用 pt-archiver 时,了解使用 –no-safe-auto-increment 选项很重要。
让我们用我们的实验室数据来验证它。
# Verifying the usage of –no-safe-auto-increment option [root@centos_2 ~]# pt-archiver --source=h=localhost,D=test,t=tt1 --purge --where "1=1" --no-safe-auto-increment mysql> select count(*) from test.tt1; +----------+ | count(*) | +----------+ | 0 | +----------+ 1 row in set (0.00 sec)
使用 –no-delete 选项的复制操作也是如此。
[root@centos_2 ~]# pt-archiver --source=h=localhost,D=test,t=tt1 --dest=h=localhost,D=test,t=tt2 --no-delete --where "1=1" --no-safe-auto-increment mysql> select count(*) from tt1; select count(*) from tt2; +----------+ | count(*) | +----------+ | 5009 | +----------+ 1 row in set (0.00 sec) +----------+ | count(*) | +----------+ | 5009 | +----------+ 1 row in set (0.00 sec)
通过上面的代码和实际测试,我们知道了 pt-archiver 的 -[no]safe-auto-increment 选项的原理和作用 。在我们得出一切都很好的结论之前,让我们多考虑一下选项本身存在的意义。
默认情况下,–no-delete 操作应包含 –no-safe-auto-increment 选项。目前,safe-auto-increment 是默认行为。当我们使用 pt-archiver 的 --no-delete 选项时,没有删除操作。这意味着 safe-auto-increment 不应成为关注的原因。
对于 MySQL 8.0,不需要 safe-auto-increment 选项。因为 MySQL 8.0 开始,自增的值是持久化的,并且在实例重新启动或崩溃后自增的最大值不变。参考:MySQL 工作日志 https://dev.mysql.com/worklog/task/?id=6204
而且由于 MySQL 8.0 auto-increment 是通过重做日志持久化的,这使得它们成为pt-archiver 不关心的一个原因。因此,我们根本不需要 safe-auto-increment 选项。
结论
pt-archiver 是归档 MySQL 数据的好工具,重要的是要了解所有选项以完全控制我们想要使用它实现的目标。
以后需要根据自增id进行归档的场景,pt-archiver 默认最大的id不会进行归档,需要添加参数:--no-safe-auto-increment 才能对最大id进行处理。
理论要掌握,实操不能落!以上关于《关于pt-archiver和自增主键的那些事》的详细介绍,大家都掌握了吧!如果想要继续提升自己的能力,那么就来关注golang学习网公众号吧!
-
217 收藏
-
103 收藏
-
469 收藏
-
134 收藏
-
234 收藏
-
165 收藏
-
397 收藏
-
489 收藏
-
209 收藏
-
497 收藏
-
335 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 立即学习 542次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 立即学习 507次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 立即学习 497次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 立即学习 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 立即学习 484次学习