Redis 查询优化原理

redis 查询优化原理

spring 封装了redis的接口,使得我们在项目中操作redis 异常的简单方便,甚至不用考虑何时释放redis连接。通常只需要两个步骤,

  1. 注入 redisTemplate

  2. 选择操作的数据类型 增、删、改、查,等等应有尽有

如此简单方便,redis 在项目开发中已经是不可或缺的存在。要知道redis是单线程的,每个命令都是原子性的,单个命令读写起来速度很快,但是多个redis 命令就不一定了,无处不在的redis操作,稍不注意可能就会出现 redis线程 或者网络阻塞现象。

Redis 查询耗时,无非浪费在网络传输,与reids 本身两个部分,因此优化查询可以针对这两个部分。

  1. 减少网络传输次数
  2. 限制一次原子性操作的内存大小

就以上两点redis提供了pipelining与 脚本等其他优化方式。本文将详细介绍pipelining为何能够加速redis读写效率,以及与其他方式比较进行详细的阐述。

pipelining 优化redis查询原理

Redis基于Request/Response协议,客户端与服务器端建立连接可能很快或许很慢,不管怎么说,网络延迟的时间是客户端发包给服务端,然后服务端发送响应给客户端,这个一来一回的耗费时间就是 RTT (Round Trip Time) 。
在这里插入图片描述

减少RTT次数

假设 一次命令 耗费的rtt 时间为250ms,那么一秒能够只处理四次redis命令。这四次请求会产生四个RTT,非常的浪费资源。pipelining就是用来优化这种情景的,pipelining需要客户端与服务端一起配合才能实现,pipelining会先在客户端把一组命令打包,然后一起发送到服务端。服务端再解析命令,返回回复给客户端。这期间只发生了一次RTT,因此大大的加速redis 的查询速度。

不仅仅RTT的问题

实际上redis 执行每条命令,访问数据结构然后产生响应,这个过程非常的廉价,真正耗费性能的是 socket I/O 。这里面包括了 read() 与 write() 系统调用,这就意味着从 user land 转换 kernel land (很多文章翻译成用户态与内核态,理解起来费劲,不如英文理解的更简单一点),这种上下文切换是比较耗时的。

当使用pipelining 时候,许多命令只会使用单个 read() 的 系统调用,对应的多个响应,也是由单个write()的系统调用来产生。

这样的话,redis 每秒处理命令的个数与pipelining打包的命令个数成正比。

是不是pipelining队列越长越好呢

答案是否定的,使用 pipelining 将客户端命令都缓存起来,但是如果缓存的数据量大小超过客户端命令的缓冲区大小,即使没有达到发送条件,也会将命令发送出去。如果pipelining中的命令没有被发送完,客户端并不会调用read()去读取socket里面的缓冲数据,如果这个客户端的响应缓存区被填满,那么就会阻止服务端再往客户端发送数据,那么服务端产生的响应就都会缓存起来,如果服务端socket 发送缓存区被填满的话,那么此时服务端调用write(socket)就会阻塞或者失败。
在这里插入图片描述

因此需要控制 pipelining 的命令个数,如果存在大量的命令,那么就使用分批pipelining 就可以了。

为什么客户端与服务端在同一台物理机,循环的redis命令依旧很慢?
FOR-ONE-SECOND:
    Redis.SET("foo","bar")
END

如上命令,循环的执行set命令,即使客户端与服务端在同一台物理机,这种循环命令依旧是非常慢的。

当redis-benchmark (redis自带的测试性能基准的工具)与redis 进程在同一台物理机上运行时候,是不是就意味着消息通过内存从一个地方到另一个地方,不会涉及网络延迟或者其他的延迟呢?

答案是否定的,因为redis 进程不总是一直运行的,实际上有个内核调度器控制redis进程的运行。

例如 启动 benchmark ,从redis server 读取响应,并写入一些新的命令,这些命令都在一个缓存区内,服务端 为了能够读到这些命令,内核会调度 redis server实例去读取,此时此刻当前程序是阻塞的状态,所以即使客户端 与服务端 在同一台物理机上,因为内核调度器的原因 ,仍然会涉及类似于网络的一种延迟。

mget mset 与pipeline 的比较

mget 与 mset 同样也能优化redis 查询,与pipeline比较,它的性能比pipeline要快的多,

有人为此使用 benchmark 做了个使用比较两者的性能。数据量为一万条。通过比较real的大小,可以看到pipeline 比 get 快了12倍,mget 比 get 快了180倍。

在这里插入图片描述

虽然 pipeline 与mget 都比get快,但是你要知道,他们俩都比 get 占用的内存多。因为redis 会将所有的 响应都先缓存起来,然后批量返回给客户端。

只比较 pipeline 与 mget , mget 拥有更快的速度,而且mget 与 mset 是原子性操作。mget 虽然有这么多优点,但是它没有 pipeline灵活,它只能够同时处理多相同的命令。pipeline 非原子性操作,同时能够处理多个不同或者相同的命令。

脚本与pipeline比较

Redis version 2.6 以上版本才支持脚本,脚本是原子性的,脚本的优势是极小的读写与计算延迟。另外还有一种场景,只有脚本能够做到,pipeline无法做到,就是想要往redis 写数据之前,要先获取读命令的响应。比如说,reids 读 key 如果这个key1存在,则往key2中写数据。在pipeline 中,命令是一起打包发送过去的,所以无法先读取 key1(先返回null)再决定要不要执行第二条命令key2。但是脚本能够做到这件事 (注:redis 也支持事务,脚本原理上也是事务,但是事务同样也跟pipeline一样,无法适用以上的场景,官方后面会根据用户的脚本与事务使用习惯,考虑是否要抛弃掉事务 )。脚本还可以缓存到redis服务器中,这样就可以极大的减少传入脚本引入的带宽。不用担心脚本会占用内存,脚本的代价是非常的小。

参考:
Using pipelining to speedup Redis queries
Redis事务
Redis get pipeline vs mget

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值