使用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 ...
随机推荐
- mac cocos2dx android
1. localhost:proj.android mxhd4$ ./build_native.sh 报错 Compile++ thumb : cocosdenshion_static <= ...
- vim 命令整理(自己经常使用)
vimm(vimsual)是Linux/UNIX系列OS中通用的全屏编辑器. vimm分为两种状态,即命令状态和编辑状态.在命令状态下.所键入的字符系统均作命令来处理.如:q代表退出,而编辑状态则是用 ...
- B. Order Book(Codeforces Round #317 )
B. Order Book time limit per test 2 seconds memory limit per test 256 megabytes input standard input ...
- 为Android Studio设置HTTP代理
大陆的墙非常厚非常高.初次安装Android Studio下载SDK等必然失败,设置代理方法例如以下: 1. 到android studio安装文件夹,打开bin文件夹.编辑idea.properti ...
- 机器学习——深度学习(Deep Learning)
Deep Learning是机器学习中一个非常接近AI的领域,其动机在于建立.模拟人脑进行分析学习的神经网络,近期研究了机器学习中一些深度学习的相关知识,本文给出一些非常实用的资料和心得. Key W ...
- Linux 高速操作IOport
在嵌入式设备中对GPIO的操作是最主要的操作. 一般的做法是写一个单独驱动程序,网上大多数的样例都是这种.事实上linux以下有一个通用的GPIO操作接口.那就是我要介绍的 "/sys/cl ...
- HTTP响应状态码含义参考
1xx:信息 100 Continue服务器仅接收到部分请求,但是一旦服务器并没有拒绝该请求,客户端应该继续发送其余的请求.101 Switching Protocols服务器转换协议:服务器将遵从客 ...
- Java数组的一些使用方法及堆栈存储
数组 用于存储一组同一数据类型数据的容器 数组会对放入其中的数据自动编号,编号是从0开始的---下标 定义格式 数据类型[] 数组名 = new 数据类型[数组的大小];---可以先声明再初始化 in ...
- 自学Zabbix3.10.1-事件通知Notifications upon events-媒介类型
自学Zabbix3.10-事件通知Notifications upon events-媒介类型 zabbix触发器到了要发送通知的情况下,需要一个中间介质来接收并传递它的消息给运维们,以往用nagio ...
- 自学Zabbix3.6.5-触发器item-Unit symbols单位符号
在zabbix里面,我们不需要使用大数字来,例如我们可以不使用86400来表示一天,这个数字又不容易理解也容易出错.用什么办法来解决大数字问题呢?我们可以使用单位来简化,例如简化zabbix触发器表达 ...