通过 Redis 生成唯一顺序主键

3 篇文章 0 订阅
1 篇文章 0 订阅

在分布式项目中经常会用到生成唯一主键,或者生成顺序号,现通过Redis实现顺序号的生成。

话不多说,直接上代码

1、生成唯一主键工厂方法接口如下

/**
 * 获取唯一序列工厂方法
 */
public interface SequenceFactory {
    /**
     * 根据指定的键 获取下一个序列
     * @param seqKey 键值
     * @return 返回唯一序列
     */
    long nextValue(String seqKey);

    /**
     * 根据指定的键,获取每天的唯一序列 设置键的过期时间为 24小时
     * @param seqKey 键值
     * @return 序列
     */
    long dailyNextValue(String seqKey);
}

1、实现方式一


/**
 * 通过 Lua 脚本执行
 */
public class RedisLuaSequenceFactory implements SequenceFactory {

    private static final long SEQ_START_ID = 0L;
    private static final int DEFAULT_EXPIRE = 3600 * 24;
    private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyyMMdd");
    private static final String LUA_SCRIPT = "local key = KEYS[1]\n" +
            "local expire = ARGV[1]\n" +
            "local isExp = nil ~= expire and -1 ~= tonumber(expire)\n" +
            "local exists = redis.call('EXISTS', key)\n" +
            "local seq = 0\n" +
            "if( exists == 0) then\n" +
            "    redis.call('SET', key, 0)\n" +
            "else\n" +
            "    seq = redis.call('INCR', key)\n" +
            "    if( seq < 0) then\n" +
            "        redis.call('SET', key, 0)\n" +
            "        seq = 0\n" +
            "    end\n" +
            "end\n" +
            "\n" +
            "local ttl = redis.call('TTL', key)\n" +
            "if( isExp and ttl == -1) then\n" +
            "    redis.call('EXPIRE', key, expire)\n" +
            "end\n" +
            "return seq";

    private Jedis jedis;
    public RedisLuaSequenceFactory(Jedis jedis){
        this.jedis = jedis;
    }

    @Override
    public long nextValue(String seqKey) {
        return value(seqKey, "-1");
    }

    @Override
    public long dailyNextValue(String seqKey) {
        String today = LocalDate.now().format(FORMATTER);
        String key = today + ":" + seqKey;
        return value(key, String.valueOf(DEFAULT_EXPIRE));
    }

    private long value(String key, String expire){
        Object eval = jedis.eval(LUA_SCRIPT, Collections.singletonList(key), Collections.singletonList(expire));
        return (Long) eval;
    }
}

 lua 脚本如下所示

--[[
   获取全局唯一ID
--]]
local key = KEYS[1]
local expire = ARGV[1]
local isExp = nil ~= expire and -1 ~= tonumber(expire) -- 判断是否设置过期时间
local seq = 0
local exists = redis.call('EXISTS', key) --首先判断key是否存在
if( exists == 0) then
    redis.call('SET', key, 0) -- 如果不存在 则设置 初始值为0
else
    -- 如果存在 则判断当前值是否为负值,如果为负值,如果为负值则初始为0
    seq = redis.call('INCR', key)
    if( seq < 0) then
        redis.call('SET', key, 0)
        seq = 0
    end
end

-- 判断是否存在过期时间,并且是否设置过期时间,如果是则执行设置过期时间
local ttl = redis.call('TTL', key)
if( isExp and ttl == -1) then
    redis.call('EXPIRE', key, expire)
end
return seq

  

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值