Lua是一種功能強大,高效,輕量級,可嵌入的腳本語言。redis中Lua 腳本功能是 Reids 2.6 版本的最大亮點, 通過內嵌對 Lua 環境的支持, Redis 解決了長久以來不能高效地處理 CAS (check-and-set)命令的缺點, 並且可以通過組合使用多個命令, 輕鬆實現以前很難實現或者不能高效實現的模式。
redis中引入lua的優勢:
- 減少網絡開銷:多個請求通過腳本一次發送,減少網絡延遲
- 原子操作:將腳本作爲一個整體執行,中間不會插入其他命令,無需使用事務
- 複用:客戶端發送的腳本永久存在redis中,其他客戶端可以複用腳本
- 可嵌入性:可嵌入JAVA,C#等多種編程語言,支持不同操作系統跨平臺交互
redis中lua腳本應用:使用lua腳本實現分佈式鎖的釋放。
詳細介紹參考:https://www.cnblogs.com/demingblog/p/9542124.html
具體代碼實現如下:
public static final String UNLOCK_LUA;
static {
StringBuilder sb = new StringBuilder();
sb.append("if redis.call(\"get\",KEYS[1]) == ARGV[1] ");
sb.append("then ");
sb.append(" return redis.call(\"del\",KEYS[1]) ");
sb.append("else ");
sb.append(" return 0 ");
sb.append("end ");
UNLOCK_LUA = sb.toString();
}
public boolean releaseLock(String key, String requestId) {
// 釋放鎖的時候,有可能因爲持鎖之後方法執行時間大於鎖的有效期,此時有可能已經被另外一個線程持有鎖,所以不能直接刪除
try {
List<String> keys = new ArrayList<>();
keys.add(key);
List<String> args = new ArrayList<>();
args.add(requestId);
// 使用lua腳本刪除redis中匹配value的key,可以避免由於方法執行時間過長而redis鎖自動過期失效的時候誤刪其他線程的鎖
// spring自帶的執行腳本方法中,集羣模式直接拋出不支持執行腳本的異常,所以只能拿到原redis的connection來執行腳本
RedisCallback<Long> callback = (connection) -> {
Object nativeConnection = connection.getNativeConnection();
// 集羣模式和單機模式雖然執行腳本的方法一樣,但是沒有共同的接口,所以只能分開執行
// 集羣模式
if (nativeConnection instanceof JedisCluster) {
return (Long) ((JedisCluster) nativeConnection).eval(UNLOCK_LUA, keys, args);
}
// 單機模式
else if (nativeConnection instanceof Jedis) {
return (Long) ((Jedis) nativeConnection).eval(UNLOCK_LUA, keys, args);
}
return 0L;
};
Long result = redisTemplate.execute(callback);
return result != null && result > 0;
} catch (Exception e) {
logger.error("release lock occured an exception", e);
} finally {
// 清除掉ThreadLocal中的數據,避免內存溢出
// lockFlag.remove();
}
return false;
}