Redis事务与可分布式锁

♀尐吖头ヾ 提交于 2020-01-28 23:39:13

1    Redis事务

1.1   Redis事务介绍

l  Redis的事务是通过MULTIEXECDISCARDWATCH这四个命令来完成的。

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

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

l  Redis不支持回滚操作

1.2   相关命令

l  MULTI

       用于标记事务块的开始

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

       语法:multi

l  EXEC

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

       语法:exec

l  DISCARD

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

       语法:discard

l  WATCH

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

       语法:watch key [key…]

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

l  UNWATCH

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

       语法:unwatch

1.3   事务失败处理

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

 

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

 

l  Redis不支持事务回滚

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

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

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

2    Redis实现分布式锁

2.1   锁的处理

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

       synchronizeLock

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

2.2   分布式锁的实现方式

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

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

l  基于redis的分布式锁

2.3   分布式锁的注意事项

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

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

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

2.4   实现分布式锁

2.4.1获取锁

 

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

/**

          * 使用redisset命令实现获取分布式锁

          * @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  方式1del命令实现):

         /**

          * 释放分布式锁

          * @param lockKey

          * @param requestId

          */

         publicstaticvoid releaseLock(String lockKey,String requestId) {

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

                 jedis.del(lockKey);

             }

         }

 

l  方式2redis+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的事务是通过MULTIEXECDISCARDWATCH这四个命令来完成的。

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

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

l  Redis不支持回滚操作

1.2   相关命令

l  MULTI

       用于标记事务块的开始

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

       语法:multi

l  EXEC

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

       语法:exec

l  DISCARD

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

       语法:discard

l  WATCH

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

       语法:watch key [key…]

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

l  UNWATCH

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

       语法:unwatch

1.3   事务失败处理

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

 

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

 

l  Redis不支持事务回滚

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

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

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

2    Redis实现分布式锁

2.1   锁的处理

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

       synchronizeLock

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

2.2   分布式锁的实现方式

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

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

l  基于redis的分布式锁

2.3   分布式锁的注意事项

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

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

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

2.4   实现分布式锁

2.4.1获取锁

 

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

/**

          * 使用redisset命令实现获取分布式锁

          * @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  方式1del命令实现):

         /**

          * 释放分布式锁

          * @param lockKey

          * @param requestId

          */

         publicstaticvoid releaseLock(String lockKey,String requestId) {

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

                 jedis.del(lockKey);

             }

         }

 

l  方式2redis+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;

         }

 

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!