分布式ID的实现方式有多种:UUID、雪花算法、数据库自增ID、数据库序列(Oracle)、Redis等等。下面将以Redis为例实现一个简单的分布式ID。
一、Redis实现分布式ID的核心指令
Redis实现分布式ID的核心指令是incr
系列指令,它们可以实现变量自增。
同时,Redis的time
指令可以生成当前时间的时间戳,可以用来生成某些时间关联性较强的ID。
二、使用lua脚本获取分布式ID的关键信息
生成分布式ID所需信息的lua脚本如下所示:
-- 自增量累加1(KEYS[1]为ID所属的业务名称,如:ORDER_ID)
local increment = redis.call('incr', KEYS[1]);
-- 当自增量达到上限时,重新开始计数(ARGV[1]为自增量上限,如:9999)
if(increment >= tonumber(ARGV[1])) then
increment = 1;
redis.call('set', KEYS[1], increment);
end
-- 获取当前时间戳
local time = redis.call('time')[1];
-- 返回当前时间戳与自增量
return {time,increment};
三、基于Redisson调用lua脚本实现分布式ID
下面将基于Redisson与上述的lua脚本,实现一个包含时间前缀与冗余自增量后缀的分布式ID。
如果对分布式ID的格式没有特殊需求,使用Redisson的RIdGenerator就足矣。
/**
* 基于Redis的分布式ID生成器
*/
public class IdGenerator {
private static final SimpleDateFormat SIMPLE_DATE_FORMAT = new SimpleDateFormat("yyyyMMddHHmmss");
private static final DecimalFormat DECIMAL_FORMAT = new DecimalFormat("0000");
private static final int INCREMENT_LIMIT = 9999;
public static String getId(RedissonClient redissonClient, String sequenceName) {
List<Number> idItems = getIdItems(redissonClient, sequenceName);
return formatId(idItems);
}
private static String getLuaScript() {
return "local increment = redis.call('incr', KEYS[1]); " +
"if(increment >= tonumber(ARGV[1])) then " +
"increment = 1; " +
"redis.call('set', KEYS[1], increment); " +
"end " +
"local time = redis.call('time')[1]; " +
"return {time,increment}; ";
}
private static List<Number> getIdItems(RedissonClient redissonClient, String sequenceName) {
RScript script = redissonClient.getScript(new JsonJacksonCodec());
return script.eval(
Mode.READ_WRITE,
getLuaScript(),
ReturnType.VALUE,
Collections.singletonList(sequenceName),
INCREMENT_LIMIT);
}
private static String formatId(List<Number> idItems) {
Integer timestamp = (Integer) idItems.get(0);
Long increment = (Long) idItems.get(1);
String idPrefix = SIMPLE_DATE_FORMAT.format(new Date(Long.parseLong(String.valueOf(timestamp))*1000));
String idSuffix = DECIMAL_FORMAT.format(increment);
return idPrefix + idSuffix;
}
}