分布式ID实现方式:
(0)要求:
a. 全局唯一性:不能出现重复的ID号,既然是唯一标识,这是最基本的要求。
b. 粗略有序:如果在分布式环境中做到完全有序,需要用到锁等,考虑到性能,采用粗略有序,具体分为秒级有序和毫秒级有序;
c. 可反解:即生成ID服务提供反解方法,这样在存储时就能以十进制存储,省下传统timestamp类字段的占用空间了;
d. 可伸缩:中心发布模式时可进行集群部署,这样在生成ID里就必须包含机器ID;
(b. 趋势递增:在MySQL InnoDB引擎中使用的是聚集索引,由于多数RDBMS使用B-tree的数据结构来存储索引数据,在主键的选择上面我们应该尽量使用有序的主键保证写入性能。
(c. 单调递增:保证下一个ID一定大于上一个ID,例如事务版本号、IM增量消息、排序等特殊需求)
d. 信息安全:如果ID是连续的,恶意用户的扒取工作就非常容易做了,直接按照顺序下载指定URL即可;如果是订单号就更危险了,竞对可以直接知道我们一天的单量。所以在一些应用场景下,会需要ID无规则、不规则。
上述123对应三类不同的场景,3和4需求还是互斥的,无法使用同一个方案满足。
(1) 方案总结:
生成全局唯一ID方案:引入全局ID生成服务、通过时间戳、依靠特定算法生成UUID等3种方案;
(2) 方案一:UUID:
简介:共128位,3部分生成:当前日期和时间+时钟序列+全局唯一机器识别号如网卡MAC地址;UUID标准型式包含32个16进制数字,分为五段,形式为8-4-4-4-12的36个字符,示例:550e8400-e29b-41d4-a716-446655440000,到目前为止业界一共有5种方式生成UUID;
优点:性能非常高,本地生成,没有网络消耗;
缺点:--太长不易于存储,32字符16个字节128位; --不具备有序性,会导致在写入B+树索引的时候有过多的随机操作(连续的ID会产生部分顺序写),另外,由于在写入时不能产生有顺讯的append操作,而需要进行insert操作,将读取整个B+数节点到内存,在插入记录完成后将整个节点写回磁盘,这种操作在记录占用空间比较大的情况下性能下降明显;
参考:https://blog.youkuaiyun.com/xinghuo0007/article/details/72868799 -- UUID生成方法
(3) 方案二:基于数据库实现
简介:设置不同初始值和相同增长步长来实现,即4个数据库,初始值为,0,1,2,3,步长为4;
缺点:--水平扩展较难;--对数据库压力较大,每获取一次都会读写一次数据库; --只有趋势递增,没有单调递增;
(4) 方案三:类snowflake方案---好!!
简介:共64位,3部分组成:0位不用+41位时间戳+10位workerID+12位自增序列;其中:10位机器ID,可手动配置,或通过zookeeper创建持久顺序节点后取回顺序号当做自己的workerId; 41位时间戳:以某一时间为基准,当前时间减去该时间生成,40位毫秒级可支撑34年,41位可支撑68年) ,最后生成的是十进制数,如3279458774233907200
优点:--不依赖第三方,性能都很高; --可以根据自身特性分配bit位,非常灵活;--毫秒为在高位,自增序列低位,整个ID是趋势递增;
缺点:强依赖时钟,如果机器时钟回拨,会导致发号重复;每毫秒自增序列归0问题;
改进方案:leaf-snowflake方案:即对snowflake强依赖的时钟问题,进行了处理;通过间隔3秒上报时钟到zookeeper,并判断是否小于上次上报的时间来判断是否有过大幅回拨,如果是新加入节点,则访问其他节点时间并计算平均值进行对比;失败则抛异常并不提供服务或报警;
参见:https://tech.meituan.com/MT_Leaf.html 美团点评分布式ID生成系统 -- 好!!
参见:《可伸缩服务架构:框架与中间件》李彦鹏----设计一款永不重复的高性能ID发号器 --- 好!!!