主键和外键的索引

时间:2010-08-20 08:07:05

标签: mysql

我有一个使用GUI工具创建的数据库,我注意到KEY(又名INDEX)定义似乎不一致:

CREATE TABLE `foo_bar` (
  `foo_id` int(10) unsigned NOT NULL,
  `bar_id` int(10) unsigned NOT NULL,
  PRIMARY KEY (`foo_id`, `bar_id`),
  KEY `foo_bar_fk2` (`bar_id`), -- <== ???
  CONSTRAINT `foo_bar_fk1` FOREIGN KEY (`foo_id`) REFERENCES `foo` (`foo_id`) ON DELETE CASCADE ON UPDATE CASCADE,
  CONSTRAINT `foo_bar_fk2` FOREIGN KEY (`bar_id`) REFERENCES `bar` (`bar_id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_spanish_ci COMMENT='Links between Foo and Bar';

我对索引

有以下问题
  1. 是否有必要明确定义主键和外键的索引?
  2. 如果不是,您实际上是否获得两个索引(并且性能较低)?
  3. InnoDB和MyISAM(除了外键)有什么不同吗?

3 个答案:

答案 0 :(得分:5)

我一直在做一些关于你告诉我的实验,我以为我会把它作为答案分享。

首先我创建一些测试表:

CREATE TABLE foo (
    foo_id int(10) unsigned NOT NULL,
    PRIMARY KEY (foo_id)
)
ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_spanish_ci;
CREATE TABLE bar (
    bar_id int(10) unsigned NOT NULL,
    PRIMARY KEY (bar_id)
)
ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_spanish_ci;


CREATE TABLE foo_bar (
    foo_id int(10) unsigned NOT NULL,
    bar_id int(10) unsigned NOT NULL
)
ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_spanish_ci;

到目前为止,还没有索引:

mysql> SHOW INDEXES FROM foo_bar;
Empty set (0.00 sec)

添加主键会生成索引:

mysql> ALTER TABLE foo_bar
    -> ADD PRIMARY KEY (`foo_id`, `bar_id`);
Query OK, 0 rows affected (0.70 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql> SHOW INDEXES FROM foo_bar;
+---------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+
| Table   | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment |
+---------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+
| foo_bar |          0 | PRIMARY  |            1 | foo_id      | A         |           0 |     NULL | NULL   |      | BTREE      |         |
| foo_bar |          0 | PRIMARY  |            2 | bar_id      | A         |           0 |     NULL | NULL   |      | BTREE      |         |
+---------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+
2 rows in set (0.02 sec)

如果我在foo_id上添加一个外键,它会重用主键索引,因为该列是索引中的第一个:

mysql> ALTER TABLE foo_bar
    -> ADD CONSTRAINT `foo_bar_fk1` FOREIGN KEY (`foo_id`) REFERENCES `foo` (`foo_id`) ON DELETE CASCADE ON UPDATE CASCADE;
Query OK, 0 rows affected (0.27 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql> SHOW INDEXES FROM foo_bar;
+---------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+
| Table   | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment |
+---------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+
| foo_bar |          0 | PRIMARY  |            1 | foo_id      | A         |           0 |     NULL | NULL   |      | BTREE      |         |
| foo_bar |          0 | PRIMARY  |            2 | bar_id      | A         |           0 |     NULL | NULL   |      | BTREE      |         |
+---------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+
2 rows in set (0.00 sec)

如果我在bar_id上添加外键,它会创建一个索引,因为不能重用现有索引:

mysql> ALTER TABLE foo_bar
    -> ADD CONSTRAINT `foo_bar_fk2` FOREIGN KEY (`bar_id`) REFERENCES `bar` (`bar_id`) ON DELETE CASCADE ON UPDATE CASCADE;
Query OK, 0 rows affected (0.25 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql> SHOW INDEXES FROM foo_bar;
+---------+------------+-------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+
| Table   | Non_unique | Key_name    | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment |
+---------+------------+-------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+
| foo_bar |          0 | PRIMARY     |            1 | foo_id      | A         |           0 |     NULL | NULL   |      | BTREE      |         |
| foo_bar |          0 | PRIMARY     |            2 | bar_id      | A         |           0 |     NULL | NULL   |      | BTREE      |         |
| foo_bar |          1 | foo_bar_fk2 |            1 | bar_id      | A         |           0 |     NULL | NULL   |      | BTREE      |         |
+---------+------------+-------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+
3 rows in set (0.02 sec)

我们的一个外键是使用主键索引。这意味着我们无法删除这样的索引!

mysql> ALTER TABLE foo_bar
    -> DROP PRIMARY KEY;
ERROR 1025 (HY000): Error on rename of '.\test\#sql-568_c7d' to '.\test\foo_bar' (errno: 150)

除非我们为外键创建索引或者我们删除密钥本身:

mysql> ALTER TABLE foo_bar
    -> DROP FOREIGN KEY `foo_bar_fk1`;
Query OK, 0 rows affected (0.19 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql> ALTER TABLE foo_bar
    -> DROP PRIMARY KEY;
Query OK, 0 rows affected (0.23 sec)
Records: 0  Duplicates: 0  Warnings: 0

结论是MySQL在功能需要时会自动创建索引(但只有在必要时才会这样做。)

答案 1 :(得分:2)

外键索引用于创建referential constraints

它们允许您定义级联删除子句,以便从父表中删除行将从子表中删除行。它还可用于确保如果您尝试在子表中创建一行,则可以将其链接到父级(即Childrow.ParentId有效)

编辑:对不起,我误解了。 InnoDB自动为外键创建索引。见does-mysql-index-foreign-key-columns-automatically

答案 2 :(得分:1)

1)主键在mysql中自动编入索引。 2)见上文,没有必要这样做 3)MyISAM不支持约束。 (我认为你暗中说过,所以对其他人来说,阅读它更多是这样)。当您尝试将外键与MyISAM一起使用时,某些图形工具(如MySQL Workbench)不会导致警报,但显然不会创建它们。

顺便说一句:您使用的是什么GUI(在哪个操作系统上)?

好读:d ata warehousing whitepaper(包含MyISAM和InnoDB表的比较)