三、Redis事务和分布式锁

三、Redis事务

3.1、Redis事务介绍

  • Redis的事务是通过MULTI,EXEC,DISCARD和WATCH这四个命令来完成的。
  • Redis的单个命令都是原子性的,所以这里确保事务性的对象是命令集合。
  • Redis将命令集合序列化并确保处于同一事务的命令集合连续且不被打断的执行。
  • Redis不支持回滚操作。

3.2、相关命令

  • MULTI

    用于标记事务块的开始。
    Redis会将后续的命令逐个放入队列中,然后使用EXEC命令原子化地执行这个命令序列。
    语法:multi

  • EXEC

    在一个事务中执行所有先前放入队列的命令,然后恢复正常的连接状态
    语法:exec

  • DISCARD

    清除所有先前在一个事务中放入队列的命令,然后恢复正常的连接状态。
    语法:discard

  • WATCH

    当某个事务需要按条件执行时,就要使用这个命令将给定的键设置为受监控的状态。
    语法:watch key [key…]
    注意事项:使用该命令可以实现redis的乐观锁。

  • UNWATCH

    清除所有先前为一个事务监控的键。
    语法:unwatch

3.3、事务失败处理

Redis语法错误(可以理解为编译期错误)
在这里插入图片描述
Redis类型错误(可以理解为运行期错误)
在这里插入图片描述
Redis不支持事务回滚
为什么redis不支持事务回滚?

  • 大多数事务失败是因为语法错误或者类型错误,这两种错误,在开发阶段都是可以预见的。
  • redis为了性能方面就忽略了事务回滚。

四、Redis实现分布式锁

4.1、锁的处理

  • 单应用中使用锁:单进程多线程

    synchronize、Lock

  • 分布式应用中使用锁:多进程

4.2、分布式锁的实现方式

  • 基于数据库的乐观锁实现分布式锁
  • 基于zookeeper临时节点的分布式锁
  • 基于redis的分布式锁

4.3、分布式锁的注意事项

互斥性:在任意时刻,只有一个客户端能持有锁
同一性:加锁和解锁必须是同一个客户端,客户端自己不能把别人加的锁给解了。
可重入性:即使有一个客户端在持有锁的期间崩溃而没有主动解锁,也能保证后续其他客户端能加锁。

4.4、实现分布式锁

4.4.1、获取锁

在这里插入图片描述
方式1(使用set命令实现) --推荐:

/**
	 * 使用redis的set命令实现获取分布式锁
	 * @param lockKey   	可以就是锁
	 * @param requestId		请求ID,保证同一性
	 * @param expireTime	过期时间,避免死锁
	 * @return
	 */
	public static boolean getLock(String lockKey,String requestId,int expireTime) {
		//NX:保证互斥性
		String result = jedis.set(lockKey, requestId, "NX", "EX", expireTime);
		if("OK".equals(result)) {
			return true;
		}
		
		return false;
	}

方式2(使用setnx命令实现):

public static boolean getLock(String lockKey,String requestId,int expireTime) {
		Long result = jedis.setnx(lockKey, requestId);
		if(result == 1) {
			jedis.expire(lockKey, expireTime);
			return true;
		}
		
		return false;
	}

4.4.2、释放锁

方式1(del命令实现):

	/**
	 * 释放分布式锁
	 * @param lockKey
	 * @param requestId
	 */
	public static void releaseLock(String lockKey,String requestId) {
	    if (requestId.equals(jedis.get(lockKey))) {
	        jedis.del(lockKey);
	    }
	}

方式2(redis+lua脚本实现)–推荐:

public static boolean releaseLock(String lockKey, String requestId) {
		String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
		Object result = jedis.eval(script, Collections.singletonList(lockKey), Collections.singletonList(requestId));

		if (result.equals(1L)) {
			return true;
		}
		return false;
	}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值