Redis 系列--06. Redis 架构

Redis 系列--06. Redis 架构

Redis 在实际使用的过程中,针对不同的场景需要对应的架构,这篇博客主要是总结 Redis 在实际在生产中遇到的架构以及各自的有点和缺点。

1. 单机架构

Redis 架构

阅读更多
Redis底层原理--05. Redis 数据库

Redis底层原理--05. Redis 数据库

1数据库

1.1 数据结构

Redis 中的每个数据库,都由一个 redis.h/redisDb 结构表示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
typedef struct redisDb {
// 保存着数据库以整数表示的号码
int id;
// 保存着数据库中的所有键值对数据
// 这个属性也被称为键空间(key space)
dict *dict;
// 保存着键的过期信息
dict *expires;
// 实现列表阻塞原语,如 BLPOP
// 在列表类型一章有详细的讨论
dict *blocking_keys;
dict *ready_keys;
// 用于实现 WATCH 命令
// 在事务章节有详细的讨论
dict *watched_keys;
} redisDb;
阅读更多
Redis底层原理--04. Redis 功能的实现
Redis底层原理--03. Redis 数据类型

Redis底层原理--03. Redis 数据类型

1. 对象处理机制

由于 redis 需要对每一个 key 产生不同的操作,所以Redis 必须让每个键都带有类型信息,使得程序可以检查键的类型,并为它选择合适的处理方式

为了解决以上问题, Redis 构建了自己的类型系统,这个系统的主要功能包括:

  • redisObject 对象。基于 redisObject 对象的类型检查。
  • 基于 redisObject 对象的显式多态函数。
  • 对 redisObject 进行分配、共享和销毁的机制。
阅读更多
Redis底层原理--02. 内存映射数据结构

Redis底层原理--02. 内存映射数据结构

1. 整数集合

整数集合(intset)用于有序、无重复地保存多个整数值,它会根据元素的值,自动选择该用什
么长度的整数类型来保存元素

举个例子,如果在一个 intset 里面,最长的元素可以用 int16_t 类型来保存,那么这个 intset
的所有元素都以 int16_t 类型来保存。

另一方面,如果有一个新元素要加入到这个 intset并且这个元素不能用 int16_t 类型来保存
——比如说,新元素的长度为 int32_t ,那么这个 intset 就会自动进行“升级” :先将集合中现有的所有元素从 int16_t 类型转换为 int32_t 类型,接着再将新元素加入到集合中。

阅读更多
Redis底层原理--01. Redis 中的数据结构

Redis底层原理--01. Redis 中的数据结构

简单的字符串

1. 设计要点

在 C 语言中,字符串可以用一个 \0 结尾的 char 数组来表示。 比如说,hello world 在 C 语言中就可以表示为 “hello world\0” 。

这种简单的字符串表示在大多数情况下都能满足要求,但是,它并不能高效地支持长度计算和 追加(append)这两种操作:

  • 每次计算字符串长度(strlen(s))的复杂度为 θ(N) 。

  • 对字符串进行 N 次追加,必定需要对字符串进行 N 次内存重分配(realloc)。

在 Redis 内部,字符串的追加和长度计算并不少见,而 APPEND 和 STRLEN 更是这两种操 作在 Redis 命令中的直接映射,这两个简单的操作不应该成为性能的瓶颈。

另外,Redis 除了处理 C 字符串之外,还需要处理单纯的字节数组,以及服务器协议等内容, 所以为了方便起见,Redis 的字符串表示还应该是二进制安全的:程序不应对字符串里面保存 的数据做任何假设,数据可以是以 \0 结尾的 C 字符串,也可以是单纯的字节数组,或者其他 格式的数据。

考虑到这两个原因,Redis 使用 sds 类型替换了 C 语言的默认字符串表示: sds 既可以高效地 实现追加和长度计算,并且它还是二进制安全的。

2. sds 数据结构

阅读更多

Redis系列--布隆过滤器

在 Redis 的使用场景中,基本的架构图如下:
布隆过滤器
如果在缓存中查询不到数据,会直接到 DB 中查询,查询的数据再插入到缓存中。例如我们根据 orderId 查询对应的订单,具体伪代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
OrderEntity getOrder(String orderId){
//1. 查询缓存
OrderEntity orderEntity= getFromRedis(orderId);
if (orderEntity != null) {
return orderEntity;
}
//2. 缓存不存在
orderEntity=getFromDb(orderId);
//3. 塞入缓存
setRedis(orderEntity);
//4. 返回查询的值
return orderEntity;
}

如果这是时候客户端查询的 orderId 绝大多数都不在缓存中,这样就会带来一个 缓存穿透 的问题。如果有客户端恶意攻击,例如客户端使用 UUID 来访问我们的数据或者接口,势必会导致访问压力都落到 DB 上面,导致 DB 压力的上升。

为了解决这个缓存穿透,可以在 Redis 和 DB 中间增加一个过滤器,在访问 DB 前询问下过滤器,然后再决定是否查询 DB,具体结构图如下:

布隆过滤器

hash过滤

Hash 是一个简单 key — value 结构,如果在数据量不大的情况下,可以尝试把 DB 中的数据中的单列存储在缓存中

1
2
3
4
5
00001 1
00002 1
....

99999 1

在客户端访问时,可以先到 hash 中看下是否有值,然后根据过滤器返回值来确定是否查询 DB。

阅读更多