场景:今天OP反馈使用DTS将zabbix同步到新实例,原先旧实例执行存储过程call partition_maintenance_all('zabbix')
成功,迁移到新实例后,执行报错,提示如下:
ALTER command denied to user 'db_arch_w'@'x.x.x.x' for table 'history'
复现
# 旧实例,执行成功
mysql -h [旧实例地址] -u db_arch_w -p zabbix -e "call partition_maintenance_all('zabbix');"
# 新实例,执行报错,提示`ALTER command denied`
mysql -h [新实例地址] -u db_arch_w -p zabbix -e "call partition_maintenance_all('zabbix');"
排查
1. 权限
提示没权限,首先比对下新旧实例db_arch_w的权限,竟然都一样,如下,且没有zabbix库的ALTER权限!
show grants for 'db_arch_w'@'%';
+---------------------------------------------------------------------------------------------------------------------------------+
| Grants for db_arch_w@% |
+---------------------------------------------------------------------------------------------------------------------------------+
| GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, REPLICATION SLAVE, REPLICATION CLIENT ON *.* TO `db_arch_w`@`%` WITH GRANT OPTION |
| GRANT SHOW_ROUTINE ON *.* TO `db_arch_w`@`%` WITH GRANT OPTION |
| GRANT EXECUTE ON `zabbix`.* TO `db_arch_w`@`%` |
+---------------------------------------------------------------------------------------------------------------------------------+
确实没有ALTER
权限,但旧实例为什么能执行?
2. 手动执行
看了下报错的位置,手动执行,执行SQL如下
# 新实例,alter报错,提示ALTER command denied
mysql -h [新实例地址] -u db_arch_w -p zabbix -e "alter table zabbix.history add partition (partition p202403260000 values less than(1711468800))"
# 旧实例,一样报错
mysql -h [旧实例地址] -u db_arch_w -p zabbix -e "alter table zabbix.history add partition (partition p202403260000 values less than(1711468800))"
这个时候新旧报错又是一致的,这是为什么呢?
3. 比对存储过程
其实在第2步的时候,就可以确定是存储过程有问题。
# 旧实例存储过程
CREATE DEFINER=`admin`@`%` PROCEDURE `partition_maintenance_all`(
IN `SCHEMA_NAME` varchar(32) charset utf8mb4 collate utf8mb4_0900_ai_ci )
BEGIN
# xxx 具体内容,忽略
END
# 新实例存储过程
CREATE DEFINER=`admin`@`%` PROCEDURE `partition_maintenance_all`(
IN `SCHEMA_NAME` varchar(32) charset utf8mb4 collate utf8mb4_0900_ai_ci )
SQL SECURITY INVOKER
BEGIN
# xxx 具体内容,忽略
END
有个细微区别,新实例存储过程中含有SQL SECURITY INVOKER
,其含义为执行权限使用调用者账号所属权限。例如我们一开始用db_arch_w
账号连接,则使用db_arch_w
的权限去执行,而存储过程中含有ALTER命令,db_arch_w没有权限就报错;而旧实例没有指定SQL SECURITY,则默认使用DEFINER定义者的权限,此处为admin账号权限(超管),因此执行不会报错。
SQL SECURITY:有两个值DEFINER和INVOKER,没有指定则默认DEFINER。
DEFINER:使用定义者权限;INVOKER:使用调用者权限。
结论
DTS同步时,存储过程会自动添加SQL SECURITY INVOKER
,导致调用时使用的是调用者的权限,当存储过程中含有调用者没有的权限时就会报错。因此仅需删除所有存储过程中SQL SECURITY INVOKER
即可执行。
思考:为什么DTS要指定INVOKER呢?可能是考虑同步的目标实例没有定义的用户,会导致无法执行。
Comments