# Redis 相关

## 致谢

把这篇博客献给我初次参与创业的公司[新枝科技](http://www.oijkl.com/)。

它是一个开放而又包容的团队，在这里开始了我大学生涯的后端之路，感谢他。

## 笔记

Redis是一个开源的、由C语言编写的，**基于内存的数据结构存储**，可用作于数据库、**缓存**、消息中间件。

### 数据类型底层

常用数据类型有hash、string、list、set、sortedset，Key-Value形式，其中Key一定是string。

string -> SDS -> set,get,decr,incr,mget

hash -> ziplist/hashtable -> hget,hset,hgetall

list -> ziplist/linkedlist -> lpush,rpush,lpop,rpop,lrange

set -> intset/hashtable -> sadd,spop,smembers,sunion

zset -> ziplist/skiplist -> zadd,zrange,zrem,zcard

#### 简单动态字符串 SDS

`char buf[]; //字节数组`

`int len; //字符串长度`

`int free; //未使用长度`

#### 哈希表 hashtable

dict 结构内部包含**两个 hashtable**，通常情况下只有一个 hashtable 是有值的，数据结构类似于Java的 hashmap 。

但是在 dict **扩容缩容**时，需要分配新的 hashtable，然后进行**渐进式搬迁/渐进式 rehash** ，这时候两个 hashtable 存储的分别是旧的 hashtable 和新的 hashtable 。待搬迁结束后，旧的 hashtable 被删除，新的 hashtable 取而代之。

Redis 的字典默认的 hash 函数是 **siphash**。

#### 压缩列表 ziplist

压缩列表是一块**连续的内存空间**，元素之间紧挨着存储，没有任何冗余空隙。

hash 中K、V紧挨着排列。

zset 中V、score紧挨着排列。

#### 双向链表 linkedlist

普通的双向链表。

#### 小整数集合 intset

intset 始终保持从小到大有序。

#### 跳跃链表 skiplist

拔高某一部分，形成一个新的链表结构，便于查找。

### 优点

* 纯内存操作。
* 单线程，避免了**上下文切换**。
* **非阻塞I/O多路复用机制/事件驱动。**

### 问题

#### 缓存雪崩

缓存数据**设置的过期时间相同**，且Redis将这部分数据全部删光了。这就会导致在这段时间内，这些缓存**同时失效**，全部请求到数据库中。

#### 缓存穿透

缓存穿透是指查询一个一定**不存在的数据**。由于缓存不命中，这将导致这个不存在的数据**每次请求都要到数据库去查询**，失去了缓存的意义。

#### 缓存、数据库双写一致

* 先更新数据库 再删除缓存
* 先删除缓存 再更新数据库

### 过期策略

* 定时删除

  到时间点上就把所有过期的键删除了。
* **惰性删除** - Redis使用

  每次从键空间取键的时候，判断一下该键是否过期了，如果过期了就删除。
* **定期删除** - Redis使用

  **每隔**一段时间去删除过期键，**限制**删除的执行时长和频率。

### 持久化方式

* RDB(快照)

  将某一时刻的所有数据保存到一个RDB文件中。
* AOF(append-only-file 文件追加)

  当Redis服务器执行**写命令**的时候，将执行的**写命令**保存到AOF文件中。

#### RDB

* SAVE会**阻塞**Redis服务器进程，服务器不能接收任何请求，直到RDB文件创建完毕为止。
* BGSAVE创建出一个**子进程**负责创建RDB文件，**已过期的键不会保存在RDB文件中**。

#### AOF重写

* AOF重写由Redis自行触发，在后台子进程进行。
* AOF重写**不需要**读取现有的AOF文件，AOF重写是通过读取服务器**当前数据库的数据**来实现的。

### 内存淘汰机制

|       机制名称      |     挑选集合     |   挑选策略  |
| :-------------: | :----------: | :-----: |
|   volatile-lru  | 从已设置过期时间数据集中 |  最近最少使用 |
|   volatile-ttl  | 从已设置过期时间数据集中 | 最接近过期时间 |
| volatile-random | 从已设置过期时间数据集中 |    随机   |
| **allkeys-lru** |    从所有数据集中   |  最近最少使用 |
|  allkeys-random |    从所有数据集中   |    随机   |
|    noeviction   |    禁止淘汰数据    |  禁止淘汰数据 |

### Redis 集群

#### 主从架构

特点

* **主**服务器负责接收**写**请求
* **从**服务器负责接收**读**请求
* 从服务器的数据由主服务器**复制**过去

优势

* 读写分离(主服务器负责写，从服务器负责读)
* 高可用(某一台从服务器挂了，其他从服务器还能继续接收请求，不影响服务)
* 处理更多的并发量(每台从服务器**都可以接收读请求**，**读QPS**就上去了)

**主从复制**

* 同步(sync)

  将从服务器的数据库状态**更新至**主服务器的数据库状态。
* 命令传播(command propagate)

  主服务器的数据库状态**被修改**，导致主从服务器的数据库状态**不一致**，让主从服务器的数据库状态**重新回到一致状态。**

**哨兵机制**(Sentinel)

如果主服务器挂了，我们可以将从服务器**升级**为主服务器，等到旧的主服务器**重连**上来，会将它变成**从服务器。**

过程称为主备切换/故障转移，用于实现Redis的**高可用性。**

## 引用

* [Java3y - 从零单排学 Redis 系列](https://github.com/ZhongFuCheng3y/3y)

## [许可协议](https://3ric.gitbook.io/blog/license)

{% hint style="info" %}
本作品采用[知识共享署名 4.0 国际许可协议(CC BY-NC-SA 4.0)](https://creativecommons.org/licenses/by-nc-sa/4.0/)进行许可，转载时请注明原文链接，图片在使用时请保留全部内容，可适当缩放并在引用处附上图片所在的文章链接。
{% endhint %}
