Redis Lua Java 随机编号 用户编号

Posted 伍有晓俐

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Redis Lua Java 随机编号 用户编号相关的知识,希望对你有一定的参考价值。

用户id主键递增的情况下,要为用户生产随机的用户编号,效果如下:

这是一种伪随机的方案,前缀随机,后面的编号递增

reids脚本

local prefix=0
local exists = redis.call('EXISTS', KEYS[1])
if exists == 1 then
-- 随机出set的一个前缀
    prefix= redis.call('SRANDMEMBER', KEYS[1])
else
-- 初始化set KEYS[1] 默认1至9 10个前缀
    for i=1, 9 do redis.call('SADD', KEYS[1],i) end
    prefix = redis.call('SRANDMEMBER', KEYS[1])
end
-- 随机出set的前缀 对应的值递增
local currentNum  = redis.call('hIncrBy', KEYS[2], prefix, ARGV[1])
if (currentNum > tonumber(ARGV[2])) then
  -- 有进位 前缀值加9,对应的旧前缀从set移除,增加新前缀
  local newPrefix = prefix + 9
  local newCurrentNum = redis.call('hIncrBy', KEYS[2], newPrefix, ARGV[1])
  redis.call('srem', KEYS[1], prefix)
  redis.call('SADD', KEYS[1], newPrefix)
  return (newPrefix * tonumber(ARGV[2])) + newCurrentNum
else
  -- 无进位 直接返回
  return (prefix * tonumber(ARGV[2])) + currentNum
end

前缀

前缀对应的自增后的值

Redis客户端测试
把脚本的注释去掉

redis:测试:1>eval "local prefix=0 local exists = redis.call('EXISTS', KEYS[1]) if exists == 1 then prefix= redis.call('SRANDMEMBER', KEYS[1]) else for i=1, 9 do redis.call('SADD', KEYS[1],i) end prefix = redis.call('SRANDMEMBER', KEYS[1]) end local currentNum = redis.call('hIncrBy', KEYS[2], prefix, ARGV[1]) if (currentNum > tonumber(ARGV[2])) then local newPrefix = prefix + 9 local newCurrentNum = redis.call('hIncrBy', KEYS[2], newPrefix, ARGV[1]) redis.call('srem', KEYS[1], prefix) redis.call('SADD', KEYS[1], newPrefix) return (newPrefix * tonumber(ARGV[2])) + newCurrentNum else return (prefix * tonumber(ARGV[2])) + currentNum end" 2 account_random_number_prefix_set account_random_number_hash 2332 10000
"13031"
redis测试:1>

Java 代码

	
    /**
     * 五位数最大值(不包含)
     */
    private static final Long FIVE_DIGITS_MAX = 100000L;
    /**
     * 用户随机ID 前缀
     * 首批前缀序号为
     * 递增规则如下:
     * 1 --10 --19 --28
     * 2 --11 --20 --29
     * 3
     * 4
     * 5
     * 6
     * 7
     * 8
     * 9 --18 --27 --36
     * 并且位数超过当前最大值 递增9为新前缀序号
     */
    String ACCOUNT_RANDOM_NUMBER_PREFIX_SET = "account_random_number_prefix_set";

    /**
     * 用户前缀下的自增number
     */
    String ACCOUNT_RANDOM_NUMBER_HASH = "account_random_number_hash";

    /**
     * lua脚本生成用户随机编号
     */
    public Long doCreateUserNumberLua() 
        /*
         * keys1 随机前缀set 默认1至9,超过
         * keys2 hash 存储前缀set对应的递增后的值
         * argv1 随机数
         * argv2 进位数 这里是5位 超过99999前缀set对应一个set进位
         * 例如:
         *  1.随机到前缀1 随机数为222 则生成的编号为 100222   hash存储前缀为1--》 222
         *  2.再次随机到前缀1 随机数为333 则生成的编号为 100555  hash存储前缀为1--》 222
         *  进位的情况
         *  3.再次随机到前缀1 随机数为555(hash存储前缀为1--》 99998)则删除前缀为1的set,生成1+9=10的前缀,生成的编号为 1000555
         */
        String script = "local prefix=0\\n" +
                "local exists = redis.call('EXISTS', KEYS[1])\\n" +
                "if exists == 1 then\\n" +
                "    prefix= redis.call('SRANDMEMBER', KEYS[1])\\n" +
                "else\\n" +
                "    for i=1, 9 do redis.call('SADD', KEYS[1],i) end\\n" +
                "    prefix = redis.call('SRANDMEMBER', KEYS[1])\\n" +
                "end\\n" +
                "local currentNum  = redis.call('hIncrBy', KEYS[2], prefix, ARGV[1])\\n" +
                "if (currentNum > tonumber(ARGV[2])) then\\n" +
                "  local newPrefix = prefix + 9\\n" +
                "  local newCurrentNum = redis.call('hIncrBy', KEYS[2], newPrefix, ARGV[1])\\n" +
                "  redis.call('srem', KEYS[1], prefix)\\n" +
                "  redis.call('SADD', KEYS[1], newPrefix)\\n" +
                "  return (newPrefix * tonumber(ARGV[2])) + newCurrentNum\\n" +
                "else\\n" +
                "  return (prefix * tonumber(ARGV[2])) + currentNum\\n" +
                "end";
        return redisTemplate.execute(new DefaultRedisScript<>(script, Long.class), Arrays.asList(ACCOUNT_RANDOM_NUMBER_PREFIX_SET, ACCOUNT_RANDOM_NUMBER_HASH), RandomUtil.randomInt(100, 500), FIVE_DIGITS_MAX);
    

测试方法


    @Test
    public void initUserIdNumber() throws InterruptedException 
        CopyOnWriteArraySet<Long> set = new CopyOnWriteArraySet<>();
        CountDownLatch latch = new CountDownLatch(1000);
        for (int i = 0; i < 1000; i++) 
            new Thread(() -> 
                long lon = accountRegisterService.doCreateUserNumberLua();
                System.out.println("lon=" + lon);
                set.add(lon);
                latch.countDown();
            ).start();
        
        latch.await();
        System.out.println("initUserIdNumber 完成");
        System.out.println(set.size());
        System.out.println(JSON.toJSONString(set));
    

以上是关于Redis Lua Java 随机编号 用户编号的主要内容,如果未能解决你的问题,请参考以下文章

如何使用redis生成唯一编号及原理

Java 生成32位随机字符编号

未使用的随机 dosieid 编号

Lua怎么把数组编号1和2的项目交换一下?

尝试使用“已更改”索引编号 | Roblox Lua

java生成订单编号工具类