当前位置: 首页 > article >正文

TiCDC 同步 SQL_MODE 相关

作者: Brian 原文来源: https://tidb.net/blog/91f38d0b

问题澄清

下游 users表中username列默认为非空,所以ticdc应该会同步报错。但是为何ticdc并没有同步报错,user_id = 2同步成功,userame的数据还和上游不一样?

问题背景

集群版本:v6.5.3

工具版本:v6.5.3

上游表结构:username 列为 default null

mysql> show create table users;
+-------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Table | Create Table                                                                                                                                                                                                         |
+-------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| users | CREATE TABLE `users` (
  `user_id` int(11) NOT NULL,
  `username` varchar(50) DEFAULT NULL,
  PRIMARY KEY (`user_id`) /*T![clustered_index] CLUSTERED */
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin |
+-------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)

下游表结构:username 列为 not null

mysql> show create table users;
+-------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Table | Create Table                                                                                                                                                                                                     |
+-------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| users | CREATE TABLE `users` (
  `user_id` int(11) NOT NULL,
  `username` varchar(50) NOT NULL,
  PRIMARY KEY (`user_id`) /*T![clustered_index] CLUSTERED */
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin |
+-------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)

上游执行语句:

mysql> insert into users (user_id ) values (2); 
Query OK, 1 row affected (0.00 sec)

mysql> select * from users;
+---------+----------+
| user_id | username |
+---------+----------+
|       2 | NULL     |
+---------+----------+
1 row in set (0.00 sec)

下游表数据:

mysql> select * from users;
+---------+----------+
| user_id | username |
+---------+----------+
|       2 |          |
+---------+----------+
1 row in set (0.00 sec)

问题分析

1.dashboard--SQL statement

上游执行

insert into users (user_id ) values (2);

查看下游实际执行的SQL为:

INSERT INTO `work`.`users` (`user_id`, `username`) VALUES(2, NULL) 

2.sql mode查看

当前SQL mode 上下游相同,均为默认SQL MODE

mysql> show variables like 'sql_mode';
+---------------+-------------------------------------------------------------------------------------------------------------------------------------------+
| Variable_name | Value                                                                                                                                     |
+---------------+-------------------------------------------------------------------------------------------------------------------------------------------+
| sql_mode      | ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION |
+---------------+-------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)

SQL_MODE 中包含strict_trans_tables按理说不应该出现这种违反约束的问题

3.分析源码

ticdc同步会临时改变SQL MODE如下代码:

会将STRICT_TRANS_TABLES,STRICT_ALL_TABLES disable掉。

func AdjustSQLModeCompatible(sqlModes string) (string, error) {
    needDisable := []string{
       "NO_ZERO_IN_DATE",
       "NO_ZERO_DATE",
       "ERROR_FOR_DIVISION_BY_ZERO",
       "NO_AUTO_CREATE_USER",
       "STRICT_TRANS_TABLES",
       "STRICT_ALL_TABLES",
    }
    needEnable := []string{
       "IGNORE_SPACE",
       "NO_AUTO_VALUE_ON_ZERO",
       "ALLOW_INVALID_DATES",
    }
    disable := strings.Join(needDisable, ",")
    enable := strings.Join(needEnable, ",")

    mode, err := tmysql.GetSQLMode(sqlModes)
    if err != nil {
       return sqlModes, err
    }
    disableMode, err2 := tmysql.GetSQLMode(disable)
    if err2 != nil {
       return sqlModes, err2
    }
    enableMode, err3 := tmysql.GetSQLMode(enable)
    if err3 != nil {
       return sqlModes, err3
    }
    // About this bit manipulation, details can be seen
    // https://github.com/pingcap/dm/pull/1869#discussion_r669771966
    mode = (mode &^ disableMode) | enableMode

    return GetSQLModeStrBySQLMode(mode), nil
}

4.tidb将严格模式禁用后

如下:

#tidb数据库中默认的SQL mode 
mysql> show variables like '%sql_mode%';
+---------------+-------------------------------------------------------------------------------------------------------------------------------------------+
| Variable_name | Value                                                                                                                                     |
+---------------+-------------------------------------------------------------------------------------------------------------------------------------------+
| sql_mode      | ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION |
+---------------+-------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)


#表结构中,username有约数据not null
mysql> show create table users;
+-------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Table | Create Table                                                                                                                                                                                                     |
+-------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| users | CREATE TABLE `users` (
  `user_id` int(11) NOT NULL,
  `username` varchar(50) NOT NULL,
  PRIMARY KEY (`user_id`) /*T![clustered_index] CLUSTERED */
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin |
+-------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)


#临时手动将默认有的STRICT_TRANS_TABLES禁用
mysql> set session sql_mode ="ONLY_FULL_GROUP_BY,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION";
Query OK, 0 rows affected (0.00 sec)


#username列插入null,成功插入伴有warning
mysql> insert into users values(4,null);
Query OK, 1 row affected, 1 warning (0.00 sec)
mysql> show warnings;
+---------+------+----------------------------------+
| Level   | Code | Message                          |
+---------+------+----------------------------------+
| Warning | 1048 | Column 'username' cannot be null |
+---------+------+----------------------------------+
1 row in set (0.00 sec)

根因总结

上下游表结构不完全不一致,上游username列default null,下游username列not null。

Ticdc v6.5.3同步是会临时将STRICT_TRANS_TABLES,STRICT_ALL_TABLES禁用,这样即使下游表列约束为not null,也可以成功插入null值,但是会伴随着对应warning

问题后续

https://github.com/pingcap/tiflow/pull/10644/files 第一次发版是 v8.0.0

上述 PR 合并之前,首先查询下游 SQL 模式,然后进行配置。该 PR 合并之后,不再查询下游,直接基于 TiDB 的默认 SQL 模式进行配置。使用配置后的 SQL 模式创建到下游系统的连接。

备注

关于此 case 的解决在此要特别感谢产研jinling 老师!


http://www.kler.cn/news/366944.html

相关文章:

  • Python | Leetcode Python题解之第509题斐波那契数
  • 【C++】抱C++中的函数式编程:使用`std::function`和Lambda表达式简化代码
  • 【Linux】线程池详解及其基本架构与单例模式实现
  • 校园表白墙源码修复版
  • 移除Microsoft Edge浏览器“由你的组织管理“提示的方法
  • supermall项目上拉加载bug分析
  • 基于SSM的BBS社区论坛系统源码
  • Linux环境下Jmeter执行压测脚本
  • 关注 dlopen(handle, mode) 中的 mode,dlsym dlclose示例
  • 学习笔记:黑马程序员JavaWeb开发教程(2024.10.26)
  • 【纯血鸿蒙】鸿蒙专项测试
  • 前端工程化面试题
  • Python | Leetcode Python题解之第508题出现次数最多的子树元素和
  • Linux下升级安装ImageMagick
  • 【rabbitmq】实现问答消息消费示例
  • qml圆形图片,qml圆形头像制作
  • STM32 HAL 点灯
  • 【K8S系列】Kubernetes Pod节点CrashLoopBackOff 状态及解决方案详解【已解决】
  • github上传文件代码以及其它github代码
  • AppleVisionPro空间定位 三维空间重现-Unity3D
  • iOS调试真机出现的 “__llvm_profile_initialize“ 错误
  • 数据结构-队列
  • Vast.ai LLM 大语言模型使用手册(2)
  • 74. 搜索二维矩阵
  • 了解 - 微格式
  • 萤石设备视频接入平台EasyCVR私有化视频平台变电站如何实现远程集中监控?