注意:
- 本篇文章需阅读者掌握MySQL索引基本原理,可提前阅读 MySQL系列 | 浅尝MySQL索引
- 本篇文章涉及的V2帖子,没有特殊能力者,是访问不通的
- 本人无特殊能力去看V2帖子,是不小心在某个地方看到,已全然忘记出处,是一位遵纪守法的好公民
看到V2一篇帖子中的提问:https://go.seeyou.me/sk7qf ,促使我写下了这篇文章。
这位楼主的问题看似简单,但挺值得思考的。
这个问题我曾经想当然的认为,答案就应该是“需要使用自增ID列作为主键”,直到曾经一次滴滴的面试,被问到几乎同样的问题,才进行了深度的思考。
很多时候我们在设计数据库表的时候,明明已经有了一个逻辑上的唯一ID,却依然新建了一个自增的ID作为物理主键,在业务上却毫无用处,仿佛就像人要吃饭睡觉一样自然,why?
非自增主键会产生的问题
我们先来看看,为什么我们需要一个自增的主键?
性能问题
MySQL InnoDB 引擎的主键索引是一棵B+树结构,为了维护索引数据的有序性,会对新增数据进行比较,如果大于最大值,则只需要在最后记录后面插入一个新记录,如果小于,那么会去查找应该插入的具体位置。
页分裂
由于数据页的大小默认是16KB,如果恰巧要插入数据的数据页已满,就需要在此位置申请一个新的数据页,然后迁移部分数据过去,这个过程称为页分裂。
所以,如果我们的主键是自增的,那么每次新增数据,都会直接在最大记录后插入,非常高效。
空间问题
页空洞
如果主键是非自增的,除了要在插入过程中,经过多次比较、页分裂导致数据挪动等额外的耗时操作外,还会产生页空洞,致使空间利用率下降。
辅助索引
当我们为了高效多维度查询,对一张表创建了多个辅助索引时,辅助索引的叶子节点存储的是主键值。
如果我们使用业务上的唯一ID来做主键,由于一般业务上的唯一ID为了保证唯一性,生成的ID可能较长,那么所有辅助索引的叶子节点数据都会变大。
例如,用18位身份证号做主键,刨除其他额外信息,需要18个字节,而如果使用整型自增,则只需要4字节。
综上来看,无论是从性能上,还是空间占用上,我们确实应该使用自增ID列做主键,而不应该使用业务唯一ID来做主键。
那么,所有场景都该如此吗?不,当然不是。
业务唯一ID适合作为主键的场景
什么场景下,我们可以使用业务唯一ID来做主键呢?
有限场景一
- 业务唯一ID必须是自增趋势的
- 只会有一个主键索引
如果同时满足以上两点,那么使用业务唯一ID作为主键是最佳的。
因为如果此时,再去建立自增ID列,那么必然要基于业务唯一ID建立辅助索引,我们知道,由于自增ID是不会在业务中被使用的,所以我们的查询,都要基于业务唯一ID来进行,这样将会在每次查询时,造成回表。
所以,使用业务唯一ID作为主键既避免了回表操作,又保证了主键的递增趋势,也不需要创建辅助索引,一举三得。
有限场景二
- 读多写少
- 数据量小
假设你的业务唯一ID不是自增趋势,是否可以使用它做主键呢?
答案是可以。
但要满足以上两个条件,第一是,这张表,必须是读多写少的,例如我曾在一家在线教育公司任职,课程产品表,就是一个读多写极少,且数据量很小的表,即使课程唯一ID无序,也完全可以作为主键,对性能几乎无影响,新增频率极低。
总结
其实当我们的软硬件资源足够充沛,并可预见的能持续保持时,我们可以减少一些开发、设计细节上的斟酌和处理,但是,思考却不能敷衍。
不同的外部条件下,我们的行为可能会做出调整或取舍,但思考必须是细致的、全面的、尽力的,为作品,也为自己的成长。
公众号: 今天404