<Redis><Pipelining>

Overview

  • About Redis pipelining
  • About Redis memory optimization
  • About Redis expire
  • About Redis transactions

Pipelining

Request/Response protocols and RTT

  • Redis is a TCP server using the client-server model and what is called a Request/Response protocol.
  • This means that usually a request is accomplished with the following steps:
    • The client sends a query to the server, and reads from the socket, usually in a blocking way, for the server response.
    • The server processes the command and sends the response back to the client.
  • Clients and servers are connected via a networking link. Such a link can be very fasr(a loopback interface) or very slow(a connection established over the Internet with many hops between two hosts).
  • The time is called RTT(Round Trip Time). This will affect the performances when a client needs to perform many requests in a row. [当客户端需要在一个批处理中执行多次请求时,RTT就会对性能产生较大的影响。] 比如说,假设RTT时间很长,250ms,这时候即使服务器每秒能处理100k的请求数,我们每秒也只能处理4个请求。
  • If the interface used is a loopback interface, the RTT is much shorter(for instance my host reports 0.044ms pinging 127.0.0.1), but it is still a lot if u need to perform many writes in a row.

Redis Pipelining

  • A Request/Response server can be implemented so that it is able to process new requests even if the client didn‘t already read the old responses. [服务器能够在旧的请求还未被响应的情况下处理新的请求,那么就可以将多个命令发送到服务器,而不用等待回复,直接在最后一个步骤中读取该回复即可。]

Summary

  • so,如果不用管道,四个请求需要8个TCP报文,4次RTT。如果网络延迟0.125s,那么需要1s完成四个请求。redis的处理能力完全发挥不出来。
  • 为了提高效率,处理利用mset、mget之类的单条命令处理多个key外,
  • 我们还可以使用pipelining的方式,从client打包多条命令,一起发出,不需要等待单条命令的响应返回,redis server会处理完多条命令后将结果打包返回给client。
  • 假设tcp报文不会因为过长而被拆分,上述4个请求就只需要两个tcp报文。
  • 但是也不是说打包的命令越多越好,因为打包的命令越多,内存消耗也越多。因为redis必须在处理完所有命令前缓存之前所有命令的处理结果。

Test

  • public void pipeLine() {
            Jedis jedis = null;
            try {
                jedis = new Jedis(REDIS_HOST, REDIS_PORT);
                Pipeline pipeline = jedis.pipelined();
                for (int i = 0; i < COUNT; i++) {
                    pipeline.incr(testKey);
                }
                pipeline.sync();
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                if(jedis != null) jedis.disconnect();
            }
    
        }
    
        public void withoutPipeline() {
            Jedis jedis = null;
            try {
                jedis = new Jedis(REDIS_HOST, REDIS_PORT);
                for (int i = 0; i < COUNT; i++) {
                    jedis.incr(testKey);
                }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                if (jedis != null) jedis.disconnect();
            }
        }
    
        public static void main(String[] args) {
            PipelineTest pipelineTest = new PipelineTest();
            long start = System.currentTimeMillis();
    
            pipelineTest.withoutPipeline();
    
            long mid = System.currentTimeMillis();
            System.out.println("Without pipeline, using: " + (mid - start));
    
            pipelineTest.pipeLine();
    
            System.out.println("With pipeline, using: " + (System.currentTimeMillis() - mid));
    
        }
  • 输出:(100条incr请求)

    Without pipeline, using: 9816
    With pipeline, using: 45

Memory Optimization

Special encoding of small aggreagte data types

  • Redis2.2以后,存储集合数据的时候会采用内存压缩技术。
  • 比如对Hash、List、Set以及Sorted Set,当这些集合中的所有数都小雨一个给定的元素,并且集合中元素数量小于某个值时,存储的数据会被以一种十分节省内存的方式进行编码。理论上能节省10倍以上内存。并且该编码技术对用户和redis api透明。
  • 实质上是用CPU换内存。
  • 阈值可以在redis.conf中修改。

Using 32 bit instances

  • 使用32bit的redis,那么对每个key将使用更少的内存。

Bit and byte level operations

  • Redis2.2引入了位级别和字级别的操作:GETRANGE, SETRANGE, GETBIT, SETBIT。
  • 通过使用这些命令,你可以把redis的字符串当成一个随机读取的(字节)数组。
  • 例子:如果用户的id是连续的整数,那么你可以使用bitmap位图来纪录用户的性别,可以直接操作在bit之上。

Uses hashes when possible

  • 小的散列表(小是指散列表内部存储的对象少)使用的内存非常小。
  • TBD...

Expire

EXPIRE Key seconds

  • 超时后只有在对key执行DEL、SET或GETSET时才会清除。[?????]
  • 过期精度:从Redis2.6起,过期时间误差缩小到0-1ms。
  • Redis如何淘汰过期key:
    • Redis keys过期有两种方式:主动和被动方式。
    • 被动:当一些客户端尝试访问它们时,key会被发现并主动的过期。
    • 主动:Redis每10s:
      • 测试随机的20个key,进行相关过期检测;
      • 删除已过期的key;
      • 若有多于25%的key过期,则重复步骤1
    • 以上,这是一个平凡的概率算法。

Redis Transactions

Introduction

  • MULTI, EXEC, DISCARD,WATCH是redis事务相关的命令。
  • 事务可以一次执行多个命令,并且:
    • 事务是一个单独的隔离操作:事务中所有命令都会序列化、按顺序地执行。执行过程中,不会被其他client的请求打断。
    • 原子操作:要么全部执行,要么全部不执行。
  • EXEC:
    • EXEC负责触发并执行事务中的所有命令
    • 如果client在使用MULTI开启了一个事务之后,如果没有执行EXEC,那么事务中所有命令都不会执行。
  • 当使用AOF方式做持久化时,redis会使用单个write命令将事务写到磁盘中。然而,若此时redis server由于某些原因挂掉,那么可能只有部分事务命令会被成功写入到磁盘中。
  • 若在redis重启时发现AOF文件出现了这样的问题,那么它会退出,并汇报一个错误。

为什么Redis不支持roll back

  • 这种做法的优点:

    • 因为不需要支持roll back,因此redis内部可以保持简单且快速。
    • redis命令只会因为错误的语法而失败(并且这些错误不能在入队时发现),或是因为命令用在了错误类型的key上。理论上这些错误应该在开发过程被发现。

使用check-and-set乐观锁

  • WATCH命令可以为redis事务提高check-and-set(CAS)行为
  • TBD...
时间: 2024-12-21 08:59:53

<Redis><Pipelining>的相关文章

CI框架源码阅读笔记3 全局函数Common.php

从本篇开始,将深入CI框架的内部,一步步去探索这个框架的实现.结构和设计. Common.php文件定义了一系列的全局函数(一般来说,全局函数具有最高的加载优先权,因此大多数的框架中BootStrap引导文件都会最先引入全局函数,以便于之后的处理工作). 打开Common.php中,第一行代码就非常诡异: if ( ! defined('BASEPATH')) exit('No direct script access allowed'); 上一篇(CI框架源码阅读笔记2 一切的入口 index

IOS测试框架之:athrun的InstrumentDriver源码阅读笔记

athrun的InstrumentDriver源码阅读笔记 作者:唯一 athrun是淘宝的开源测试项目,InstrumentDriver是ios端的实现,之前在公司项目中用过这个框架,没有深入了解,现在回来记录下. 官方介绍:http://code.taobao.org/p/athrun/wiki/instrumentDriver/ 优点:这个框架是对UIAutomation的java实现,在代码提示.用例维护方面比UIAutomation强多了,借junit4的光,我们可以通过junit4的

Yii源码阅读笔记 - 日志组件

?使用 Yii框架为开发者提供两个静态方法进行日志记录: Yii::log($message, $level, $category);Yii::trace($message, $category); 两者的区别在于后者依赖于应用开启调试模式,即定义常量YII_DEBUG: defined('YII_DEBUG') or define('YII_DEBUG', true); Yii::log方法的调用需要指定message的level和category.category是格式为“xxx.yyy.z

源码阅读笔记 - 1 MSVC2015中的std::sort

大约寒假开始的时候我就已经把std::sort的源码阅读完毕并理解其中的做法了,到了寒假结尾,姑且把它写出来 这是我的第一篇源码阅读笔记,以后会发更多的,包括算法和库实现,源码会按照我自己的代码风格格式化,去掉或者展开用于条件编译或者debug检查的宏,依重要程度重新排序函数,但是不会改变命名方式(虽然MSVC的STL命名实在是我不能接受的那种),对于代码块的解释会在代码块前(上面)用注释标明. template<class _RanIt, class _Diff, class _Pr> in

CI框架源码阅读笔记5 基准测试 BenchMark.php

上一篇博客(CI框架源码阅读笔记4 引导文件CodeIgniter.php)中,我们已经看到:CI中核心流程的核心功能都是由不同的组件来完成的.这些组件类似于一个一个单独的模块,不同的模块完成不同的功能,各模块之间可以相互调用,共同构成了CI的核心骨架. 从本篇开始,将进一步去分析各组件的实现细节,深入CI核心的黑盒内部(研究之后,其实就应该是白盒了,仅仅对于应用来说,它应该算是黑盒),从而更好的去认识.把握这个框架. 按照惯例,在开始之前,我们贴上CI中不完全的核心组件图: 由于BenchMa

CI框架源码阅读笔记2 一切的入口 index.php

上一节(CI框架源码阅读笔记1 - 环境准备.基本术语和框架流程)中,我们提到了CI框架的基本流程,这里这次贴出流程图,以备参考: 作为CI框架的入口文件,源码阅读,自然由此开始.在源码阅读的过程中,我们并不会逐行进行解释,而只解释核心的功能和实现. 1.       设置应用程序环境 define('ENVIRONMENT', 'development'); 这里的development可以是任何你喜欢的环境名称(比如dev,再如test),相对应的,你要在下面的switch case代码块中

Apache Storm源码阅读笔记

欢迎转载,转载请注明出处. 楔子 自从建了Spark交流的QQ群之后,热情加入的同学不少,大家不仅对Spark很热衷对于Storm也是充满好奇.大家都提到一个问题就是有关storm内部实现机理的资料比较少,理解起来非常费劲. 尽管自己也陆续对storm的源码走读发表了一些博文,当时写的时候比较匆忙,有时候衔接的不是太好,此番做了一些整理,主要是针对TridentTopology部分,修改过的内容采用pdf格式发布,方便打印. 文章中有些内容的理解得益于徐明明和fxjwind两位的指点,非常感谢.

CI框架源码阅读笔记4 引导文件CodeIgniter.php

到了这里,终于进入CI框架的核心了.既然是"引导"文件,那么就是对用户的请求.参数等做相应的导向,让用户请求和数据流按照正确的线路各就各位.例如,用户的请求url: http://you.host.com/usr/reg 经过引导文件,实际上会交给Application中的UsrController控制器的reg方法去处理. 这之中,CodeIgniter.php做了哪些工作?我们一步步来看. 1.    导入预定义常量.框架环境初始化 之前的一篇博客(CI框架源码阅读笔记2 一切的入

jdk源码阅读笔记之java集合框架(二)(ArrayList)

关于ArrayList的分析,会从且仅从其添加(add)与删除(remove)方法入手. ArrayList类定义: p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 18.0px Monaco } span.s1 { color: #931a68 } public class ArrayList<E> extends AbstractList<E> implements List<E> ArrayList基本属性: /** *

dubbo源码阅读笔记--服务调用时序

上接dubbo源码阅读笔记--暴露服务时序,继续梳理服务调用时序,下图右面红线流程. 整理了调用时序图 分为3步,connect,decode,invoke. 连接 AllChannelHandler.connected(Channel) line: 38 HeartbeatHandler.connected(Channel) line: 47 MultiMessageHandler(AbstractChannelHandlerDelegate).connected(Channel) line: