使用redis设计一个简单的分布式锁
最近看了有关redis的一些东西,了解了redis的一下命令,就记录一下:
redis中的setnx命令:
关于redis的操作命令,我们一般会使用set,get等一系列操作,数据结构也有很多,这里我们使用最简单的string来存储锁。
redis下提供一个setnx命令用来将key值设为value,类似于set功能,但是它和set是有区别的,在于后面的nx,setnx是SET if Not eXists。就是:当且仅当key值不存在的时候,将该key值设置为value。
也就是说使用setnx加入元素的时候,当key值存在的时候,是没有办法加入内容的。
加锁:
下面将使用python控制redis,python控制redis的方式和命令一样,有一个setnx(key, value)方法,通过这个方法可以实现setnx命令的效果。
首先连接redis:
文件connect.py
import redis def connect_redis():
return redis.Redis(host='127.0.0.1', port=6379)
然后就可以使用setnx加锁了,key对应的value中需要填入相应的值,这里设置Value为一个uuid值。获取到uuid之后,将key和value填入redis,其他的客户端想要访问并获取到锁,也使用setnx方式,key值只要相同就好。那么代码如下:
文件operate.py
# 加锁的过程
def acquire_lock(conn, lockname):
identifier = str(uuid.uuid4())
conn.setnx('lock:' + lockname, identifier):
这样就通过setnx将key值写入了。但是,这样显然是不合理的,客户端可以等待一会再次获取锁,这样,不至于每次请求都会出现问题。那可以设置30秒的时间,让程序在30秒内不停的尝试获取锁,知道30秒的时间过了或者由其他客户端释放了锁。
所以加锁可以变为如下:
# 加锁的过程
def acquire_lock(conn, lockname, args, acquite_timeout = 30):
identifier = str(uuid.uuid4())
end = time.time() + acquite_timeout
while time.time() < end:
# 这里尝试取得锁 setnx 设置-如果不存在的时候才会set
if conn.setnx('lock:' + lockname, identifier):
# 获得锁之后输出获得锁的‘进程’号
print('获得锁:进程'+ str(args))
return identifier
return False
这样就可以通过setnx加锁了,这个加锁的过程实际上就是在redis中存入了一个值,之后当其他的客户端再次想要通过这个key加入这个值的时候,发现这个key已经存在就不往进写值了,但是在这30秒内还会不断尝试的去获取锁,也就是不断的尝试写入这个值,一旦key被删除——也就是锁被释放,则该客户端就可以竞争获取锁——进程写入这个值。这种方式就像是操作系统中的自旋锁。
自旋锁
这里先简单介绍一下自旋锁。
和自旋锁对应的还有一种锁,叫做对于互斥锁。
互斥锁:如果资源已经被占用,资源申请者只能进入睡眠状态。
自旋锁:自旋锁不会引起调用者睡眠,如果自旋锁已经被别的执行单元保持,调用者就一直循环在那里看是否该自旋锁的保持者已经释放了锁,"自旋"一词就是因此而得名。
释放锁:
既然锁可以添加,那么也就可以释放。释放锁实际上就是将redis中的数据删除。这里可以使用redis提供的事务流水线去执行。为了保障在执行的时候确确实实释放的所示没有问题的。
代码如下,调用delete()方法——del命令删除redis中这个key的元素。
# 释放锁
def release_lock(conn, lockname, identifier):
pipe = conn.pipeline(True)
lockname = 'lock:' + lockname
while True:
try:
pipe.watch(lockname)
identifier_real = pipe.get(lockname)
if identifier_real == identifier:
pipe.multi()
pipe.delete(lockname)
pipe.execute()
return True;
pipe.unwatch()
break
except redis.exceptions.WatchError:
pass
return False
这里就遇到了python3中的一个坑,获取到的数据为byte类型,所以identifier_real这个变量实际上是这个字符串之前加了个b的,例如b'xxxx',所以这和传入的identifier的类型为string,这样比较当然会出现Fasle,所以我们在这里将这个字符串类型转码:
pipe.get(lockname).decode()
这样才是整整的字符串类型的字符串,最终的代码如下:
def release_lock(conn, lockname, identifier):
pipe = conn.pipeline(True)
lockname = 'lock:' + lockname
while True:
try:
pipe.watch(lockname)
identifier_real = pipe.get(lockname).decode()
if identifier_real == identifier:
pipe.multi()
pipe.delete(lockname)
pipe.execute()
return True;
pipe.unwatch()
break
except redis.exceptions.WatchError:
pass
return False
执行:
为了验证这个分布式锁的正确性,可以写一个测试的方法进行测试,先去获取锁,如果获取到之后,则sleep三秒,等待3秒之后,执行释放锁。
模拟过程入下:
文件operate.py
# 模拟加锁解锁的过程
def exec_test(conn, lockname, args):
id = acquire_lock(conn, lockname, args)
if id != False:
print(id)
time.sleep(3)
release_lock(conn, lockname, id)
这样我们就可以使用多进程并发访问的方式进行对锁进行抢占和释放:
使用9个进程进行测试,操作方式如下:
from connect import connect_redis
from operate import acquire_lock
from multiprocessing import Process
from operate import exec_test if __name__ == '__main__':
redis = connect_redis()
for i in range(0, 9):
Process(target = exec_test, args = (redis, 'test', i)).start()
执行结果如下所示:
注:以上python运行环境为python3.6
使用redis设计一个简单的分布式锁的更多相关文章
- ZooKeeper学习笔记四:使用ZooKeeper实现一个简单的分布式锁
作者:Grey 原文地址: ZooKeeper学习笔记四:使用ZooKeeper实现一个简单的分布式锁 前置知识 完成ZooKeeper集群搭建以及熟悉ZooKeeperAPI基本使用 需求 当多个进 ...
- 180626-Spring之借助Redis设计一个简单访问计数器
文章链接:https://liuyueyi.github.io/hexblog/2018/06/26/180626-Spring之借助Redis设计一个简单访问计数器/ Spring之借助Redis设 ...
- redis实现简单的分布式锁
在分布式系统中多个请求并发对少数资源进行争抢,例如10个人同时秒杀一件商品,如果不用分布式的锁进行处理(当然还有其它的处理方案),则很容易出现多个人抢到一个商品(超卖)的情况,用redis可以比较容易 ...
- SpringBoot基于数据库实现简单的分布式锁
本文介绍SpringBoot基于数据库实现简单的分布式锁. 1.简介 分布式锁的方式有很多种,通常方案有: 基于mysql数据库 基于redis 基于ZooKeeper 网上的实现方式有很多,本文主要 ...
- python使用redis实现协同控制的分布式锁
python使用redis实现协同控制的分布式锁 上午的时候,有个腾讯的朋友问我,关于用zookeeper分布式锁的设计,他的需求其实很简单,就是节点之间的协同合作. 我以前用redis写过一个网络锁 ...
- 利用consul在spring boot中实现最简单的分布式锁
因为在项目实际过程中所采用的是微服务架构,考虑到承载量基本每个相同业务的服务都是多节点部署,所以针对某些资源的访问就不得不用到用到分布式锁了. 这里列举一个最简单的场景,假如有一个智能售货机,由于机器 ...
- Redis中是如何实现分布式锁的?
分布式锁常见的三种实现方式: 数据库乐观锁: 基于Redis的分布式锁: 基于ZooKeeper的分布式锁. 本地面试考点是,你对Redis使用熟悉吗?Redis中是如何实现分布式锁的. 要点 Red ...
- Redis的“假事务”与分布式锁
关注公众号:CoderBuff,回复"redis"获取<Redis5.x入门教程>完整版PDF. <Redis5.x入门教程>目录 第一章 · 准备工作 第 ...
- 使用数据库、Redis、ZK分别实现分布式锁!
分布式锁三种实现方式: 基于数据库实现分布式锁: 基于缓存(Redis等)实现分布式锁: 基于Zookeeper实现分布式锁: 基于数据库实现分布式锁 悲观锁 利用select - where - f ...
随机推荐
- 工作中git 操作汇总
1. git branch -l 查看本地branch 2. git reset --hard 回滚全部修改 3. git status 查看本地修改 4. git pull 更新代码 5. gi ...
- iOS 开发人员不可缺少的75个工具
原文出处: Ben Scheirman 译文出处: Njuxjy 假设你去到一位熟练的木匠的工作室,你总是能发现他/她有一堆工具来完毕不同的任务. 软件开发相同如此. 你能够从软件开发人员怎样使用工具 ...
- Android ListView Adapter的getItemViewType和getViewTypeCount多种布局
<Android ListView Adapter的getItemViewType和getViewTypeCount多种布局> 在Android的ListView中.假设在一个Lis ...
- Automatic Preferred Max Layout Width is not available on iOS versions prior to
警告:Automatic Preferred Max Layout Width is not available on iOS versions prior to 8.0 如: 找到: : 改动为:
- 机器学习——深度学习(Deep Learning)
Deep Learning是机器学习中一个非常接近AI的领域,其动机在于建立.模拟人脑进行分析学习的神经网络,近期研究了机器学习中一些深度学习的相关知识,本文给出一些非常实用的资料和心得. Key W ...
- webpack+babel项目在IE下报Promise未定义错误引出的思考
低版本浏览器引起的问题 最近开发一个基于webpack+babel+react的项目,一般本地是在chrome浏览上面开发,chrome浏览器开发因为支持大部分新的js特性,所以一般不怎么需要poly ...
- 【博客目录】SqlServer篇
SqlServer系列篇 [SqlServer系列]SQLSERVER安装教程 [SqlServer系列]数据库三大范式 [SqlServer系列]表单查询 [SqlSer ...
- 【转载】Spring AOP详解 、 JDK动态代理、CGLib动态代理
Spring AOP详解 . JDK动态代理.CGLib动态代理 原文地址:https://www.cnblogs.com/kukudelaomao/p/5897893.html AOP是Aspec ...
- C#:StreamReader读取.CSV文件(转换成DataTable)
using System.Data; using System.IO; /// <summary> /// Stream读取.csv文件 /// </summary> /// ...
- 【java】获取当前日期时间:java.util.Date
public class TestDate { public static void main(String[] args) { System.out.println(new java.util.Da ...