# Lua
- 能够保证 准备一系列连续的操作不会中断。即:将所有连续的操作看做是一个操作一个事务,其他的操作根本不能插队,要等这一系列的操作执行完了才可以。
- 这一系列的操作,就是lua里面的执行脚本。原子性
```
①
printf " war3sxkcs "
②
eval "return redis.call('set','key','value')" 0
# 带有返回值的 执行redis的lua脚本方法;调用set方法,存入的键为"key",值为"value"。
#即: get key ==> value
③
EVAL "return redis.call ('SET',KEYS[2],ARGV[3])" 3 imooc1 imooc2 imooc3 imooc4 imooc5 imooc6 imooc7
④
# 3 代表:将后面的7个元素中的前三个装进KEYS[i]中,剩余的就装进ARGV[i]中。
# 所以此处的KEYS[i]和ARGV[i]都是数组了。
# KEYS[2],ARGV[3] 的意思是:KEYS[2]中的第二个元素作为"key",ARGV[3]中的第三个元素作为"value"
#即: get imooc2 ==> imooc6 (获取其他的key均为空。因为只有把KEYS[2]中第二个元素拿来作为了key)
⑤
EVAL "redis.pcall ('SET',KEYS[2],ARGV[3]);redis.pcall('SET',KEYS[3],ARGV[1])" 3 imooc1 imooc2 imooc3 imooc4 imooc5 imooc6 imooc7
```
- 上面最后一个命令⑤中使用了<code>pcall</code>
- 如果只是使用<code>call</code>并且是有多个call需要同步执行的话,如果报了错出现了异常,会将异常打印出来,并且不会执行异常后面的代码
- 但是如果是使用了<code>pcall</code>的话,不会打印错误异常,二是将错误异常抛出去,继续执行后续代码
---
### 将lua脚本缓存到redis本地
- 将脚本缓存到redis后会返回一个64bit的SHA密文,执行(调用)这个密文即可。
- 或者:任何一个lua脚本都可以进行加密成一个64位的SHA密文,将生成的这个64位SHA密码带到redis的cli端去调用执行即可。
```
①: 生成
SCRIPT LOAD "return redis.call('SET',KEYS[1],ARGV[1])" 执行;
返回: sd5asf456ds5dfg5dfg4.......64bit ........4d5as46d
②:调用
EVALSHA sd5asf456ds5dfg5dfg4.......64bit ........4d5as46d 2 testImooc1 testImooc2 testImooc3
get testImooc1 ==> testImooc3
清除脚本: SCRIPT FLUSH (redis中的缓存里面就没有这个脚本了,就会执行失败!)
```
##### 其他命令:
- kill (杀死一个正在执行的lua脚本) (一般用于redis处于阻塞状态)
```
①:具体kill
./redis-cli --ldb --eval /tmp/script.lua mykey somekey , agr1 arg2
②:笼统kill
SCRIPT KILL
```
- EXISTS (查看一个脚本是否存在)
```
SCRIPT EXISTS sd5asf456ds5dfg5dfg4.......64bit ........4d5as46d 执行
返回: (integer) 0 或者 (integer) 1 表示存在或者不存在
```
- [x] 一般使用: ++先查看一个脚本命令是否存在,再去执行一个相应的脚本命令。最好的是不能够通过异常报错来决定是否处理各种事情!++
#### redis 分布式锁
- 1
```
public interface Lock {
/** 测试 加锁 */
boolean tryLock(Jedis jedis, String key, String val, Long ttl);
/** 释放锁 **/
boolean releaseLock(Jedis jedis, String key, String val);
}
class LockInitial implements Lock {
private static final String LOCK_SUCCESS = "OK";
private static final String SET_IF_NOT_EXIST = "NX";
private static final String SET_WITH_EXPIRE_TIME = "EX";
private static final Long EXCUTE_SUCCESS = 1L;
@Override
public boolean tryLock(Jedis jedis, String key, String val, Long ttl) {
String result = jedis.set(key, val, SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME, ttl);
return LOCK_SUCCESS.equalsIgnoreCase(result);
}
@Override
public boolean releaseLock(Jedis jedis, String key, String val) {
/** 自己加的锁 自己去释放 **/
if (val.equals(jedis.get(key))) {
return jedis.del(key) > 0;
}
return false;
}
}
```
- 2 (单机情况下是这样的;集群情况下是有框架的 redision)
<code>requestId</code>就是拿这个去锁。释放锁的时候它也是唯一的释放条件,不然任何一个线程都可能会去解锁的
```
@Component
public class RedisUtils {
private static final String LOCK_SUCCESS = "OK";
private static final String SET_IF_NOT_EXIST = "NX";
private static final String SET_WITH_EXPIRE_TIME = "PX";
private static final Long EXCUTE_SUCCESS = 1L;
/**lua脚本 在redis中 lua脚本执行是串行的 原子的 */
private static final String UNLOCK_LUA=
"if redis.call('get', KEYS[1]) == ARGV[1] then " +
" return redis.call('del', KEYS[1]) " +
"else " +
" return 0 " +
"end";
/**
*
* @Description 扣减库存lua脚本
* @Author JackWang<coder520.com>
* @Date 2018/3/27 16:58
* @Param
* @Return 0 key不存在 错误 -1 库存不足 返回list 扣减成功
*/
public static final String STOCK_REDUCE_LUA=
"local stock = KEYS[1]\n" +
"local stock_lock = KEYS[2]\n" +
"local stock_change = tonumber(ARGV[1])\n" +
"local stock_lock_change = tonumber(ARGV[2])\n" +
"local is_exists = redis.call(\"EXISTS\", stock)\n" +
"if is_exists == 1 then\n" +
" local stockAftChange = redis.call(\"INCRBY\", stock,stock_change)\n" +
" if(stockAftChange<0) then\n" +
" redis.call(\"DECRBY\", stock,stock_change)\n" +
" return -1\n" +
" else \n" +
" local stockLockAftChange = redis.call(\"INCRBY\", stock_lock,stock_lock_change)\n" +
" return {stockAftChange,stockLockAftChange}\n" +
" end " +
"else \n" +
" return 0\n" +
"end";
public static final String STOCK_CACHE_LUA =
"local stock = KEYS[1] " +
"local stock_lock = KEYS[2] " +
"local stock_val = tonumber(ARGV[1]) " +
"local stock_lock_val = tonumber(ARGV[2]) " +
"local is_exists = redis.call(\"EXISTS\", stock) " +
"if is_exists == 1 then " +
" return 0 " +
"else " +
" redis.call(\"SET\", stock, stock_val) " +
" redis.call(\"SET\", stock_lock, stock_lock_val) " +
" return 1 " +
"end";
@Autowired
private RedisTemplate<String,String> redisTemplate;
/**
*
* @Description 分布式锁
* @Author JackWang<coder520.com>
* @Date 2018/3/27 17:03
* @Param [lockKey, requestId, expireTime]
* @Return boolean
*/
public boolean distributeLock(String lockKey, String requestId, int expireTime){
String result = redisTemplate.execute((RedisCallback<String>) redisConnection -> {
JedisCommands commands = (JedisCommands)redisConnection.getNativeConnection();
return commands.set(lockKey,requestId,SET_IF_NOT_EXIST,SET_WITH_EXPIRE_TIME,expireTime);
});
if (LOCK_SUCCESS.equals(result)) {
return true;
}
return false;
}
/**
*
* @Description 释放分布式锁
* @Author JackWang<coder520.com>
* @Date 2018/3/27 17:04
* @Param [lockKey, requestId]
* @Return boolean
*/
public boolean releaseDistributelock(String lockKey, String requestId){
Object result = redisTemplate.execute((RedisCallback<Object>) redisConnection -> {
Jedis jedis = (Jedis)redisConnection.getNativeConnection();
return jedis.eval(UNLOCK_LUA, Collections.singletonList(lockKey), Collections.singletonList(requestId));
});
if (EXCUTE_SUCCESS.equals(result)) {
return true;
}
return false;
}
/**
*
* @Description 缓存sku库存 以及锁定库存
* @Author JackWang<coder520.com>
* @Date 2018/3/27 17:04
* @Param [stockKey, stockLockKey, stock, stockLock]
* @Return boolean
*/
public boolean skuStockInit(String stockKey,String stockLockKey,String stock,String stockLock){
Object result = redisTemplate.execute((RedisCallback<Object>) redisConnection -> {
Jedis jedis = (Jedis)redisConnection.getNativeConnection();
return jedis.eval(STOCK_CACHE_LUA, Collections.unmodifiableList(Arrays.asList(stockKey,stockLockKey))
,Collections.unmodifiableList(Arrays.asList(stock, stockLock)));
});
if (EXCUTE_SUCCESS.equals(result)) {
return true;
}
return false;
}
/**
*
* @Description 扣减库存
* @Author JackWang<coder520.com>
* @Date 2018/3/27 17:07
* @Param [stockKey, stockLockKey, stockChange, stockLockChange]
* @Return java.lang.Object
*/
public Object reduceStock(String stockKey,String stockLockKey,String stockChange,String stockLockChange){
Object result = redisTemplate.execute((RedisCallback<Object>) redisConnection -> {
Jedis jedis = (Jedis)redisConnection.getNativeConnection();
return jedis.eval(STOCK_REDUCE_LUA, Collections.unmodifiableList(Arrays.asList(stockKey,stockLockKey))
,Collections.unmodifiableList(Arrays.asList(stockChange, stockLockChange)));
});
return result;
}
}
```
##### 3.redis事务中操作多个key
- 需要在key上打花括号,redis可以识别将所有打花括号的在同一个槽中执行
```
public static final String CACHE_PRODUCT_STOCK = "{product:stock}";
public static final String CACHE_PRODUCT_STOCK_LOCK = "{product:stock:lock}";
public static final String ORDER_RETRY_LOCK = "{trade:retry:lock}";
```
评论区