Redis设计思路学习与总结

版权声明:本文由宋增宽原创文章,转载请注明出处: 
文章原文连接:https://www.qcloud.com/community/article/222redis

来源:腾云阁 https://www.qcloud.com/community算法

 

宋增宽,腾讯工程师,16年毕业加入腾讯,从事海量服务后台设计与研发工做,如今负责QQ群后台等项目,喜欢研究技术,并思考技术演变,专一于高并发业务架构的设计与性能优化。数据库

下半年利用空余时间研究和分析了部分Redis源码,本文从网络模型、数据结构和内存管理、持久化和多机协做四个角度对redis的设计思路进行了分析,如有不正确之处,但愿各路大神指出。缓存

Redis是业界广泛应用的缓存组件,研究一个组件框架,最直观的办法就是从应用方的角度出发,将每一个步骤的考虑一番,从这些步骤入手去研究每每可以最快的体会到一个组件框架的设计哲学。以Redis为例,每当发起一条请求时,redis是如何管理管理网络请求,收到请求后又是经过什么样的数据结构进行组织并操做内存,这些数据又是如何dump到磁盘实现持久化,再到多机环境下如何同步和保证一致性……本文就是从网络模型、数据结构设计与内存管理、持久化方法和多机四个角度简要描述了redis的设计和本身的一点体会。安全

一.网络模型

Redis是典型的基于Reactor的事件驱动模型,单进程单线程,高效的框架老是相似的。网络模型与spp的异步模型几乎一致。性能优化

Redis流程上总体分为接受请求处理器、响应处理器和应答处理器三个同步模块,每个请求都是要经历这三个部分。服务器

Redis集成了libevent/epoll/kqueue/select等多种事件管理机制,能够根据操做系统版本自由选择合适的管理机制,其中libevent是最优选择的机制。网络

Redis的网络模型有着全部事件驱动模型的优势,高效低耗。可是面对耗时较长的操做的时候,一样没法处理请求,只能等到事件处理完毕才能响应,以前在业务中也遇到过这样的场景,删除redis中全量的key-value,整个操做时间较长,操做期间全部的请求都没法响应。因此了解清楚网络模型有助于在业务中扬长避短,减小长耗时的请求,尽量多一些简单的短耗时请求发挥异步模型的最大的威力,事实上在Redis的设计中也屡次体现这一点。数据结构

二.数据结构和内存管理

1.字符串

1.1 结构

Redis的字符串是对C语言原始字符串的二次封装,结构以下:架构

struct sdshdr {
    long len;
    long free;
    char buf[];
};

能够看出,每当定义一个字符串时,除了保存字符的空间,Redis还分配了额外的空间用于管理属性字段。

1.2 内存管理方式

动态内存管理方式,动态方式最大的好处就是可以较为充分的利用内存空间,减小内存碎片化,与此同时带来的劣势就是容易引发频繁的内存抖动,一般采用“空间预分配”和“惰性空间释放”两种优化策略来减小内存抖动,redis也不例外。

每次修改字符串内容时,首先检查内存空间是否符合要求,不然就扩大2倍或者按M增加;减小字符串内容时,内存并不会马上回收,而是按需回收。

关于内存管理的优化,最基本的出发点就是浪费一点空间仍是牺牲一些时间的权衡,像STL、tcmalloc、protobuf3的arena机制等采用的核心思路都是“预分配迟回收”,Redis也是同样的。

1.3 二进制安全

判断字符串结束与否的标识是len字段,而不是C语言的'\0',所以是二进制安全的。
放心的将pb序列化后的二进制字符串存入redis。
简而言之,经过redis的简单封装,redis的字符串的操做更加方便,性能更友好,而且屏蔽了C语言字符串的一些须要用户关心的问题。

2.字典(哈希)

字典的底层必定是hash,涉及到hash必定会涉及到hash算法、冲突的解决方法和hash表扩容和缩容。

2.1 hash算法

Redis使用的就是经常使用的Murmurhash2,Murmurhash算法可以给出在任意输入序列下的散列分布性,而且计算速度很快。以前作共享内存的Local-Cache的需求时也正是利用了Murmurhash的优点,解决了原有结构的hash函数散列分布性差的问题。

2.2 hash冲突解决方法

链地址法解决hash冲突,通用解决方案没什么特殊的。多说一句,若是选用链地址解决冲突,那么势必要有一个散列性很是好的hash函数,不然hash的性能将会大大折扣。Redis选用了Murmurhash,因此能够放心大胆的采用链地址方案。

2.3 hash扩容和缩容

维持hash表在一个合理的负载范围以内,简称为rehash过程。
rehash的过程也是一个权衡的过程,在作评估以前首先明确一点,无论中间采用什么样的rehash策略,rehash在宏观上看必定是:分配一个新的内存块,老数据搬到新的内存块上,释放旧内存块。
老数据什么时候搬?怎么搬?就变成了一个须要权衡的问题。
第一部分的网络模型上明确的指出Redis的事件驱动模型特色,不适合玩长耗时操做。若是一个hashtable很是大,须要进行扩容就一次性把老数据copy过去,那就会很是耗时,违背事件驱动的特色。因此Redis依旧采用了一种惰性的方案:
新空间分配完毕后,启动rehashidx标识符代表rehash过程的开始;以后全部增删改查涉及的操做时都会将数据迁移到新空间,直到老空间数据大小为0代表数据已经所有在新空间,将rehashidx禁用,代表rehash结束。
将一次性的集中问题分而治之,在Redis的设计哲学中体现的淋漓尽致,主要是为了不大耗时操做,影响Redis响应客户请求。

3.整数集合

变长整数存储,整数分为16/32/64三个变长尺度,根据存入的数据所属的类型,进行规划。
每次插入新元素都有可能致使尺度升级(例如由16位涨到32位),所以插入整数的时间复杂度为O(n)。这里也是一个权衡,内存空间和时间的一个折中,尽量节省内存。

4.跳跃表

Redis的skilplist和普通的skiplist没什么不一样,都是冗余数据实现的从粗到细的多层次链表,Redis中应用跳表的地方很少,常见的就是有序集合。
Redis的跳表和普通skiplist没有什么特殊之处。

5.链表

Redis的链表是双向非循环链表,拥有表头和表尾指针,对于首尾的操做时间复杂度是O(1),查找时间复杂度O(n),插入时间复杂度O(1)。
Redis的链表和普通链表没有什么特殊之处。

三.AOF和RDB持久化

AOF持久化日志,RDB持久化实体数据,AOF优先级大于RDB。

1.AOF持久化

机制:经过定时事件将aof缓冲区内的数据定时写到磁盘上。

2.AOF重写

为了减小AOF大小,Redis提供了AOF重写功能,这个重写功能作的工做就是建立一个新AOF文件代替老的AOF,而且这个新的AOF文件没有一条冗余指令。(例如对list先插入A/B/C,后删除B/C,再插入D共6条指令,最终状态为A/D,只需1条指令就能够)
实现原理就是读现有数据库的状态,根据状态反推指令,跟以前的AOF无关。一样,为了不长时间耗时,重写工做放在子进程进行。

3.RDB持久化

SAVE和BGSAVE两个命令都是用于生成RDB文件,区别在于BGSAVE会fork出一个子进程单独进行,不影响Redis处理正常请求。
定时和定次数后进行持久化操做。
简而言之,RDB的过程实际上是比较简单的,知足条件后直接去写RDB文件就结束了。

四.多机和集群

1.主从服务器

避免单点是全部服务的通用问题,Redis也不例外。解决单点就要有备机,有备机就要解决固有的数据同步问题。

1.1 sync——原始版主从同步

Redis最初的同步作法是sync指令,经过sync每次都会全量数据,显然每次都全量复制的设计比较消耗资源。改进思路也是常规逻辑,第一次全量,剩下的增量,这就是如今的psync指令的活。

1.2 psync

部分重同步实现的技术手段是“偏移序号+积压缓冲区”,具体作法以下:
(1)主从分别维护一个seq,主每次完成一个请求便seq+1,从每同步完后更新本身seq;
(2)从每次打算同步时都是携带着本身的seq到主,主将自身的seq与从作差结果与积压缓冲区大小比较,若是小于积压缓冲区大小,直接从积压缓冲区取相应的操做进行部分重同步;
(3)不然说明积压缓冲区不可以cover掉主从不一致的数据,进行全量同步。
本质作法用空间换时间,显然在这里牺牲部分空间换回高效的部分重同步,收益比很大。

2.Sentinel

本质:多主从服务器的Redis系统,多台主从上加了管理监控,以保证系统高可用性。

3.集群

Redis的官方版集群还没有在工业界普及起来,下面主要介绍一下集群的管理体系和运转体系。

2.1 slot-集群单位

集群的数据区由slot组成,每一个节点负责的slot是在集群启动时分配的。

2.2 客户请求

客户请求时若是相应数据hash后不属于请求节点所管理的slots,会给客户返回MOVED错误,并给出正确的slots。
从这个层面看,redis的集群还不够友好,集群内部的状态必须由客户感知。

2.3 容灾

主从服务器,从用于备份主,一旦主故障,从代替主。

经过Redis的研究,深入体会到的一点就是:全部设计的过程都是权衡和割舍的过程。一样放到平常的工做和开发中也是如此,一句代码写的好很差,一个模块设计的是否科学,就从速度和内存的角度去衡量看是否须要优化,并去评估每一种优化会收益到什么,同时会损失什么,收益远大于损失的就是好的优化,这样每每对于开发和提高更有针对性,更能提升效率。

相关文章
相关标签/搜索