外键,是用来保证数据参照完整性的。mysql5.7中,数据库表的默认存储引擎为InnoDB,InnoDB存储引擎完整支持外键约束。

    外键约束定义如下:

[CONSTRAINT [symbol] FOREIGN KEY
[index_name] (index_col_name, ...)
REFERENCES tbl_name (index_col_name, ...)
[ON DELETE reference_option]
[ON UPDATE reference_option]
reference_option:
RESTRICT | CASCADE | SET NULL | NO ACTION

 

    定义中,reference_option有RESTRICT、CASCADE、SET NULL、NO ACTION四个参数,每个对应作用不同:

 

    1,RESTRICT:当父表发生dml中的delete或update操作时,抛出错误,提示不允许此类操作发生。当没指定option时,这是默认设置。

 

    2,CASCADE:当父表发生dml中的delete或update操作时,子表对应的数据也被delete或update。

 

    3,SET NULL:当父表发生dml中的delete或update操作时,子表对应的数据被更新为NULL值。

   

    4,NO ACTION:作用和RESTRICT一样。

 

外键重点知识

    我们先建立基本父表和子表,并写入数据:

mysql> create table person(
    -> person_id int primary key,
    -> person_name varchar(20
));
Query OK, 0 rows affected (0.02 sec)

mysql> create table job(
    -> job_id int primary key,
    -> person_id int,
    -> job_name varchar(30
),
    -> constraint fk_person_id foreign key(person_id) references person(person_id)
    -> )
;
Query OK, 0 rows affected (0.02 sec)

mysql> insert into person values (1,'李白');
Query OK, 1 row affected (0.00 sec)

mysql> insert into person values (2,'杜甫');
Query OK, 1 row affected (0.01 sec)

mysql> insert into job values (1,1,'诗人');
Query OK, 1 row affected (0.00 sec)

 

重点1

 

    mysql InnoDB存储引擎对于建立的外键的列,默认会自动建立索引,这就可以很好的避免外键列上无索引而导致的死锁问题出现。

 

mysql> show index from job\G;
*************************** 1. row ***************************
        Table: job
   Non_unique: 0
     Key_name: PRIMARY
 Seq_in_index: 1
  Column_name: job_id
    Collation: A
  Cardinality: 1
     Sub_part: NULL
       Packed: NULL
         Null:
   Index_type: BTREE
      Comment:
Index_comment:
*************************** 2. row ***************************
        Table: job
   Non_unique: 1
     Key_name: fk_person_id
 Seq_in_index: 1
  Column_name: person_id
    Collation: A
  Cardinality: 1
     Sub_part: NULL
       Packed: NULL
         Null: YES
   Index_type: BTREE
      Comment:
Index_comment:
2 rows in set (0.00 sec)

 

 重点2

 

    由于MySQL的外键检查是即时检查的,因此子表中导入的每一行都是即时进行外键检查。对于大批量的导入操作,将花费太多时间进行外键检查,为了节约时间,我们在导入数据时可以设置忽视外键的检查。

 

mysql> show variables like 'foreign_key_checks';
+--------------------+-------+
| Variable_name      | Value |
+--------------------+-------+
| foreign_key_checks | ON    |
+--------------------+-------+
1 row in set, 1 warning (0.00 sec)

mysql> set foreign_key_checks='OFF';
Query OK, 0 rows affected (0.00 sec)

mysql> show variables like 'foreign_key_checks';
+--------------------+-------+
| Variable_name      | Value |
+--------------------+-------+
| foreign_key_checks | OFF   |
+--------------------+-------+
1 row in set, 1 warning (0.00 sec)

 

 重点3

 

    当子表中进行外键值得插入或者更新时。为了保证数据一致性,此时会对父表请求加上一个SHARE模式的锁(即S锁)。所以当父表中存在X锁时(例如表中存在update或者delete操作时),子表上的操作会被阻塞。

 

    A会话更新person表:

mysql> begin;
Query OK, 0 rows affected (0.00 sec)

mysql> update person set person_name='白居易' where person_id=2;
Query OK, 1 row affected (0.01 sec)
Rows matched: 1  Changed: 1  Warnings: 0

mysql>

 

    B会话插入数据,发现阻塞:

mysql> insert into job values(2,2,'诗人');

 

    当A会话rollback后,发现B会话执行成功:

mysql> rollback;
Query OK, 0 rows affected (0.00 sec)
mysql> insert into job values(2,2,'诗人');
Query OK, 1 row affected (1 min 58.11 sec)

 

    在使用mysql外键过程中,注意以上几点,基本上在很好使用外键完整约束的基础上,保障好数据库的性能。

    

注意点:oracle外键创建后,默认是不会在外键约束上建立索引,所以在oracle数据库中,经常出现因外键问题导致的性能低下,作为dba需要特别注意!