Redis使用Lua腳本的兩個小問題

Redis使用Lua腳本的兩個小問題

  • 最近在項目中使用redisTemplate 執行Lua 腳本發現兩個比較坑的地方,發現之後其實也很簡單,過程很容易讓人抓狂,

一、整型轉換

1.1 場景

  • 邏輯是通過一段 Lua 腳本去給redis中的一個值加上參數1的值,如果結果大於參數2,那麼就把值歸零並返回1,如果小於參數二就不歸零且返回0,
static String SCRIPT =
            "local sum = redis.call(\"GET\", KEYS[1]) + ARGV[1];\n" +
                    "if sum >=  ARGV[2] then\n" +
                    "\t redis.call(\"set\",KEYS[1], 0)\n" +
                    "     return 1\n" +
                    "   else\n" +
                    "     redis.call(\"INCRBY\",KEYS[1],ARGV[1])\n" +
                    "\t return 0\n" +
                    "   end";
  • 測試代碼:
    @Test
    public void test3() {
        DefaultRedisScript<Integer> redisScript = new DefaultRedisScript<>(SCRIPT,Integer.class);
        List<String> keys = Lists.newArrayList("dynamic_car.car__statistical") ;

        Object execResult = (Object) redisTemplate.execute(redisScript, keys, 3, 10);
        if (execResult != null) {
            System.out.println(execResult);
        } else {
            System.out.println("End ... ");
        }
    }

1.2 問題

  • 錯誤1:執行的時候報錯,首先報的是String無法轉換爲Integer,這裏需要將Lua中的 ARGV[2] 改爲: tomumber(ARGV[2]),將參數2轉換爲數字,即使傳進去就是整型也要轉,日誌如下,提示第二行嘗試將String轉換爲number,這裏也感覺很奇怪,明顯傳參是Integer類型。
 @user_script:2: user_script:2: attempt to compare string with number
  • 錯誤2:在 Lua 中根據條件返回1和0,因此初始化 DefaultRedisScript的泛型是 Integer 類型,執行的時候報錯:拋出異常,但是查看redis Lua的執行卻起了效果,redis中數據已經修改了,由此推測這個異常是和返回值相關,因此把DefaultRedisScript的Integer改成 Long 就好了。
Caused by: io.lettuce.core.RedisException: java.lang.IllegalStateException
  • 錯誤3:這個不算錯誤,算是一個bug,在如下Lua中第一行call中如果get的key不存在,則返回的是false,那麼加運算就會拋出異常提示Boolean類型不能運算,這裏需要校驗
"local present = redis.call(\"GET\", KEYS[1]);"

1.3 修復

  • 最後修改後的Lua和代碼如下:
    public void test3() {
        DefaultRedisScript<Long> getRedisScript = new DefaultRedisScript<>(SCRIPT, Long.class);
        String redisKet = "dynamic_car.car__statistical1";
        List<String> keys = Lists.newArrayList(redisKet);
        Object execResult = (Object) redisTemplate.execute(getRedisScript, keys, 3, 10);
        System.out.println(execResult + " -- "+ execResult.getClass());
    }
  • lua
public static String SCRIPT =
            "local present = redis.call(\"GET\", KEYS[1]);\n" +
                    "if  present == false  then \n" +
                    "\t redis.call(\"set\",KEYS[1], ARGV[1])\n" +
                    "\t return 0 \n" +
                    "\t end" +
                    "\t local sum = redis.call(\"GET\", KEYS[1]) + ARGV[1];\n" +
                    "\t if sum >=  tonumber(ARGV[2]) then\n" +
                    "\t redis.call(\"set\",KEYS[1],0)\n" +
                    "     return 1\n" +
                    "   else\n" +
                    "     redis.call(\"INCRBY\",KEYS[1],ARGV[1])\n" +
                    "\t return 0\n" +
                    "   end";

二、獲取Set元素

2.1 場景

  • 通過 Lua 腳本嘗試去獲取一個 Set 集合的全部元素,如果集合存在,就獲取元素並刪除集合,如果不存在,就什麼都不做;
    public static String SCRIPT = "\n" +
            "\t if redis.call(\"EXISTS\",KEYS[1]) > 0 then\n" +
            "\t local datas = redis.call(\"SMEMBERS\",KEYS[1])\n" +
            "\t  redis.call(\"DEL\",KEYS[1])\n" +
            "\treturn datas\n" +
            "end\n" +
            "\t";
  • java代碼
@Test
    public void test11() {
        DefaultRedisScript<Set> getRedisScript = new DefaultRedisScript<>(SCRIPT1,Set.class);
        List<String> keys = Lists.newArrayList("setSchemaTableIds-1");
        Object execute = redisTemplate.execute(getRedisScript, keys);
        System.out.println(execute);
    }

2.2 問題

  • 執行後拋出異常:而且發現 execute 返回的總是Set裏面的一個元素,很奇怪,改了很多遍 Lua 腳本,定義Set等都不奏效
java.lang.ClassCastException: java.lang.String cannot be cast to java.util.List

2.3 修復

  • 最後將DefaultRedisScript 改成 DefaultRedisScript,就解決問題了,明顯是獲取Set,接收是List,也算一個小坑了。不過網上看到說原始的 Lua 獲取到 Set 裏面的元素順序是不固定的,猜測可能是 redisTemplate 做了封裝,使用List 固定了返回的順序

  • 注意我使用的版本是

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
            <version>2.0.2.RELEASE</version>
        </dependency>
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章