分布式ID
参考:
zhuanlan.zhihu.com/p/152179727
方案一:
数据库自增(含Redis自增):使用数据库的id自增策略(如: Mysql的auto_increment)。
实现
- 数据库水平拆分,设置不同的初始值和相同的自增步长
核心思想:将数据库进行水平拆分,每个数据库设置不同的初始值和相同的自增步长。
优点:可保证每台数据库生成的ID是不冲突的,
缺点:固定步长的方式也会带来扩容的问题
- 批量缓存自增ID
核心思想:如果使用单台机器做ID生成,可以避免固定步长带来的扩容问题(方案1的缺点)。
具体做法是:每次批量生成一批ID给不同的机器去慢慢消费,这样数据库的压力也会减小到N分之一,且故障后可坚持一段时间。
优点:解决了扩容
缺点:服务器重启、单点故障会造成ID不连续。
- Redis生成ID
核心思想:Redis的所有命令操作都是单线程的,本身提供像 incr 和 increby 这样的自增原子命令,所以能保证生成的 ID 肯定是唯一有序的。
优点:不依赖于数据库,灵活方便,且性能优于数据库。数字ID天然排序,对分页或者需要排序的结果很有帮助。
缺点:如果系统中没有Redis,还需要引入新的组件,增加系统复杂度。需要编码和配置的工作量比较大。
方案二:
UUID:结合机器的网卡(基于名字空间/名字的散列值MD5/SHA1)、当地时间(基于时间戳&时钟序列)、一个随记数来生成UUID。
优点:本地生成,没有网络消耗
缺点:UUID太长,不易于存储。信息不安全:MAC地址泄露。无序查询效率低。
方案三:
雪花算法(时间调整):把64-bit分别划分成多段,分开来标示机器、时间、某一并发序列等,从而使每台机器及同一机器生成的ID都是互不相同
优点:
一般情况下生成的ID不重复。ID生成性能高。基于时间戳,可以基本保证有序递增。
缺点:
依赖机器时钟,如果机器时钟回拨,会导致重复ID生成(时间回拨,导致机器基于同样的时间产生同样的ID)。
在单机上是递增的,但是由于设计到分布式环境,每台机器的时钟不可能完全同步,有时候会出现不是全局递增的情况(此缺点可以认为无所谓,一般分布式ID只要求趋势递增,并不会严格要求递增,90%的需求都只要求趋势递增。
时间回拨解决方案:
- 不影响方案:如果回拨时间小的时候,不生成 ID,循环等待到时间点到达。
- 拒绝服务方案:如果间隔过大,超过一定大小的回拨直接报错,拒绝服务
- 最大值方案:保存过去一段时间内每一台机器产生的ID的最大值,比如使用Map形式,就是<machine_id,max_id>,这样如果某台机器发生了时钟回拨,直接在这台机器对应的max_id的基础上继续自增生成ID即可。
- 拓展位方案:回拨之后在拓展位上加1就可以了,这样ID依然可以保持唯一。但是这个要求我们提前预留出位数,要么从机器id中,要么从序列号中,腾出一定的位,在时间回拨的时候,这个位置 +1.