侧边栏壁纸
博主头像
春潮带雨晚来急博主等级

行动起来,活在当下

  • 累计撰写 32 篇文章
  • 累计创建 1 个标签
  • 累计收到 0 条评论

目 录CONTENT

文章目录

lua_redis 分布式锁

Administrator
2023-12-26 / 0 评论 / 0 点赞 / 35 阅读 / 13079 字

# 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}";

```

0

评论区