使用Redis构建全局并发锁
谈起Redis的用途,小伙伴们都会说使用它作为缓存,目前很多公司都用Redis作为缓存,但是使用Redis仅仅作为缓存未免太大材小用了。深究Redis的原理后你会发现它有很多用途,在很多场景下能够使用它快速地解决问题。常见的用途有:分布式锁控制并发、结合bloom filter用于推荐去重、HyperLogLog用于统计UV、限流控制流量等等;这里我谈下Redis分布式锁控制并发的问题。
高并发是个老生常谈的问题,当产品达到一定规模用户量后,这个问题是不得不考虑的,即使当前用户量不大(例如博主现在的公司),但自己平时在设计API的时候最好也尽可能地考虑到并发问题。
Redis分布式锁控制并发主要是通过在Redis里面创建一个key,当其它进程准备占用的时候只能等待key释放再占用。Redis里面有一个原子性指令setnx,当key存在时,它返回0,表示当前已有进程占用,当它返回1时可以执行业务逻辑,此时没有进程占用,等逻辑执行完后,可以删除key释放锁,这样可以简单的控制并发。
但是细想之下你会发现,在业务逻辑执行的过程中如果发生异常,此时key并没有删除,这样就会造成死锁,死锁带来的后果想必大家都很清楚。为了解决这个问题,可以在setnx加锁后设置key的过期时间,当key到期自动删除。
但是仔细想想你还会发现,如果在执行setnx后,执行expire前Redis发生宕机了,这样就不会执行expire,也会造成死锁。由于setnx与expire是两条命令,并且expire依赖setnx的执行结果,为了解决这个问题可以使用set key value [expiration EX seconds|PX milliseconds] [NX|XX] ,这是一条原子性的指令,同时包含setnx和expire。
使用python实现的代码:
class RedisLock(object):
"""
踩坑 Redis并发锁
""" def __init__(self, key):
self.redis_conn = get_redis_conn()
self.lock_key = "{}_redis_gil".format(key) @staticmethod
def get_lock_value(cls):
"""
获取value
:param cls:
:return:
"""
cls.get_lok = cls.redis_conn.get(cls.lock_key)
return cls.get_lok @staticmethod
def set_lock(cls, random_value):
"""
不能使用setnx 没有设置过期时间,可能会出现死锁
引入random_value :自己加的锁只能自己释放
:param cls:
:param random_value:
:return:
"""
cls._lock = cls.redis_conn.set(cls.lock_key, random_value, nx=True, ex=5) # 如果返回null 表示key存在存在并发
if cls._lock:
return True
else:
LOGGER = logging.getLogger('core.utils')
LOGGER.warning(u"试题复制存在并发")
raise RsError("试题复制存在并发,请稍后再试") @staticmethod
def release(cls):
"""
释放锁
:param cls:
:return:
"""
cls.redis_conn.delete(cls.lock_key) @staticmethod
def redis_lock(cls):
"""
只有当设置的value与do_something执行完后所获取的值相同时才删除key
防止在分布式中: clientA由于执行时间过期(clientA的执行时间比设置的过期时间大),clientB获取锁,
clientA执行完后释放锁(删除key),其实这时候删除的是B的key,
为防止这种情况引入random_value 只有当前值为random_value时才删除
:param cls:
:return:
"""
random_value = time.time()
if cls.set_lock(cls, random_value):
do_something()
now_value = cls.get_lock_value(cls)
if now_value == random_value:
cls.release()
return True
else:
return False def do_something():
pass
在实际业务中调用Redis全局锁,进行加锁示例:
# 公库试题复制到平台考虑并发问题,加锁处理
if self.visible_scope == 10:
key = hash(self.question_id)
cls = RedisLock(key)
cls.redis_lock(cls)
try:
self.insert_question()
except Exception:
raise RsError("试题插入失败")
finally:
cls.release(cls)
如果是Redis集群下此方法可能仍然有问题,试想下:在一个redis集群中,主节点由于某种原因挂掉了,从节点变成了主节点,而此时redis锁还未同步到原从节点中,那么这个锁也就失效了,当其它进程申请锁时仍然可以申请成功。
针对这个问题,新版的redis引入了redlock,通过redlock.Redlock对多个redis节点进行加锁,当超过一半的节点加锁成功时锁才生效。这样在一定程度上提高了高可用性,但由于每次加锁和释放锁要对多个节点进行读写,所以性能上肯定是没有单节点锁高的。
使用Redis构建全局并发锁的更多相关文章
- Redis构建全局并发锁
Redis构建全局并发锁 https://www.cnblogs.com/FG123/p/9990336.html 谈起Redis的用途,小伙伴们都会说使用它作为缓存,目前很多公司都用Redis作为缓 ...
- nginx+lua+redis构建高并发应用(转)
nginx+lua+redis构建高并发应用 ngx_lua将lua嵌入到nginx,让nginx执行lua脚本,高并发,非阻塞的处理各种请求. url请求nginx服务器,然后lua查询redis, ...
- 【redis】基于redis实现分布式并发锁
基于redis实现分布式并发锁(注解实现) 说明 前提, 应用服务是分布式或多服务, 而这些"多"有共同的"redis"; (2017-12-04) 笑哭, 写 ...
- 使用redis构建可靠分布式锁
关于分布式锁的概念,具体实现方式,直接参阅下面两个帖子,这里就不多介绍了. 分布式锁的多种实现方式 分布式锁总结 对于分布式锁的几种实现方式的优劣,这里再列举下 1. 数据库实现方式 优点:易理解 缺 ...
- Nginx+Lua+Redis构建高并发应用
一. 源文来自:http://www.ttlsa.com/nginx/nginx-lua-redis/ 二. 预览如下:
- Redis构建分布式锁
1.前言 为什么要构建锁呢?因为构建合适的锁可以在高并发下能够保持数据的一致性,即客户端在执行连贯的命令时上锁的数据不会被别的客户端的更改而发生错误.同时还能够保证命令执行的成功率. 看到这里你不禁要 ...
- 《Redis官方文档》用Redis构建分布式锁
用Redis构建分布式锁 在不同进程需要互斥地访问共享资源时,分布式锁是一种非常有用的技术手段. 有很多三方库和文章描述如何用Redis实现一个分布式锁管理器,但是这些库实现的方式差别很大,而且很多简 ...
- Redis修改数据多线程并发—Redis并发锁
本文版权归博客园和作者本人吴双共同所有 .转载爬虫请注明地址,博客园蜗牛 http://www.cnblogs.com/tdws/p/5712835.html 蜗牛Redis系列文章目录http:// ...
- Redis并发锁控制
为了防止用户在页面上重复点击或者同时发起多次请求,请求处理需要操作redis缓存,这个时候需要对并发边界进行并发锁控制,实现思路: 由于每个页面发起的请求带的token具备唯一性,可以将token作为 ...
随机推荐
- Zookeeper分布式集群原理与功能
Zookeeper功能简介 ZooKeeper 是一个开源的分布式协调服务,由雅虎创建,是 Google Chubby 的开源实现. 分布式应用程序可以基于 ZooKeeper 实现诸如数据发布/订阅 ...
- C++:位操作基础篇之位操作全面总结
位操作篇共分为基础篇和提高篇,基础篇主要对位操作进行全面总结,帮助大家梳理知识.提高篇则针对各大IT公司如微软.腾讯.百度.360等公司的笔试面试题作详细的解答,使大家能熟练应对在笔试面试中位操作题目 ...
- bzoj2253 纸箱堆叠
题目链接 题意 求三元组的严格上升子序列 思路 先考虑暴力\(dp\)一下 for(int i = 1;i <= n;++i) for(int j = 1;j < i;++j) if(x[ ...
- Spring MVC -- Spring框架入门(IoC和DI)
Spring MVC是Spring框架中用于Web应用开发的一个模块.Spring MVC的MVC是Model-View-Controller的缩写.它是一个广泛应用于图像化用户交互开发中的设计模式, ...
- Mybatis的应用1 Mybatis和logback的应用配置
首先新建一个module, 然后,在pom文件里面添加一些引用的项. pom.xml <?xml version="1.0" encoding="UTF-8&quo ...
- 使用vlc打开usb摄像头
打开vlc播放器 可以打开网络串流的方式打开摄像头,但只支持第一个摄像头 这一串地址拼凑方法看下面,下面可以选择摄像头 为什么只支持第一个摄像头可以参考下一篇使用Vlc.DotNet打开摄像头并截图 ...
- pandas常用函数之shift
shift函数是对数据进行移动的操作,假如现在有一个DataFrame数据df,如下所示: index value1 A 0 B 1 C 2 D 3 那么如果执行以下代码: df.shift() 就会 ...
- 第六节:框架搭建之EF的Fluent Api模式的使用流程
一. 前言 沉寂了约一个月的时间,今天用一篇简单的文章重新回归博客,主要来探讨一下Fluent Api模式在实际项目中的使用流程. 1. Fluent API属于EF CodeFirst模式的一种,E ...
- [物理学与PDEs]第2章第2节 粘性流体力学方程组 2.5 粘性热传导流体动力学方程组的数学结构
1. 粘性热传导流体动力学方程组可化为 $$\beex \bea \cfrac{\p \rho}{\p t}&+({\bf u}\cdot\n)\rho=-\rho \Div{\bf u}, ...
- Chrome firefox ie等浏览器空格 宽度不一样
用半角空格 或者全角空格 相当于半格中文字符的宽度, 相当于一个中文字符宽度. 注:在chrome中两个 占一个汉字的宽度;,而在IE.firefox中四个 才占一个汉字的宽度.