为什么MySQL里必须要有主键
- One minute read - 46 words有一道面试题,是问“为什么在 MySQL 表里一定要有一个主键”。注意这里提到的主键包括用户自定义的主键ID或MySQL默认隐含添加的主键ID。
对于主键的设置,在日常开发中已经深深的刻在我们的大脑里,基本形成了肌肉记忆,于是在设置表结构时,对每个表必须手动设置一个主键,一般推荐使用自增的INT类型,且有时间还需要无业务逻辑。但如果突然遇到这个问题,一时还真不知道应该如何系统的回答。
下面我将根据自己的理解来分析一下,虽然可能并不完整,但应该可以成为一个及格的答案了。 这里主要指的是InnoDB存储引擎,回答它可能需要从表引擎本身上下手。
存储结构
首先我们知道在MySQL里的,InnoDB 是一个以“聚簇索引”组织数据进行存储,它是将主键与表数据存储在一起的。主键也是一种特殊的unique key,这个主键不可能重复(这句是废话)。一个主键就代表一条记录,也就是说,如果我们想找到一条记录,则必须根据主键才能找到对应的记录。如果没有这个唯一键,将无法对应多条记录,更不可能多条记录对应一个主键,或多个主键对应一条记录。

如图所示,蓝色字段是主键,绿色字段data就是记录数据,两者是一块连续的存储地址,即它们是一起存储的。
查询优化
当我们执行一条主键查询语句时,如 SELECT * FROM t WHERE id = 10 FOR UPDATE;,这时它会将这条记录锁定,别人将无法对这条记录进行修改,即 Record lock。重点记住锁一行数据 = 锁主键记录。
如果没有主键 ,可能使用 间隙锁 (gap lock) , 大大的增加了锁的粒度,这在高并发下,很可能会出现锁冲突,导致性能下降,尤其是在写多、读写混合型业务里非常致命。
除此之外,还有一些索引查询时的回表问题。在普通索引(二级索引)里,我们找到的结果是主键,而不是完整的记录(这里仍是innodb,不是myisam),没有主键,如何回表?或者说回表性能又如何呢?

删除记录
上面提到一个主键就表示一条记录。如果没有主键的话,那应该如何删除指定记录呢?逐行扫描?还有如果遇到多条记录相同的呢?如何判断是否要删除的记录?
主从复制
在主从复制场景里,当 MySQL 在执行 Binlog(复制时)和 Redo/Undo(崩溃恢复时)需要“精确找到某一行”来重放操作。如果你没有主键,MySQL 没有办法快速准确地定位那一行。它只能全表扫描,这时性能很差,甚至会出现遇到多条记录数据完全一样,不知道要修改哪条记录的情况或修改错误数据导致的主从节点数据不一致的严重问题。
总结
以上只是其中的几点,可能并不能完全覆盖所有的原因,但在开发中,通过这些问题,基本可以判断主键的重要性。另外或者有人会说可以用唯一键来代替主键,可是这样的话,这个唯一键就变在了主键了。所谓主键就是一个特殊的不能重复定义多个的主键而已罢了。