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是基于内存的数据库,效率贼高,所以每次进行连接比真正使用消耗的资源和时间还多.所以为了节省资源,减少多次连接损耗,连接池的作用 ...
随机推荐
- 【OT1.0 + TP3.2】开启trace调试、输出调试信息、开启自定义菜单
1.开启trace调试 A- 后台系统设置 show-page-trace = 1 B-config.php文件.配置 show-page-trace = true 2.输出调试信息 很奇怪,OT竟然 ...
- xml中“ < > ”转义为“ < > ”问题处理
曾经也碰到过类似问题,解决方法是在发送或者解析报文前执行上面的方法将内容转义一下,现在我用dom4j组装如下的报文(报文体中内容传输时加密处理),大致介绍一下上面方法的使用,具体看代码. import ...
- 查看tcp连接信息socket几个常用的命令
查看tcp连接几个常用的命令 1 ss ss 列出所有的连接,包括tcp连接.udp连接.unix socket.raw socket ss -t 列出所有tcp连接 ss -tl 列出所有处于监听状 ...
- iOS 上传App Store提示WARNING ITMS-90703错误的说明
今天上传app到appstore的时候,上传到最后一步的时候,报了一个警告: 原文如下: WARNING ITMS-90703: "Deprecated Xcode Build. Due t ...
- placement new 笔记
之前看到 偶尔用placement new 的用法,当分配内存频繁,而且对效率要求很高的情况下,可以先申请一块大内存,然后在此内存上构建对象,关键是可以自动调用其构造函数,否则要悲剧. 但是之后要自己 ...
- C#调用XmlSerializer序列化时生成CDATA节点解决方法
public class Person{ public string Name { get; set; } public int Age { get; set; } } 引用 ...
- stm8时钟
为使系统快速启动,复位后时钟控制器自动使用HSI的8分频(HSI/8)做为主时钟(2M).其原因为HSI的稳定时间短,而8分频可保证系统在较差的VDD条件下安全启动.一旦主时钟源稳定,用户程序可将主时 ...
- java File类中的mkdir()和mkdirs()有什么区别
mkdir() 只能在已经存在的目录中创建创建文件夹. mkdirs() 可以在不存在的目录中创建文件夹.诸如:a\\b,既可以创建多级目录.
- [转]Greenplum 资源隔离的原理与源码分析
摘要: 背景 Greenplum是一个MPP的数据仓库系统,最大的优点是水平扩展,并且一个QUERY就能将硬件资源的能力发挥到极致. 但这也是被一些用户诟病的一点,因为一个的QUERY就可能占光所有的 ...
- Oozie workflow工作流action间参数传递实现
假设workflow里有两个action节点,shell和hive,hive需要用到shell节点里的值,shell脚本如下 #!/bin/sh day=`date '+%Y%m%d%H'` echo ...