一、问题背景
今天在设计表结构的时候,有个记录表只需要记录下是谁得了什么东西,什么时间得的,也就是3个字段:user_id、medal_id、created_time,通常表也会加主键 id,但是这个表其实不怎么用到,也就是查询谁的记录,或者某东西的记录时会关联查一下。你看都没有 主键id 啥事,所以我就有了个疑问,不加 id, 表不是就会小一点吗,那像这种情况到底需不需要加主键id呢?什么情况下不需要建主键id、建与不建的好处各是啥呢?
二、阿里规范中为什么要求表必须有主键id
数据库会默认为主键字段创建索引【主键索引】。
那么如果表中具有多个主键,数据库会不会为每个主键都创建索引呢?先建一张表【user:两个主键id,name】
CREATE TABLE `user` (
`id` int NOT NULL,
`name` varchar(255) NOT NULL,
PRIMARY KEY (`id`, `name`)
);
-- 运行命令查看索引
show index FROM user
可以看到:数据库为 id 和 name 都创建了索引。现在终于有点明白阿里规范中为什么要求创建主键id了。但是还是有疑问:那么难道仅仅只是创建了索引的区别吗?很多表查询不用索引查询,数据量小时,影响也并不大,那么如果不建主键 id 会有什么影响呢?
三、MySQL InnoDB数据表缺少主键会怎样
1、问题
MySQL数据表使用InnoDB作为存储引擎的时候,数据结构就是使用B+树,而数据本身存储在主键索引上,也就是通常所说的聚簇索引,也就是每个表都需要有个聚簇索引树,但是,在建表的时候却发现可以不用指定主键,那么MySQL对于没有指定主键的表示如何处理的呢?
2、InnoDB索引
对于InnoDB,可以简单地把所有数据视为索引,每一个索引都对应一个B+数,而主键对应的索引就是聚簇索引,表的所有数据都存储在聚簇索引上,而除了聚簇索引的普通索引存储的只是主键的引用,所以,查询的时候对于普通索引需要进行回表才能取到具体数据
3、缺少主键MySQL如何处理
既然InnoDB对数据的存储必须依赖于主键,那么对于没有创建主键的表,该怎么办?
InnoDB对聚簇索引处理如下:
(1)如果定义了主键,那么InnoDB会使用主键作为聚簇索引
(2)如果没有定义主键,那么会使用第一非空的唯一索引(NOT NULL and UNIQUE INDEX)作为聚簇索引
(3)如果既没有主键也找不到合适的非空索引,那么InnoDB会自动生成一个不可见的名为ROW_ID的列名为GEN_CLUST_INDEX的聚簇索引,该列是一个6字节的自增数值,随着插入而自增
很明显,缺少主键的表,InnoDB会内置一列用于聚簇索引来组织数据。而没有建立主键的话就没法通过主键来进行索引,查询的时候都是全表扫描,小数据量没问题,大数据量就会出现性能问题。
但是,问题真的只是查询影响吗?不是的,对于生成的ROW_ID,其自增的实现来源于一个全局的序列,而所有以ROW_ID为主键的表均共享该序列,这也意味着插入的时候生成需要共享一个序列,那么高并发插入的时候为了保持唯一性就避免不了锁的竞争,进而影响性能。
4、缺少主键或者非空索引存在问题
(1)使用不了主键索引,查询会进行全表扫描
(2)影响数据插入性能,插入数据需要生成ROW_ID,而生成的ROW_ID是全局共享的,并发会导致锁竞争,影响性能
5、为每个表设置主键
既然知道InnoDB对数据的存储和处理都是基于聚簇索引的,那么,在建表时候要注意主键的重要性,为每个表都设置一个主键,如果没有合适的字段来作为主键,可以设置一个业务无关的的代理主键,可以是自增ID,也可以是UUID(建议使用自增ID,性能较好)。
6、总结:
在理解InnoDB的数据结构之后,自然而然就会知道主键的重要性,在建表的时候也不会忘记设置主键,无论表设计有无合适的唯一字段,都需要设置一个主键,提高性能的同时也是一种好的习惯,对于后续的拓展以及表之间关联都有一定的拓展性。