Redis事务与可分布式锁

1    Redis事务

1.1   Redis事务介绍

l  Redis的事务是通过MULTI,EXEC,DISCARD和WATCH这四个命令来完成的。

l  Redis的单个命令都是原子性的,所以这里确保事务性的对象是命令集合

l  Redis将命令集合序列化并确保处于同一事务的命令集合连续且不被打断的执行

l  Redis不支持回滚操作

1.2   相关命令

MULTI

用于标记事务块的开始

Redis会将后续的命令逐个放入队列中,然后使用EXEC命令原子化地执行这个命令序列。

语法:multi

EXEC

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

语法:exec

DISCARD

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

语法:discard

WATCH

当某个事务需要按条件执行时,就要使用这个命令将给定的键设置为受监控的状态。

语法:watch key [key…]

       注意事项:使用该命令可以实现redis的乐观锁

UNWATCH

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

语法:unwatch

1.3   事务失败处理

l  Redis语法错误(可以理解为编译期错误)

l  Redis类型错误(可以理解为运行期错误)

Redis不支持事务回滚

       为什么redis不支持事务回滚?

1、大多数事务失败是因为语法错误或者类型错误,这两种错误,在开发阶段都是可以预见的

2、redis为了性能方面就忽略了事务回滚

2    Redis实现分布式锁

2.1   锁的处理

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

synchronize、Lock

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

2.2   分布式锁的实现方式

l  基于数据库的乐观锁实现分布式锁

l  基于zookeeper临时节点的分布式锁

基于redis的分布式锁

2.3   分布式锁的注意事项

互斥性在任意时刻,只有一个客户端能持有锁

同一性:加锁和解锁必须是同一个客户端,客户端自己不能把别人加的锁给解了。

可重入性:即使有一个客户端在持有锁的期间崩溃而没有主动解锁,也能保证后续其他客户端能加锁。

2.4   实现分布式锁

2.4.1获取锁

l  方式1(使用set命令实现) --推荐:


/**

* 使用redis的set命令实现获取分布式锁

* @param lockKey      可以就是锁

* @param requestId             请求ID,保证同一性

* @param expireTime  过期时间,避免死锁

* @return

*/

publicstaticboolean getLock(String lockKey,String requestId,intexpireTime) {

//NX:保证互斥性

String result = jedis.set(lockKey, requestId, "NX", "EX", expireTime);

if("OK".equals(result)) {

returntrue;

}

returnfalse;

}

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


publicstaticboolean getLock(String lockKey,String requestId,intexpireTime) {

Long result = jedis.setnx(lockKey, requestId);

if(result == 1) {

jedis.expire(lockKey, expireTime);

returntrue;

}

returnfalse;

}

2.4.2释放锁

l  方式1(del命令实现):


/**

* 释放分布式锁

* @param lockKey

* @param requestId

*/

publicstaticvoid releaseLock(String lockKey,String requestId) {

if (requestId.equals(jedis.get(lockKey))) {

jedis.del(lockKey);

}

}

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


publicstaticboolean 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)) {

returntrue;

}

returnfalse;

}

1    Redis事务

1.1   Redis事务介绍

l  Redis的事务是通过MULTI,EXEC,DISCARD和WATCH这四个命令来完成的。

l  Redis的单个命令都是原子性的,所以这里确保事务性的对象是命令集合

l  Redis将命令集合序列化并确保处于同一事务的命令集合连续且不被打断的执行

l  Redis不支持回滚操作

1.2   相关命令

MULTI

用于标记事务块的开始

Redis会将后续的命令逐个放入队列中,然后使用EXEC命令原子化地执行这个命令序列。

语法:multi

EXEC

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

语法:exec

DISCARD

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

语法:discard

WATCH

当某个事务需要按条件执行时,就要使用这个命令将给定的键设置为受监控的状态。

语法:watch key [key…]

       注意事项:使用该命令可以实现redis的乐观锁

UNWATCH

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

语法:unwatch

1.3   事务失败处理

l  Redis语法错误(可以理解为编译期错误)

l  Redis类型错误(可以理解为运行期错误)

Redis不支持事务回滚

       为什么redis不支持事务回滚?

1、大多数事务失败是因为语法错误或者类型错误,这两种错误,在开发阶段都是可以预见的

2、redis为了性能方面就忽略了事务回滚

2    Redis实现分布式锁

2.1   锁的处理

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

synchronize、Lock

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

2.2   分布式锁的实现方式

l  基于数据库的乐观锁实现分布式锁

l  基于zookeeper临时节点的分布式锁

基于redis的分布式锁

2.3   分布式锁的注意事项

互斥性在任意时刻,只有一个客户端能持有锁

同一性:加锁和解锁必须是同一个客户端,客户端自己不能把别人加的锁给解了。

可重入性:即使有一个客户端在持有锁的期间崩溃而没有主动解锁,也能保证后续其他客户端能加锁。

2.4   实现分布式锁

2.4.1获取锁

l  方式1(使用set命令实现) --推荐:


/**

* 使用redis的set命令实现获取分布式锁

* @param lockKey      可以就是锁

* @param requestId             请求ID,保证同一性

* @param expireTime  过期时间,避免死锁

* @return

*/

publicstaticboolean getLock(String lockKey,String requestId,intexpireTime) {

//NX:保证互斥性

String result = jedis.set(lockKey, requestId, "NX", "EX", expireTime);

if("OK".equals(result)) {

returntrue;

}

returnfalse;

}

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


publicstaticboolean getLock(String lockKey,String requestId,intexpireTime) {

Long result = jedis.setnx(lockKey, requestId);

if(result == 1) {

jedis.expire(lockKey, expireTime);

returntrue;

}

returnfalse;

}

2.4.2释放锁

l  方式1(del命令实现):


/**

* 释放分布式锁

* @param lockKey

* @param requestId

*/

publicstaticvoid releaseLock(String lockKey,String requestId) {

if (requestId.equals(jedis.get(lockKey))) {

jedis.del(lockKey);

}

}

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


publicstaticboolean 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)) {

returntrue;

}

returnfalse;

}

原文地址:https://www.cnblogs.com/ddqy/p/12239143.html

时间: 2024-08-07 20:34:44

Redis事务与可分布式锁的相关文章

使用Redis SETNX 命令实现分布式锁

转自:http://blog.csdn.net/lihao21/article/details/49104695 使用Redis的 SETNX 命令可以实现分布式锁,下文介绍其实现方法. SETNX命令简介 命令格式 SETNX key value 将 key 的值设为 value,当且仅当 key 不存在. 若给定的 key 已经存在,则 SETNX 不做任何动作. SETNX 是SET if Not eXists的简写. 返回值 返回整数,具体为 - 1,当 key 的值被设置 - 0,当

基于redis和zookeeper的分布式锁实现方式

先来说说什么是分布式锁,简单来说,分布式锁就是在分布式并发场景中,能够实现多节点的代码同步的一种机制.从实现角度来看,主要有两种方式:基于redis的方式和基于zookeeper的方式,下面分别简单介绍下这两种方式: 一.基于redis的分布式锁实现 1.获取锁 redis是一种key-value形式的NOSQL数据库,常用于作服务器的缓存.从redis v2.6.12开始,set命令开始变成如下格式: SET key value [EX seconds] [PX milliseconds] [

【连载】redis库存操作,分布式锁的四种实现方式[三]--基于Redis watch机制实现分布式锁

一.redis的事务介绍 1. Redis保证一个事务中的所有命令要么都执行,要么都不执行.如果在发送EXEC命令前客户端断线了,则Redis会清空事务队列,事务中的所有命令都不会执行.而一旦客户端发送了EXEC命令,所有的命令就都会被执行,即使此后客户端断线也没关系,因为Redis中已经记录了所有要执行的命令. 2. 除此之外,Redis的事务还能保证一个事务内的命令依次执行而不被其他命令插入.试想客户端A需要执行几条命令,同时客户端B发送了一条命令,如果不使用事务,则客户端B的命令可能会插入

基于redis的一种分布式锁

前言:本文介绍了一种基于redis的分布式锁,利用jedis实现应用(本文应用于多客户端+一个redis的架构,并未考虑在redis为主从架构时的情况) 文章理论来源部分引自:https://i.cnblogs.com/EditPosts.aspx?opt=1 一.基本原理 1.用一个状态值表示锁,对锁的占用和释放通过状态值来标识. 2.redis采用单进程单线程模式,采用队列模式将并发访问变成串行访问,多客户端对Redis的连接并不存在竞争关系. 二.基本命令 1.setNX(SET if N

深入Redis(一)分布式锁

分布式锁 由于分布式应用在逻辑处理时存在并发问题,比方修改数据,要先读取到内存,在内存中修改后再保存回去,这两个操作是单独的,如果同时进行,就会出现并发问题. 此时就要用到分布式锁来限制程序的并发执行. 本质 本质就是在Redis内占一个位置,若别的进程也想占用该位置,发现有进程在使用该位置,就放弃或等待. 在Redis中实现依靠setnx(set if not exists)指令,用完了再调用del指令来释放位置. 在1中,如果逻辑执行到中间出现异常,可能导致del未调用,这就陷入死锁,锁永远

redis击穿,穿透,雪崩,分布式锁,api(jedis,luttuce)

击穿:(redis做缓存用,肯定发生了高并发,到达数据库查询)设置key 的过期时间,过期后没有这个key,找不到了,就穿过了(其中一个key过期导致并发访问数据库)LRU (LRU,即:最近最少使用淘汰算法(Least Recently Used).LRU是淘汰最长时间没有被使用的页面.)LFU (LFU,即:最不经常使用淘汰算法(Least Frequently Used).LFU是淘汰一段时间内,使用次数最少的页面) 1.key为null 2.setnx 如果key存在,则什么都不做 在

基于Redis的分布式锁到底安全吗(上)?

网上有关Redis分布式锁的文章可谓多如牛毛了,不信的话你可以拿关键词"Redis 分布式锁"随便到哪个搜索引擎上去搜索一下就知道了.这些文章的思路大体相近,给出的实现算法也看似合乎逻辑,但当我们着手去实现它们的时候,却发现如果你越是仔细推敲,疑虑也就越来越多. 实际上,大概在一年以前,关于Redis分布式锁的安全性问题,在分布式系统专家Martin Kleppmann和Redis的作者antirez之间就发生过一场争论.由于对这个问题一直以来比较关注,所以我前些日子仔细阅读了与这场争

Redis构建分布式锁

阅读目录 1.前言 2.简单理解redis的单线程IO多路复用 3.并发测试 4.事务解决与原子性操作解决 4.1.事务解决 4.2.原子性操作incr解决 5.构建分布式锁 回到顶部 1.前言 为什么要构建锁呢?因为构建合适的锁可以在高并发下能够保持数据的一致性,即客户端在执行连贯的命令时上锁的数据不会被别的客户端的更改而发生错误.同时还能够保证命令执行的成功率. 看到这里你不禁要问redis中不是有事务操作么?事务操作不能够实现上面的功能么? 的确,redis中的事务可以watch可以监控数

基于Redis实现分布式锁-Redisson使用及源码分析

在分布式场景下,有很多种情况都需要实现最终一致性.在设计远程上下文的领域事件的时候,为了保证最终一致性,在通过领域事件进行通讯的方式中,可以共享存储(领域模型和消息的持久化数据源),或者做全局XA事务(两阶段提交,数据源可分开),也可以借助消息中间件(消费者处理需要能幂等).通过Observer模式来发布领域事件可以提供很好的高并发性能,并且事件存储也能追溯更小粒度的事件数据,使各个应用系统拥有更好的自治性. 本文主要探讨另外一种实现分布式最终一致性的解决方案--采用分布式锁.基于分布式锁的解决