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记录具体的外键引用列信息。

关键类对象

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失败

 

时间: 2025-01-19 12:32:02

InnoDB foreign key 实现的相关文章

InnoDB 中foreign key使用注意事项

Innodb foreign key 和 sql  standards的区别: 在 sel statement 中的inserts,deletes,updates 很多行的时候,fk 会一行一行检查. innodb 是设置 shared row_level locks 在父表或者子表上,MySQL CHECK 会立即检查是否有符合的行, 并不会推迟到事务提交的时候, 在innodb上建立fk的条件: 1.innodb允许关联到一个index或者一组 columns(第一个column 必须是in

mysql外键(Foreign Key)介绍和创建外键的方法_Mysql

在MySQL中,InnoDB引擎类型的表支持了外键约束.外键的使用条件:1.两个表必须是InnoDB表,MyISAM表暂时不支持外键(据说以后的版本有可能支持,但至少目前不支持):2.外键列必须建立了索引,MySQL 4.1.2以后的版本在建立外键时会自动创建索引,但如果在较早的版本则需要显示建立:3.外键关系的两个表的列必须是数据类型相似,也就是可以相互转换类型的列,比如int和tinyint可以,而int和char则不可以: 外键的好处:可以使得两张表关联,保证数据的一致性和实现一些级联操作

MySQL添加外键时报错:1215 Cannot add the foreign key constraint的解决方法_Mysql

前言 这篇文章主要涉及到在数据创建表时,遇到ERROR 1215 (HY000): Cannot add foreign key constraint 问题方面的内容,对于在数据创建表时,遇到同样问题感兴趣的同学可以参考一下. 一.问题的提出 创建两个表:        product:  商品表      sealer:  供货商表 相应的SQL如下: product表: DROP TABLE IF EXISTS `product`; CREATE TABLE `product` ( `id`

数据库中的参照完整性(Foreign Key)

之前在项目中遇到了这样一个问题,我举得简单的例子来说明, 比如我们有两个表,一个表(department)存放的是部门的信息,例如部门id,部门名称等:另一个表是员工表(staff),员工表里面肯定要存放每个员工所在的部门.那问题来了,如果我们这个时候删除了部门表中的某条记录,在staff表中会发生什么? 为了解答上面的问题,让我们先来回顾一下什么是参照完整性. 我们常常希望保证在一个关系中给定属性集上的取值也在另一个关系的特定属性集的取值中出现.这种情况称为参照完整性(referential

mysql-数据库foreign key error 1215

问题描述 数据库foreign key error 1215 create table department(dept_name varchar(20) not null primary key building varchar(20)budget numeric(122)); create table instructor( ID varchar(5) name varchar(20) dept_name varchar(20) salary numeric(121));create tabl

c#-UPDATE 语句与 FOREIGN KEY 约束"FK_DeskInfo_RoomInfo"冲突。

问题描述 UPDATE 语句与 FOREIGN KEY 约束"FK_DeskInfo_RoomInfo"冲突. 比如我有一个Room表 主键是RoomId. 一个Desk表,主键是DeskId,外键是RoonId Room表有 : RoomId(标示列), RoomName , RoomType 其中 一条数据 RoomId 1 , RoomName 测试 ,RoomType=1 在Desk表 : DeskId(标示列) RoomId,DeskName, 我插入一条数据 insert

SQL FOREIGN KEY 外键实例教程

SQL FOREIGN KEY 外键实例教程 外国的SQL关键的制约因素 外键在一个表点,主键在另一就座. 让我们说明了外国钥匙与一个例子.看看以下两个表格: 在"人"表:  LastName FirstName Address City 1 Hansen Ola Timoteivn 10 Sandnes 2 Svendson Tove Borgvn 23 Sandnes 3 Pettersen Kari Storgt 20 Stavanger order 表. O_Id OrderN

关于primary key和foreign key的问题处理

今天碰到一个问题,在drop primary key的时候,提示ORA-02273的错误. SQL> ALTER TABLE AR1_ACCOUNT DROP PRIMARY KEY; ALTER TABLE AR1_ACCOUNT DROP PRIMARY KEY * ERROR at line 1: ORA-02273: this unique/primary key is referenced by some foreign keys 通过下面的字典表查找的关联的表 SQL> SELEC

【Foreign Key】Oracle外键约束三种删除行为

Oracle使用外键来限制子表中参考的字段值,要求子表中的数据必须在主表中存在.当主表的记录发生变化时导致外键参考唯一约束值发生了变化时,Oracle指定了三种动作:默认值(类似于restrict).delete cascade和delete set null.实际体验一下他们对删除操作的不同效果. 1.创建主表及子表并简单初始化几条数据1)创建主表t_parent,并初始化三条记录sec@ora10g> create table t_parent (parent_id int primary