Distributed System Id Gen

2017/07/31 Java

分布式系统高效唯一ID生成方案

背景:在分布式系统中,对于订单ID如何保证全局唯一地高效的生成,并对性能影响不大的情况下,利于建立索引和业务使用?

方案一:使用数据库主键ID

优势:实现简单; 弊端:使用数据库增加写库的压力,生成订单的上限受限于数据库的写性能上限;扩展性差;

方案二:使用数据库的列+1

主键采用业务编码,普通列current_value标识id当前值,通过数据库的行锁来保障唯一的; 优势:一张表可以存储不同业务类型的生成规则,没必要每一个id规则都新建一张附表; 劣势:涉及行锁,性能不高;

需要注意使用此方式生成数字序列事务隔离级别需要是RR。

方案三:单点批量生成订单号的服务

优势:批量降低了数据库的读写压力 弊端:服务单点风险;

方案四:使用UUID

优势:本地生成ID,不进行远程调用,延迟低; 弊端:无法保证趋势增长;uuid过长且建立索引效率低下;64位太长;

方案五:使用毫秒数

优势:本地生成ID,不进行远程调用,延迟低;ID为整数利于建立索引; 弊端:并发超过1000,可能会重复; 优化方案:使用LRU Cache存储最近生成的100Key,去除后先判断是否重复;进一步降低重复的可能性;但是无法根本上解决高并发下的重复;

方案六:订单号分段表示具体含义

方案描述:使用39bit表示毫秒数、4bit表示业务线、7bit表示机器等 优势:实现简单,且重复可能性很低; 弊端:订单长度过长,不利于业务使用;

方案七:使用Redis服务+毫秒数双重方案

优势:使用redis的自增序列特性,每次获取一定数量的数据(1000个),降低了每次调用Redis的开销;当Redis挂了,使用基于毫秒数的本地生成ID优化方案; 弊端:redis挂了后,订单号的重复性不可避免;

使用redis需要配置主备,避免在极端情况下redis节点down机, 导致丢失序列或序列重复。

总结:

我们在设计分布式全局唯一单号的时候,不仅需要考虑生成单号的是否重复性,还要考虑生成时的性能开销、是否有容灾方案、是否便于建立索引、是否利于业务使用等。基于这些点的考虑,我们采用了方案六作为线上的唯一生成方案,并把此方案作为一个业务隔离的二方库,可以提供给不同业务方式用;

参考:高可用高性能可扩展的单号生成方案

Search

    Post Directory