Python 基于Redis实现一个分布式读写锁

Python 专栏收录该内容
149 篇文章 4 订阅

1、简述出现背景

Python 基于Redis实现一个简单的分布式锁
Python 基于Redis实现一个分布式可重入锁
在前面实现了简单分布式锁是用来解决分布式场景下的并发写数据问题,分布式可重入锁是用来解决并发场景下同一线程下再次或多次获得锁的问题。
那么分布式读写锁又是解决什么问题的呢?
首先来理解下概念,读写锁其实可以拆分为读锁和写锁,又称共享锁和排它锁,没错,和MySQL中的共享锁、排它锁几乎是同一个东西。那可能就有同学要问了,既然MySQL有,还用Redis实现干什么,因为Redis足够快。
它们的作用分开来讲就是:读锁允许其他读锁存在,可以进行并发的读数据。写锁会阻塞其他的读锁和写锁,在写数据时,阻塞所有读写操作,目的是尽最大的可能保证数据一致性。

2、简述原理

简述一下分布式读写锁实现原理:分为两种情况,第一种是先加了读锁,那么后面再来的读锁,都可以获得锁,不影响所有的读操作,但当出现写锁想获得锁时,就必须等读锁释放后,才能加上写锁,此时便会阻塞所有读写操作。第二种是先加了写锁,那么会阻塞所有的读写操作,直到写锁释放锁,才能被其他读锁或写锁获得锁继续向下执行。

3、代码参考

# read_write_lock.py
import time
import threading
import redis
import os, sys

class ReadWriteLock(object):
    def __init__(self, cache_key, cache_type, time_out=20):
        self.redis_con = self.__get_redis_con()
        self.cache_key = cache_key
        self.cache_type = cache_type
        self.time_out = time_out

    def __get_redis_con(self):
        pool = redis.ConnectionPool(host='127.0.0.1',port=6379)
        redis_con = redis.Redis(connection_pool=pool)
        return redis_con

    def get_lock(self, val):
        val = val + ':' + self.cache_type
        while True:
            res = self.redis_con.set(self.cache_key, val, nx=True, ex=self.time_out)
            if res:
                # 表示获得锁成功,跳出循环
                break
            else:
                # 此时说明已经存在数据
                # 表示等待锁的过程,但是有一种情况是:如果检测到锁为读锁,来的操作也是读操作,那么不阻塞
                if self.cache_type == 'read':
                    check_type = str(self.redis_con.get(self.cache_key).decode()).split(':')[1]
                    if check_type == 'read':
                        break
            time.sleep(0.1)

    def del_lock(self, val):
        val = val + ':' + self.cache_type
        old_val = self.redis_con.get(self.cache_key)
        if old_val == val.encode():
            self.redis_con.delete(self.cache_key)


SUMS = 0

def test_lock(name, num, val):
    try:
        if num % 2 == 0:
            lock = ReadWriteLock('new_key', 'write')
        else:
            lock = ReadWriteLock('new_key', 'read')
        print('%s 开始工作' % name)
        print('%s 准备获取锁并加锁' % name)
        lock.get_lock(val)
        print('%s 得到锁,继续工作' % name)
        global SUMS
        if num % 2 == 0:
            SUMS += 15
            print('+++++++++++++++++++写操作++++++++++++++++')
        else:
            print('**********************读操作******************')
        time.sleep(2)
        print(SUMS)
    except Exception as e:
        print('发生异常:%s' % str(e))
    finally:
        print('%s 操作完成,准备释放锁'%name)
        lock.del_lock(val)


if __name__ == '__main__':
    start_time = time.time()
    tasks = []
    for num in range(1,4):
        t = threading.Thread(target=test_lock, args=('任务%d'%num, num, 'lock%d'%num))
        tasks.append(t)
        t.start()
    [item.join() for item in tasks]
    print('总耗时:', time.time() - start_time)

4、运行测试

python read_write_lock.py

结果:

任务1 开始工作
任务1 准备获取锁并加锁
任务2 开始工作
任务2 准备获取锁并加锁
任务3 开始工作
任务3 准备获取锁并加锁
任务2 得到锁,继续工作
+++++++++++++++++++写操作++++++++++++++++
15
任务2 操作完成,准备释放锁
任务3 得到锁,继续工作
**********************读操作******************
任务1 得到锁,继续工作
**********************读操作******************
15
任务3 操作完成,准备释放锁
15
任务1 操作完成,准备释放锁
总耗时: 4.038355827331543
  • 0
    点赞
  • 0
    评论
  • 0
    收藏
  • 打赏
    打赏
  • 扫一扫,分享海报

参与评论 您还未登录,请先 登录 后发表或查看评论
©️2022 CSDN 皮肤主题:大白 设计师:CSDN官方博客 返回首页

打赏作者

haeasringnar

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值