之前章节首次介绍multi和exec的时候讨论过它们的”事务“性质:被multi和exec包裹的命令在执行时不会被其他客户端打扰。而使用事务的其中一个好处就是底层的客户端会通过使用流水线来提高事务执行的性能。本节将介绍如何在不使用事务的情况下,通过使用流水线来进一步提升命令的执行性能。

创建redis实例,连接数据库

  • StrictRedis
from redis import StrictRedis

# 使用默认方式连接到数据库
conn = StrictRedis(host='localhost', port=6379, db=0) # 使用url方式连接到数据库
conn = StrictRedis.from_url('redis://@localhost:6379/1')
  • ConnectionPool
from redis import StrictRedis,ConnectionPool

# 使用默认方式连接到数据库
pool = ConnectionPool(host='localhost', port=6379, db=0)
conn = StrictRedis(connection_pool=pool) # 使用url方式连接到数据库
pool = ConnectionPool.from_url('redis://@localhost:6379/1')
conn = StrictRedis(connection_pool=pool)

构造url方式连接到数据库,有以下三种模式:

redis://[:password]@host:port/db    # TCP连接
rediss://[:password]@host:port/db # Redis TCP+SSL 连接
unix://[:password]@/path/to/socket.sock?db=db # Redis Unix Socket 连接

之前章节曾经介绍过一些可以接受多个参数的添加命令和更新命令,如mget、mset、hmget、hmset、rpush、lpush、sadd、zadd等。这些命令简化了那些需要重复执行相同命令的操作,并且极大地提升了性能。尽管效果可能没有以上提到的命令那么显著,但使用非事务型流水线同样可以获得相似的性能提升,并且可以让用户同时执行多个不同的命令。

在需要执行大量命令的情况下,即使命令实际上并不需要放在事务里面执行,但是为了通过一次发送所有命令来减少通信次数并降低延迟值,用户也可能会将命令包裹在multi和exec里面执行。遗憾的是,multi和exec并不是免费的:它们也会消耗资源,并且可能会导致其他重要的命令被延迟执行。不过好消息是,我们实际上可以在不使用multi和exec的情况下,获得流水线代理的所有好处。之前章节使用了一下语句来在Python中执行multi和exec命令:

pipe=conn.pipeline()

如果用户在执行pipeline()时传入True作为参数,或者不传入任何参数,那么客户端将使用multi和exec包裹起用户要执行的所有命令。另一方面,如果用户在执行pipeline()时传入False为参数,那么客户端同样会像执行事务那样收集起用户要执行的所有命令,只是不再使用multi和exec包裹这些命令。如果用户需要向Redis发送多个命令,并且对于这些命令来说,一个命令的执行结果并不会影响另一个命令的输入,而且这些命令也不需要以实物的方式来执行的话,那么我们可以通过向pipeline()方法传入False来进一步提升Redis的整体性能。让我们来看一个这方面的例子。

前面章节曾经编写并更新过一个名为update_token()函数,它负责记录用户最近浏览过的商品以及用户最近访问过的页面,并更新用户的登录cookie。下面代码是之前展示过得更新版update_token()函数,这个函数每次执行都会调用2个或者5个Redis命令,使得客户端和Redis之间产生2次或5次通信往返。

import time

def update_token(conn,token,user,item=None):
#获取时间戳
timestamp=time.time()
#创建令牌和已登陆用户之间的映射
conn.hset('login:',token,user)
#记录令牌最后一次出现的时间
conn.zadd('recent:',token,timestamp)
if item:
#把用户浏览过的商品记录起来
conn.zadd('viewed:'+token,item,timestamp)
#移除旧商品,只记录最新浏览的25件商品
conn.zremrangebyrank('viewed:'+token,0,-26)
#更新给定商品的被浏览慈善
conn.zincrby('viewed:',item,-1)

如果Redis和Web服务器通过局域网进行连接,那么他们之前的每次通信往返大概需要耗费一两毫秒,因此需要进行2次或者5次通信往返的update_token()函数大概需要花费2~10毫秒来执行,按照这个速度计算,单个Web服务器线程每秒可以处理100~500个请求,尽管这种速度已经非常可观了,但是我们还可以在这个速度的基础上更新一步:通过修改update_token()函数,让它创建一个非事务型流水线,然后使用这个流水线来发送所有请求,这样我们就的带了下面代码:

import time

def update_token_pipeline(conn,token,user,item=None):
#获取时间戳
#设置流水线
pipe=conn.pipeline(False)
timestamp=time.time()
#创建令牌和已登陆用户之间的映射
conn.hset('login:',token,user)
#记录令牌最后一次出现的时间
conn.zadd('recent:',token,timestamp)
if item:
#把用户浏览过的商品记录起来
conn.zadd('viewed:'+token,item,timestamp)
#移除旧商品,只记录最新浏览的25件商品
conn.zremrangebyrank('viewed:'+token,0,-26)
#更新给定商品的被浏览慈善
conn.zincrby('viewed:',item,-1)
pipe.execute()

通过将标准的Redis连接替换成流水线连接,程序可以将通信往返的次数减少至原来的1/2到1/5,并将update_token_pipeline()函数的预期执行时间降低1~2毫秒。按照这个速度来计算的话,如果一个Web服务器只需要执行update_token_pipeline()来更新商品的浏览信息,那么这个Web服务器每秒可以处理500~1000个请求。从理论上来看,update_token_pipeline() 函数的效果非常棒,但是它的实际运行速度又是怎样的呢?

为了回答这个问题,我们将对update_token()函数和update_token_pipeline()函数进行一些简单的测试。我们将分别通过快速低延迟网络和慢速高延迟网络来访问同一台机器,并测试运行在机器上面的Redis每秒可以处理的请求数量。下面代码展示了性能测试函数,这个函数会在给定的时限内重复执行update_token()函数或者update_token_pipeline()函数,然后计算被测试的函数每秒执行了多少次。

import time

def benchmark_update_token(conn,duration):
#测试会分别执行update_token函数和update_token_pipeline函数
for function in (update_token,update_token_pipeline):
#设置计数器以及测试结束的条件
count=0
start=time.time()
end=start+duration
while time.time()<end:
count+=1
#调用两个函数的其中一个
function(conn,'token','user','item')
#计算函数的执行时长
delta=time.time()-start
#打印测试结果
print(function.__name__+":"+str(count)+","+str(delta)+","+str(count/delta))

下面展示了在不同宽带以及不同延迟值的网络上执行性能测试函数所得到的数据。

在不同类型的网络上执行流水线和非流水线连接:对于高速网络,测试程序几乎达到了单核处理器可以编码/解码Redis命令的极限;而对于低俗网络,测试程序的运行则受到网络带宽和延迟值的影响

描述 带宽 延迟值 每秒调用update_table()的次数 每秒调用update_table_pipeline()的次数
本地服务器,Unix域套接字 大于1Gb(gigabit) 0.015ms 3761 6394
本地服务器,本地连接 大于1Gb 0.015ms 3257 5991
远程服务器,共享交换机 1Gb 0.271ms 739 2841
远程服务器,通过VPN连接 1.8Mb(megabit) 48ms 3.67 18.2

根据上表数据显示,高延迟网络使用流水线时的速度要比不使用流水线时的速度快5倍,低延迟网络使用流水线也可以带来接近4倍的速度提升,而本地网络的测试结果实际上已经达到了Python在单核环境下使用Redis协议发送和接受短命令序列的性能极限了。

现在我们已经知道如何在不使用事务的情况下,通过使用流水线来提示Redis的性能了,那么除了流水线之外,还有其他可以提升Redis性能的常规方法吗?

阅读原文

使用python来操作redis用法详解

Python操作Redis数据库

Pyhton--Redis实战:Mac brew安装redis

Python--Redis实战:第一章:初识Redis:第一节:Redis简介

Python--Redis实战:第一章:初识Redis:第二节:Redis数据结构简介

Python--Redis实战:第一章:初识Redis:第三节:你好Redis-文章投票试炼

Python--Redis实战:第二章:使用Redis构建Web应用:第一节:登录和cookie缓存

Python--Redis实战:第二章:使用Redis构建Web应用:第二节:使用Redis实现购物车

Python--Redis实战:第二章:使用Redis构建Web应用:第三节:网页缓存

Python--Redis实战:第二章:使用Redis构建Web应用:第四节:数据行缓存

Python--Redis实战:第二章:使用Redis构建Web应用:第五节:网页分析

Python--Redis实战:第三章:Redis命令:第一节:字符串

Python--Redis实战:第三章:Redis命令:第二节:列表

Python--Redis实战:第三章:Redis命令:第三节:集合

Python--Redis实战:第三章:Redis命令:第四节:散列

Python--Redis实战:第三章:Redis命令:第五节:有序集合

Python--Redis实战:第三章:Redis命令:第六节:发布与订阅

Python--Redis实战:第三章:Redis命令:第七节:其他命令

Python--Redis实战:第四章:数据安全与性能保障:第1节:持久化选项

Python--Redis实战:第四章:数据安全与性能保障:第2节:快照持久化

Python--Redis实战:第四章:数据安全与性能保障:第3节:AOF持久化

Python--Redis实战:第四章:数据安全与性能保障:第4节:复制

Python--Redis实战:第四章:数据安全与性能保障:第5节:处理系统故障

Python--Redis实战:第四章:数据安全与性能保障:第6节:Redis事务

Python--Redis实战:第四章:数据安全与性能保障:第7节:非事务型流水线

Python--Redis实战:第四章:数据安全与性能保障:第8节:关于性能方面的注意事项

Python--Redis实战:第四章:数据安全与性能保障:第7节:非事务型流水线的更多相关文章

  1. redis实战笔记(4)-第4章 数据安全与性能保障

    本章主要内容 4.1 将数据持久化至硬盘 4.2 将数据复制至其他机器 4.3 处理系统故障 4.4 Redis事务 4.5 非事务型流水线( non-transactional pipeline) ...

  2. python学习心得第四章

     python 学习心得第四章 1.lambda表达式 1:什么是lambda表达式 为了简化简单函数的代码,选择使用lambda表达式 上面两个函数的表达式虽然不一样,但是本质是一样的,并且lamb ...

  3. python机器学习实战(四)

    python机器学习实战(三) 版权声明:本文为博主原创文章,转载请指明转载地址 www.cnblogs.com/fydeblog/p/7364317.html 前言 这篇notebook是关于机器学 ...

  4. Spring实战第四章学习笔记————面向切面的Spring

    Spring实战第四章学习笔记----面向切面的Spring 什么是面向切面的编程 我们把影响应用多处的功能描述为横切关注点.比如安全就是一个横切关注点,应用中许多方法都会涉及安全规则.而切面可以帮我 ...

  5. 进击的Python【第十四章】:Web前端基础之Javascript

    进击的Python[第十四章]:Web前端基础之Javascript 一.javascript是什么 JavaScript 是一种轻量级的编程语言. JavaScript 是可插入 HTML 页面的编 ...

  6. Rxjava2实战--第四章 Rxjava的线程操作

    Rxjava2实战--第四章 Rxjava的线程操作 1 调度器(Scheduler)种类 1.1 RxJava线程介绍 默认情况下, 1.2 Scheduler Sheduler 作用 single ...

  7. Redis的数据安全与性能保障

    1.持久化选项 Redis提供了2种不同的持久化方法来将数据存储到硬盘里面.一种方法叫快照(snapshotting),它可以将存在于某一时刻的所有数据都写入硬盘里.另一种方法叫只追加文件(appen ...

  8. Redis 数据安全与性能保障

    数据安全与性能保障 ·将数据持久化至硬盘·将数据复制至其他机器·处理系统故障·reids事务·非实物型流水线·诊断性能问题 持久化选项: 共享选项,这个选项决定了快照文件和AOF文件的保存位置dir ...

  9. 《Python CookBook2》 第四章 Python技巧 对象拷贝 && 通过列表推导构建列表

    (先学第四章) 对象拷贝 任务: Python通常只是使用指向原对象的引用,并不是真正的拷贝. 解决方案: >>> a = [1,2,3] >>> import c ...

随机推荐

  1. 敌兵布阵 HDU1166

    基础线段树 #include<cstdio> #include<iostream> using namespace std; int n,p,a,b,m,x,y,ans; st ...

  2. 【LeetCode】157. Read N Characters Given Read4

    Difficulty: Easy  More:[目录]LeetCode Java实现 Description The API: int read4(char *buf) reads 4 charact ...

  3. 017 在SecureCRT中安装rz小工具

    1.安装yum 2.上传本地的文件进虚拟机 3.注意点 只是属于SecureCRT的命令,同时,在上传的位置是现在所在的位置 4.测试

  4. Brute-Force算法

    #include <iostream> #include <string> using namespace std; int BF(const string& fath ...

  5. Nodejs学习之mongodb Error: failed to connect to [localhost:27017]

    在连接mongodb时出现以下错误提示信息 events.js: throw er; // Unhandled 'error' event ^ Error: failed to connect to ...

  6. 码云,git使用 教程-便签

    码云,git使用 教程-便签 Code cloud, git use tutorial - note 作者:韩梦飞沙 Author:han_meng_fei_sha 邮箱:313134555@qq.c ...

  7. Bzoj2673 3961: [WF2011]Chips Challenge 费用流

    国际惯例题面:如果我们枚举放几个零件的话,第二个限制很容易解决,但是第一个怎么办?(好的,这么建图不可做)考虑我们枚举每行每列最多放几个零件t,然后计算零件总数sum.这样如果可行的话,则有t*B&l ...

  8. 51Nod.1244.莫比乌斯函数之和(杜教筛)

    题目链接 map: //杜教筛 #include<map> #include<cstdio> typedef long long LL; const int N=5e6; in ...

  9. FlarumChina SQL injection Vulnerability

    First,We need to download our vulnerable program in GitHub links:https://github.com/skywalker512/Fla ...

  10. php 允许浏览器跨域访问web服务端的解决方案

    今天和同事探讨了前后端如何真正实现隔离开发的问题,如果前端单独作为服务发布,势必会涉及到无法直接调用后端的接口的问题,因为浏览器是不允许跨域提交请求的. 所谓跨域访问,就是在浏览器窗口,和某个服务端通 ...