“惟一ID”在应用程序中是一个很常见的需求,它用于惟一标识一个业务对象、一个资源、或者一个消息等等。在数据库中,惟一ID通常是用来作为一个数据的主键。看过前面介绍MySQL索引原理的文章的朋友应该知道,主键对于数据库的重要性不言而喻。算法
在单机场景下,要获得一个全局惟一的ID是很是容易的,你可使用数据库的自增功能。数据库
可是若是在分布式的场景下,想要构建构建一个全局惟一的ID就有些不同。由于分布式系统通常是高并发场景,那天然不适合使用单机数据库的自增功能了。若是你的技术选型刚好是MySQL这样的“非分布式数据库”,那就得参考一下业界常见的分布式全局惟一ID生成策略了。缓存
UUID全称是Universally Unique Identifier,翻译过来叫通用惟一识别码。标准型式包含32个16进制数字,以连字号分为五段,形式为8-4-4-4-12的36个字符,示例:9628f6e9-70ca-45aa-9f7c-77afe0d26e05
,到目前为止业界一共有5种方式生成UUID,详情见IETF发布的UUID规范《A Universally Unique IDentifier (UUID) URN Namespace》,分别称为UUID的5个版本。服务器
在JDK自带的UUID
类能够产生版本3和版本4的UUID。因此这里简单介绍一下版本3和版本4的UUID的生成方式。数据结构
也有在线生成UUID的网站,若是你的项目上用到了UUID,能够用来生成临时的测试数据。www.uuidgenerator.net/并发
UUID的优点是实现起来很简单,用JDK原生的API便可获得。劣势是与基于b-Tree引擎的数据库的主键索引策略不太符合,不适合做为高性能需求的场景下的数据库主键。分布式
都用分布式了,多半要上个缓存。用缓存的话,可能会使用Redis。Redis的INCR
函数在单机上是原子操做,能够保证惟一且递增。函数
单机Redis可能没法支撑高并发。而若是使用Redis集群,如何保证ID的惟一性呢?可使用步长的方式。好比有5个Redis节点组成的集群,它们生成的ID分别为:高并发
A: 1,6,11,16,21性能
B: 2,7,12,17,22
C: 3,8,13,18,23
D: 4,9,14,19,24
E: 5,10,15,20,25
Twitter利用Zookeeper实现了一个全局ID生成的服务snowflake。其生成ID的数据结构以下图所示:
共64位,正好对应Java中的long
型,第一个符号位不用,而后41位用于表示时间戳。后续10位用来表示节点的id,若是是多机房节点,能够划分前5位用来表示机房id,后5位用来表示每一个机房下的机器的id。最后12位用来表示序列号,这样能够作到同一毫秒,同一机器生成多个id,12位算下来最多支持4096个。
这里的时间戳并非当前时间的Time Stamp,而是当前时间相对于起始时间的差值。若是基于毫秒来计算的话,41位大约能够用69年。
snowflake算法有许多变种。能够根据本身的实际状况调整位数的分配,好比时间戳占42位,机器id占9位。42位时间戳就能够用138年等。
百度的UidGenerator和美团的Leaf都是基于snowflake的变种。
snowflake是一种比较好的生成ID方式,保证全局惟一,且支持高并发。并且是long
类型的,趋势递增,能够用于数据库主键。还能够根据时间来排序。
但也有其缺点,就是强依赖服务器的时钟,若是服务器的时钟出现回拨(好比闰秒或者NTP同步),就会致使ID重复。
美团的Leaf解决了时钟回拨的问题,具体流程以下图,能够了解一下:
固然,还有一些其余的ID生成方案,好比:
滴滴:时间+起点编号+车牌号
淘宝订单:时间戳+用户ID
其余电商:时间戳+下单渠道+用户ID,有的会加上订单第一个商品的ID。
MongoDB的ID:也算是类snowflake的一种。经过“时间+机器码+pid+inc”共12个字节,4+3+2+3的方式最终标识成一个24长度的十六进制字符。
若是不用于数据库主键,建议直接用UUID。
若是想要用来作数据库主键,又没有使用分布式数据库(好比TiDB、MongoDB等),能够考虑使用snowflake算法,建议使用美团的Leaf。
数据库中间件sharding-jdbc的分布式ID采用twitter开源的snowflake算法,不须要依赖任何第三方组件,这样其扩展性和维护性获得最大的简化;可是snowflake算法的缺陷(强依赖时间,若是时钟回拨,就会生成重复的ID),sharding-jdbc没有给出解决方案,若是用户想要强化,须要自行扩展。
认真写文章,用心作分享。
我的网站:yasinshaw.com
公众号:xy的技术圈