日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > 数据库 >内容正文

数据库

Redis中使用Lua语言

發布時間:2025/3/12 数据库 22 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Redis中使用Lua语言 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

在 Redis 的 2.6 以上版本中,除了可以使用命令外,還可以使用 Lua 語言操作 Redis。從前面的命令可以看出 Redis 命令的計算能力并不算很強大,而使用 Lua 語言則在很大程度上彌補了 Redis 的這個不足。

只是在 Redis 中,執行 Lua 語言是原子性的,也就說 Redis 執行 Lua 的時候是不會被中斷的,具備原子性,這個特性有助于 Redis 對并發數據一致性的支持。

Redis 支持兩種方法運行腳本,一種是直接輸入一些 Lua 語言的程序代碼;另外一種是將 Lua 語言編寫成文件。

在實際應用中,一些簡單的腳本可以采取第一種方式,對于有一定邏輯的一般采用第二種方式。而對于采用簡單腳本的,Redis 支持緩存腳本,只是它會使用 SHA-1 算法對腳本進行簽名,然后把 SHA-1 標識返回回來,只要通過這個標識運行就可以了。

執行輸入 Lua 程序代碼

它的命令格式為:

eval lua-script key-num [key1 key2 key3 ...] [value1 value2 value3 ...]

解說:

eval 代表執行 Lua 語言的命令。Lua-script 代表 Lua 語言腳本。key-num 整數代表參數中有多少個 key,需要注意的是 Redis 中 key 是從 1 開始的,如果沒有 key 的參數,那么寫 0。[key1key2key3...] 是 key 作為參數傳遞給 Lua 語言,也可以不填它是 key 的參數,但是需要和 key-num 的個數對應起來。[value1 value2 value3...] 這些參數傳遞給 Lua 語言,它們是可填可不填的。

這里難理解的是 key-num 的意義,舉例說明。

可以看到執行了兩個 Lua 腳本。

eval "return'hello java'" 0

這個腳本只是返回一個字符串,并不需要任何參數,所以 key-num 填寫了 0,代表著沒有任何 key 參數。按照腳本的結果就是返回了 hello java,所以執行后 Redis 也是這樣返回的。這個例子很簡單,只是返回一個字符串。

eval "redis.call('set',KEYS[1],ARGV[1])" 1 lua-key lua-value

設置一個鍵值對,可以在 Lua 語言中采用 redis.call(command,key[param1,param2…]) 進行操作,其中:

command 是命令,包括 set、get、del 等。Key 是被操作的鍵。param1,param2...代表給 key 的參數。

腳本中的 KEYS[1] 代表讀取傳遞給 Lua 腳本的第一個 key 參數,而 ARGV[1] 代表第一個非 key 參數。

這里共有一個 key 參數,所以填寫的 key-num 為 1,這樣 Redis 就知道 key-value 是 key 參數,而 lua-value 是其他參數,它起到的是一種間隔的作用。

最后我們可以看到使用 get 命令獲取數據是成功的,所以 Lua 腳本運行成功了。

有時可能需要多次執行同樣一段腳本,這個時候可以使用 Redis 緩存腳本的功能,在 Redis 中腳本會通過 SHA-1 簽名算法加密腳本,然后返回一個標識字符串,可以通過這個字符串執行加密后的腳本。

這樣的一個好處在于,如果腳本很長,從客戶端傳輸可能需要很長的時間,那么使用標識字符串,則只需要傳遞 32 位字符串即可,這樣就能提高傳輸的效率,從而提高性能。

首先使用命令:

script load script

這個腳本的返回值是一個 SHA-1 簽名過后的標識字符串,我們把它記為 shastring。通過 shastring 可以使用命令執行簽名后的腳本,命令的格式是:

evalsha shastring keynum [key1 key2 key3 ...] [param1 param2 param3 ...]

下面演示過程。

對腳本簽名后就可以使用 SHA-1 簽名標識運行腳本了。在 Spring 中演示這樣的一個過程,如果是簡單存儲,筆者認為原來的 API 中的 Jedis 對象就簡單些,所以先獲取了原來的 connection 對象,代碼如下所示。

// 如果是簡單的對象,使用原來的封裝會簡易些 ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml"); RedisTemplate redisTemplate = applicationContext.getBean(RedisTemplate.class); applicationContext.getBean(RedisTemplate.class); // 如果是簡單的操作,使用原來的Jedis會簡易些 Jedis jedis = (Jedis) redisTemplate.getConnectionFactory().getConnection().getNativeConnection(); // 執行簡單的腳本 String helloJava = (String) jedis.eval("return 'hello java'"); System.out.println(helloJava); // 執行帶參數的腳本 jedis.eval("redis.call ('set', KEYS [1],ARGV [1])", 1, "lua-key","lua-value"); String luaKey = (String) jedis.get("lua-key"); System.out.println(luaKey); // 緩存腳本,返回shal簽名標識 String shal = jedis.scriptLoad("redis.call('set',KEYS[1], ARGV[1])"); // 通過標識執行腳本 jedis.evalsha(shal, 1, new String[] { "sha-key", "sha-val" }); // 獲取執行腳本后的數據 String shaVal = jedis.get("sha-key"); System.out.println(shaVal); // 關閉連接 jedis.close();

上面演示的是簡單字符串的存儲,但現實中可能要存儲對象,這個時候可以考慮使用 Spring 提供的 RedisScript 接口,它還是提供了一個實現類—— DefaultRedisScript,讓我們來了解它的使用方法。

這里先來定義一個可序列化的對象 Role,因為要序列化所以需要實現 Serializable 接口,代碼如下所示。

public class Role implements Serializable {/*** 注意,對象要可序列化,需要實現Serializable接口,往往要重寫serialVersionUID*/private static final long serialVersionUID = 3447499459461375642L;private long id;private String roleName;private String note;// 省略setter和getter }

這個時候,就可以通過 Spring 提供的 DefaultRedisScript 對象執行 Lua 腳本來操作對象了,代碼如下所示。

ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml"); RedisTemplate redisTemplate = applicationContext.getBean(RedisTemplate.class); // 定義默認腳本封裝類 DefaultRedisScript<Role> redisScript = new DefaultRedisScript<Role>(); // 設置腳本 redisScript.setScriptText("redis.call('set',KEYS[1], ARGV[1]) return redis.call('get', KEYS[1])"); // 定義操作的key列表 List<String> keyList = new ArrayList<String>(); keyList.add("role1"); // 需要序列化保存和讀取的對象 Role role = new Role(); role.setId(1L); role.setRoleName("role_name_1"); role.setNote("note_1"); // 獲得標識字符串 String sha1 = redisScript.getSha1(); System.out.println(sha1); // 設置返回結果類型,如果沒有這句話,結果返回為空 redisScript.setResultType(Role.class); // 定義序列化器 JdkSerializationRedisSerializer serializer = new JdkSerializationRedisSerializer(); // 執行腳本 // 第一個是RedisScript接口對象,第二個是參數序列化器 // 第三個是結果序列化器,第四個是Reids的key列表,最后是參數列表 Role obj = (Role) redisTemplate.execute(redisScript, serializer,serializer, keyList, role); // 打印結果 System.out.println(obj);

注意加粗的代碼,兩個序列化器第一個是參數序列化器,第二個是結果序列化器。這里配置的是 Spring 提供的 JdkSerializationRedisSerializer,如果在 Spring 配置文件中將 RedisTemplate 的 valueSerializer 屬性設置為 JdkSerializationRedisSerializer,那么使用默認的序列化器即可。

執行 Lua 文件

我們把 Lua 變為一個字符串傳遞給 Redis 執行,而有些時候要直接執行 Lua 文件,尤其是當 Lua 腳本存在較多邏輯的時候,就很有必要單獨編寫一個獨立的 Lua 文件。比如編寫了一段 Lua 腳本,代碼如下所示。

redis.call('set',KEYS[1],ARGV[1]) redis.call('set',KEYS[2],ARGV[2]) local n1 = tonumber(redis.call('get',KEYS[1])) local n2 = tonumber(redis.call('get',KEYS[2])) if n1 > n2 thenreturn 1 end if n1 == n2 thenreturn 0 end if n1 < n2 thenreturn 2 end

這是一個可以輸入兩個鍵和兩個數字(記為 n1 和 n2)的腳本,其意義就是先按鍵保存兩個數字,然后去比較這兩個數字的大小。當 n1==n2 時,就返回 0;當 n1>n2 時,就返回 1;當 n1<n2 時,就返回 2,且把它以文件名 test.lua 保存起來。這個時候可以對其進行測試,在 Windows 或者在 Linux 操作系統上執行下面的命令:

redis-cli --eval test.lua key1 key2 , 2 4

注意:redis-cli 的命令需要注冊環境,或者把文件放置在正確的目錄下才能正確執行,這樣就能看到效果,如圖所示。

看到結果就知道已經運行成功了。只是這里需要非常注意命令,執行的命令鍵和參數是使用逗號分隔的,而鍵之間用空格分開。在本例中 key2 和參數之間是用逗號分隔的,而這個逗號前后的空格是不能省略的,這是要非常注意的地方,一旦左邊的空格被省略了,那么 Redis 就會認為“key2,”是一個鍵,一旦右邊的空格被省略了,Redis 就會認為“,2”是一個鍵。

在 Java 中沒有辦法執行這樣的文件腳本,可以考慮使用 evalsha 命令,這里更多的時候我們會考慮 evalsha 而不是 eval,因為 evalsha 可以緩存腳本,并返回 32 位 sha1 標識,我們只需要傳遞這個標識和參數給 Redis 就可以了,使得通過網絡傳遞給 Redis 的信息較少,從而提高了性能。

如果使用 eval 命令去執行文件里的字符串,一旦文件很大,那么就需要通過網絡反復傳遞文件,問題往往就出現在網絡上,而不是 Redis 的執行效率上了。參考上面的例子去執行,下面我們模擬這樣的一個過程,使用 Java 執行 Redis 腳本代碼如下所示。

public static void testLuaFile() {ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");RedisTemplate redisTemplate = applicationContext.getBean(RedisTemplate.class);//讀入文件流File file = new File("G:\\dev\\redis\\test.lua");byte[] bytes = getFileToByte(file);Jedis jedis = (Jedis)redisTemplate.getConnectionFactory().getConnection().getNativeConnection();//發送文件二進制給Redis,這樣REdis就會返回shal標識byte[] shal = jedis.scriptLoad(bytes);//使用返回的標識執行,其中第二個參數2,表示使用2個鍵//而后面的字符串都轉化為了二進制字節進行傳輸Object obj = jedis.evalsha(shal,2, "key1".getBytes(),"key2".getBytes(),"2".getBytes(), "4".getBytes());System.out.println(obj); } /** * 把文件轉化為二進制數組 * @param file 文件 * return二進制數組 */ public static byte[] getFileToByte(File file) {byte[] by = new byte[(int) file.length()];try {InputStream is = new FileinputStream(file);ByteArrayOutputStream bytestream = new ByteArrayOutputStream(); byte[] bb = new byte[2048];int ch;ch = is.read(bb);while (ch != -1) {bytestream.write(bb, 0, ch);ch = is.read(bb);}by = bytestream.toByteArray();} catch (Exception ex) {ex.printStackTrace();}return by; }

如果我們將 sha1 這個二進制標識保存下來,那么可以通過這個標識反復執行腳本,只需要傳遞 32 位標識和參數即可,無需多次傳遞腳本。

從對 Redis 的流水線的分析可知,系統性能不佳的問題往往并非是 Redis 服務器的處理能力,更多的是網絡傳遞,因此傳遞更少的內容,有利于系統性能的提高。

這里采用比較原始的 Java Redis 連接操作 Redis,還可以采用 Spring 提供的 RedisScript 操作文件,這樣就可以通過序列化器直接操作對象了。

總結

以上是生活随笔為你收集整理的Redis中使用Lua语言的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。