《蒋德钧的 Redis 集训班》学习笔记 Day one

536 阅读6分钟

对Redis的第一印象

三高一丰富

  • 高性能:性能特性
  • 丰富的数据类型:应用接口
  • 内存数据库:持久化模式(高可用)
  • 缓存:应用场景
  • Redis集群:部署架构,主从,切片
  • 端口号:6379

Redis的系统架构

要学习一门技术,就要系统学习他的架构,而不是细枝末节,Redis的系统架构图如下所示:

image.png

Redis技术栈

从设计角度上看,设计一个缓存数据库需要实现什么功能,我们拆解一下Redis到底是什么样的,整体都有哪些功能?

需求1:数据存取

  • 提供不同数据类型的数据结构满足应用多样化需求
  • 充分优化数据结构,在时间和空间上取得平衡
  • 有限内存空间,内存分配,释放和数据淘汰
  • 数据存取需求实现要包含的两大部分:基本数据结构和内存管理,如下图所示:

image.png

需求2:应用的访问

  • 通过网络访问Redis实例,快速响应网络请求
  • 不同语言的客户端,访问协议,集群功能的支持
  • 数据库安全机制
  • 应用的访问需求实现的功能,如下图所示:

image.png

需求3:实例的可用性

  • 通过主从节点集群创建数据副本,通过副本保证数据可靠性
  • 复制机制,数据一致性保证
  • 故障恢复与哨兵机制
  • 实例的可用性需求实现的功能,如下图所示:

image.png

需求4:缓存应用

  • 缓存的基本原理
  • 缓存数据替换和Redis数据淘汰策略相结合
  • 缓存和后端数据库一致性保证
  • 缓存需求需要实现的功能,如下图所示:

image.png

需求5:性能优化

  • 避免任何阻塞Redis操作的潜在瓶颈
  • 理解Redis线程模型,掌握和内存分配、磁盘读写相关的关键机制
  • 性能优化需求实现,如下图所示:自己可以对比下,检验自己是否掌握了这些,掌握了这些之后就是实战了。

image.png

数据存取

下面开始就是数据存取需求的具体细节了,这个功能是如此重要,所以是一定要掌握的。

基本数据模型

这个章节开始展开讲第一个技术栈,数据存取。包含基本数据类型,内存管理

1.键值基本类型

整体来说redis就是靠一张全局的HashTable来存储数据的,下面来了解下我们的基本类型:

  • key-value
  • key:字符串,Redis数据库的键总是字符串
  • value:类型多样化,满足应用多样化需求;Redis数据库的值可以字符串,集合,列表等多种类型的对象。

2.全局哈希表(初始值大小是4)

  • 保存所有的key-value对,注意跟基本数据类型的hash类型的区别
  • 使用两个哈希表,实现渐进式Rehash
  • 哈希表表项:dictEntry,如下图:表项包含三个字段!

image.png

  • dictEntry中的key,value都是指向一个redisObject,是c语言的一个结构体,占16字节。然后这个key,value,next都是指针,占8byte
typedef struct redisObject {
    unsigned type:4;       // 4个bit位,数据类型 integer  string  list  set zset
    unsigned encoding:4;  // 指定使用ziplist,quicklist等等底层数据结构
    unsigned lru:LRU_BITS; /* 占24位,LRU time (relative to global lru_clock) or
                            * LFU data (least significant 8 bits frequency
                            * and most significant 16 bits access time). 
                            * redis用24个位来保存LRU和LFU的信息,当使用LRU时保存上次
                            * 读写的时间戳(秒),使用LFU时保存上次时间戳(16位 min级) 保存近似统计数8位 */
    int refcount;          // 占4个字节,引用计数 
    void *ptr;              // 占8个字节,指针指向具体存储的值,类型用type区分
} robj;
  • 键值对的hash值对哈希表的size取模
  • hash值取模后对于相同的键值使用链表连接
  • Rehash操作
    • 哈希冲突较多时,哈希表表项的链长增加,哈希表操作变慢
    • 两张哈希表ht0,ht1,实现渐进式rehash,比当前bucket数量值大的最小的2的n次方
    • 为什么说是渐进式rehash,其实都是为了减少阻塞,渐进式rehash怎么实现的
    • Rehash的触发条件:负载因子,约束条件
    • Rehash操作时机:伴随正常读写操作执行,或者周期性执行(由databasecron定时器控制)

3.String类型

  • 二进制安全的字节数组
  • 适用场景:数值,文本数据,图片
  • 底层是SDS(Simple Dynamic String)

4.List类型

  • 双向链表,支持双向的POP和PUSH,元素可以重复
  • 使用场景:排行榜,关注列表

5.Hash类型

  • Hash字典,一个key对应多个value
  • 使用场景:结构化数据

6.Set类型

  • 无序集合,元素去重,支持集合操作
  • 使用场景:共同关注,共同爱好,二度好友

7.Sorted Set类型

  • 有序集合,元素去重,支持集合操作
  • 使用场景:有序排行榜,排序

8.位图(bitmap)类型

  • 二进制安全的字节数组,每个bit位表示位图的一位
  • 使用场景:用户签到,用户状态统计

9.HyperLogLog类型

  • 基数统计,统计一个集合中不重复的元素的个数,实现近似统计
  • 使用场景:用户日活,月活统计

底层数据结构

SDS

  • 是String类型的底层数据结构,是二进制安全的,即没有像C语言那样,字符串里面不能以\0结尾,这样一来,就会以第一个空字符做为结尾了。
  • 3.x.x版本,具体实现源码,如下代码所示,最新版本有所改变:
typedef char *sds;

struct sdshdr {
    // buf 已占用长度
    int len;

    // buf 剩余可用长度
    int free;

    // 实际保存字符串数据的地方
    char buf[];
};
  • 对比C字符串,sds有以下的特性:
    • 可以高效地执行长度计算:(strlen),传统C语言的字符串实现是一个char* 数组,要想获取字符串长度的话,需要遍历数组,时间复杂度是O(N),而使用SDS的话,因为存有字符串的长度,直接获取就行,时间复杂度O(1)
    • 可以高效地执行追加操作:(append)
    • SDS能保存的数据更加多种多样
    • 如下图所示,两者对比之下,SDS无疑是更加好的选择
C字符串SDS
获取字符串长度的复杂度为 O(N)获取字符串长度的复杂度为 O(N)
操作字符串函数不安全,可能造成缓冲区溢出安全的操作字符串API,避免缓冲区溢出
修改字符串长度 N 次必然需要执行 N 次内存重分配修改字符串长度 N 次必然需要执行 N 次内存重分配
只能保存文本数据可以保存文本以及图片、音频、视频、压缩文件这样的二进制数据。