最近在开发项目过程中,关于数据库主键到底是用UUID还是自增。我跟同事产生了很大的分歧。于是痛定思痛,整理了这篇关于数据库主键的文章。

先说下uuid和 auto_increment(数据库自增主键)的优缺点吧,因为是个人理解,如有错误恳请指出:

auto_incremen的优点:

  1. 字段长度较uuid小很多,可以是bigint甚至是int类型,这对检索的性能会有所影响。我们平时数据库一般用的都是innodb引擎的表,这种表格检索数据的时候,哪怕走索引,也是先根据索引找到主键,然后由主键找到这条记录。所以主键的长度短的话,读性能是会好一点的。
  2. 在写的方面,因为是自增的,所以主键是趋势自增的,也就是说新增的数据永远在后面,这点对于性能有很大的提升(这点我接下来会在uuid的优缺点分析中解释,虽然用词可能不太专业)。

auto_incremen的缺点:

  1. 最致命的一个缺点就是,很容易被别人知晓业务量,然后很容易被网络爬虫教做人
  2. 高并发的情况下,竞争自增锁会降低数据库的吞吐能力
  3. 数据迁移的时候,特别是发生表格合并这种操作的时候,会非常蛋疼

接下来说下uuid的优点:

  1. 地球唯一的guid,绝对不会冲突
  2. 可以在应用层生成,提高数据库吞吐能力
  3. 是string类型,写代码的时候方便很多

uuid的缺点

  1. 与自增相比,最大的缺陷就是随机io。这一点又要谈到我们的innodb了,因为这个默认引擎,表中数据是按照主键顺序存放的。也就是说,如果发生了随机io,那么就会频繁地移动磁盘块。当数据量大的时候,写的短板将非常明显。当然,这个缺点可以通过nosql那些产品解决。
  2. 读取出来的数据也是没有规律的,通常需要order by,其实也很消耗数据库资源
  3. 看起来比较丑

这样子一看,似乎两种主键方案都有其致命的缺点,那怎么搞呢?比如遇到必须用mysql但是量比较大,并发比较高的情况?其实行业内大鳄已经给出了一个比较好的解决方案,那就是推特的雪花算法(snowflake):

mysql自增主键问题 mysql自增主键好处_主键


雪花算法简单描述: 

+ 最高位是符号位,始终为0,不可用。 

+ 41位的时间序列,精确到毫秒级,41位的长度可以使用69年。时间位还有一个很重要的作用是可以根据时间进行排序。 

+ 10位的机器标识,10位的长度最多支持部署1024个节点。 

+ 12位的计数序列号,序列号即一系列的自增id,可以支持同一节点同一毫秒生成多个ID序号,12位的计数序列号支持每个节点每毫秒产生4096个ID序号。

看的出来,这个算法很简洁也很简单,但依旧是一个很好的ID生成策略。最高的那几位是时间戳,这能保证不管是几台机子在跑主键的生成必然是趋势自增的,基本上下一毫秒产生的主键必然比上一毫秒的大。最后的几位是加锁的方法生成的,一台机器一毫秒钟4096个,够用了吧?唯一的难点是机器号的生成。我想了想,大概有几个方法:

  1. 根据机器的mac地址等信息生成,简单点的话,就根据机器的ip生成(这样子做是有风险的,哪怕保证买来的机器都是连号的,根据ip计算一样可能导致算出同样的机器id)
  2. 用zookeeper给机器发号,这是最稳妥的,真要用雪花算法生成线上数据的id,我觉得还是得靠这个方案
  3. 用其他中间