InnoDB foreign key 实现

本文是对Innodb外键实现代码路径的简单记录,对外键实现逻辑熟悉的同学直接忽略吧。。。。。

前言

外键代表两张表之间的引用约束关系:在子表上出现的列记录,必须在父表上已经存在。通过外键,我们可以确保业务上的逻辑一致性,同时还能实现一些级联操作;MySQL目前只有InnoDB引擎支持外键,类似MyISAM、Tokudb等引擎都不支持外键。

InnoDB支持建立多个列的外键,但被外键约束的父表上必须对这些列建立索引,并且子表上的外键列 和父表上索引上的顺序是一致的。默认情况下,当删除父表中被外键约束的记录时,会产生报错,但我们也可以通过在建外键索引时加上ON DELETE CASCADE 来级联的更新子表,更新同理。其他行为包括RESTRICT(限制父表的外键改动,默认值)、CASCADE(跟随父表的改动)、SET NULL(子表对应列设置为NULL)、SET DEFAULT(设置为默认值,InnoDB不支持,但Server层支持)、NO ACTION(无动作)。

由于InnoDB支持父表上被外键约束的索引可以不是唯一索引,因此会出现子表上一条记录 对应父表上多条记录;这是InnoDB的扩展特性,并不符合标准SQL。

你可以从两张INFORMATION_ SCHEMA表来查询实例上的外键信息:INNODB_SYS_FOREIGN_COLS 及 INNODB_SYS_FOREIGN

系统表

在INNODB层单独存储了外键约束信息,SYS_FOREIGN系统表记录表和被引用表的相关信息;SYS_FOREIGN_COLS记录具体的外键引用列信息。

关键类对象

fk

DML操作检查

插入子表的逻辑比较简单,只需要检查外键是否存在即可,堆栈如下:
row_ins_clust_index_entry/row_ins_sec_index_entry 
|–> row_ins_check_foreign_constraints 
#针对外键索引,调用函数row_ins_check_foreign_constraint,检查对应的父表中是否存在索引记录。

更新父表触发检查
row_update_for_mysql
|–>row_update_for_mysql_using_upd_graph
       |–> row_upd_sec_step –>row_upd_step –>row_upd 
              |–> row_upd_sec_index_entry 
              |–> row_upd_check_references_constraints 
               #检查表的table->referenced_set是否为空,为空表示没有子表
               #轮询每个被当前表约束的子表外键索引(table->referenced_set),检查是否修改了外键约束列 or 删除记录
               # 检查子表记录row_ins_check_foreign_constraint
               |–> row_ins_check_foreign_constraint
               #根据子表上的外键列索引,查找是否有匹配的记录(子表的外键列被隐含的创建了索引)
               # 当设置了ON UPDATE/ON DELETE属性时,需要构建联动更新的记录,调用下述函数
                      |–> row_ins_foreign_check_on_constraint
                      #根据dict_foreign_t::type类型,查询子表的聚集索引,构建更新vector和cascade node
        #当前表更新完成后 
        |–> #如果需要联动更新子表,则使用之前构建的cascade node,并Loop 调用row_upd_sec_step更新子表

DDL操作

测试表:

mysql> show create table t3\G

*************************** 1. row ***************************

Table: t3

Create Table: CREATE TABLE `t3` (

`a` int(11) NOT NULL,

`b` int(11) DEFAULT NULL,

`c` int(11) DEFAULT NULL,

`d` int(11) DEFAULT NULL,

PRIMARY KEY (`a`)

) ENGINE=InnoDB DEFAULT CHARSET=latin1

1 row in set (0.00 sec)

增加外键

mysql_alter_table 
–> ha_create_table –> ha_create –> ha_innobase::create –> create_table_info_t::create_table  // 使用copy的方式构建外键索引
|–> row_table_add_foreign_constraints // 创建表上的外键约束
     |–> dict_create_foreign_constraints
          |–> dict_create_foreign_constraints_low
          # 在该函数中,会去解析SQL语句,判断SQL里的各个关键字,例如ALTER、TABLE、FOREIGN之类来检查合法性,例如不允许分区表上的外键….居然是字符串匹配!!!!
          # 分配内存对象dict_mem_foreign_create
          #  如果用户没有指定,自动生产foreign key的命名:dict_create_add_foreign_id
          |–>dict_scan_table_name
          #  解出父表名,并获取对应的父表对象
               |–>dict_get_referenced_table
          # 不允许父表为分区表的外键约束
          #父表上被外键约束的字段上需要存在索引:dict_foreign_find_index

临时表在完成DDL后,需要rename成原表,这时候需要reload外键约束信息到内存数据词典cache中
–> mysql_rename_table –> ha_innobase::rename_table –>  innobase_rename_table –> row_rename_table_for_mysql
|–> dict_load_foreigns

          

删除外键

删除外键是in-place操作,堆栈为:
mysql_alter_table –> mysql_inplace_alter_table –> ha_innobase::commit_inplace_alter_table 
|–> commit_try_norebuild
     |–> innobase_update_foreign_try
          |–> innobase_drop_foreign_try  //删除系统表SYS_FOREIGN和SYS_FOREIGN_COLS上的记录 
|–> innobase_update_foreign_cache  // 更新cache信息

TRUNCATE父表


truncate 父表之前,会检查其是否为父表,如果是的话,直接拒绝操作,即使表上没有任何数据(Sql_cmd_truncate_table::handler_truncate  –> fk_truncate_illegal_if_parent)

rename  父表列名


当父表上被外键约束的列名修改时,需要修改对应的外键信息,堆栈:
mysql_alter_table –> mysql_inplace_alter_table 
–> ha_innobase::commit_inplace_alter_table 
|–> commit_try_norebuild –>innobase_rename_columns_try –> innobase_rename_column_try 
     #修改系统表中信息,包括SYS_FOREIGN_COLS
     #将修改过的外键信息从cache中移除:dict_foreign_remove_from_cache
|–> innobase_update_foreign_cache  // 更新cache信息

删除父表

drop 父表时,在函数row_drop_table_for_mysql中,检查其是否被其他表引用,如果是的话,则drop失败
 

参考:
MySQL 5.7.7-RC代码

原创文章,转载请注明: 转载自Simple Life

本文链接地址: InnoDB foreign key 实现

Post Footer automatically generated by wp-posturl plugin for wordpress.


Comments

  • a thought by 江疑

    赞,不断在review新模块啊

    Reply

    • a thought by zhaiwx1987

      哈哈,暂时对现在的模版满意了

      Reply

Leave a Reply

Your email address will not be published. Name and email are required


Current month ye@r day *