1.分布式锁的定义与理解

在并发任务中,当对数据执行修改和删除时为了防止多个任务同时拿到数据而产生的混乱,这时就要用到分布式锁来限制程序的并发执行。
Redis分布式锁本质上要实现的目标就是在Redis里声明一块暂时领地,当其他进程要来使用这块领地时,发现已经有一个进程在占有这块领地时不得不选择放弃或者等待。

2.Redis分布式锁的使用

在Redis中声明一块领地一般会使用setnx(set if not exists)指令,只允许被一个客户端占据。先到者先得,使用完成时调用del指令离开领地。

  1. >setnx island-1 ll
  2. (integer) 1
  3. >setnx island-1 pp
  4. (integer) 0
  5. >get island-1
  6. "ll"
  7. >del island-1
  8. (integer) 0
  9. >get island-1
  10. (nil)

但这样使用会出现一些问题,如果占到领地的进程执行到了一半,出现异常导致无法调用后续的del指令来释放,这样就会造成死锁的现象,那这块领地将会一直被占用,锁一直无法释放。
这时通常的做法是给这个锁添加一个过期时间,比如5s(expire key 5),这样即使中间出现了异常也可以保证5s之后锁会自动释放。

```Redis
>setnx island-2 ll
ok
>expire island-2 5
>get island-2
"ll"
...5s之后...
>get island-2
(nil)
```

但是这样做还是会有问题,如果程序在 setnx指令和expire指令之间挂掉如突然断电或人为操作等,那么同样可能会造成死锁现象。问题的根源在于setnx与expire指令并不是同时执行。
一般的想法可能会想到用事务来解决,但遗憾的是这种方法并不可行,因为expire是依赖于setnx的执行结果的,如果setnx圈地失败,expire就无法执行,而事务的特点就是要么全部执行, 要么都不执行。所幸在redis2.8以后的版本中添加了set指令的扩展参数,使得这个问题得以解决。

```Redis
>set island-2 ll ex 5 nx
ok
>get island-2
"ll"
... 5s之后...
>get island-2
(nil)
```

其中 nx( if not exists), ex即expire

3. Redis分布式锁扩展
===
3.1 超时问题
---

redis的分布式锁不能解决超时问题,原因在于如果加锁与释放锁之间执行的时间太长,以至于超过了设定的超时限制,就会导致第一个线程的逻辑还未执行完,其他线程就会劫持到这把锁。为了避免这种情况,Redis分布式锁一般不用于较长时间的任务。

3.2 可重入性

可重入是指在原先持有锁的情况下再次请求加锁,如果同一线程中的一个锁支持这种特性,那么这个锁就是可重入的。Redis分布式锁要想实现可重入性,就必须对客户端的set方法进行包装,使用线程的Threadlocal变量存储当前持有锁的计数。python版本的代码如下:

```python
import redis
import threading

  1. locks = threading.local()
  2. locks.redis = {}
  3. def key_for(user_id):
  4. return 'account_{}'.format(user_id)
  5. # 加锁
  6. def _lock(client,key):
  7. return bool(client.set(key,True,nx=True,ex=5))
  8. # 解锁
  9. def _unlock(client,key):
  10. client.delete(key)
  11. # 执行加锁 + 计数
  12. def lock(client,user_id):
  13. key = key_for(user_id)
  14. if key in locks.redis:
  15. locks.redis[key] += 1
  16. return True
  17. ok = _lock(client,key)
  18. if not ok:
  19. return False
  20. locks.redis[key] = 1
  21. return True
  22. # 执行解锁 + 计数
  23. def unlock(user_id):
  24. key = key_for(user_id)
  25. if key in locks.redis:
  26. locks.redis[key] -= 1
  27. if locks.redis[key] <= 0:
  28. del locks.redis[key]
  29. return True
  30. return False
  31. client = redis.StrictRedis()
  32. print('lock',lock(client,'ll')) # lock True
  33. print('lock',lock(client,'ll')) # lock True
  34. print('unlock',unlock('ll')) # unlock False 未完全解锁
  35. print('unlock',unlock('ll')) # unlock False
  1. <font size=3>
  2. 这并不是一个精确的可重入锁,还可以加入过期时间等等,但代码的复杂度会一直增加,所以并不推荐使用可重入锁。
  3. </font>
  4. -----------------------------------------------------------------------------------------
  5. 文章借鉴于《Redis深度历险:核心原理与应用实践》 --作者:钱文品
  6. -----------------------------------------------------------------------------------------

Redis学习笔记01-分布式锁的更多相关文章

  1. Redis学习系列七分布式锁

    一.简介 熟悉.Net多线程的都知道,当多个线程同时操作一个全局缓存对象(static对象实例.Dictionary.List等)时,会存在多线程争用问题,包括EF.Dapper等本身的缓存机制,都存 ...

  2. Redis学习笔记11--Redis分布式

    Redis-2.4.15目前没有提供集群的功能,Redis作者在博客中说将在3.0中实现集群机制.目前Redis实现集群的方法主要是采用一致性哈稀分片(Shard),将不同的key分配到不同的redi ...

  3. redis学习笔记-01:redis简介

    1.redis是一个高性能的Nosql数据库,遵守BSD协议,使用c语言编写.支持网络.可基于内存亦可持久化,是一种日志型.Key-Value数据库,也可看做是一个分布式的.基于内存的缓存工具. 2. ...

  4. redis学习笔记01 — 基本介绍、安装配置及常用命令

    redis--NoSQL的一种 为了解决高并发.高可用.高扩展.大数据存储等一系列问题而产生的数据库解决方案,就是NoSQL NoSQL,非关系型数据库,全名:Not Only Sql,它不能代替关系 ...

  5. redis学习笔记-01 string类型命令

    一.set key value set joker 123456 #设定key为joker,value为123456的数据 二.keys * keys * #用于查看该数据库中所有的key值 三.se ...

  6. Redis:学习笔记-01

    Redis:学习笔记-01 该部分内容,参考了 bilibili 上讲解 Redis 中,观看数最多的课程 Redis最新超详细版教程通俗易懂,来自 UP主 遇见狂神说 1. Redis入门 2.1 ...

  7. Redis学习笔记~目录

    回到占占推荐博客索引 百度百科 redis是一个key-value存储系统.和Memcached类似,它支持存储的value类型相对更多,包括string(字符串).list(链表).set(集合). ...

  8. redis学习笔记(详细)——高级篇

    redis学习笔记(详细)--初级篇 redis学习笔记(详细)--高级篇 redis配置文件介绍 linux环境下配置大于编程 redis 的配置文件位于 Redis 安装目录下,文件名为 redi ...

  9. redis 学习笔记(6)-cluster集群搭建

    上次写redis的学习笔记还是2014年,一转眼已经快2年过去了,在段时间里,redis最大的变化之一就是cluster功能的正式发布,以前要搞redis集群,得借助一致性hash来自己搞shardi ...

  10. Redis学习笔记(1)——Redis简介

    一.Redis是什么? Remote Dictionary Server(Redis) 是一个开源的使用ANSI C语言编写.遵守BSD协议.支持网络.可基于内存亦可持久化的日志型.Key-Value ...

随机推荐

  1. go 函数和流程控制

    if/else分支判断 基本结构如下: if condition1 { } if condition1 { } else { } if condition1 { } else if condition ...

  2. mysql sql时间戳格式化语句

    FROM_UNIXTIME(c.lastUpdateTime/1000,'%Y-%c-%d %h:%i:%s' ) as updatetime; select c.roleid, r.username ...

  3. caffe安装 总结

    用的是matlab2018a,搞了一天 ubuntu 系统下的Caffe环境搭建 https://blog.csdn.net/hjimce/article/details/48781693 caffe ...

  4. java继承,多态

    子类继承父类,用父类去接收子类,其实我觉得用父类,子类来形容继承关系是不恰当的,比如再发生多态的时候,Car c = new W();w是大众,你能说Car 和W是父子关系吗,我觉得用所属关系类描述可 ...

  5. jq给页面添加覆盖层遮罩的实例

    先引入jq代码,然后代码如下: $(function(){ var docHeight = $(document).height(); //获取窗口高度 $('body').append('<d ...

  6. NIO 详解

    同步非阻塞 NIO之所以是同步,是因为它的accept read write方法的内核I/O操作都会阻塞当前线程 IO模型 IO NIO 通信 面向流(Stream Oriented) 面向缓冲区(B ...

  7. idea for mac 项目打开的情况下import project

    commad + ,进入设置页面,搜索框输入menu,选择file下面的任意一个都行: 点击打开的窗口中的others 不需要重启 参考:https://blog.csdn.net/zengxiaos ...

  8. css3@media实现原理

    window.matchMedia() 基本用法 window.matchMedia方法用来检查CSS的mediaQuery语句.各种浏览器的最新版本(包括IE 10+)都支持该方法,对于不支持该方法 ...

  9. Cocos2d-x通过Jni实现C++与Java相互调用

    在cocos2dx项目中与运营平台(java sdk)对接时使用了JNI. 通过C++调用Java 在JniUtil.h文件中如下实现: #ifndef _JNIUTIL_H_ #define _JN ...

  10. Date、DateFormat、Calendar、System、Math类总结

    java.util.Date: 构造方法 public Date() 空参构造,返回当前时间 public Date(long 毫秒值) 指定毫秒值的时间 普通方法 long getTime() 获取 ...