Skip to content

Commit

Permalink
fix: markdown lint
Browse files Browse the repository at this point in the history
dongyuanxin committed Jul 12, 2019

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
1 parent e550d46 commit f48a393
Showing 2 changed files with 40 additions and 62 deletions.
1 change: 1 addition & 0 deletions .vuepress/config/sidebar.js
Original file line number Diff line number Diff line change
@@ -131,6 +131,7 @@ const design = [

const weekly = [
'',
'2019/04-redis热key等问题研究',
'2019/03-心谭-无声半年',
'2019/02-心谭-如何缩小学习反馈周期',
'2019/01-心谭-第一期',
101 changes: 39 additions & 62 deletions 每周分享/2019/04-redis热key等问题研究.md
Original file line number Diff line number Diff line change
@@ -1,117 +1,101 @@
---
title: "redis热key,缓存穿透,击穿,集群等问题研究"
date: "2019-07-012"
date: "2019-07-12"
permalink: "2019-07-12-redis-cache-question"
---

Redis主要用途有2个
> 本篇由来自 **字节跳动**[Hongkai](https://github.com/HuangHongkai) 分享。
- 数据缓存,减轻db的压力
- 数据同步,在分布式系统中各个实例的信息同步

redis的QPS量通常相当的大,动辄百万QPS,一般企业都需要很庞大的redis集群资源。由于redis的使用相当频繁,经常会出现各种问题。根据目前积累的线上经验,redis出现问题第一步就是要想到:**redis是单进程单线程实例** 理解这一点通常能解决很多问题。
Redis 主要用途有 2 个

下面介绍一些redis的一些理论知识。
- 数据缓存,减轻 db 的压力
- 数据同步,在分布式系统中各个实例的信息同步

redis 的 QPS 量通常相当的大,动辄百万 QPS,一般企业都需要很庞大的 redis 集群资源。由于 redis 的使用相当频繁,经常会出现各种问题。根据目前积累的线上经验,redis 出现问题第一步就是要想到:**redis 是单进程单线程实例** 理解这一点通常能解决很多问题。

下面介绍一些 redis 的一些理论知识。

## 主从模式

将数据分散到多个服务器上,每个服务拥有同样的样本,这样即使有一台服务器出现故障,其他服务器依然可以继续提供服务。为此, Redis 提供了复制(replication)功能,可以实现当一台数据库中的数据更新后,自动将更新的数据同步到其他数据库上。

### 概念

在redis或mysql等数据库中,数据库分为两类,一类是主数据库(master),另一类是从数据库(slave)。主数据库可以进行读写操作,当写操作导致数据变化时会自动将数据同步给从数据库。而从数据库一般是只读的,并接受主数据库同步过来的数据。一个主数据库可以拥有多个从数据库,而一个从数据库只能拥有一个主数据库。


在 redis 或 mysql 等数据库中,数据库分为两类,一类是主数据库(master),另一类是从数据库(slave)。主数据库可以进行读写操作,当写操作导致数据变化时会自动将数据同步给从数据库。而从数据库一般是只读的,并接受主数据库同步过来的数据。一个主数据库可以拥有多个从数据库,而一个从数据库只能拥有一个主数据库。

### 复制原理

当从数据库启动时,会向主数据库发送sync命令,主数据库接收到sync后开始在后台保存快照rdb,在保存快照期间受到的命名缓存起来,当快照完成时,主数据库会将快照和缓存的命令一块发送给从。复制初始化结束。之后,主每接收到1个命令就同步发送给从。 当出现断开重连后,2.8之后的版本会将断线期间的命令传给从数据库。


当从数据库启动时,会向主数据库发送 sync 命令,主数据库接收到 sync 后开始在后台保存快照 rdb,在保存快照期间受到的命名缓存起来,当快照完成时,主数据库会将快照和缓存的命令一块发送给从。复制初始化结束。之后,主每接收到 1 个命令就同步发送给从。 当出现断开重连后,2.8 之后的版本会将断线期间的命令传给从数据库。

### 优点:

- 减轻单点压力

- 提升系统容灾能力



### 缺点:

- 承载的容量非常有限,假设服务器的内存都是40G,有10台服务器,意味着只能容纳40G的数据。
- 刚写入的数据可能获取不到(因为数据还没有更新到其他服务器上)


- 承载的容量非常有限,假设服务器的内存都是 40G,有 10 台服务器,意味着只能容纳 40G 的数据。
- 刚写入的数据可能获取不到(因为数据还没有更新到其他服务器上)

## 哨兵

在主从模式下,当主服务出现异常会导致整体redis不可写。开发者需要手动将一个从数据库升级为主数据库保证服务正常运行。该过程难以自动后,为此在redis2.8后提供了哨兵来实现故障恢复功能
在主从模式下,当主服务出现异常会导致整体 redis 不可写。开发者需要手动将一个从数据库升级为主数据库保证服务正常运行。该过程难以自动后,为此在 redis2.8 后提供了哨兵来实现故障恢复功能

哨兵就是redis的监控系统,包括2个功能
哨兵就是 redis 的监控系统,包括 2 个功能

- 监控主数据库和从数据库是否正常运行。
- 主数据库出现故障时自动将从数据库转换为主数据库。
- 监控主数据库和从数据库是否正常运行。
- 主数据库出现故障时自动将从数据库转换为主数据库。

### 主从切换过程

- slave leader升级为master
- 其他slave修改为新master的slave
- 客户端修改连接
- 老的master如果重启成功,变为新master的slave


- slave leader 升级为 master
- 其他 slave 修改为新 master 的 slave
- 客户端修改连接
- 老的 master 如果重启成功,变为新 master 的 slave

### 集群

使用哨兵,redis每个实例也是全量存储,每个redis存储的内容都是完整的数据,浪费内存且有木桶效应。为了最大化利用内存,可以采用集群,即每台机器存储不同的内容。

简单来讲:通过不断的扩容和增加运行redis实例的机器数量,使得redis能撑得住更大的访问量

使用哨兵,redis 每个实例也是全量存储,每个 redis 存储的内容都是完整的数据,浪费内存且有木桶效应。为了最大化利用内存,可以采用集群,即每台机器存储不同的内容。

简单来讲:通过不断的扩容和增加运行 redis 实例的机器数量,使得 redis 能撑得住更大的访问量

### 原理

redis cluster中有一个16384(2^4 * 2^10)长度的槽的概念。通过哈希算法再加上取模运算可以将一个值固定地映射到某个区间,区间由连续的slot组成
redis cluster 中有一个 16384(2^4 \* 2^10)长度的槽的概念。通过哈希算法再加上取模运算可以将一个值固定地映射到某个区间,区间由连续的 slot 组成

redis cluster采用虚拟槽分区,所有的键根据哈希函数(CRC16[key]&16383)映射到0-16383槽内,共16384个槽位,每个节点维护部分槽及槽所映射的键值数据,可以为节点设置权重,权重高的节点维护的槽的数量比较多。
redis cluster 采用虚拟槽分区,所有的键根据哈希函数(CRC16[key]&16383)映射到 0-16383 槽内,共 16384 个槽位,每个节点维护部分槽及槽所映射的键值数据,可以为节点设置权重,权重高的节点维护的槽的数量比较多。

哈希函数: Hash()=CRC16[key]&16383 按位与

redis用虚拟槽分区原因:解耦数据与节点关系,节点自身维护槽映射关系,分布式存储


redis 用虚拟槽分区原因:解耦数据与节点关系,节点自身维护槽映射关系,分布式存储

### 集群模式

- 客户端模式

在客户端做redis负载均衡,通过服务发现注册redis节点,实现redis节点的动态改变(增删或者节点扩容)
在客户端做 redis 负载均衡,通过服务发现注册 redis 节点,实现 redis 节点的动态改变(增删或者节点扩容)

![img](https://i.loli.net/2019/07/12/5d27764947bf559054.png)

优点

- 数据分散,使得整体容量得到提升
- 当一个节点不可用不会导致整体服务不可用
- 性能高,客户端直接连接redis,不通过代理
- 性能高,客户端直接连接 redis,不通过代理
- 扩容方便

目前企业使用的redis集群基本都是客户端模式,这样可以减轻服务端的压力。
目前企业使用的 redis 集群基本都是客户端模式,这样可以减轻服务端的压力。

- 代理模式

客户端不是直接连接redis,而是连接代理,这样子客户端的开发就变得简单很多了。。同时服务端也可以控制redis的连接数
客户端不是直接连接 redis,而是连接代理,这样子客户端的开发就变得简单很多了。。同时服务端也可以控制 redis 的连接数

![img](https://i.loli.net/2019/07/12/5d27764e1a87934952.png)

### 分析

集群模式的解决了容量的问题,但是带来了很多的问题,例如redis的热key问题,单点高并发会导致redis负载及其不均衡,进而服务可用性极低。


集群模式的解决了容量的问题,但是带来了很多的问题,例如 redis 的热 key 问题,单点高并发会导致 redis 负载及其不均衡,进而服务可用性极低。

## 穿透

@@ -123,44 +107,37 @@ redis用虚拟槽分区原因:解耦数据与节点关系,节点自身维护

频繁查询一个不存在的数据,由于缓存不命中,每次都要查询持久层。从而失去缓存的意义。

如果恶意用户知道我们redis key的构造规则,每次故意查询一个在缓存内必然不存在的数据,导致每次请求都要去存储层去查询,这样缓存就失去了意义。如果在高并发下数据库可能挂掉。这就是**缓存击穿**


如果恶意用户知道我们 redis key 的构造规则,每次故意查询一个在缓存内必然不存在的数据,导致每次请求都要去存储层去查询,这样缓存就失去了意义。如果在高并发下数据库可能挂掉。这就是**缓存击穿**

## 雪崩

redis缓存大量失效的时候,引发大量查询数据库。如果有的数据只放在redis上(例如用来同步多个服务实例信息之间的共享数据),redis缓存失效会导致服务出现异常,这比查数据库更加严重

引起这个问题的原因主要是redis单点被打垮,也就是热key问题。

redis 缓存大量失效的时候,引发大量查询数据库。如果有的数据只放在 redis 上(例如用来同步多个服务实例信息之间的共享数据),redis 缓存失效会导致服务出现异常,这比查数据库更加严重

引起这个问题的原因主要是 redis 单点被打垮,也就是热 key 问题。

## 击穿

缓存击穿是指缓存中没有但数据库中有的数据(一般是缓存时间到期),这时由于并发用户特别多,同时读缓存没读到数据,又同时去数据库去取数据,引起数据库压力瞬间增大,造成过大压力
缓存击穿是指缓存中没有但数据库中有的数据(一般是缓存时间到期),这时由于并发用户特别多,同时读缓存没读到数据,又同时去数据库去取数据,引起数据库压力瞬间增大,造成过大压力

解决方案:

- 设置频繁访问的数据过期(让缓存持续命中)
- 加分布式锁(如果缓存不存在,只让一个进程去读db,有点类似于单例模式的实现,下面是一个从网上找的小demo
- 加分布式锁(如果缓存不存在,只让一个进程去读 db,有点类似于单例模式的实现,下面是一个从网上找的小 demo

![img](https://i.loli.net/2019/07/12/5d277650ad21c42141.png)



### 热key
### 热 key

在集群模式下经常会遇到这个问题,业界依然没有好的解决方案(参见微博只要热点事件就垮了)

热key:某个key访问非常的频繁**redis集群负载不均衡,导致单点延迟增加,从而导致单点redis上的缓存不可用**
热 key:某个 key 访问非常的频繁**redis 集群负载不均衡,导致单点延迟增加,从而导致单点 redis 上的缓存不可用**

这个问题相当的难以解决,微博在出现热点事件后服务不可用,很大原因是热key问题,导致单点redis被打垮了, 增加db访问量,进而系统雪崩。
这个问题相当的难以解决,微博在出现热点事件后服务不可用,很大原因是热 key 问题,导致单点 redis 被打垮了, 增加 db 访问量,进而系统雪崩。

因为**redis是单进程单线程**redis的处理线程忙起来,会导致redis没空accept来自client的tcp请求,进而出现连接超时的错误,缓存未命中。
因为**redis 是单进程单线程**redis 的处理线程忙起来,会导致 redis 没空 accept 来自 client 的 tcp 请求,进而出现连接超时的错误,缓存未命中。

### 解决方案

没有具体的解决方案,需要根据业务来制定。思路**将key打散**就是将redis访问某个热key,转化为访问多个key,减轻单点压力
没有具体的解决方案,需要根据业务来制定。思路**将 key 打散**就是将 redis 访问某个热 key,转化为访问多个 key,减轻单点压力

但并不是所有的业务都能使用该解决方案。

0 comments on commit f48a393

Please sign in to comment.