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. 关于UIPageViewController去除边缘点击手势

    如果page上方还有一层UI控件的话,不去除边缘点击手势会造成手势的冲突干扰. 首先我做的处理是设置pageView的手势代理 for (UIGestureRecognizer *gr in _pag ...

  2. python库之lightgbm

    一.安装 https://blog.csdn.net/qq_40317897/article/details/81021958 参考文献: [1].LightGBM中文文档 https://light ...

  3. 一个有关group by的错误

    事例:查询有奖金的每个部门的部门名和部门的领导编号和该部门的最低工资 SELECT department_name,MIN(salary),departments.manager_idFROM dep ...

  4. ArcGIS Server 10.x查询管理用户名和修改管理员密码

    在x:\Program Files\ArcGIS\Server\tools\passwordreset下有个bat文件,用管理员用户运行它. PasswordReset -l PasswordRese ...

  5. 关于 wpf 的ICommand 的 CanExecute CanExecuteChanged func action的认识

    关于 wpf 的ICommand 的 CanExecute CanExecuteChanged  func  action的认识

  6. SQL Server Management Studio 的账号密码

    使用“Windows身份验证”方式无法登陆 使用“SQL Server身份验证” 方式无法登陆 解决办法:关闭当前所有服务.通过[Microsoft SQL Server 2008]|[配置工具]|[ ...

  7. cookie的设置与销毁

    <?php /* 2个参数设置cookie cookie随着浏览器的关闭,就失效了 ); /* 下面我们让cookir多活一会 3个参数来设置cookie,第3个参数指的就是cookie的声明周 ...

  8. selenium python bindings 初步用法及简单参考例子

    掌握selenium最简单的方法就是参考例子进行学习,下面给出之前项目的测试例子及分析 # -*- coding: utf-8 -*- import time from selenium import ...

  9. System.Web.Mvc.IActionFilter.cs

    ylbtech-System.Web.Mvc.IActionFilter.cs 1.程序集 System.Web.Mvc, Version=5.2.3.0, Culture=neutral, Publ ...

  10. <a>标签的SEO优化细节

    <a>标签的SEO优化细节 如果需要在新窗口中打开链接,我们使用的方法是在a上加上taget=“_blank”,但很多人不知道这是不符合w3c的规范的,在使用严格的DOCTYPE(xhtm ...