MySQL · 捉虫动态 · 并行复制外键约束问题二

简介: 背景 并行复制可以大大提高备库的 binlog 应用速度,内核月报也多次对并行复制特性进行介绍,感兴趣的朋友可以回顾下:5.6 并行复制实现分析、5.6 并行复制恢复实现 和 5.6并行复制事件分发机制。 在早期的内核月报,有一篇 并行复制外建约束问题,介绍阿里在 5.5 版本中自己实现并行复制

背景

并行复制可以大大提高备库的 binlog 应用速度,内核月报也多次对并行复制特性进行介绍,感兴趣的朋友可以回顾下:5.6 并行复制实现分析5.6 并行复制恢复实现 和 5.6并行复制事件分发机制

在早期的内核月报,有一篇 并行复制外建约束问题,介绍阿里在 5.5 版本中自己实现并行复制时遇到的外键约束问题,本文接着前作继续介绍并行复制外键约束问题,这次场景不一样,并且目前官方 5.6 最新版本(5.6.30)中也有这个问题。

问题描述

一般情况的复制是 A->B 这样一主一备,本文要描述的场景是 A->B->C 这样一主两备,并且备库级联,其中备库 C 开启了并行复制,B 可以串行也可以并行,binlog_fomat 都是 row。

在主库A上执行如下语句:

CREATE DATABASE db1;
CREATE DATABASE db2;
USE db1;
CREATE TABLE `parent` (
`id` int(11) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB;

USE db2;
CREATE TABLE `child` (
`id` int(11) DEFAULT NULL,
`parent_id` int(11) DEFAULT NULL,
KEY `par_ind` (`parent_id`),
CONSTRAINT `child_ibfk_1` FOREIGN KEY (`parent_id`) REFERENCES `db1`.`parent` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB;

INSERT INTO db1.parent VALUES(1);
INSERT INTO db2.child VALUES(1, 1);

备库 C 上会报错如下,非常明显的一个外键约束的错误:

Last_SQL_Errno: 1452
Last_SQL_Error: Worker 7 failed executing transaction '' at master log mysqld-bin.000001, end_log_pos 1008; Could not execute Write_rows event on table db2.child; Cannot add or update a child row: a foreign key constraint fails (`db2`.`child`, CONSTRAINT `child_ibfk_1` FOREIGN KEY (`parent_id`) REFERENCES `db1`.`parent` (`id`) ON DELETE CASCADE), Error_code: 1452; handler error HA_ERR_NO_REFERENCED_ROW; the event's master log mysqld-bin.000001, end_log_pos 1008

问题分析

如前文并行复制外建约束问题 所述,5.6 并行复制已经解了外键问题,遇到被外键约束的表,会先切为串行,当前事务执行完成后,再开始并行,为什么还会出问题呢?分析这个问题前,我们先来看下,5.6 是怎么解决外键约束问题的。

5.6 并行复制是基于db进行分发的,不同的db分发到不同的 worker 线程,对 row 格式的 binlog,分发信息是体现在 table_map event 中的。5.6 对 table_map 中加了一个专门的 flag TM_REFERRED_FK_DB_F,表示当前表被外键约束(具体参考commit 299ccba1e145c29ed3c242c152ced4cc345328b7),这样备库分发线程(Coordinator)在遇到有这种标志的 table_map,就切换为串行,具体逻辑参考Log_event::get_slave_worker() 和apply_event_and_update_pos()

这个机制是没问题的,如果 flag 能从 A 传到 B 再传到 C,就不会出现这个问题,现在问题的出现是因为备库 B 执行完父表(parent)的更新后,写 binlog 时 flag 没写进去,导致 C 在并行模式下执行 parent 表更新时,没有切换到串行模式,和 child 表的更新同时在跑,如果执行 child 表更新的 worker 先做,那么就会出现外键约束报错。

问题解决

TM_REFERRED_FK_DB_F 这个 flag 是在 Table_map_log_event::Table_map_log_event() 构造函数中设置的,逻辑如下:

/*
Marking event to require sequential execution in MTS
if the query might have updated FK-referenced db.
Unlike Query_log_event where this fact is encoded through
the accessed db list in the Table_map case m_flags is exploited.
*/
uchar dbs= thd->get_binlog_accessed_db_names() ?
thd->get_binlog_accessed_db_names()->elements : 0;
if (dbs == 1)
{
  char *db_name= thd->get_binlog_accessed_db_names()->head();
  if (!strcmp(db_name, ""))
  m_flags |= TM_REFERRED_FK_DB_F;
}

如果当前访问到的 db 个数为1,并且 db 是空字符串 "" 的话,就设置这个 flag。binlog_accessed_db_names 中只有 "" 这一个元素是一个特殊构造的场景,正常情况下db不会是 ""的,构造这样 db 的逻辑在 THD::decide_logging_format,如下:

if (is_write &&
    lex->sql_command != SQLCOM_END /* rows-event applying by slave */)
{
  /*
    Master side of DML in the STMT format events parallelization.
    All involving table db:s are stored in a abc-ordered name list.
    In case the number of databases exceeds MAX_DBS_IN_EVENT_MTS maximum
    the list gathering breaks since it won't be sent to the slave.
  */
  for (TABLE_LIST *table= tables; table; table= table->next_global)
  {
    if (table->placeholder())
      continue;

    DBUG_ASSERT(table->table);

    if (table->table->file->referenced_by_foreign_key())
    {
      /*
         FK-referenced dbs can't be gathered currently. The following
         event will be marked for sequential execution on slave.
      */
      binlog_accessed_db_names= NULL;
      add_to_binlog_accessed_dbs("");
      break;
    }
    if (!is_current_stmt_binlog_format_row())
      add_to_binlog_accessed_dbs(table->db);
  }
}

可以看到,如果有当前表被外键约束的话(table->table->file->referenced_by_foreign_key()),会清掉binlog_accessed_db_names,只放一个空字符串进去。

但是 SQL 线程在应用 row_event 时,不会走到上面的逻辑,因为 lex->sql_command 的值为 SQLCOM_END,所以备库 B 生成的 parent 表的 table_map 就不包含这个 flag。

修复也比较简单,把 lex->sql_command != SQLCOM_END 这个条件去掉即可,或者参考官方 bug 这里提供的修复方法,也是可以的。

相关实践学习
每个IT人都想学的“Web应用上云经典架构”实战
本实验从Web应用上云这个最基本的、最普遍的需求出发,帮助IT从业者们通过“阿里云Web应用上云解决方案”,了解一个企业级Web应用上云的常见架构,了解如何构建一个高可用、可扩展的企业级应用架构。
MySQL数据库入门学习
本课程通过最流行的开源数据库MySQL带你了解数据库的世界。   相关的阿里云产品:云数据库RDS MySQL 版 阿里云关系型数据库RDS(Relational Database Service)是一种稳定可靠、可弹性伸缩的在线数据库服务,提供容灾、备份、恢复、迁移等方面的全套解决方案,彻底解决数据库运维的烦恼。 了解产品详情: https://www.aliyun.com/product/rds/mysql 
目录
相关文章
|
4月前
|
存储 安全 关系型数据库
MySQL数据库约束条件概述及其重要性讨论。
正确地实现并管理好各类紧缩条件将直接影响到企业信息管理水平与服务质量,在当今大数据背景下更显得格外重要;任何设计师都需要深刻理解其原理与运作机晰承担起责任使得所托管资料安全稳固同时又具备良好伸缩灵活度迎合日益复杂商务需求变动.
185 11
|
NoSQL 关系型数据库 MySQL
2024Mysql And Redis基础与进阶操作系列(4-2)作者——LJS[含MySQL非空、唯一性、PRIMARY KEY、自增列/自增约束举例说明等详解步骤及常见报错问题对应的解决方法]
24MySQL非空、唯一性、PRIMARY KEY、自增列/自增约束举例说明等详解步骤及常见报错问题对应的解决方法(4-2) 学不会你来砍我!!!
|
12月前
|
存储 SQL 关系型数据库
【MySQL基础篇】MySQL约束语法
文章介绍了MySQL中表的约束概念,包括非空、唯一、主键、默认和外键约束,以及如何在创建和修改表时指定这些约束。外键约束用于保持数据的一致性和完整性,文章通过示例展示了添加、删除外键的语法,并讨论了不同的删除/更新行为,如CASCADE和SETNULL。
【MySQL基础篇】MySQL约束语法
|
关系型数据库 MySQL 数据处理
MySQL函数与约束
MySQL 提供了丰富的函数和强大的约束机制,用于数据处理和完整性维护。通过掌握这些工具,可以有效地管理和分析数据库中的数据,确保数据的一致性和准确性。无论是在日常数据查询中使用内置函数,还是在数据库设计中应用各种约束,都是确保数据库系统稳定、高效运行的关键。希望本文对您理解和应用 MySQL 函数与约束有所帮助。
184 1
|
12月前
|
存储 关系型数据库 MySQL
MySQL(条件约束)
为了校验数据,让数据的正确性能够得到保证,约束,能够引进更多的检查操作,但是也会增加系统的成本开销
|
Ubuntu 关系型数据库 MySQL
ubuntu使用aliyun源+mysql删除有外键约束的数据+查看特定目录的大小
ubuntu使用aliyun源+mysql删除有外键约束的数据+查看特定目录的大小
182 4
|
SQL 关系型数据库 MySQL
MySQL中外键的使用及外键约束策略
这篇文章讨论了MySQL中使用外键的重要性,包括外键的概念、不使用外键可能导致的问题、如何设置外键约束以及不同的外键约束策略(如CASCADE和SET NULL),并通过示例演示了这些概念。
MySQL中外键的使用及外键约束策略
|
存储 关系型数据库 MySQL
MySQL数据库基础:约束
约束是对数据库表中字段施加的规则,确保数据的正确性、有效性和完整性。主要分为非空约束、唯一约束、默认约束、主键约束和外键约束。非空约束禁止字段值为null;唯一约束确保字段值唯一,允许null值重复;默认约束设定默认值;主键约束结合非空与唯一约束,并可设为自增型;外键约束则通过关联其他表的主键,保证数据一致性。检查约束确保字段值满足特定条件。
269 1
|
数据采集 关系型数据库 MySQL
在 MySQL 中使用约束
【8月更文挑战第11天】
333 0
在 MySQL 中使用约束
|
SQL 关系型数据库 MySQL
实时计算 Flink版产品使用问题之如何进行MySQL到MySQL的动态同步
实时计算Flink版作为一种强大的流处理和批处理统一的计算框架,广泛应用于各种需要实时数据处理和分析的场景。实时计算Flink版通常结合SQL接口、DataStream API、以及与上下游数据源和存储系统的丰富连接器,提供了一套全面的解决方案,以应对各种实时计算需求。其低延迟、高吞吐、容错性强的特点,使其成为众多企业和组织实时数据处理首选的技术平台。以下是实时计算Flink版的一些典型使用合集。

相关产品

  • 云数据库 RDS MySQL 版
  • 推荐镜像

    更多