redis之lua脚本的简单使用

1 事务ACID
  • 原子性 Atomicity

事务要么全部执行成功,要么全部不执行

  • 一致性 Consistency

事务执行的结果必须是使数据库从一个一致性状态转变到另一个一致性状态,有些事务尚未完成就被迫中断,这些未完成的事务对数据库所做的修改有一部分已写入物理数据库,这时数据库就处于一种不正确的状态,或者说是不一致的状态.

  • 隔离性 Isolation

并发的事务是相互隔离的,一个事务的执行不能被其他事务干扰

  • 持久性 Durability

指一个事务一旦提交,它对数据库中对应数据的状态变更就应该是永久性的

2 lua脚本嵌入redis的优势
  • 减少网络开销: 不使用 Lua 的代码需要向 Redis 发送多次请求, 而脚本只需一次即可, 减少网络传输;
  • 原子性: Redis 将整个脚本作为一个原子执行, 无需担心并发, 也就无需事务;
  • 复用: 脚本会永久保存 Redis 中, 其他客户端可继续使用.
3 lua脚本实现分布式事务锁
  1. 实现一: 该实现主要是因为高版本的redis已经可以支持set直接设置过期时间 ,如果存在返回nil

在这里插入图片描述

public boolean tryLock(String lockKey, String value, int expireTime) {
    return redisTemplate.opsForValue().setIfAbsent(lockKey, value, expireTime, TimeUnit.SECONDS);
}
  1. 实现二 使用lua脚本
@Autowired
@Qualifier("redisTemplate")
private RedisTemplate<String, Object> redisTemplate;
@Autowired
private GenericToStringSerializer genericToStringSerializer;
private static final Long SUCCESS = 1L;

public boolean tryLock(String lockKey, String value, int expireTime) {
    String script = "if redis.call('setnx', KEYS[1], ARGV[1]) == 1 then redis.call('expire', KEYS[1], ARGV[2]) return 1 else return 0 end";
    RedisScript<Long> redisScript = new DefaultRedisScript<>(script, Long.class);
    Object result = redisTemplate.execute(redisScript, genericToStringSerializer,genericToStringSerializer,
                                          Collections.singletonList(lockKey),
                                          value, expireTime);
    if (SUCCESS.equals(result)) {
        return true;
    }
    return false;
}
  1. 解锁语句
private static final Long SUCCESS = 1L;
public boolean unLock(String key, String value) {
    String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
    RedisScript<Long> redisScript = new DefaultRedisScript<>(script, Long.class);
    Object result = redisTemplate.execute(redisScript,genericToStringSerializer,genericToStringSerializer, Collections.singletonList(key), value);
    if (SUCCESS.equals(result)) {
        return true;
    }
    return false;
}
4 lua脚本部分解释
  1. KEYS[1]: 标识作为键值的占位符

  2. ARGV[1]: 标识作为值的占位符

  3. redis.call('命令', 参数1, 参数2, ...): 在lua脚本中执行redis命令,返回值可以直接获取到

  4. 1==tonumber('1'): 字符串转数字,返回true

  5. '1'==tostring(1): 转字符串,返回true

  6. local len=#KEYS: 定义变量len = 所有key的总数

  7. for i=1,10 do print(tostring(i)) end: 循环print在redis执行时无法打印,看不出来效果

  8. a..b: 表示a连接b 以下实例使用
    在这里插入图片描述

  9. ipairs: 为数组创建迭代器

5 其他lua脚本
  • m秒内192.168.1.1访问是否超过了n次
public boolean repeatVisit(String ipAddress, int visitNumber,int expireTime){
    String script  = "local cnt = redis.call('INCR',KEYS[1]) if cnt > tonumber(ARGV[1]) then   return 1 end if cnt == 1 then   redis.call('EXPIRE', KEYS[1], ARGV[2]) end return 0";
    RedisScript<Long> redisScript = new DefaultRedisScript<>(script, Long.class);
    Object result = redisTemplate.execute(redisScript,genericToStringSerializer,genericToStringSerializer, Collections.singletonList(IP+ipAddress), visitNumber, expireTime);
    if (SUCCESS.equals(result)) {
        return true;
    }
    return false;
}
  • 批量增加数据
public boolean puts(Map<String,String> map){
    AssertUtils.notNull(map,"redis values is null!");
    String[] keys = new String[map.size()];
    String[] values = new String[map.size()];
    int index = 0;
    for(Map.Entry<String,String> m : map.entrySet()){
        keys[index++] = m.getKey();
        values[index++] = m.getValue();
    }

    String script = "local keys,values=KEYS,ARGV for i,v in ipairs(keys) do redis.call('SET',keys[i],values[i]) end  return true";
    RedisScript<Long> redisScript = new DefaultRedisScript<>(script, Long.class);
    List<String> keysList = Arrays.asList(keys);
    Object result = redisTemplate.execute(redisScript,genericToStringSerializer,genericToStringSerializer, keysList,values);
    if(SUCCESS.equals(result)){
        return true;
    }
    return false;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值