ID 生成器
如果你恰好选择了 mysql,恰好又数据量过大进行了分库分表操作,那接下来的问题就必须考虑了:主键得保持唯一。你也可以说,我从此就放弃 getById 的操作了,也放弃任何跟 id 相关的操作了,也不是不行,但这样或许会对业务造成比较大的改动,没有哪个服务的代码上来就按照分库分表来写的。
竟然我们还是要保主键唯一,就要进行一番设计。
UUID
这东西是一个古老的算法,重复率接近于 0,完全可以当作唯一 id 使用。有些魔怔的面试官或者评审人可能会习惯性的问,怎么保证不重复?你这个只是接近 0,又不是 0。首先,这个概率从数学角度是可以证明的:大约 85 年每秒产生 10 亿个 UUID 才会产生一次 50%概率的碰撞。但凡每秒没有 10 亿笔订单新增,这个数字还要更夸张,比如 850 年,8500 年。详细介绍可以去看wikipedia。
所以 UUID 用作唯一 id 完全没有问题,但考虑到我们用的是不太行的 mysql,会带来 3 个问题。
UUID 没有自增,对于 mysql 的 B+树索引非常不友好,简而言之就是写入性能差。
UUID 不具备业务含义,在一些业务场景中具备业务含义的主键可能会事半功倍。
UUID 所需要的存储空间大,一定程度上浪费了数据库的磁盘。
基于以上,UUID 可以先 pass。
SnowFlake
这个东西相信做技术的多少有点耳闻,或者早就用上了。这是 twitter 发明的唯一 id 生成器,解决了 UUID 的 2 个问题,同时还具备较高的性能。可以先抛开具体的原理不谈,我们先脑暴一下满足什么样的条件才能做到唯一:
短了肯定不行,必须要长,长了才有足够的空间来设计避免碰撞
随机数肯定不行,又回到上面的问题,而且随机算法 UUID 是天花板
时间在流逝,是最好的唯一数生成素材
具备一些业务意义是最好这也是 snowflake 的生成思想,先看图:
41 位的时间戳可以用 69 年,10 位的机器 id,12 位的自定义序列号。分别用 3 段不同的数位段来区分,这段代码的实现很简单,可以写成独立的工具类也可以稍加改造做成可部署的服务。比如,想要进一步增加区分性,可以在序列号中增加 userId 之类的业务 id。
小结
看似简单的一个 id 设计,其实想要做好落地并不简单,如何保证每次都能获取正确的机器 id,时间精度如果到微秒,可能会存在大量重复的时间戳等等。需要根据实践和业务场景做出对应的调整。
评论