redis 腳本介紹

redis腳本簡介

  腳本使用 Lua 解釋器來執行腳本。 Redis 從2.6版本開始,經過內嵌支持 Lua 環境。執行腳本的經常使用命令爲 EVAL。php

redis lua腳本的好處

  • 減小網絡開銷。能夠將多個請求經過腳本的形式一次發送,減小網絡延遲。
  • 原子操做。redis將整個腳本當作一個總體去執行,中間不會被其餘命令插入。所以無需擔憂腳本執行過程當中會出現競態條件
  • 複用。客戶端發送的腳本會永久保存在redis中,這樣,能夠複用這一腳本而不用使用代碼完成相同的邏輯。

redis lua腳本如何使用

redis內嵌lua解釋器,從redis 2.6版本開始,redis可使用EVAL命令執行lua腳本git

  • EVAL script numkeys key [key ...] arg [arg ...]
    • 說明:EVAL命令使用lua解釋器執行腳本
    • 參數:
      • script :參數是一段lua5.1腳本程序,也能夠是lua腳本的地址。腳本沒必要(也不該該)定義爲一個lua函數
      • numkeys:用於指定鍵名參數的個數
      • keyN:從EVAL的第三個參數開始算起,表示在腳本中全部用到的redis鍵(key),這些鍵名參數能夠在lua腳本中經過全局變量KEYS數組的形式使用,用1爲基址的形式訪問(KEYS[1],KEYS[2],....,KEYS[N])
      • argN:附加參數,在lua腳本中經過全局變量ARGV數組的形式使用,用1爲基址的形式訪問(ARGV[1],ARGV[2],.....,ARGV[N])
    • 返回:lua腳本中指定的返回值
    • 實例:
    10.117.8.188:6379> eval "return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}" 2 string1 string2 first second
    1) "string1"
    2) "string2"
    3) "first"
    4) "second"
    
    10.117.8.188:6379> EVAL "redis.call('set',KEYS[1],ARGV[1]);redis.call('set',KEYS[2],ARGV[2]);return 'ok';" 2 string1 string2 str1111 str2222
    "ok"
    • 注:
      • {}在lua裏是指數據類型table,相似數組。
      • redis.call()能夠調用redis命令,redis.pcall()有着相同的功能
  • EVALSHA sha1 numkeys key [key ...] arg [arg ...]
    • 說明:EVALSHA命令根據給定的sha1校驗碼,執行緩存在redis中的lua腳本;將腳本緩存到服務器可使用SCRIPT LOAD命令實現。這個命令和EVAL命令基本類似,除了傳的第一個參數不一樣以外,其餘都同樣
    • 參數:
      • sha1:經過SCRIPT LOAD生成的sha1校驗碼
      • 其餘參數和EVAL命令同樣
    • 返回:lua腳本中指定的返回值
    • 實例:
    10.117.8.188:6379> SCRIPT LOAD "return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}"
    "a42059b356c875f0717db19a51f6aaca9ae659ea"
    10.117.8.188:6379> EVALSHA a42059b356c875f0717db19a51f6aaca9ae659ea 2 string1 string2 first second
    1) "string1"
    2) "string2"
    3) "first"
    4) "second"
  • SCRIPT LOAD script
    • 說明:SCRIPT LOAD命令用於將腳本script添加到腳本緩存中,並不當即執行該腳本,若是給定的腳本已經在緩存中存在了,那不執行任何操做;EVAL命令也會將腳本script添加到腳本緩存中,可是會當即執行;在腳本加入到緩存以後,經過EVALSHA命令,可使用腳本的sha1校驗和調用該腳本;腳本能夠在緩存中保存無限長的時間,知道執行 SCRIPT FLUSH命令爲止。
    • 參數:script(lua腳本)
    • 返回:給定的lua腳本的sha1校驗碼
    • 實例:
    10.117.8.188:6379> SCRIPT LOAD "return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}"
    "a42059b356c875f0717db19a51f6aaca9ae659ea"
  • SCRIPT EXISTS sha1 [sha1 ...]
    • 說明:用於校驗指定的腳本是否已被保存到緩存中
    • 參數:shaN(指定的腳本的sha1校驗碼)
    • 返回:一個列表,包含0和1;0表明指定的腳本未被保存到緩存中,1表明指定腳本已被保存到緩存中
    • 實例:
    10.117.8.188:6379> SCRIPT EXISTS a42059b356c875f0717db19a51f6aaca9ae659ea
    1) (integer) 1
    10.117.8.188:6379> SCRIPT EXISTS np2059b356c875f0717db19a51f6aaca9ae659ea
    1) (integer) 0
    10.117.8.188:6379> SCRIPT EXISTS a42059b356c875f0717db19a51f6aaca9ae659ea np2059b356c875f0717db19a51f6aaca9ae659ea
    1) (integer) 1
    2) (integer) 0
  • SCRIPT FLUSH
    • 說明:用於清除全部腳本緩存
    • 返回:老是返回OK
    • 實例:
    10.117.8.188:6379> SCRIPT FLUSH
    OK
    10.117.8.188:6379> SCRIPT EXISTS a42059b356c875f0717db19a51f6aaca9ae659ea
    1) (integer) 0
  • SCRIPT KILL
    • 說明:殺死正在執行的腳本,當且僅當該腳本沒有執行任何寫操做,該命令才生效;主要用於終止執行時間過長的腳本,例如某腳本進入死循環;SCRIPT KILL 執行以後,當前正在執行的腳本會被終止,執行這個腳本的客戶端會從EVAL命令的阻塞中退出,並返回一個錯誤
    • 返回: 執行成功返回OK,失敗則返回一個錯誤信息
    • 實例:
    10.117.8.188:6379> SCRIPT KILL
    (error) NOTBUSY No scripts in execution right now. //沒有腳本在執行
    (error) ERR Sorry the script already executed write commands against the dataset. You can either wait the script termination or kill the server in an hard way using the SHUTDOWN NOSAVE command.//嘗試殺死一個執行過寫操做的腳本
    (error) ERR Error running script (call to f_694a5fe1ddb97a4c6a1bf299d9537c7d3d0f84e7): Script killed by user with SCRIPT KILL... // 腳本被殺死後,客戶端返回的錯誤信息

    命令行中怎麼使用redis腳本

    直接使用redis-cli命令,格式以下:
    redis-cli --eval lua_file key1 key2 , arg1 arg2 arg3
    • redis-cli要想在命令行使用,須要在redis.conf中配置bind 127.0.0.1,不然會報錯:Could not connect to Redis at 127.0.0.1:6379: Connection refused
    • eval 後面參數是lua腳本文件,.lua後綴
    • 不用寫numkeys,鍵名和附加參數之間使用「,」隔開。注意,先後要有空格。
  • 實例:
setGet.lua
local num = redis.call('GET',KEYS[1]);
if not ARGV[1] then
   redis.call('DEL',KEYS[1]);
else
   redis.call('SET',KEYS[1],ARGV[1]);
end
return num;

命令行運行
www@iZ23dvyt70vZ:~ $ redis-cli --eval getSet.lua ll , 6
"5"
www@iZ23dvyt70vZ:~ $ redis-cli --eval getSet.lua ll , 8
"6"
www@iZ23dvyt70vZ:~ $ redis-cli --eval getSet.lua ll 
"8"
www@iZ23dvyt70vZ:~ $ redis-cli --eval getSet.lua ll ,9
(nil)

phpredis中使用redis腳本

  • eval(script,[args],[num_keys])
    • 說明:執行redis服務器的一個lua腳本,對應Redis::EVAL
    • 參數:
      • script:一段lua腳本,string類型
      • args:lua腳本中使用到的redis鍵和附加參數組成的數組,可選參數
      • num_keys:指定鍵名參數的個數,可選參數
    • 返回:返回值由lua腳本的返回決定
    • 實例:
    public function redisTestAction(){
        $redis = new \Redis();
        $redis->connect('10.117.8.188',6379);
        // $redis->connect('127.0.0.1',6379); 兩個均可以
    
        $luaScript = <<<EOF
        local num = redis.call('GET',KEYS[1]);
        if not ARGV[1] then
            redis.call('DEL',KEYS[1]);
        else
            redis.call('SET',KEYS[1],ARGV[1]);
        end
        return num;
    EOF;
        $res = $redis->eval($luaScript,['ll',20],1);
        var_dump($res);
    // 若是key:ll不存在,則res=false,ll不爲空,則返回ll的舊值,並將新值賦值給ll
    }
  • evalSha(script_sha,[args],[num_keys])
    • 說明:執行一個redis服務端的lua腳本,只是腳本是有該腳本生成的sha1散列值script_sha代替;script_sha散列值必須在執行evalSha以前,使用SCRIPT LOAD命令生成。
    • 參數:
      • script_sha:lua腳本生成的sha1散列,string類型
      • args:lua腳本的參數,array類型,可選參數
      • num_keys:指定參數的個數,int類型,可選參數
    • 返回:返回值由lua腳本的返回決定
    • 實例:
    public function redisTestAction(){
        $redis = new \Redis();
        $redis->connect('10.117.8.188',6379);
    
        $luaScript = <<<EOF
        local num = redis.call('GET',KEYS[1]);
        if not ARGV[1] then
            redis.call('DEL',KEYS[1]);
        else
            redis.call('SET',KEYS[1],ARGV[1]);
        end
        return num;
    EOF;
        $luasha = $redis->script('load',$luaScript);
        var_dump($luasha);
        echo "<br/>";
        $res = $redis->evalSha($luasha,['ll',335],1);
        var_dump($res);
    
        //返回結果
        //string(40) "5f31d46a307c1c36487a8af9bd263a29ab4fedcd"
        //string(3) "330"
        //string(40) "5f31d46a307c1c36487a8af9bd263a29ab4fedcd" 
        //string(3) "335"
    }
  • script(command,[options])
    • 說明:執行Redis腳本相關的命令對當前客戶端作各類操做
    • 參數:
      • command:redis腳本相關的各個命令
      • options:根據第一個參數,肯定第二個參數
    • 返回:
      • SCRIPT LOAD:將在成功時返回所傳遞腳本的SHA1散列,在失敗時返回FALSE
      • SCRIPT FLUSH:老是返回true
      • SCRIPT KILL:若是殺死了當前的腳本則返回true,失敗則返回false
      • SCRIPT EXISTS:對全部的腳本進行判斷是否存在,返回一個包含true和false的數組
    • 實例:
    public function redisTestAction(){
        $redis = new \Redis();
        $redis->connect('10.117.8.188',6379);
    
        $luaScript = <<<EOF
        local num = redis.call('GET',KEYS[1]);
        if not ARGV[1] then
            redis.call('DEL',KEYS[1]);
        else
            redis.call('SET',KEYS[1],ARGV[1]);
        end
        return num;
    EOF;
        $res = $redis->script('load', $luaScript);
        var_dump($res); // 5f31d46a307c1c36487a8af9bd263a29ab4fedcd
        $res = $redis->script('exists', '5f31d46a307c1c36487a8af9bd263a29ab4fedcd');
        var_dump($res); // array(1) { [0]=> int(1) }
        $res = $redis->script('flush');
        var_dump($res); // bool(true)
        $res = $redis->script('kill');
        var_dump($res); // bool(false)
    }
  • getLastError()
    • 說明:獲取最後一個錯誤消息(若是有的話)
    • 參數:無
    • 返回:若是存在錯誤,則返回錯誤信息,不存在錯誤則返回null
    • 實例:
    public function redisTestAction(){
        $redis = new \Redis();
        $redis->connect('10.117.8.188',6379);
    
        $luaScript = <<<EOF
        local num = redis.call('GET',KEYS[1]);
        if not ARGV[1] then
            redis.call('DEL',KEYS[1]);
        else
            redis.call('SET',KEYS[1],ARGV[1]);
        end
        return num;
    EOF;
        $err1 = $redis->getLastError();
        var_dump($err1); //     NULL 
        echo "<br>";
        $redis->eval($luaScript,['lrre',4]);
        $err = $redis->getLastError();
        var_dump($err); 
        //string(74) "@user_script: 1: Lua redis() command arguments must be strings or integers"
    }
  • clearLastError()
    • 說明:清除最後的一個錯誤信息
    • 參數: 無
    • 返回: 老是返回true
    • 實例:
    public function redisTestAction(){
        $redis = new \Redis();
        $redis->connect('10.117.8.188',6379);
    
        $luaScript = <<<EOF
        local num = redis.call('GET',KEYS[1]);
        if not ARGV[1] then
            redis.call('DEL',KEYS[1]);
        else
            redis.call('SET',KEYS[1],ARGV[1]);
        end
        return num;
    EOF;
        $redis->eval($luaScript,['lrre',4]);
        $err = $redis->getLastError();
        var_dump($err);
        // string(74) "@user_script: 1: Lua redis() command arguments must be strings or integers" 
        echo "<br>";
        $res = $redis->clearLastError();
        var_dump($res); // bool(true) 
        echo "<br>";
        $err = $redis->getLastError();
        var_dump($err);// NULL
    }
  • **_prefix(key)**
    • 說明:爲指定的鍵值key設置前綴prefix
    • 參數:key(給定的鍵值)
    • 返回:返回prefixkey
    • 實例:
    public function redisTestAction(){
        $redis = new \Redis();
        $redis->connect('10.117.8.188',6379);
        $redis->setOption(\Redis::OPT_PREFIX, 'prefix1:'); //全部新建的key都會帶有該前綴prefix1:
        $res1 = $redis->set('gg','gggggt'); // key爲gg,實際在redis中該鍵爲prefix1:gg
        var_dump($res1);
        $res = $redis->_prefix('gg');
        var_dump($res); // 返回:prefix1:gg
        $res2 = $redis->_prefix('tt'); //鍵tt在redis中不存在,一樣返回prefix1:tt,並不會新建一個鍵prefix1:tt
        var_dump($res2);// 返回 prefix1:tt
    }
  • **_serialize(value)**
    • 說明:序列化一個值value,可使用任意的序列化器對value進行序列化,若是沒有設置序列化器,phpredis則會把array值更改成「array」,對象Objects值更改成「Objects」
    • 參數:value(準備序列化的值)
    • 返回:序列化以後的值
    • 實例:實例來自phpredis的github,因爲我得redis版本內沒有_serialize方法,因此沒有驗證
    public function redisTestAction(){
        $redis = new \Redis();
        $redis->connect('10.117.8.188',6379);
    
        $redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_NONE);
        $redis->_serialize("foo"); // returns "foo"
        $redis->_serialize(Array()); // Returns "Array"
        $redis->_serialize(new stdClass()); // Returns "Object"
    
        $redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_PHP);
        $redis->_serialize("foo"); // Returns 's:3:"foo";'
    }
  • **_unserialize(value)**
    • 說明:對序列化後的值value進行反序列化
    • 參數:value(序列化以後的值)
    • 返回:序列化以前的值
    • 實例:
    public function redisTestAction(){
        $redis = new \Redis();
        $redis->connect('10.117.8.188',6379);
        $redis->setOption(\Redis::OPT_SERIALIZER, \Redis::SERIALIZER_PHP);
        $data = ['it'=>'it','if'=>'kl','ig'=>'gj'];
        $res = serialize($data);
        var_dump($res); // string(60) "a:3:{s:2:"it";s:2:"it";s:2:"if";s:2:"kl";s:2:"ig";s:2:"gj";}"
        $ret = $redis->_unserialize($res);
        var_dump($ret);//array(3) {["it"]=>string(2) "it" ["if"]=>string(2) "kl" ["ig"]=>string(2) "gj"}
    }
相關文章
相關標籤/搜索