《Redis设计与实现》第一部分

前言

工作中经常会用到Redis,虽说会一些Redis的增删改查的API基本可以满足日常工作的需求,但是在一些时候还是会遇到一些疑惑。比如在插入string类型然后取出来的时候我遇到过解码问题,还有插入二进制数据的一些问题。当然,面试的时候Redis也是经常会被问到的一个问题。听说Redis的实现是非常精妙的,代码质量也非常高。总之是时候了解一下Redis的底层实现了。

第一部分 数据结构与对象

简单动态字符串

Redis不是使用的C语言的传统字符串而是构建了一种简单动态字符串SDS的抽象类型。

SDS用来保存数据库中的字符串值,也被用作缓冲区(buffer)。在这里插入图片描述

  • 记录长度的另外一个好处就是可以杜绝缓冲区溢出
  • sds的另外一个属性free记录的是还有多少空余的空间,这样可以实现空间预分配和惰性空间释放两种优化策略。
  • SDS是二进制安全的,因为不以\0作为结束,而是以长度做判断,所以SDS可以存储任意二进制信息。
  • SDS也可以复用一些C语言的库函数。

链表

当一个列表键包含了很多元素,或者列表中包含的元素都是比较长的字符串时,Redis就会使用链表作为列表键的底层实现。
在这里插入图片描述
在这里插入图片描述

  • 双端,无环,带头尾指针,长度计数器
  • 多态:通过为链接设置不同的类型特定函数,Redis的链表可以用于保存不同类型的值。

字典

Redis的数据库就是使用字典来作为底层实现,增删改查也是构建在对字典的操作之上。
字典使用哈希表作为底层实现。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
看到这里发现Redis的字典的实现其实和Java里面字典的实现非常像,都是一个数组,通过hash值作为索引,然后用链表来解决键的hash冲突。
在这里插入图片描述
关于rehash,我理解是用来扩展大小用的。
https://www.jianshu.com/p/13c650a25ed3

  1. 首先创建一个比现有哈希表更大的新哈希表(expand)
  2. 然后将旧哈希表的所有元素都迁移到新哈希表去(rehash)
    下面这张图就很好的说明了Redis的字典结构了:
    在这里插入图片描述

hash算法

添加键值对时,通过键计算出hash值和索引。
在这里插入图片描述
解决键冲突的方式也适合hashmap一样用一个链表,新加的键放在表头位置,保证O(1)

rehash

看到这里明白dict.ht[2]了,有两个ht是为了rehash时使用的,扩展或缩小时,吧ht[0]的rehash一下,放到ht[1],然后释放0,把1再放到0上。这就完成了dict的缩放。
这一rehash过程也不是一次完成,如果多的话需要渐进式rehash,用dict.trehashidx来表示是否在rehash,如果是-1表示不在rehash。

跳跃表

在每个节点中维持多个指向其他节点的指针,达到快速访问节点的目的。Redis使用跳跃表来作为有序集合键的底层实现。
跳表的效率和红黑树以及 AVL 树不相上下,但跳表的原理相当简单,只要你能熟练操作链表,就能轻松实现一个 SkipList。
关于跳表的概念下面两个博客写得比较好:
https://www.cnblogs.com/thrillerz/p/4505550.html
https://www.cnblogs.com/Leo_wl/p/11557614.html

有序表

考虑一个有序表:
在这里插入图片描述
从该有序表中搜索元素 < 23, 43, 59 > ,需要比较的次数分别为 < 2, 4, 6 >,总共比较的次数
为 2 + 4 + 6 = 12 次。有没有优化的算法吗? 链表是有序的,但不能使用二分查找。类似二叉
搜索树,我们把一些节点提取出来,作为索引。得到如下结构:
在这里插入图片描述
这里我们把 < 14, 34, 50, 72 > 提取出来作为一级索引,这样搜索的时候就可以减少比较次数了。
我们还可以再从一级索引提取一些元素出来,作为二级索引,变成如下结构:
在这里插入图片描述
这里元素不多,体现不出优势,如果元素足够多,这种索引结构就能体现出优势来了。

跳跃表

其中 -1 表示 INT_MIN, 链表的最小值,1 表示 INT_MAX,链表的最大值。
在这里插入图片描述
跳表具有如下性质:
(1) 由很多层结构组成
(2) 每一层都是一个有序的链表
(3) 最底层(Level 1)的链表包含所有元素
(4) 如果一个元素出现在 Level i 的链表中,则它在 Level i 之下的链表也都会出现。
(5) 每个节点包含两个指针,一个指向同一链表中的下一个元素,一个指向下面一层的元素。

跳表的搜索

在这里插入图片描述
例子:查找元素 117
(1) 比较 21, 比 21 大,往后面找
(2) 比较 37, 比 37大,比链表最大值小,从 37 的下面一层开始找
(3) 比较 71, 比 71 大,比链表最大值小,从 71 的下面一层开始找
(4) 比较 85, 比 85 大,从后面找
(5) 比较 117, 等于 117, 找到了节点。

跳表的插入

先确定该元素要占据的层数 K(采用丢硬币的方式,这完全是随机的)
然后在 Level 1 … Level K 各个层的链表都插入元素。
例子:插入 119, K = 2
在这里插入图片描述
如果 K 大于链表的层数,则要添加新的层。
例子:插入 119, K = 4
在这里插入图片描述丢硬币决定 K
插入元素的时候,元素所占有的层数完全是随机的
相当与做一次丢硬币的实验,如果遇到正面,继续丢,遇到反面,则停止,
用实验中丢硬币的次数 K 作为元素占有的层数。显然随机变量 K 满足参数为 p = 1/2 的几何分布,
K 的期望值 E[K] = 1/p = 2. 就是说,各个元素的层数,期望值是 2 层。

跳表的删除

在各个层中找到包含 x 的节点,使用标准的 delete from list 方法删除该节点。

例子:删除 71
在这里插入图片描述

跳跃表节点实现:
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

整数集合

当一个集合只包含整数值元素,而且数量不多时,Redis就会使用整数集合作为集合键的底层实现。
在这里插入图片描述

集合升级降级

如果之前都是int16,然后要存一个int32的数字,就要对整数集合进行升级,重新分配空间,然后把现有的元素转型。
整数集合不支持降级操作。

压缩列表

是列表键和hash键的底层实现之一
压缩列表结构
每个压缩列表的节点(entry)都可以保存一个字节数组或者一个整数值。
entry的构成:在这里插入图片描述
这张图片其实比较好的说明了压缩列表。
这篇博客的内容关于Redis压缩列表的介绍更详细:https://www.cnblogs.com/hunternet/p/11306690.html 在这里插入图片描述

对象

前面介绍的几种数据结构,Redis并没有直接使用这些数据结构来实现键值对数据库,而是基于这些数据结构创建了一个对象系统。
一个优点就是可以针对不同的使用场景来为对象设置多种不同的数据结构实现,从而优化对象在不同场景下的使用效率。
Redis还实现了基于引用计数的内存回收机制,程序不再使用某个对象的时候,这个对象占用的内存就会被释放。还有空转市场这个概念。
对象结构:
在这里插入图片描述
type属性有字符串,列表,hash,集合,有序集合。
在这里插入图片描述
ptr指向的是对象的底层实现数据结构。由encoding属性决定。
在这里插入图片描述

字符串对象

字符串对象的编码可以是int,rwa,embstr
例子:在这里插入图片描述

列表对象

底层可以使ziplist或者linkedlist。
使用压缩表的列表对象ptr指向一个ziplist。
双端链表对象每个节点都是一个字符串对象。后面的hash,集合等对象也会嵌套字符串对象。
在这里插入图片描述

哈希对象

编码可以使压缩列表或者hash表。在这里插入图片描述
在这里插入图片描述

集合对象

intset或者hashtable

有序集合

ziplist or skiplist

类型检查

类型检查这个东西其实就是多态,针对不同的类型有不同的处理方式。

内存回收

基于引用计数实现的内存回收机制

对象共享

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 为了实现 Nginx、Django、MySQL 和 Redis 的高可用架构,您可以使用以下步骤: 1. Nginx 集群:通过使用 Nginx 的负载均衡功能,您可以将多台服务器组合在一起,从而实现高可用性。 2. Django 集群:通过在多台服务器上运行 Django 应用程序,并使用 Nginx 对请求进行负载均衡,您可以实现 Django 的高可用性。 3. MySQL 集群:通过使用 MySQL 的主从复制功能,您可以将多个数据库服务器组合在一起,从而实现数据的高可用性。 4. Redis 集群:通过使用 Redis 的分布式存储功能,您可以将数据存储在多台服务器上,从而实现高可用性。 通过实施这些技术,您可以实现 Nginx、Django、MySQL 和 Redis 的高可用架构。 ### 回答2: 要实现nginx, django, mysql, redis的高可用架构,可以按照以下步骤进行设计与配置。 第一步,搭建负载均衡 使用nginx来实现负载均衡,可以配置多个nginx服务器组成一个集群,采用轮询或者IP hash等负载均衡算法,将流量均匀分配给后端的django服务器。 第二步,配置django高可用 可以通过使用uwsgi或者gunicorn等工具将django应用部署为多个实例,并将它们放在不同的服务器上。通过配置nginx反向代理,将请求通过负载均衡方式分发给这些实例。当有实例出现故障时,负载均衡将请求自动切换到健康的实例上。 第三步,设置mysql高可用 数据库是应用的核心组件,可以通过使用主从复制来实现mysql的高可用性。将一台mysql服务器配置为主服务器,多台配置为从服务器,主服务器负责写操作,从服务器进行读操作。当主服务器发生故障时,可以通过自动切换将从服务器提升为主服务器,确保数据库的可用性。 第四步,配置redis高可用 可以使用redis的主从复制或者redis集群来实现高可用性。主从复制中,将一台redis服务器配置为主服务器,多台配置为从服务器。主服务器负责写操作,从服务器进行读操作。在主服务器发生故障时,可以将从服务器提升为主服务器。在redis集群中,将多个redis节点组成一个集群,每个节点保存部分数据,通过使用一致性哈希算法将数据均匀分布在不同的节点上,当有节点出现故障时,集群会自动进行恢复。 通过以上步骤的配置与设计,可以实现nginx, django, mysql, redis的高可用架构,确保系统能够在单个组件出现故障时继续提供服务。 ### 回答3: 要实现nginx django mysql redis高可用架构,可以采用以下步骤: 1. 配置负载均衡:使用nginx作为反向代理服务器,通过配置upstream来实现负载均衡。将多个django应用部署在不同的服务器上,且这些服务器的IP都添加到upstream中。这样,nginx将根据负载均衡算法将请求分发到不同的django应用服务器中。 2. 数据库高可用:使用MySQL的主从复制或者主从集群来实现数据库的高可用。在主从复制中,将一个MySQL服务器配置为主服务器,其他服务器配置为从服务器。主服务器负责写操作,从服务器负责读操作。在主从集群中,使用多个MySQL实例进行复制,并通过主服务器自动选举机制来保证高可用性。这样,当其中一个数据库服务器出现故障时,其他服务器仍然可以提供服务。 3. 缓存层高可用:使用redis的主从复制或者redis集群来实现缓存层的高可用。在主从复制中,将一个redis服务器配置为主服务器,其他服务器配置为从服务器。主服务器负责写操作,从服务器负责读操作。在redis集群中,使用多个redis节点进行数据分片,同时实现数据复制和故障转移。这样,当其中一个redis节点出现故障时,其他节点仍然可以提供服务。 4. 监控和故障转移:使用监控系统对nginx、django、mysql和redis进行监控,及时发现并处理故障。使用自动化工具来实现故障转移,当一个节点或服务器故障时,自动将请求切换到其他正常的节点或服务器上。 通过以上的架构设计,可以实现nginx django mysql redis的高可用性,提高系统的可靠性和稳定性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值