Redis入门指南(第2版) Redis设计思路学习与总结

https://www.qcloud.com/community/article/222

宋增宽,腾讯工程师,16年毕业加入腾讯,从事海量服务后台设计与研发工作,现在负责QQ群后台等项目,喜欢研究技术,并思考技术演变,专注于高并发业务架构的设计与性能优化。
下半年利用空余时间研究和分析了部分Redis源码,本文从网络模型、数据结构和内存管理、持久化和多机协作四个角度对redis的设计思路进行了分析,若有不正确之处,希望各路大神指出。

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

一.网络模型

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

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

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

安装redis最好源码编译,而不是yum安装或rpm安装

因为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没有什么特殊之处。

http://www.cnblogs.com/xuqiang/archive/2011/05/22/2053516.html
http://www.cppblog.com/mysileng/archive/2013/04/06/199159.html

http://www.cnblogs.com/huangxincheng/p/4979789.html


Skip List 介绍
Skip List是一种随机化的数据结构,基于并联的链表,其效率可比拟于二叉查找树(对于大多数操作需要O(log n)平均时间)。基本上,跳跃列表是对有序的链表增加上附加的前进链接,增加是以随机化的方式进行的,所以在列表中的查找可以快速的跳过部分列表(因此得名)。所有操作都以对数随机化的时间进行。Skip List可以很好解决有序链表查找特定值的困难。

跳出平衡二叉树的思维

聊一聊作者的其人其事

跳表是由William Pugh发明。他在 Communications of the ACM June 1990, 33(6) 668-676 发表了Skip lists: a probabilistic alternative to balanced trees,在该论文中详细解释了跳表的数据结构和插入删除操作。



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的研究,深刻体会到的一点就是:所有设计的过程都是权衡和割舍的过程。同样放到日常的工作和开发中也是如此,一句代码写的好不好,一个模块设计的是否科学,就从速度和内存的角度去衡量看是否需要优化,并去评估每一种优化会收益到什么,同时会损失什么,收益远大于损失的就是好的优化,这样往往对于开发和提升更有针对性,更能提高效率。

 

 

 

 

 

 

 

 

f

 

转载于:https://www.cnblogs.com/MYSQLZOUQI/p/6178009.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/469708.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

智慧交通day02-车流量检测实现07:匈牙利算法

匈牙利算法(Hungarian Algorithm)与KM算法(Kuhn-Munkres Algorithm)是用来解决多目标跟踪中的数据关联问题,匈牙利算法与KM算法都是为了求解二分图的最大匹配问题。 有一种很特别的图,就做二分图&#xff0…

非线性回归(Non-linear Regression)学习笔记

非线性回归&#xff08;Non-linear Regression&#xff09; 1.概率: 1.1定义概率Probability:对一件事情发生的可能性的衡量 1.2范围 0<P<1 1.3计算方法: 1.3.1根据个人置信 1.3.2根据历史数据 1.3.3根据模拟数据 1.4条件概率:&#xff08;A发生的条件下B发生的概率&…

智慧交通day02-车流量检测实现08:目标跟踪中的数据关联(将检测框bbox与卡尔曼滤波器的跟踪框进行关联匹配)

# 将YOLO模型的检测框和卡尔曼滤波的跟踪框进行匹配 def associate_detection_to_tracker(detections,trackers,iou_threshold0.3):"""将检测框bbox与卡尔曼滤波器的跟踪框进行关联匹配:param detections:检测框:param trackers:跟踪框&#xff0c;即跟踪目标:p…

回归中的相关度和R平方值 学习笔记

回归中的相关度和R平方值 自变量x和因变量y的相关度 1.皮尔逊相关系数(Pearson Correlation Coefficient): 1.1衡量两个值线性相关强度的量 1.2取值范围[-1,1]: 正向相关: >0,负向相关: <0,无相关性: 0 公式&#xff1a;correlation&#xff0c; correlationvariance(Co…

智慧交通day02-车流量检测实现09:SORT/deepSORT

SORT和DeepSORT是多目标跟踪中两个知名度比较高的算法。DeepSORT是原团队对SORT的改进版本。现在来解析一下SORT和DeepSORT的基本思路。 1.SORT SORT核心是卡尔曼滤波和匈牙利匹配两个算法。流程图如下所示&#xff0c;可以看到整体可以拆分为两个部分&#xff0c;分别是匹配…

素数环 与 算法 全排列

在说起全排列前&#xff0c;先说一下昨天碰到的一个题目&#xff08;答案不是我做出来的&#xff0c;但是我感觉有好多个亮点&#xff0c;贴出来方便日后的学习&#xff09;&#xff1a; 素数环 时间限制&#xff1a;1000 ms | 内存限制&#xff1a;65535 KB难度&#xff1a;…

简单线性回归(Simple Linear Regression)和多元线性回归(Multiple Regression)学习笔记

简单线性回归(Simple Linear Regression) 0.前提介绍: 为什么需要统计量? 统计量:描述数据特征 0.1集中趋势衡量 0.1.1均值(平均数&#xff0c;平均值) (mean)&#xff1a;&#xff08;求和除以个数&#xff0c;Ex也可以表示x求均值&#xff09; 0.1.2中位数(median) : 将数…

智慧交通day02-车流量检测实现10:多目标追踪实现

在这里我们主要实现了一个多目标跟踪器&#xff0c;管理多个卡尔曼滤波器对象&#xff0c;主要包括以下内容&#xff1a; 初始化&#xff1a;最大检测数&#xff0c;目标未被检测的最大帧数 目标跟踪结果的更新&#xff0c;即跟踪成功和失败的目标的更新 初始化 def __init_…

智慧交通day02-车流量检测实现11:yoloV3模型

yoloV3以V1&#xff0c;V2为基础进行的改进&#xff0c;主要有&#xff1a;利用多尺度特征进行目标检测&#xff1b;先验框更丰富&#xff1b;调整了网络结构&#xff1b;对象分类使用logistic代替了softmax,更适用于多标签分类任务。 1.算法简介 YOLOv3是YOLO (You Only Loo…

bzoj1992鬼谷子的钱袋(二分乱搞 二进制)

1192: [HNOI2006]鬼谷子的钱袋 Time Limit: 10 Sec Memory Limit: 162 MBSubmit: 3223 Solved: 2333Descriptio 鬼谷子非常聪明&#xff0c;正因为这样&#xff0c;他非常繁忙&#xff0c;经常有各诸侯车的特派员前来向他咨询时政。有一天&#xff0c;他在咸阳游历的时候&…

聚类(Clustering): K-means算法

聚类(Clustering): K-means算法 1.归类: 聚类(clustering)属于非监督学习(unsupervised learning) 无类别标记( class label) 3. K-means 算法&#xff1a; 3.1 Clustering 中的经典算法&#xff0c;数据挖掘十大经典算法之一 3.2 算法接受参数 k &#xff1b;然后将事先输入…

智慧交通day02-车流量检测实现12:基于yoloV3的目标检测

在本章节代码编写中&#xff0c;发现之前的代码所处的环境是python3&#xff0c;因此导致了cv2.dnn.readNetFromDarknet()在代码运行中导致了i[0]的获值失败&#xff0c;故总结如下&#xff1a; cv2.dnn.readNetFromDarknet()在python3上遇到的问题_李大狗的读研日记-CSDN博客…

cv2.dnn.readNetFromDarknet()在python3上遇到的问题

问题描述&#xff1a; 代码如下 net cv2.dnn.readNetFromDarknet(configPath,weightsPath) #获取YOLO每一层的名称 #getLayerNames&#xff08;&#xff09;&#xff1a;获取网络所有层的名称。 ln net.getLayerNames() # 获取输出层的名称: [yolo-82,yolo-94,yolo-106] # …

智慧交通day02-车流量检测实现13:基于虚拟线圈法的车辆统计+视频中的车流量统计原理解析

1.基于虚拟线圈法的车辆统计 基于虚拟线圈的车流量统计算法原理与交通道路上的常见的传统的物理线圈类似&#xff0c;由于物理线圈需要埋设在路面之下&#xff0c;因此会有安装、维护费用高&#xff0c;造成路面破坏等问题&#xff0c;而采用基于视频的虚拟线圈的车辆计数方法…

ubuntu 12.04 eclipse 安装

方法二&#xff1a;(优点是安装内容清爽&#xff0c;缺点是配置麻烦) 1、安装JDK&#xff0c;参考 Ubuntu 12.04 下安装 JDK 7 2、下载 Eclipse 从 http://www.eclipse.org/downloads/index-developer.php下载合适版本&#xff0c;如&#xff1a;Eclipse IDE for C/C Develope…

智慧交通day02-车流量检测实现14:代码汇总+问题修正

代码权重文件资源https://download.csdn.net/download/qq_39237205/43072746https://download.csdn.net/download/qq_39237205/43072746 环境要求&#xff1a;python2.7 环境配置&#xff1a;见文末requirements.txt 1.YOLO.py # encoding:utf-8 import imutils import tim…

从资源池和管理的角度理解物理内存

早就想搞一下内存问题了&#xff01;这次正趁着搞bigmemory内核&#xff0c;可以写一篇文章了。本文旨在记录&#xff0c;不包含细节&#xff0c;细节的话&#xff0c;google&#xff0c;百度均可&#xff0c;很多人已经写了不少了。我只是按照自己的理解记录一下内存的点点滴滴…

从头开始学一个android activity

一、类层次结构&#xff1a; 二、什么是Activity&#xff0c;如何理解Activity 1、 用户与应用程序的交互的接口 2、 控件的容器&#xff0c;我们要把控件摆放在这个容器中 三、如何创建一个Activity 新建一个类&#xff1a; 1、 继承Activity类 [java] view plaincopyprint…

python3 numpy中矩阵np.dot(a,b)乘法运算

python np.dot(a,b)乘法运算 首先我们知道矩阵运算是不满足交换律的&#xff0c;np.dot(a, b)与np.dot(b, a)是不一样的 另外np.dot(a,b)和a.dot(b)果是一样的 1.numpy中数组&#xff08;矩阵&#xff09;相乘np.dot(a,b)运算&#xff1a; 对于两数组a和b &#xff1a; 示例…

ML Backpropagation算法的计算过程举例

Backpropagation计算过程举例 初始权重(initialize weights)是随机产生的(如-1~1之间) 初始化可以选择均值为0&#xff0c;方差为1/n_in的正态分布&#xff0c;n_in为输入的实例个数&#xff0c;Python中可使用np.random.normal函数来初始化权重&#xff1a; np.random.normal…