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

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

2.Redis分布式锁的使用

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

    >setnx island-1 ll
(integer) 1
>setnx island-1 pp
(integer) 0
>get island-1
"ll"
>del island-1
(integer) 0
>get island-1
(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

locks = threading.local()
locks.redis = {} def key_for(user_id): return 'account_{}'.format(user_id) # 加锁
def _lock(client,key): return bool(client.set(key,True,nx=True,ex=5))
# 解锁
def _unlock(client,key): client.delete(key) # 执行加锁 + 计数
def lock(client,user_id):
key = key_for(user_id)
if key in locks.redis:
locks.redis[key] += 1
return True
ok = _lock(client,key)
if not ok:
return False
locks.redis[key] = 1
return True # 执行解锁 + 计数
def unlock(user_id):
key = key_for(user_id)
if key in locks.redis:
locks.redis[key] -= 1
if locks.redis[key] <= 0:
del locks.redis[key]
return True
return False client = redis.StrictRedis()
print('lock',lock(client,'ll')) # lock True
print('lock',lock(client,'ll')) # lock True
print('unlock',unlock('ll')) # unlock False 未完全解锁
print('unlock',unlock('ll')) # unlock False
<font size=3>
这并不是一个精确的可重入锁,还可以加入过期时间等等,但代码的复杂度会一直增加,所以并不推荐使用可重入锁。
</font>
-----------------------------------------------------------------------------------------
文章借鉴于《Redis深度历险:核心原理与应用实践》 --作者:钱文品
-----------------------------------------------------------------------------------------

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. 2018-12-22-WPF-在绑定表达式添加计算

    title author date CreateTime categories WPF 在绑定表达式添加计算 lindexi 2018-12-22 16:12:56 +0800 2018-12-22 ...

  2. JS对象 JavaScript 中的所有事物都是对象,如:字符串、数值、数组、函数等,每个对象带有属性和方法。

    什么是对象 JavaScript 中的所有事物都是对象,如:字符串.数值.数组.函数等,每个对象带有属性和方法. 对象的属性:反映该对象某些特定的性质的,如:字符串的长度.图像的长宽等: 对象的方法: ...

  3. codeforces 1180E Serge and Dining Room 线段树

    题目传送门 题目大意: 给出a序列和b序列,a序列为各种食物的价格,b序列为一列排着队的小朋友拥有的钱,小朋友依次购买食物,每个人都买自己能买的起的最贵的食物,买不起就离开队伍.给出q次操作,操作1是 ...

  4. PHP算法之转换成小写字母

    实现函数 ToLowerCase(),该函数接收一个字符串参数 str,并将该字符串中的大写字母转换成小写字母,之后返回新的字符串. 示例 1: 输入: "Hello"输出: &q ...

  5. es 3.0 、es 5.0 、es 6.0

    es 5.0 的严格模式 “use strict” /在页面最顶端写启动全局 es 5.0 严格模式 为什么使用字符串可以 向下兼容 ,,不会报错 可以写在局部方法中,推荐使用 (例如 不再兼容es ...

  6. Java 多线程 - 死锁deadlock产生原因+避免方法

    ref: java中产生死锁的原因及如何避免 https://blog.csdn.net/m0_38126177/article/details/78587845 java如何避免死锁 http:// ...

  7. 「题解」:$d$

    问题 A: $d$ 时间限制: 1 Sec  内存限制: 512 MB 题面 题面谢绝公开. 题解 赛时切掉了然而过程十分曲折. 贪心思路很好想.然而一开始错误以为是单峰.其实几个峰都有可能. 开场写 ...

  8. docker-compose安装及docker-compose.yml详解

    1.下载安装 [root@cx-- ~]# curl -L https://github.com/docker/compose/releases/download/1.24.1/docker-comp ...

  9. hibernate_01_SSH环境搭建

    1.maven工程pom.xml文件 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="h ...

  10. python元组与字典

    一.元组 1.元组的表达 (1,2,3,4) ('olive',123) ("python",) 创建元组: a=tuple((1,2,3,)) b=("python&q ...