Redis Luaスクリプト使用(分散ロック)

19567 ワード

Luaスクリプト
Luaは標準Cで作成され、コードは簡潔で優美で、ほとんどのオペレーティングシステムとプラットフォームでコンパイルし、実行することができます.完全なLua解釈器は200 kにすぎず、現在のすべてのスクリプトエンジンの中でLuaの速度が最も速い.これらはすべてLuaが埋め込みスクリプトとして最適な選択であることを決定します.
RedisでLuaスクリプトを使用するメリット
  • 複数のリクエストが1つにまとめる送信され、リクエスト回数
  • が減少する.
  • redisはluaスクリプトを全体として動作し、redisにおける原子動作
  • を提供する.
  • クライアントによって送信されたスクリプトはredis中に永続的に存在し、他のクライアントはスクリプト
  • を多重化することができる.
  • 高速、移植可能、ソースサイズコンパクト
  • Luaスクリプト使用(spring-boot)
    開発環境
    Spring-bootプロジェクト、spring-boot-starter-data-redis依存に参加
    <parent>
       <groupId>org.springframework.bootgroupId>
       <artifactId>spring-boot-starter-parentartifactId>
       <version>2.2.5.RELEASEversion>
       <relativePath/> 
    parent>
    	<dependencies>
    		<dependency>
    			<groupId>org.springframework.bootgroupId>
    			<artifactId>spring-boot-starter-webartifactId>
    		dependency>
    		<dependency>
    			<groupId>org.springframework.bootgroupId>
    			<artifactId>spring-boot-starter-data-redisartifactId>
    		dependency>
    	dependencies>
    	
    

    maven buildにスクリプトファイルに関するresourceを必ず追加し、luaスクリプトをclassesディレクトリにパッケージします.次のようにします.
    <build>
       <resources>
          <resource>
             <directory>${basedir}/src/main/javadirectory>
             
             <includes>
                <include>**/*.luainclude>
             includes>
          resource>
       resources>
    build>
    

    Luaスクリプトの作成
    ディレクトリ構造src/main/java/demo/script/xx.lua
    redisのsetnxを使用してkeyをロックする
    local expire=tonumber(ARGV[2])
    local ret=redis.call('set',KEYS[1],ARGV[1],'NX','PX',expire)
    local strret=tostring(ret)
    --       ,            "table: 0x55d040f2edc0",    false
    redis.call('set','result',strret)
    if strret == 'false' then
        return false
    else
        return true
    end
    

    lock.lua
    KEYS,ARGVキーワードの意味:
    KEYS[n]は入力パラメータkeyのプライマリ・キーの最初のプライマリ・キー値を表し、0はなしを表し、複数入力可能であり、KEYS[2]が入力した2番目のプライマリ・キー
    ARGV[n]は、入力されたパラメータ値を表し、同様に複数の入力が可能である
    ロック解除スクリプト
    redis.call('del','result')
    if redis.call('get',KEYS[1]) == ARGV[1] then
        return redis.call('del',KEYS[1])
    else
        return 0
    end
    

    unlock.lua
    Luaスクリプト読み出しパッケージ
    パッケージされたluaスクリプトをSpringコンテナに追加し、呼び出しが容易になります.
    @Configuration
    public class RedisScriptConfig {
    
        @Bean
        public RedisScript<Boolean> lockScript(){
            RedisScript<Boolean> redisScript=null;
            try {
                ScriptSource scriptSource=new ResourceScriptSource(new ClassPathResource("demo\\script\\lock.lua"));
                redisScript = RedisScript.of(scriptSource.getScriptAsString(), Boolean.class);
            } catch (IOException e) {
                e.printStackTrace();
            }
            return redisScript;
        }
    
        @Bean
        public RedisScript<Long> unlockScript(){
            RedisScript<Long> redisScript=null;
            try {
                ScriptSource scriptSource = new ResourceScriptSource(new ClassPathResource("demo\\script\\unlock.lua"));
                redisScript=RedisScript.of(scriptSource.getScriptAsString(),Long.class);
            } catch (IOException e) {
                e.printStackTrace();
            }
    
            return redisScript;
        }
    }
    

    RedisTemplateがluaスクリプトを呼び出す
    @RestController
    public class RedisLuaController {
        private final Log logger= LogFactory.getLog(RedisLuaController.class);
    
        @Autowired
        private RedisTemplate<String,String> redisTemplate;
    
        @Autowired
        RedisScript<Boolean> lockScript;
    
        @Autowired
        RedisScript<Long> unlockScript;
    
        @RequestMapping("distrLock/{key}/{uuid}")
        public void Lock(@PathVariable String key,@PathVariable String uuid){
            try {
                Boolean flag = redisTemplate.execute(lockScript, Collections.singletonList(key), uuid, "50000");
                logger.info("locked:{}"+flag);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    
        @RequestMapping("distrUnlock/{key}/{uuid}")
        public void unlock(@PathVariable String key,@PathVariable String uuid){
            try {
                Long unlocked = redisTemplate.execute(unlockScript, Collections.singletonList(key), uuid);
                logger.info("unlock status "+unlocked.toString());
            } catch (Exception e) {
                logger.error("    "+e.getMessage());
            }
        }
    }
    

    まとめ
    luaスクリプトがredisに現れる原子性は、いくつかの複雑な操作にトランザクション保証を提供します.2つ目は、ネットワークのオーバーヘッドとリクエスト回数を削減します.この二つの点は筆者が最も重要だと思う.