Python Redis pipeline操作和Redis乐观锁保持数据一致性
Redis是建立在TCP协议基础上的CS架构,客户端client对redis server采取请求响应的方式交互。
redis 乐观锁:也可理解为版本号比较机制,主要是说在读取数据逇时候同时读取其版本号,然后在写入的时候,进行版本号比较,如果一致,则表明此数据在监听期间未被改变,可以写入,如果不一致说明此数据被修改过,不能写入,否则会导致数据不一致的问题。
一般来说客户端从提交请求到得到服务器相应,需要传送两个tcp报文。
设想这样的一个场景,你要批量的执行一系列redis命令,例如执行100次get key,这时你要向redis请求100次+获取响应100次。如果能一次性将100个请求提交给redis server,执行完成之后批量的获取相应,只需要向redis请求1次,然后批量执行完命令,一次性结果,性能是不是会好很多呢?
答案是肯定的,节约的时间是客户端client和服务器redis server之间往返网络延迟的时间。这个时间可以用ping命令查看。
网络延迟高:批量执行,性能提升明显
网络延迟低(本机):批量执行,性能提升不明显
某些客户端(java和python)提供了一种叫做pipeline的编程模式用来解决批量提交请求的方式。
这里我们用python客户端来举例说明一下。
1、pipeline
网络延迟
client与server机器之间网络延迟如下,大约是30ms。
测试用例
分别执行其中的try_pipeline和without_pipeline统计处理时间。
# -*- coding:utf-8 -*- import redis
import time
from concurrent.futures import ProcessPoolExecutor r = redis.Redis(host='10.93.84.53', port=6379, password='bigdata123') def try_pipeline():
start = time.time()
with r.pipeline(transaction=False) as p:
p.sadd('seta', 1).sadd('seta', 2).srem('seta', 2).lpush('lista', 1).lrange('lista', 0, -1)
p.execute()
print time.time() - start def without_pipeline():
start = time.time()
r.sadd('seta', 1)
r.sadd('seta', 2)
r.srem('seta', 2)
r.lpush('lista', 1)
r.lrange('lista', 0, -1)
print time.time() - start def worker():
while True:
try_pipeline() with ProcessPoolExecutor(max_workers=12) as pool:
for _ in range(10):
pool.submit(worker)
结果分析
try_pipeline平均处理时间:0.04659
without_pipeline平均处理时间:0.16672
我们的批量里有5个操作,在处理时间维度上性能提升了4倍!
网络延迟大约是30ms,不使用批量的情况下,网络上的时间损耗就有0.15s(30ms*5)以上。而pipeline批量操作只进行一次网络往返,所以延迟只有0.03s。可以看到节省的时间基本都是网路延迟。
2、pipeline与transation(事务)
pipeline不仅仅用来批量的提交命令,还用来实现事务transation。
这里对redis事务的讨论不会太多,只是给出一个demo。详细的描述你可以参见这篇博客。redis事务
细心的你可能发现了,使用transaction与否不同之处在与创建pipeline实例的时候,transaction是否打开,默认是打开的。
# -*- coding:utf-8 -*- import redis
from redis import WatchError
from concurrent.futures import ProcessPoolExecutor r = redis.Redis(host='127.0.0.1', port=6379) # 减库存函数, 循环直到减库存完成
# 库存充足, 减库存成功, 返回True
# 库存不足, 减库存失败, 返回False
def decr_stock(): # python中redis事务是通过pipeline的封装实现的
with r.pipeline() as pipe:
while True:
try:
# watch库存键, multi后如果该key被其他客户端改变, 事务操作会抛出WatchError异常
pipe.watch('stock:count')
count = int(pipe.get('stock:count'))
if count > 0: # 有库存
# 事务开始
pipe.multi() # 这里起始位置???????????????
pipe.decr('stock:count')
# 把命令推送过去
# execute返回命令执行结果列表, 这里只有一个decr返回当前值
print pipe.execute()[0]
return True
else:
return False
except WatchError, ex:
# 打印WatchError异常, 观察被watch锁住的情况
print ex
pipe.unwatch() def worker():
while True:
# 没有库存就退出
if not decr_stock():
break # 实验开始
# 设置库存为100
r.set("stock:count", 100) # 多进程模拟多个客户端提交
with ProcessPoolExecutor(max_workers=2) as pool:
for _ in range(10):
pool.submit(worker)
Python Redis pipeline操作和Redis乐观锁保持数据一致性的更多相关文章
- redis的高级事务CAS(乐观锁)
Optimistic locking using check-and-set(乐观锁) 乐观锁介绍:watch指令在redis事物中提供了CAS的行为.为了检测被watch的keys在是否有多个cli ...
- Redis中的事务及乐观锁的实现
介绍 Redis中的事务(transaction)是一组命令的集合. 事务同命令一样都是Redis最小的执行单位,一个事务中的命令要么都执行,要么都不执行. Redis事务的实现需要用 ...
- Redis 事物、悲观、乐观锁 (详细)
1,概论 事物这东西相信大家都不陌生吧,在学习Spring,Mybatis等框架中, 只要是涉及到数据存储和修改的,都会有事物的存在, 废话就不多说了下面我们来简单的介绍下Redis事物以及锁. 2, ...
- Python Redis pipeline操作
Redis是建立在TCP协议基础上的CS架构,客户端client对redis server采取请求响应的方式交互. 一般来说客户端从提交请求到得到服务器相应,需要传送两个tcp报文. 设想这样的一个场 ...
- Python Redis pipeline操作(秒杀实现)
设想这样的一个场景,你要批量的执行一系列redis命令,例如执行100次get key,这时你要向redis请求100次+获取响应100次.如果能一次性将100个请求提交给redis server,执 ...
- ServiceStack.Redis常用操作 - 事务、并发锁_转
一.事务 使用IRedisClient执行事务示例: using (IRedisClient RClient = prcm.GetClient()) { RClient.Add("key&q ...
- ServiceStack.Redis常用操作 - 事务、并发锁
一.事务 使用IRedisClient执行事务示例: using (IRedisClient RClient = prcm.GetClient()) { RClient.Add("key&q ...
- 文成小盆友python-num11-(2) python操作Memcache Redis
本部分主要内容: python操作memcache python操作redis 一.python 操作 memcache memcache是一套分布式的高速缓存系统,由LiveJournal的Brad ...
- python通过连接池连接redis,操作redis队列
在每次使用redis都进行连接的话会拉低redis的效率,都知道redis是基于内存的数据库,效率贼高,所以每次进行连接比真正使用消耗的资源和时间还多.所以为了节省资源,减少多次连接损耗,连接池的作用 ...
随机推荐
- QQ登录整合/oauth2.0认证-02-跳转到QQ互联页
---------------------------目录---------------------------------- QQ登录整合/oauth2.0认证-01-申请appkey和appid ...
- (原+转)Ubuntu16.04软件中心闪退及wifi消失
转载请注明出处: http://www.cnblogs.com/darkknightzh/p/6321889.html 参考网址: http://blog.csdn.net/felcon/articl ...
- CocoaAsyncSocket UDP发送数据超过包大小限制(Message too long)
最近在做iOS上,基于UDP传输音视频时遇到的一个问题,这边纪录一下: 由于考虑实时性比较高,所以采用了 CocoaAsyncSocket 的UDP框架来实现,将视频切割成一帧帧的图片发给服务端,不过 ...
- E325 注意 发现交换文件
git中的 交换文件应该是保持当前git的session的一种文件. git中,如果出现这个有两种原因: 1.你开了两个git客户端对同一个git仓库进行了操作.如果是这样的话,退出一个.正常退出,不 ...
- js中的getBoundingClientRect()函数
在Chrome上,此函数返回结果包含x,y,它们的取值对应于left,top.但是x,y这种表示方式非常不直观(需要先理解x轴和y轴的方向),而left,top则清晰无歧义地表达了元素的位置.正是因此 ...
- Weex学习与实践
Weex学习与实践(一):Weex,你需要知道的事 本文主要介绍包括Weex基本介绍.Weex源码结构.初始化工程.we代码结构.Weex的生命周期.Weex的工作原理.页面间通信.boxmodel ...
- 转:mvc 当中 [ValidateAntiForgeryToken] 的作用
一.CSRF是什么? CSRF(Cross-site request forgery),中文名称:跨站请求伪造,也被称为:one click attack/session riding,缩写为:CSR ...
- mvc area出现“找到多个与名为“Home”的控制器匹配的类型”错误的解决方法
出现该问题的原因是在默认的Golbal.asax.cs文件中已经注册了默认路由 public class MvcApplication : System.Web.HttpApplication ...
- process credentials(三)
主要内容包括: 1.进程描述符中Realtime Mutex相关数据结构的初始化 2.子进程如何复制父进程的credentials 3.per-task delay accounting的处理 4.子 ...
- 转:纯CSS实现“鼠标移过显示层”效果
利用<a>标签的a:hover状态触发鼠标移过的动作,其中未触发状态显示为单个图片.兼容IE6/7/8以及FF/Chrome等主流浏览器.以下是图示及完整代码. 原文:http://www ...