分布式ID生成方案小结
几乎所有的系统都存在生成唯一ID的需求,如用户ID、账单ID等,由于系统通常是分布式架构,因而需要有合适的分布式ID生成方案。
常见的分布式唯一ID方法有(欢迎补充):
- 时间戳
- 数据库自增ID
- UUID
- 放号系统
- 类snowflake
一、时间戳
原理: 使用直接使用时间戳毫秒值或微秒值作为ID
缺点: 每个时间单位只能生成一个ID, 在分布式架构中不好保证唯一性。
适用场景: 一般很少适用这种方案
二、数据库自增ID
原理: 基于MySQL等数据库的auto_inscrement功能
优势: 实现简单(数据库自带功能),生成的ID单调递增,保证全局唯一;ID长度灵活
缺点: 存储性能上限(MySQL性能), 对数据库重度依赖,一旦数据库宕机则无法生产继续生成ID。
三、UUID
原理: 多个版本,大致原理是 时间戳+机器ID+CPU时钟+随机数。 通过CPU时钟保证本地唯一,通过机器ID+CPU时钟保证全局唯一
优势: 全局唯一,本地生成,没有性能上限
缺点: 生成ID过长(32位16进制字符),不是单调递增
四、放号系统
原理: 通常是基于数据库自增ID的方案改造,只是每次批量获取一个号段,提升了性能。
优势: 和”数据库自增ID“方案类似
缺点: 同样仍然有性能上限,依赖数据库的可用性。数据库主备切换时可能发生ID冲突。存储耗时尖刺(更新DB时延时高)。因为号段通常是定长,拓展性差,对流量波动大的场景不太适用。
参考: https://tech.meituan.com/2019/03/07/open-source-project-leaf.html
升级
放号系统可以升级多个变种,比如采用多个数据库(通过数据库实例ID+数据库自增ID), 在ID消耗完之前(比如消耗了50%)就预申请号段等方案。
五、类snowflake算法方案
原理: 时间戳+机器ID+序列号
优势: uint64型,全局唯一,单调递增,本地生成性能高,每秒能生成的ID较多。
缺点: 需要解决三大问题(增加了复杂度)
三大问题
时间回拨
时间回拨问题的几种解决方案:
- 直接抛异常(体验不太好)
- 采用历史时间(需要维护历史时间,重启时异常?)
- 多时间线(如果多次回拨仍然不能继续放号)
机器ID的分配和回收
机器ID的分配:
- 手动配置
- 取IP地址的hash值(比较适用于IP地址hash值不冲突的场景)
- 引入其他系统维护机器ID
机器ID的上限
snowflake算法使用10bit存储机器,所以机器ID的上线是2^10=1024; 不过这一般也能满足业务需要了(只要做好分配和回收)
六、小结
实际业务中,<放号系统>和<类snowflake>两种方案适用的场景较广。 使用时,可以使用具体的场景选择合适的方案。同时,可以结合需要进行优化。
比如:用户ID可以使用<放号系统>生成,订单号等可以使用类snowflake方案来生成。
使用类snowflake方案时,使用哪种方式分配机器ID也可以根据具体的场景选择。 比如机器较少,各个进程配置方便手动配置时,可以采用手动配置的方式; 如果实在集群中,Pods节点的最后8bit肯定不同,Pods自动扩缩容的场景,可以采用IP地址Hash值的方式来生成机器ID。