使用redis设计一个简单的分布式锁

时间:2022-10-07 19:43:30

最近看了有关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()

执行结果如下所示:

使用redis设计一个简单的分布式锁

注:以上python运行环境为python3.6

使用redis设计一个简单的分布式锁的更多相关文章

  1. ZooKeeper学习笔记四:使用ZooKeeper实现一个简单的分布式锁

    作者:Grey 原文地址: ZooKeeper学习笔记四:使用ZooKeeper实现一个简单的分布式锁 前置知识 完成ZooKeeper集群搭建以及熟悉ZooKeeperAPI基本使用 需求 当多个进 ...

  2. 180626-Spring之借助Redis设计一个简单访问计数器

    文章链接:https://liuyueyi.github.io/hexblog/2018/06/26/180626-Spring之借助Redis设计一个简单访问计数器/ Spring之借助Redis设 ...

  3. redis实现简单的分布式锁

    在分布式系统中多个请求并发对少数资源进行争抢,例如10个人同时秒杀一件商品,如果不用分布式的锁进行处理(当然还有其它的处理方案),则很容易出现多个人抢到一个商品(超卖)的情况,用redis可以比较容易 ...

  4. SpringBoot基于数据库实现简单的分布式锁

    本文介绍SpringBoot基于数据库实现简单的分布式锁. 1.简介 分布式锁的方式有很多种,通常方案有: 基于mysql数据库 基于redis 基于ZooKeeper 网上的实现方式有很多,本文主要 ...

  5. python使用redis实现协同控制的分布式锁

    python使用redis实现协同控制的分布式锁 上午的时候,有个腾讯的朋友问我,关于用zookeeper分布式锁的设计,他的需求其实很简单,就是节点之间的协同合作. 我以前用redis写过一个网络锁 ...

  6. 利用consul在spring boot中实现最简单的分布式锁

    因为在项目实际过程中所采用的是微服务架构,考虑到承载量基本每个相同业务的服务都是多节点部署,所以针对某些资源的访问就不得不用到用到分布式锁了. 这里列举一个最简单的场景,假如有一个智能售货机,由于机器 ...

  7. Redis中是如何实现分布式锁的?

    分布式锁常见的三种实现方式: 数据库乐观锁: 基于Redis的分布式锁: 基于ZooKeeper的分布式锁. 本地面试考点是,你对Redis使用熟悉吗?Redis中是如何实现分布式锁的. 要点 Red ...

  8. Redis的&OpenCurlyDoubleQuote;假事务”与分布式锁

    关注公众号:CoderBuff,回复"redis"获取<Redis5.x入门教程>完整版PDF. <Redis5.x入门教程>目录 第一章 · 准备工作 第 ...

  9. 基于zookeeper简单实现分布式锁

    https://blog.csdn.net/desilting/article/details/41280869 这里利用zookeeper的EPHEMERAL_SEQUENTIAL类型节点及watc ...

随机推荐

  1. 【ios 7】 之后的设置系统的状态栏隐藏的方法分享

    由于在做视频播放的的项目,一直困扰的是,视频全屏幕播放的时候,系统的状态栏会隐藏不掉,虽然可以设置为透明的状态来显示,但是电池的状态一直隐藏不掉,查看网上的说法也就是说,要么来控制,他的hidden ...

  2. swift中editingStyleForRowAtIndexPath的写法

    效果图: 首先要实现这句tableView.setEditing(true, animated: true)才能弹出左侧的小圆圈 然而在oc中tableview删除的写法百度一下很常见但是swift中 ...

  3. 远程连接Kali Linux使用PuTTY实现SSH远程连接

    远程连接Kali Linux使用PuTTY实现SSH远程连接 本书主要以在Android设备上安装的Kali Linux操作系统为主,介绍基于Bash Shell渗透测试.由于在默认情况下,在Andr ...

  4. Install Apache&comma; PHP And MySQL On CentOS 7 &lpar;LAMP&rpar;

    This tutorial shows how you can install an Apache2 webserver on a CentOS 7.0 server with PHP5 suppor ...

  5. 网站添加到IIS和附件进程调试(新手使用篇)

    一.网站添加到IIS 做网站开发,很有必要把项目添加到IIS中,这对浏览和后期的调试很有帮助.怎么把网站添加到IIS上? 1). 打开IIS,然后操作步骤如下图: 选择Default Web Site ...

  6. POJ1789Truck History

    题意 : 说实话,题意我没看懂,后来让人给我讲的样例..... 4 aaaaaaa baaaaaa abaaaaa aabaaaa 0 这个样例的话,就是输入n下面n行,每行7个字母,让你依次选两行进 ...

  7. Eclipse配置JDK的源代码的src&period;zip

    Eclipse配置JDK的源代码的src.zip包很简单.只需要简单的几个步骤. 1.点 “window”-> “Preferences” -> “Java” -> “Install ...

  8. 多个文件目录下Makefile的写法

    1.前言 目前从事于linux下程序开发,涉及到多个文件,多个目录,这时候编译文件的任务量比较大,需要写Makefile.关于Makefile的详细内容可以参考网上流传非常广泛的<跟我一起写Ma ...

  9. Angular之Providers &lpar;Value&comma; Factory&comma; Service and Constant &rpar;

    官方文档Providers Each web application you build is composed of objects that collaborate to get stuff do ...

  10. find unique values in an array

    Problem: given an array that contains duplicates (except one value), find the one value that does no ...