问题简介
pt-osc版本
ashe@macos:/data/PerconaToolkit (master) $ ./pt-online-schema-change --version
pt-online-schema-change 2.2.20
在使用pt-osc进行在线表结构变更时,有时会报表不存在的错误,然而进入通过show create table发现表是存在的。
问题排查
pt-osc命令如下
ashe@ubuntu:~$ pt-online-schema-change --alter="add name_1 varchar(10)" \
--host=127.0.0.1 \
--user=ashe \
--port=13307 \
--password=ashe \
--charset='utf8' \
--check-interval 5 \
--max-lag 2 \
--drop-old-table \
--check-replication-filters \
--chunk-time 1 \
--critical-load='Threads_connected=1300,Threads_running=256' \
--max-load='Threads_connected=1500,Threads_running=256' \
--set-vars='lock_wait_timeout=10' \
--tries create_triggers:1:1,drop_triggers:3:1,swap_tables:3:1 \
--progress=time,5 \
--no-check-alter --execute --print D=ashe,t=ASHE
执行报错如下
ashe@ubuntu:~$ pt-online-schema-change --alter="add name_1 varchar(10)" \
> --host=127.0.0.1 \
> --user=ashe \
> --port=13307 \
> --password=ashe \
> --charset='utf8' \
> --check-interval 5 \
> --max-lag 2 \
> --drop-old-table \
> --check-replication-filters \
> --chunk-time 1 \
> --critical-load='Threads_connected=1300,Threads_running=256' \
> --max-load='Threads_connected=1500,Threads_running=256' \
> --set-vars='lock_wait_timeout=10' \
> --tries create_triggers:1:1,drop_triggers:3:1,swap_tables:3:1 \
> --progress=time,5 \
> --no-check-alter --execute --print D=ashe,t=ASHE
No slaves found. See --recursion-method if host ubuntu has slaves.
Not checking slave lag because no slaves were found and --check-slave-lag was not specified.
The original table `ashe`.`ASHE` does not exist.
通过数据库show create table
mysql> show create table ashe.ASHE\G
*************************** 1. row ***************************
Table: ASHE
Create Table: CREATE TABLE `ashe` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(10) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=266222 DEFAULT CHARSET=utf8mb4
1 row in set (0.00 sec)
可以看出数据库中确实存在此表。问题的关键在于pt-osc的命令中使用的是大写的表名,按理说,数据库设置的参数
lower_case_table_names=1
即便使用大写,也是没有问题的,因为数据库自动忽略大小写了。将pt-osc的命令中改成小写可以解决这个问题。但是并有没有就此停止,看了下pt-osc的perl脚本源码,找到了问题的根本所在,如下
根本原因
pt-osc在函数check_table中,通过show tables from ashe like ‘ASHE’;找到对应的目标表,
mysql> show tables from ashe like 'ASHE';
+-----------------------+
| Tables_in_ashe (ASHE) |
+-----------------------+
| ashe |
+-----------------------+
1 row in set (0.00 sec)
此时获取到的表名为小写的,紧接着用此表名对比命令行输入的大写ASHE,
if ( !$row->[0] || $row->[0] ne $tbl ) {
PTDEBUG && _d('Table does not exist');
return 0;
}
$row->[0] ne $tbl
为区分大小写的对比,此时报错。但其实表确实存在,并且用大写访问是没有问题的。
由于目前线上环境的SQL发布已经自动化,DBA不会人工进行审核,在RD看来,数据库不区分大小写,所以提交的上线单中使用大写是没有问题的,但是却报表不存在,难以接受。
问题修复
考虑两种情况,一种是数据库区分大小写,一种是不区分大小写。
首先获取参数设置情况
my $sql_check_lower_case_table_name = "SELECT \@\@lower_case_table_names";
PTDEBUG && _d($sql_check_lower_case_table_name);
my $row_lower_case = $dbh->selectrow_arrayref($sql_check_lower_case_table_name);
lower_case_table_names=0,区分大小写
在此时,依然按照pt-osc之前的方式,进行数据库表名的判断
if ( $row_lower_case->[0] eq '0' ) {
if ( !$row->[0] || $row->[0] ne $tbl ) {
PTDEBUG && _d('Table does not exist');
return 0;
}
}
lower_case_table_names=1,不区分大小写
在数据库不区分大小写时,字符串对比时也要忽略大小写
if( $row_lower_case->[0] eq '1' ) {
if ( !$row->[0] || lc($row->[0]) ne lc($tbl) ) {
PTDEBUG && _d('Table does not exist');
return 0;
}
}
Patch file
对于不同的版本,可以酌情修改。
ashe@macos:/data/PerconaToolkit (master) $ git diff 7c23a808d9555c3e62c42e76503734f463e86fea 53c10fd3d5f4e61a3624b39bdfa22068e7fda30e
diff --git a/pt-online-schema-change b/pt-online-schema-change
index 5af900e..abc83f0 100755
--- a/pt-online-schema-change
+++ b/pt-online-schema-change
@@ -3386,10 +3386,23 @@ sub check_table {
$self->{check_table_error} = $e;
return 0;
}
- if ( !$row->[0] || $row->[0] ne $tbl ) {
- PTDEBUG && _d('Table does not exist');
- return 0;
- }
+ my $sql_check_lower_case_table_name = "SELECT \@\@lower_case_table_names";
+ PTDEBUG && _d($sql_check_lower_case_table_name);
+
+ my $row_lower_case = $dbh->selectrow_arrayref($sql_check_lower_case_table_name);
+ if ( $row_lower_case->[0] eq '0' ) {
+ if ( !$row->[0] || $row->[0] ne $tbl ) {
+ PTDEBUG && _d('Table does not exist');
+ return 0;
+ }
+ }
+
+ if( $row_lower_case->[0] eq '1' ) {
+ if ( !$row->[0] || lc($row->[0]) ne lc($tbl) ) {
+ PTDEBUG && _d('Table does not exist');
+ return 0;
+ }
+ }
PTDEBUG && _d('Table', $db, $tbl, 'exists');
return 1;