使用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 ...
随机推荐
- Android Studio怎样选择查看指定进程的log?
今天说一个简单的东东,关于Android Studio在logcat中怎样查看指定进程的log. 关于过滤你自己所开发的应用的log.在这篇文章中:Android Studio怎样给log加入过滤项( ...
- OracleOraDb11g_home1TNSListener服务启动后停止,某些服务在未由其他服务或程序使用时将自己主动停止
解决的方法,大家来分享一下 1:注冊表中 HKEY_LOCAL_MACHINE/SYSTEM/CurrentControlSet/services/OracleOraDb11g_home1TNSLis ...
- C3P0 APPARENT DEADLOCK
一,c3p0执行一段时间后报错例如以下 W 07-26_00:58:27 ThreadPoolAsynchronousRunner.java 608 com.mchange.v2.async.Thre ...
- Launcher知识的demo,手机管家小伙家与悬浮窗
Launcher知识的demo.主要仿的手机管家小伙家与悬浮窗的效果.东西比較简单.直接贴代码 主要用到了windowManager 中的更新updateViewLayout(view,params) ...
- JavaScript 实现命名空间(namespace)的最佳方案——兼容主流的定义类(class)的方法,兼容所有浏览器,支持用JSDuck生成文档
作者: zyl910 一.缘由 在很多的面向对象编程语言中,我们可以使用命名空间(namespace)来组织代码,避免全局变量污染.命名冲突.遗憾的是,JavaScript中并不提供对命名空间的原生支 ...
- Selenium与phantomJS 登入豆瓣 有bug
# -*- coding:utf-8 -*- from selenium import webdriver from selenium.webdriver.common.keys import Key ...
- eclipse中JPA插件的安装与使用
说明 上周实验室学习了数据库设计相关的内容,其中涉及到将数据库实体化的问题,JPA是一个很好的实现工具,便开始着手于JPA的学习.因为JPA涉及到的知识还是挺多的,需要学习许多新的知识,所以对于JPA ...
- gradle发布jar包
在.gradle文件中添加以下代码 apply plugin: 'application' mainClassName = 'com.excel.ExcelApplication' jar { doF ...
- 浅谈Unix I/O模型
关于I/O模型的文章比较多,参考多篇后理解上仍然不太满意,终需自己整理一次,也是编写高吞吐量高性能网络接口模块的基础.这里所说的主要针对网络I/O,近几年面对越来越大的用户请求量,如何优化这些步骤直接 ...
- iOS NSString 文本不同的颜色 标题+文本字体大小 行间距/删除不需要的字符 /以及自适应高度
#import <Foundation/Foundation.h> @interface TextsForRow : NSObject @property(nonatomic,copy)N ...