关于后端:15万字总结-Redis-常见面试题知识点

3次阅读

共计 21319 个字符,预计需要花费 54 分钟才能阅读完成。

以下内容来源于于我开源的 JavaGuide(Java 学习 && 面试指南,Github 130k star,370 人独特参加爱欠缺), 万字总结,品质有保障!

这篇文章最早写于 2019 年,通过不断完善,内容也更全面了,外面的很多内容也被很多人参考借鉴。

Redis 根底

什么是 Redis?

Redis 是一个基于 C 语言开发的开源数据库(BSD 许可),与传统数据库不同的是 Redis 的数据是存在内存中的(内存数据库),读写速度十分快,被广泛应用于缓存方向。并且,Redis 存储的是 KV 键值对数据。

为了满足不同的业务场景,Redis 内置了多种数据类型实现(比方 String、Hash、Sorted Set、Bitmap)。并且,Redis 还反对事务、长久化、Lua 脚本、多种开箱即用的集群计划(Redis Sentinel、Redis Cluster)。

Redis 没有内部依赖,Linux 和 OS X 是 Redis 开发和测试最多的两个操作系统,官网举荐生产环境应用 Linux 部署 Redis。

集体学习的话,你能够本人本机装置 Redis 或者通过 Redis 官网提供的在线 Redis 环境来理论体验 Redis。

全世界有十分多的网站应用到了 Redis,techstacks.io 专门保护了一个应用 Redis 的热门站点列表,感兴趣的话能够看看。

Redis 为什么这么快?

Redis 外部做了十分多的性能优化,比拟重要的次要有上面 3 点:

  • Redis 基于内存,内存的访问速度是磁盘的上千倍;
  • Redis 基于 Reactor 模式设计开发了一套高效的事件处理模型,次要是单线程事件循环和 IO 多路复用(Redis 线程模式前面会具体介绍到);
  • Redis 内置了多种优化过后的数据结构实现,性能十分高。

上面这张图片总结的挺不错的,分享一下,出自 Why is Redis so fast?。

分布式缓存常见的技术选型计划有哪些?

分布式缓存的话,比拟老牌同时也是应用的比拟多的还是 MemcachedRedis。不过,当初根本没有看过还有我的项目应用 Memcached 来做缓存,都是间接用 Redis

Memcached 是分布式缓存最开始衰亡的那会,比拟罕用的。起初,随着 Redis 的倒退,大家缓缓都转而应用更加弱小的 Redis 了。

另外,腾讯也开源了一款相似于 Redis 的分布式高性能 KV 存储数据库,基于出名的开源我的项目 RocksDB 作为存储引擎,100% 兼容 Redis 协定和 Redis4.0 所有数据模型,名为 Tendis。

对于 Redis 和 Tendis 的比照,腾讯官网已经发过一篇文章:Redis vs Tendis:冷热混合存储版架构揭秘,能够简略参考一下。

从这个我的项目的 Github 提交记录能够看出,Tendis 开源版简直曾经没有被保护更新了,加上其关注度并不高,应用的公司也比拟少。因而,不倡议你应用 Tendis 来实现分布式缓存。

说一下 Redis 和 Memcached 的区别和共同点

当初公司个别都是用 Redis 来实现缓存,而且 Redis 本身也越来越弱小了!不过,理解 Redis 和 Memcached 的区别和共同点,有助于咱们在做相应的技术选型的时候,可能做到有理有据!

共同点

  1. 都是基于内存的数据库,个别都用来当做缓存应用。
  2. 都有过期策略。
  3. 两者的性能都十分高。

区别

  1. Redis 反对更丰盛的数据类型(反对更简单的利用场景)。Redis 不仅仅反对简略的 k/v 类型的数据,同时还提供 list,set,zset,hash 等数据结构的存储。Memcached 只反对最简略的 k/v 数据类型。
  2. Redis 反对数据的长久化,能够将内存中的数据放弃在磁盘中,重启的时候能够再次加载进行应用, 而 Memcached 把数据全副存在内存之中。
  3. Redis 有劫难复原机制。 因为能够把缓存中的数据长久化到磁盘上。
  4. Redis 在服务器内存应用完之后,能够将不必的数据放到磁盘上。然而,Memcached 在服务器内存应用完之后,就会间接报异样。
  5. Memcached 没有原生的集群模式,须要依附客户端来实现往集群中分片写入数据;然而 Redis 目前是原生反对 cluster 模式的。
  6. Memcached 是多线程,非阻塞 IO 复用的网络模型;Redis 应用单线程的多路 IO 复用模型。(Redis 6.0 引入了多线程 IO)
  7. Redis 反对公布订阅模型、Lua 脚本、事务等性能,而 Memcached 不反对。并且,Redis 反对更多的编程语言。
  8. Memcached 过期数据的删除策略只用了惰性删除,而 Redis 同时应用了惰性删除与定期删除。

置信看了下面的比照之后,咱们曾经没有什么理由能够抉择应用 Memcached 来作为本人我的项目的分布式缓存了。

为什么要用 Redis/ 为什么要用缓存?

上面咱们次要从“高性能”和“高并发”这两点来答复这个问题。

高性能

如果用户第一次拜访数据库中的某些数据的话,这个过程是比较慢,毕竟是从硬盘中读取的。然而,如果说,用户拜访的数据属于高频数据并且不会常常扭转的话,那么咱们就能够很释怀地将该用户拜访的数据存在缓存中。

这样有什么益处呢? 那就是保障用户下一次再拜访这些数据的时候就能够间接从缓存中获取了。操作缓存就是间接操作内存,所以速度相当快。

高并发

个别像 MySQL 这类的数据库的 QPS 大略都在 1w 左右(4 核 8g),然而应用 Redis 缓存之后很容易达到 10w+,甚至最高能达到 30w+(就单机 Redis 的状况,Redis 集群的话会更高)。

QPS(Query Per Second):服务器每秒能够执行的查问次数;

由此可见,间接操作缓存可能接受的数据库申请数量是远远大于间接拜访数据库的,所以咱们能够思考把数据库中的局部数据转移到缓存中去,这样用户的一部分申请会间接到缓存这里而不必通过数据库。进而,咱们也就进步了零碎整体的并发。

Redis 除了做缓存,还能做什么?

  • 分布式锁:通过 Redis 来做分布式锁是一种比拟常见的形式。通常状况下,咱们都是基于 Redisson 来实现分布式锁。对于 Redis 实现分布式锁的具体介绍,能够看我写的这篇文章:分布式锁详解。
  • 限流:个别是通过 Redis + Lua 脚本的形式来实现限流。相干浏览:《我司用了 6 年的 Redis 分布式限流器,能够说是十分厉害了!》。
  • 音讯队列:Redis 自带的 list 数据结构能够作为一个简略的队列应用。Redis 5.0 中减少的 Stream 类型的数据结构更加适宜用来做音讯队列。它比拟相似于 Kafka,有主题和生产组的概念,反对音讯长久化以及 ACK 机制。
  • 简单业务场景:通过 Redis 以及 Redis 扩大(比方 Redisson)提供的数据结构,咱们能够很不便地实现很多简单的业务场景比方通过 bitmap 统计沉闷用户、通过 sorted set 保护排行榜。
  • ……

Redis 能够做音讯队列么?

Redis 5.0 新减少的一个数据结构 Stream 能够用来做音讯队列,Stream 反对:

  • 公布 / 订阅模式
  • 依照消费者组进行生产
  • 音讯长久化(RDB 和 AOF)

不过,和业余的音讯队列相比,还是有很多欠缺的中央比方音讯失落和沉积问题不好解决。因而,咱们通常倡议是不应用 Redis 来做音讯队列的,你齐全能够抉择市面上比拟成熟的一些音讯队列比方 RocketMQ、Kafka。

相干文章举荐:Redis 音讯队列的三种计划(List、Streams、Pub/Sub)。

如何基于 Redis 实现分布式锁?

对于 Redis 实现分布式锁的具体介绍,能够看我写的这篇文章:分布式锁详解。

Redis 数据结构

Redis 罕用的数据结构有哪些?

  • 5 种根底数据结构:String(字符串)、List(列表)、Set(汇合)、Hash(散列)、Zset(有序汇合)。
  • 3 种非凡数据结构:HyperLogLogs(基数统计)、Bitmap(位存储)、Geospatial (地理位置)。

对于 5 种根底数据结构的具体介绍请看这篇文章:Redis 5 种根本数据结构详解。

对于 3 种非凡数据结构的具体介绍请看这篇文章:Redis 3 种非凡数据结构详解。

String 的利用场景有哪些?

  • 惯例数据(比方 session、token、、序列化后的对象)的缓存;
  • 计数比方用户单位工夫的申请数(简略限流能够用到)、页面单位工夫的拜访数;
  • 分布式锁(利用 SETNX key value 命令能够实现一个最繁难的分布式锁);
  • ……

对于 String 的具体介绍请看这篇文章:Redis 5 种根本数据结构详解。

String 还是 Hash 存储对象数据更好呢?

  • String 存储的是序列化后的对象数据,寄存的是整个对象。Hash 是对对象的每个字段独自存储,能够获取局部字段的信息,也能够批改或者增加局部字段,节俭网络流量。如果对象中某些字段须要常常变动或者常常须要独自查问对象中的个别字段信息,Hash 就非常适合。
  • String 存储相对来说更加节俭内存,缓存雷同数量的对象数据,String 耗费的内存约是 Hash 的一半。并且,存储具备多层嵌套的对象时也不便很多。如果系统对性能和资源耗费十分敏感的话,String 就非常适合。

在绝大部分状况,咱们倡议应用 String 来存储对象数据即可!

String 的底层实现是什么?

Redis 是基于 C 语言编写的,但 Redis 的 String 类型的底层实现并不是 C 语言中的字符串(即以空字符 \0 结尾的字符数组),而是本人编写了 SDS(Simple Dynamic String,简略动静字符串)来作为底层实现。

SDS 最早是 Redis 作者为日常 C 语言开发而设计的 C 字符串,起初被利用到了 Redis 上,并通过了大量的批改欠缺以适宜高性能操作。

Redis7.0 的 SDS 的局部源码如下(https://github.com/redis/redi…):

/* Note: sdshdr5 is never used, we just access the flags byte directly.
 * However is here to document the layout of type 5 SDS strings. */
struct __attribute__ ((__packed__)) sdshdr5 {
    unsigned char flags; /* 3 lsb of type, and 5 msb of string length */
    char buf[];};
struct __attribute__ ((__packed__)) sdshdr8 {
    uint8_t len; /* used */
    uint8_t alloc; /* excluding the header and null terminator */
    unsigned char flags; /* 3 lsb of type, 5 unused bits */
    char buf[];};
struct __attribute__ ((__packed__)) sdshdr16 {
    uint16_t len; /* used */
    uint16_t alloc; /* excluding the header and null terminator */
    unsigned char flags; /* 3 lsb of type, 5 unused bits */
    char buf[];};
struct __attribute__ ((__packed__)) sdshdr32 {
    uint32_t len; /* used */
    uint32_t alloc; /* excluding the header and null terminator */
    unsigned char flags; /* 3 lsb of type, 5 unused bits */
    char buf[];};
struct __attribute__ ((__packed__)) sdshdr64 {
    uint64_t len; /* used */
    uint64_t alloc; /* excluding the header and null terminator */
    unsigned char flags; /* 3 lsb of type, 5 unused bits */
    char buf[];};

通过源码能够看出,SDS 共有五种实现形式 SDS_TYPE_5(并未用到)、SDS_TYPE_8、SDS_TYPE_16、SDS_TYPE_32、SDS_TYPE_64,其中只有后四种理论用到。Redis 会依据初始化的长度决定应用哪种类型,从而缩小内存的应用。

类型 字节
sdshdr5 < 1 <8
sdshdr8 1 8
sdshdr16 2 16
sdshdr32 4 32
sdshdr64 8 64

对于后四种实现都蕴含了上面这 4 个属性:

  • len:字符串的长度也就是曾经应用的字节数
  • alloc:总共可用的字符空间大小,alloc-len 就是 SDS 残余的空间大小
  • buf[]:理论存储字符串的数组
  • flags:低三位保留类型标记

SDS 相比于 C 语言中的字符串有如下晋升:

  1. 能够防止缓冲区溢出:C 语言中的字符串被批改(比方拼接)时,一旦没有调配足够长度的内存空间,就会造成缓冲区溢出。SDS 被批改时,会先依据 len 属性查看空间大小是否满足要求,如果不满足,则先扩大至所需大小再进行批改操作。
  2. 获取字符串长度的复杂度较低:C 语言中的字符串的长度通常是通过遍历计数来实现的,工夫复杂度为 O(n)。SDS 的长度获取间接读取 len 属性即可,工夫复杂度为 O(1)。
  3. 缩小内存调配次数:为了防止批改(减少 / 缩小)字符串时,每次都须要从新分配内存(C 语言的字符串是这样的),SDS 实现了空间预调配和惰性空间开释两种优化策略。当 SDS 须要减少字符串时,Redis 会为 SDS 调配好内存,并且依据特定的算法调配多余的内存,这样能够缩小间断执行字符串增长操作所需的内存重调配次数。当 SDS 须要缩小字符串时,这部分内存不会立刻被回收,会被记录下来,期待后续应用(反对手动开释,有对应的 API)。
  4. 二进制平安:C 语言中的字符串以空字符 \0 作为字符串完结的标识,这存在一些问题,像一些二进制文件(比方图片、视频、音频)就可能包含空字符,C 字符串无奈正确保留。SDS 应用 len 属性判断字符串是否完结,不存在这个问题。

多提一嘴,很多文章里 SDS 的定义是上面这样的:

struct sdshdr {
    unsigned int len;
    unsigned int free;
    char buf[];};

这个也没错,Redis 3.2 之前就是这样定义的。起初,因为这种形式的定义存在问题,lenfree 的定义用了 4 个字节,造成了节约。Redis 3.2 之后,Redis 改良了 SDS 的定义,将其划分为了当初的 5 种类型。

购物车信息用 String 还是 Hash 存储更好呢?

因为购物车中的商品频繁批改和变动,购物车信息倡议应用 Hash 存储:

  • 用户 id 为 key
  • 商品 id 为 field,商品数量为 value

那用户购物车信息的保护具体应该怎么操作呢?

  • 用户增加商品就是往 Hash 外面减少新的 field 与 value;
  • 查问购物车信息就是遍历对应的 Hash;
  • 更改商品数量间接批改对应的 value 值(间接 set 或者做运算皆可);
  • 删除商品就是删除 Hash 中对应的 field;
  • 清空购物车间接删除对应的 key 即可。

这里只是以业务比较简单的购物车场景举例,理论电商场景下,field 只保留一个商品 id 是没方法满足需要的。

应用 Redis 实现一个排行榜怎么做?

Redis 中有一个叫做 sorted set 的数据结构常常被用在各种排行榜的场景,比方直播间送礼物的排行榜、朋友圈的微信步数排行榜、王者光荣中的段位排行榜、话题热度排行榜等等。

相干的一些 Redis 命令: ZRANGE (从小到大排序)、ZREVRANGE(从大到小排序)、ZREVRANK (指定元素排名)。

《Java 面试指北》的「技术面试题篇」就有一篇文章具体介绍如何应用 Sorted Set 来设计制作一个排行榜。

应用 Set 实现抽奖零碎须要用到什么命令?

  • SPOP key count:随机移除并获取指定汇合中一个或多个元素,适宜不容许反复中奖的场景。
  • SRANDMEMBER key count : 随机获取指定汇合中指定数量的元素,适宜容许反复中奖的场景。

应用 Bitmap 统计沉闷用户怎么做?

应用日期(准确到天)作为 key,而后用户 ID 为 offset,如果当日沉闷过就设置为 1。

初始化数据:

> SETBIT 20210308 1 1
(integer) 0
> SETBIT 20210308 2 1
(integer) 0
> SETBIT 20210309 1 1
(integer) 0

统计 20210308~20210309 总沉闷用户数:

> BITOP and desk1 20210308 20210309
(integer) 1
> BITCOUNT desk1
(integer) 1

统计 20210308~20210309 在线沉闷用户数:

> BITOP or desk2 20210308 20210309
(integer) 1
> BITCOUNT desk2
(integer) 2

应用 HyperLogLog 统计页面 UV 怎么做?

1、将拜访指定页面的每个用户 ID 增加到 HyperLogLog 中。

PFADD PAGE_1:UV USER1 USER2 ...... USERn

2、统计指定页面的 UV。

PFCOUNT PAGE_1:UV

Redis 线程模型

对于读写命令来说,Redis 始终是单线程模型。不过,在 Redis 4.0 版本之后引入了多线程来执行一些大键值对的异步删除操作,Redis 6.0 版本之后引入了多线程来解决网络申请(进步网络 IO 读写性能)。

Redis 单线程模型理解吗?

Redis 基于 Reactor 模式设计开发了一套高效的事件处理模型(Netty 的线程模型也基于 Reactor 模式,Reactor 模式不愧是高性能 IO 的基石),这套事件处理模型对应的是 Redis 中的文件事件处理器(file event handler)。因为文件事件处理器(file event handler)是单线程形式运行的,所以咱们个别都说 Redis 是单线程模型。

《Redis 设计与实现》有一段话是如是介绍文件事件处理器的,我感觉写得挺不错。

Redis 基于 Reactor 模式开发了本人的网络事件处理器:这个处理器被称为文件事件处理器(file event handler)。

  • 文件事件处理器应用 I/O 多路复用(multiplexing)程序来同时监听多个套接字,并依据套接字目前执行的工作来为套接字关联不同的事件处理器。
  • 当被监听的套接字筹备好执行连贯应答(accept)、读取(read)、写入(write)、关 闭(close)等操作时,与操作绝对应的文件事件就会产生,这时文件事件处理器就会调用套接字之前关联好的事件处理器来解决这些事件。

尽管文件事件处理器以单线程形式运行,但通过应用 I/O 多路复用程序来监听多个套接字,文件事件处理器既实现了高性能的网络通信模型,又能够很好地与 Redis 服务器中其余同样以单线程形式运行的模块进行对接,这放弃了 Redis 外部单线程设计的简略性。

既然是单线程,那怎么监听大量的客户端连贯呢?

Redis 通过 IO 多路复用程序 来监听来自客户端的大量连贯(或者说是监听多个 socket),它会将感兴趣的事件及类型(读、写)注册到内核中并监听每个事件是否产生。

这样的益处非常明显:I/O 多路复用技术的应用让 Redis 不须要额定创立多余的线程来监听客户端的大量连贯,升高了资源的耗费(和 NIO 中的 Selector 组件很像)。

文件事件处理器(file event handler)次要是蕴含 4 个局部:

  • 多个 socket(客户端连贯)
  • IO 多路复用程序(反对多个客户端连贯的要害)
  • 文件事件分派器(将 socket 关联到相应的事件处理器)
  • 事件处理器(连贯应答处理器、命令申请处理器、命令回复处理器)

相干浏览:Redis 事件机制详解。

Redis6.0 之前为什么不应用多线程?

尽管说 Redis 是单线程模型,然而,实际上,Redis 在 4.0 之后的版本中就曾经退出了对多线程的反对。

不过,Redis 4.0 减少的多线程次要是针对一些大键值对的删除操作的命令,应用这些命令就会应用主线程之外的其余线程来“异步解决”。

为此,Redis 4.0 之后新增了UNLINK(能够看作是 DEL 的异步版本)、FLUSHALL ASYNC(清空所有数据库的所有 key,不仅仅是以后 SELECT 的数据库)、FLUSHDB ASYNC(清空以后 SELECT 数据库中的所有 key)等异步命令。

大体上来说,Redis 6.0 之前次要还是单线程解决。

那 Redis6.0 之前为什么不应用多线程? 我感觉次要起因有 3 点:

  • 单线程编程容易并且更容易保护;
  • Redis 的性能瓶颈不在 CPU,次要在内存和网络;
  • 多线程就会存在死锁、线程上下文切换等问题,甚至会影响性能。

相干浏览:为什么 Redis 抉择单线程模型。

Redis6.0 之后为何引入了多线程?

Redis6.0 引入多线程次要是为了进步网络 IO 读写性能,因为这个算是 Redis 中的一个性能瓶颈(Redis 的瓶颈次要受限于内存和网络)。

尽管,Redis6.0 引入了多线程,然而 Redis 的多线程只是在网络数据的读写这类耗时操作上应用了,执行命令依然是单线程程序执行。因而,你也不须要放心线程平安问题。

Redis6.0 的多线程默认是禁用的,只应用主线程。如需开启须要设置 IO 线程数 > 1,须要批改 redis 配置文件 redis.conf

io-threads 4 #设置 1 的话只会开启主线程,官网倡议 4 核的机器倡议设置为 2 或 3 个线程,8 核的倡议设置为 6 个线程

另外:

  • io-threads 的个数一旦设置,不能通过 config 动静设置
  • 当设置 ssl 后,io-threads 将不工作

开启多线程后,默认只会应用多线程进行 IO 写入 writes,即发送数据给客户端,如果须要开启多线程 IO 读取 reads,同样须要批改 redis 配置文件 redis.conf :

io-threads-do-reads yes

然而官网形容开启多线程读并不能有太大晋升,因而个别状况下并不倡议开启

相干浏览:

  • Redis 6.0 新个性 - 多线程连环 13 问!
  • Redis 多线程网络模型全面揭秘(举荐)

Redis 内存治理

Redis 给缓存数据设置过期工夫有啥用?

个别状况下,咱们设置保留的缓存数据的时候都会设置一个过期工夫。为什么呢?

因为内存是无限的,如果缓存中的所有数据都是始终保留的话,分分钟间接 Out of memory。

Redis 自带了给缓存数据设置过期工夫的性能,比方:

127.0.0.1:6379> expire key 60 # 数据在 60s 后过期
(integer) 1
127.0.0.1:6379> setex key 60 value # 数据在 60s 后过期 (setex:[set] + [ex]pire)
OK
127.0.0.1:6379> ttl key # 查看数据还有多久过期
(integer) 56

留神:Redis 中除了字符串类型有本人独有设置过期工夫的命令 setex 外,其余办法都须要依附 expire 命令来设置过期工夫。另外,persist 命令能够移除一个键的过期工夫。

过期工夫除了有助于缓解内存的耗费,还有什么其余用么?

很多时候,咱们的业务场景就是须要某个数据只在某一时间段内存在,比方咱们的短信验证码可能只在 1 分钟内无效,用户登录的 token 可能只在 1 天内无效。

如果应用传统的数据库来解决的话,个别都是本人判断过期,这样更麻烦并且性能要差很多。

Redis 是如何判断数据是否过期的呢?

Redis 通过一个叫做过期字典(能够看作是 hash 表)来保留数据过期的工夫。过期字典的键指向 Redis 数据库中的某个 key(键),过期字典的值是一个 long long 类型的整数,这个整数保留了 key 所指向的数据库键的过期工夫(毫秒精度的 UNIX 工夫戳)。

过期字典是存储在 redisDb 这个构造里的:

typedef struct redisDb {
    ...

    dict *dict;     // 数据库键空间, 保留着数据库中所有键值对
    dict *expires   // 过期字典, 保留着键的过期工夫
    ...
} redisDb;

过期的数据的删除策略理解么?

如果假如你设置了一批 key 只能存活 1 分钟,那么 1 分钟后,Redis 是怎么对这批 key 进行删除的呢?

罕用的过期数据的删除策略就两个(重要!本人造缓存轮子的时候须要分外思考的货色):

  1. 惰性删除:只会在取出 key 的时候才对数据进行过期查看。这样对 CPU 最敌对,然而可能会造成太多过期 key 没有被删除。
  2. 定期删除:每隔一段时间抽取一批 key 执行删除过期 key 操作。并且,Redis 底层会通过限度删除操作执行的时长和频率来缩小删除操作对 CPU 工夫的影响。

定期删除对内存更加敌对,惰性删除对 CPU 更加敌对。两者各有千秋,所以 Redis 采纳的是 定期删除 + 惰性 / 懒汉式删除

然而,仅仅通过给 key 设置过期工夫还是有问题的。因为还是可能存在定期删除和惰性删除漏掉了很多过期 key 的状况。这样就导致大量过期 key 沉积在内存里,而后就 Out of memory 了。

怎么解决这个问题呢?答案就是:Redis 内存淘汰机制。

Redis 内存淘汰机制理解么?

相干问题:MySQL 里有 2000w 数据,Redis 中只存 20w 的数据,如何保障 Redis 中的数据都是热点数据?

Redis 提供 6 种数据淘汰策略:

  1. volatile-lru(least recently used):从已设置过期工夫的数据集(server.db[i].expires)中筛选最近起码应用的数据淘汰
  2. volatile-ttl:从已设置过期工夫的数据集(server.db[i].expires)中筛选将要过期的数据淘汰
  3. volatile-random:从已设置过期工夫的数据集(server.db[i].expires)中任意抉择数据淘汰
  4. allkeys-lru(least recently used):当内存不足以包容新写入数据时,在键空间中,移除最近起码应用的 key(这个是最罕用的)
  5. allkeys-random:从数据集(server.db[i].dict)中任意抉择数据淘汰
  6. no-eviction:禁止驱赶数据,也就是说当内存不足以包容新写入数据时,新写入操作会报错。这个应该没人应用吧!

4.0 版本后减少以下两种:

  1. volatile-lfu(least frequently used):从已设置过期工夫的数据集(server.db[i].expires)中筛选最不常常应用的数据淘汰
  2. allkeys-lfu(least frequently used):当内存不足以包容新写入数据时,在键空间中,移除最不常常应用的 key

Redis 长久化机制

怎么保障 Redis 挂掉之后再重启数据能够进行复原?

很多时候咱们须要长久化数据也就是将内存中的数据写入到硬盘外面,大部分起因是为了之后重用数据(比方重启机器、机器故障之后复原数据),或者是为了避免系统故障而将数据备份到一个近程地位。

Redis 不同于 Memcached 的很重要一点就是,Redis 反对长久化,而且反对两种不同的长久化操作。Redis 的一种长久化形式叫快照(snapshotting,RDB),另一种形式是只追加文件(append-only file, AOF)。这两种办法各有千秋,上面我会具体这两种长久化办法是什么,怎么用,如何抉择适宜本人的长久化办法。

什么是 RDB 长久化?

Redis 能够通过创立快照来取得存储在内存外面的数据在某个工夫点上的正本。Redis 创立快照之后,能够对快照进行备份,能够将快照复制到其余服务器从而创立具备雷同数据的服务器正本(Redis 主从构造,次要用来进步 Redis 性能),还能够将快照留在原地以便重启服务器的时候应用。

快照长久化是 Redis 默认采纳的长久化形式,在 redis.conf 配置文件中默认有此下配置:

save 900 1           #在 900 秒 (15 分钟) 之后,如果至多有 1 个 key 发生变化,Redis 就会主动触发 bgsave 命令创立快照。save 300 10          #在 300 秒 (5 分钟) 之后,如果至多有 10 个 key 发生变化,Redis 就会主动触发 bgsave 命令创立快照。save 60 10000        #在 60 秒 (1 分钟) 之后,如果至多有 10000 个 key 发生变化,Redis 就会主动触发 bgsave 命令创立快照。

RDB 创立快照时会阻塞主线程吗?

Redis 提供了两个命令来生成 RDB 快照文件:

  • save : 主线程执行,会阻塞主线程;
  • bgsave : 子线程执行,不会阻塞主线程,默认选项。

什么是 AOF 长久化?

与快照长久化相比,AOF 长久化的实时性更好,因而已成为支流的长久化计划。默认状况下 Redis 没有开启 AOF(append only file)形式的长久化,能够通过 appendonly 参数开启:

appendonly yes

开启 AOF 长久化后每执行一条会更改 Redis 中的数据的命令,Redis 就会将该命令写入到内存缓存 server.aof_buf 中,而后再依据 appendfsync 配置来决定何时将其同步到硬盘中的 AOF 文件。

AOF 文件的保留地位和 RDB 文件的地位雷同,都是通过 dir 参数设置的,默认的文件名是 appendonly.aof

在 Redis 的配置文件中存在三种不同的 AOF 长久化形式,它们别离是:

appendfsync always    #每次有数据批改产生时都会写入 AOF 文件, 这样会重大升高 Redis 的速度
appendfsync everysec  #每秒钟同步一次,显式地将多个写命令同步到硬盘
appendfsync no        #让操作系统决定何时进行同步

为了兼顾数据和写入性能,用户能够思考 appendfsync everysec 选项,让 Redis 每秒同步一次 AOF 文件,Redis 性能简直没受到任何影响。而且这样即便呈现零碎解体,用户最多只会失落一秒之内产生的数据。当硬盘忙于执行写入操作的时候,Redis 还会优雅的加快本人的速度以便适应硬盘的最大写入速度。

相干 issue

  • Redis 的 AOF 形式 #783
  • Redis AOF 重写形容不精确 #1439

AOF 日志是如何实现的?

关系型数据库(如 MySQL)通常都是执行命令之前记录日志(不便故障复原),而 Redis AOF 长久化机制是在执行完命令之后再记录日志。

为什么是在执行完命令之后记录日志呢?

  • 防止额定的查看开销,AOF 记录日志不会对命令进行语法查看;
  • 在命令执行完之后再记录,不会阻塞以后的命令执行。

这样也带来了危险(我在后面介绍 AOF 长久化的时候也提到过):

  • 如果刚执行完命令 Redis 就宕机会导致对应的批改失落;
  • 可能会阻塞后续其余命令的执行(AOF 记录日志是在 Redis 主线程中进行的)。

AOF 重写理解吗?

当 AOF 变得太大时,Redis 可能在后盾主动重写 AOF 产生一个新的 AOF 文件,这个新的 AOF 文件和原有的 AOF 文件所保留的数据库状态一样,但体积更小。

AOF 重写是一个有歧义的名字,该性能是通过读取数据库中的键值对来实现的,程序毋庸对现有 AOF 文件进行任何读入、剖析或者写入操作。

在执行 BGREWRITEAOF 命令时,Redis 服务器会保护一个 AOF 重写缓冲区,该缓冲区会在子过程创立新 AOF 文件期间,记录服务器执行的所有写命令。当子过程实现创立新 AOF 文件的工作之后,服务器会将重写缓冲区中的所有内容追加到新 AOF 文件的开端,使得新的 AOF 文件保留的数据库状态与现有的数据库状态统一。最初,服务器用新的 AOF 文件替换旧的 AOF 文件,以此来实现 AOF 文件重写操作。

Redis 7.0 版本之前,如果在重写期间有写入命令,AOF 可能会应用大量内存,重写期间达到的所有写入命令都会写入磁盘两次。

如何抉择 RDB 和 AOF?

对于 RDB 和 AOF 的优缺点,官网下面也给了比拟具体的阐明 Redis persistence,这里联合本人的了解简略总结一下。

RDB 比 AOF 优良的中央

  • RDB 文件存储的内容是通过压缩的二进制数据,保留着某个工夫点的数据集,文件很小,适宜做数据的备份,劫难复原。AOF 文件存储的是每一次写命令,相似于 MySQL 的 binlog 日志,通常会必 RDB 文件大很多。当 AOF 变得太大时,Redis 可能在后盾主动重写 AOF。新的 AOF 文件和原有的 AOF 文件所保留的数据库状态一样,但体积更小。不过,Redis 7.0 版本之前,如果在重写期间有写入命令,AOF 可能会应用大量内存,重写期间达到的所有写入命令都会写入磁盘两次。
  • 应用 RDB 文件复原数据,间接解析还原数据即可,不须要一条一条地执行命令,速度十分快。而 AOF 则须要顺次执行每个写命令,速度十分慢。也就是说,与 AOF 相比,复原大数据集的时候,RDB 速度更快。

AOF 比 RDB 优良的中央

  • RDB 的数据安全性不如 AOF,没有方法实时或者秒级长久化数据。生成 RDB 文件的过程是比沉重的,尽管 BGSAVE 子过程写入 RDB 文件的工作不会阻塞主线程,但会对机器的 CPU 资源和内存资源产生影响,重大的状况下甚至会间接把 Redis 服务干宕机。AOF 反对秒级数据失落(取决 fsync 策略,如果是 everysec,最多失落 1 秒的数据),仅仅是追加命令到 AOF 文件,操作轻量。
  • RDB 文件是以特定的二进制格局保留的,并且在 Redis 版本演进中有多个版本的 RDB,所以存在老版本的 Redis 服务不兼容新版本的 RDB 格局的问题。
  • AOF 以一种易于了解和解析的格局蕴含所有操作的日志。你能够轻松地导出 AOF 文件进行剖析,你也能够间接操作 AOF 文件来解决一些问题。比方,如果执行 FLUSHALL 命令意外地刷新了所有内容后,只有 AOF 文件没有被重写,删除最新命令并重启即可复原之前的状态。

Redis 4.0 对于长久化机制做了什么优化?

因为 RDB 和 AOF 各有劣势,于是,Redis 4.0 开始反对 RDB 和 AOF 的混合长久化(默认敞开,能够通过配置项 aof-use-rdb-preamble 开启)。

如果把混合长久化关上,AOF 重写的时候就间接把 RDB 的内容写到 AOF 文件结尾。这样做的益处是能够联合 RDB 和 AOF 的长处, 疾速加载同时防止失落过多的数据。当然毛病也是有的,AOF 外面的 RDB 局部是压缩格局不再是 AOF 格局,可读性较差。

官网文档地址:https://redis.io/topics/persi…

Redis 事务

如何应用 Redis 事务?

Redis 能够通过 MULTIEXECDISCARDWATCH 等命令来实现事务 (transaction) 性能。

> MULTI
OK
> SET PROJECT "JavaGuide"
QUEUED
> GET PROJECT
QUEUED
> EXEC
1) OK
2) "JavaGuide"

MULTI 命令后能够输出多个命令,Redis 不会立刻执行这些命令,而是将它们放到队列,当调用了 EXEC 命令后,再执行所有的命令。

这个过程是这样的:

  1. 开始事务(MULTI);
  2. 命令入队(批量操作 Redis 的命令,先进先出(FIFO)的程序执行);
  3. 执行事务(EXEC)。

你也能够通过 DISCARD 命令勾销一个事务,它会清空事务队列中保留的所有命令。

> MULTI
OK
> SET PROJECT "JavaGuide"
QUEUED
> GET PROJECT
QUEUED
> DISCARD
OK

你能够通过 WATCH 命令监听指定的 Key,当调用 EXEC 命令执行事务时,如果一个被 WATCH 命令监督的 Key 被 其余客户端 /Session 批改的话,整个事务都不会被执行。

# 客户端 1
> SET PROJECT "RustGuide"
OK
> WATCH PROJECT
OK
> MULTI
OK
> SET PROJECT "JavaGuide"
QUEUED

# 客户端 2
# 在客户端 1 执行 EXEC 命令提交事务之前批改 PROJECT 的值
> SET PROJECT "GoGuide"

# 客户端 1
# 批改失败,因为 PROJECT 的值被客户端 2 批改了
> EXEC
(nil)
> GET PROJECT
"GoGuide"

不过,如果 WATCH事务 在同一个 Session 里,并且被 WATCH 监督的 Key 被批改的操作产生在事务外部,这个事务是能够被执行胜利的(相干 issue:WATCH 命令碰到 MULTI 命令时的不同成果)。

事务外部批改 WATCH 监督的 Key:

> SET PROJECT "JavaGuide"
OK
> WATCH PROJECT
OK
> MULTI
OK
> SET PROJECT "JavaGuide1"
QUEUED
> SET PROJECT "JavaGuide2"
QUEUED
> SET PROJECT "JavaGuide3"
QUEUED
> EXEC
1) OK
2) OK
3) OK
127.0.0.1:6379> GET PROJECT
"JavaGuide3"

事务内部批改 WATCH 监督的 Key:

> SET PROJECT "JavaGuide"
OK
> WATCH PROJECT
OK
> SET PROJECT "JavaGuide2"
OK
> MULTI
OK
> GET USER
QUEUED
> EXEC
(nil)

Redis 官网相干介绍 https://redis.io/topics/transactions 如下:

Redis 反对原子性吗?

Redis 的事务和咱们平时了解的关系型数据库的事务不同。咱们晓得事务具备四大个性:1. 原子性2. 隔离性3. 持久性4. 一致性

  1. 原子性(Atomicity): 事务是最小的执行单位,不容许宰割。事务的原子性确保动作要么全副实现,要么齐全不起作用;
  2. 隔离性(Isolation): 并发拜访数据库时,一个用户的事务不被其余事务所烦扰,各并发事务之间数据库是独立的;
  3. 持久性(Durability): 一个事务被提交之后。它对数据库中数据的扭转是长久的,即便数据库产生故障也不应该对其有任何影响。
  4. 一致性(Consistency): 执行事务前后,数据保持一致,多个事务对同一个数据读取的后果是雷同的;

Redis 事务在运行谬误的状况下,除了执行过程中呈现谬误的命令外,其余命令都能失常执行。并且,Redis 是不反对回滚(roll back)操作的。因而,Redis 事务其实是不满足原子性的(而且不满足持久性)。

Redis 官网也解释了本人为啥不反对回滚。简略来说就是 Redis 开发者们感觉没必要反对回滚,这样更简略便捷并且性能更好。Redis 开发者感觉即便命令执行谬误也应该在开发过程中就被发现而不是生产过程中。

你能够将 Redis 中的事务就了解为:Redis 事务提供了一种将多个命令申请打包的性能。而后,再按程序执行打包的所有命令,并且不会被中途打断。

除了不满足原子性之外,事务中的每条命令都会与 Redis 服务器进行网络交互,这是比拟浪费资源的行为。明明一次批量执行多个命令就能够了,这种操作切实是看不懂。

因而,Redis 事务是不倡议在日常开发中应用的。

相干 issue :

  • issue452: 对于 Redis 事务不满足原子性的问题。
  • Issue491: 对于 redis 没有事务回滚?

如何解决 Redis 事务的缺点?

Redis 从 2.6 版本开始反对执行 Lua 脚本,它的性能和事务十分相似。咱们能够利用 Lua 脚本来批量执行多条 Redis 命令,这些 Redis 命令会被提交到 Redis 服务器一次性执行实现,大幅减小了网络开销。

一段 Lua 脚本能够视作一条命令执行,一段 Lua 脚本执行过程中不会有其余脚本或 Redis 命令同时执行,保障了操作不会被其余指令插入或打搅。

如果 Lua 脚本运行时出错并中途完结,出错之后的命令是不会被执行的。并且,出错之前执行的命令是无奈被撤销的。因而,严格来说,通过 Lua 脚本来批量执行 Redis 命令也是不满足原子性的。

另外,Redis 7.0 新增了 Redis functions 个性,你能够将 Redis functions 看作是比 Lua 更弱小的脚本。

Redis 性能优化

Redis bigkey

什么是 bigkey?

简略来说,如果一个 key 对应的 value 所占用的内存比拟大,那这个 key 就能够看作是 bigkey。具体多大才算大呢?有一个不是特地准确的参考规范:string 类型的 value 超过 10 kb,复合类型的 value 蕴含的元素超过 5000 个(对于复合类型的 value 来说,不肯定蕴含的元素越多,占用的内存就越多)。

bigkey 有什么危害?

除了会耗费更多的内存空间,bigkey 对性能也会有比拟大的影响。

因而,咱们应该尽量避免写入 bigkey!

如何发现 bigkey?

1、应用 Redis 自带的 --bigkeys 参数来查找。

# redis-cli -p 6379 --bigkeys

# Scanning the entire keyspace to find biggest keys as well as
# average sizes per key type.  You can use -i 0.1 to sleep 0.1 sec
# per 100 SCAN commands (not usually needed).

[00.00%] Biggest string found so far '"ballcat:oauth:refresh_auth:f6cdb384-9a9d-4f2f-af01-dc3f28057c20"' with 4437 bytes
[00.00%] Biggest list   found so far '"my-list"' with 17 items

-------- summary -------

Sampled 5 keys in the keyspace!
Total key length in bytes is 264 (avg len 52.80)

Biggest   list found '"my-list"' has 17 items
Biggest string found '"ballcat:oauth:refresh_auth:f6cdb384-9a9d-4f2f-af01-dc3f28057c20"' has 4437 bytes

1 lists with 17 items (20.00% of keys, avg size 17.00)
0 hashs with 0 fields (00.00% of keys, avg size 0.00)
4 strings with 4831 bytes (80.00% of keys, avg size 1207.75)
0 streams with 0 entries (00.00% of keys, avg size 0.00)
0 sets with 0 members (00.00% of keys, avg size 0.00)
0 zsets with 0 members (00.00% of keys, avg size 0.00

从这个命令的运行后果,咱们能够看出:这个命令会扫描(Scan) Redis 中的所有 key,会对 Redis 的性能有一点影响。并且,这种形式只能找出每种数据结构 top 1 bigkey(占用内存最大的 string 数据类型,蕴含元素最多的复合数据类型)。

2、剖析 RDB 文件

通过剖析 RDB 文件来找出 big key。这种计划的前提是你的 Redis 采纳的是 RDB 长久化。

网上有现成的代码 / 工具能够间接拿来应用:

  • redis-rdb-tools:Python 语言写的用来剖析 Redis 的 RDB 快照文件用的工具
  • rdb_bigkeys : Go 语言写的用来剖析 Redis 的 RDB 快照文件用的工具,性能更好。

大量 key 集中过期问题

我在下面提到过:对于过期 key,Redis 采纳的是 定期删除 + 惰性 / 懒汉式删除 策略。

定期删除执行过程中,如果忽然遇到大量过期 key 的话,客户端申请必须期待定期清理过期 key 工作线程执行实现,因为这个这个定期工作线程是在 Redis 主线程中执行的。这就导致客户端申请没方法被及时处理,响应速度会比较慢。

如何解决呢?上面是两种常见的办法:

  1. 给 key 设置随机过期工夫。
  2. 开启 lazy-free(惰性删除 / 提早开释)。lazy-free 个性是 Redis 4.0 开始引入的,指的是让 Redis 采纳异步形式提早开释 key 应用的内存,将该操作交给独自的子线程解决,防止阻塞主线程。

集体倡议不论是否开启 lazy-free,咱们都尽量给 key 设置随机过期工夫。

Redis 生产问题

缓存穿透

什么是缓存穿透?

缓存穿透说简略点就是大量申请的 key 是不合理的,基本不存在于缓存中,也不存在于数据库中。这就导致这些申请间接到了数据库上,基本没有通过缓存这一层,对数据库造成了微小的压力,可能间接就被这么多申请弄宕机了。

举个例子:某个黑客成心制作一些非法的 key 发动大量申请,导致大量申请落到数据库,后果数据库上也没有查到对应的数据。也就是说这些申请最终都落到了数据库上,对数据库造成了微小的压力。

有哪些解决办法?

最根本的就是首先做好参数校验,一些不非法的参数申请间接抛出异样信息返回给客户端。比方查问的数据库 id 不能小于 0、传入的邮箱格局不对的时候间接返回谬误音讯给客户端等等。

1)缓存有效 key

如果缓存和数据库都查不到某个 key 的数据就写一个到 Redis 中去并设置过期工夫,具体命令如下:SET key value EX 10086。这种形式能够解决申请的 key 变动不频繁的状况,如果黑客歹意攻打,每次构建不同的申请 key,会导致 Redis 中缓存大量有效的 key。很显著,这种计划并不能从根本上解决此问题。如果非要用这种形式来解决穿透问题的话,尽量将有效的 key 的过期工夫设置短一点比方 1 分钟。

另外,这里多说一嘴,个别状况下咱们是这样设计 key 的:表名: 列名: 主键名: 主键值

如果用 Java 代码展现的话,差不多是上面这样的:

public Object getObjectInclNullById(Integer id) {
    // 从缓存中获取数据
    Object cacheValue = cache.get(id);
    // 缓存为空
    if (cacheValue == null) {
        // 从数据库中获取
        Object storageValue = storage.get(key);
        // 缓存空对象
        cache.set(key, storageValue);
        // 如果存储数据为空,须要设置一个过期工夫(300 秒)
        if (storageValue == null) {
            // 必须设置过期工夫,否则有被攻打的危险
            cache.expire(key, 60 * 5);
        }
        return storageValue;
    }
    return cacheValue;
}

2)布隆过滤器

布隆过滤器是一个十分神奇的数据结构,通过它咱们能够十分不便地判断一个给定数据是否存在于海量数据中。咱们须要的就是判断 key 是否非法,有没有感觉布隆过滤器就是咱们想要找的那个“人”。

具体是这样做的:把所有可能存在的申请的值都寄存在布隆过滤器中,当用户申请过去,先判断用户发来的申请的值是否存在于布隆过滤器中。不存在的话,间接返回申请参数错误信息给客户端,存在的话才会走上面的流程。

退出布隆过滤器之后的缓存解决流程图如下。

然而,须要留神的是布隆过滤器可能会存在误判的状况。总结来说就是:布隆过滤器说某个元素存在,小概率会误判。布隆过滤器说某个元素不在,那么这个元素肯定不在。

为什么会呈现误判的状况呢? 咱们还要从布隆过滤器的原理来说!

咱们先来看一下,当一个元素退出布隆过滤器中的时候,会进行哪些操作:

  1. 应用布隆过滤器中的哈希函数对元素值进行计算,失去哈希值(有几个哈希函数失去几个哈希值)。
  2. 依据失去的哈希值,在位数组中把对应下标的值置为 1。

咱们再来看一下,当咱们须要判断一个元素是否存在于布隆过滤器的时候,会进行哪些操作:

  1. 对给定元素再次进行雷同的哈希计算;
  2. 失去值之后判断位数组中的每个元素是否都为 1,如果值都为 1,那么阐明这个值在布隆过滤器中,如果存在一个值不为 1,阐明该元素不在布隆过滤器中。

而后,肯定会呈现这样一种状况:不同的字符串可能哈希进去的地位雷同。(能够适当减少位数组大小或者调整咱们的哈希函数来升高概率)

更多对于布隆过滤器的内容能够看我的这篇原创:《不理解布隆过滤器?一文给你整的明明白白!》,强烈推荐,个人感觉网上应该找不到总结的这么明明白白的文章了。

缓存击穿

什么是缓存击穿?

缓存击穿中,申请的 key 对应的是 热点数据 ,该数据 存在于数据库中,但不存在于缓存中(通常是因为缓存中的那份数据曾经过期)。这就可能会导致刹时大量的申请间接打到了数据库上,对数据库造成了微小的压力,可能间接就被这么多申请弄宕机了。

举个例子:秒杀进行过程中,缓存中的某个秒杀商品的数据忽然过期,这就导致刹时大量对该商品的申请间接落到数据库上,对数据库造成了微小的压力。

有哪些解决办法?

  • 设置热点数据永不过期或者过期工夫比拟长。
  • 针对热点数据提前预热,将其存入缓存中并设置正当的过期工夫比方秒杀场景下的数据在秒杀完结之前不过期。
  • 申请数据库写数据到缓存之前,先获取互斥锁,保障只有一个申请会落到数据库上,缩小数据库的压力。

缓存穿透和缓存击穿有什么区别?

缓存穿透中,申请的 key 既不存在于缓存中,也不存在于数据库中。

缓存击穿中,申请的 key 对应的是 热点数据 ,该数据 存在于数据库中,但不存在于缓存中(通常是因为缓存中的那份数据曾经过期)

缓存雪崩

什么是缓存雪崩?

我发现缓存雪崩这名字起的有点意思,哈哈。

实际上,缓存雪崩形容的就是这样一个简略的场景:缓存在同一时间大面积的生效,导致大量的申请都间接落到了数据库上,对数据库造成了微小的压力。 这就好比雪崩一样,不堪一击之势,数据库的压力可想而知,可能间接就被这么多申请弄宕机了。

另外,缓存服务宕机也会导致缓存雪崩景象,导致所有的申请都落到了数据库上。

举个例子:数据库中的大量数据在同一时间过期,这个时候忽然有大量的申请须要拜访这些过期的数据。这就导致大量的申请间接落到数据库上,对数据库造成了微小的压力。

有哪些解决办法?

针对 Redis 服务不可用的状况:

  1. 采纳 Redis 集群,防止单机呈现问题整个缓存服务都没方法应用。
  2. 限流,防止同时解决大量的申请。

针对热点缓存生效的状况:

  1. 设置不同的生效工夫比方随机设置缓存的生效工夫。
  2. 缓存永不生效(不太举荐,实用性太差)。
  3. 设置二级缓存。

缓存雪崩和缓存击穿有什么区别?

缓存雪崩和缓存击穿比拟像,但缓存雪崩导致的起因是缓存中的大量或者所有数据生效,缓存击穿导致的起因次要是某个热点数据不存在与缓存中(通常是因为缓存中的那份数据曾经过期)。

如何保障缓存和数据库数据的一致性?

细说的话能够扯很多,然而我感觉其实没太大必要(小声 BB:很多解决方案我也没太弄明确)。我集体感觉引入缓存之后,如果为了短时间的不一致性问题,抉择让零碎设计变得更加简单的话,齐全没必要。

上面独自对 Cache Aside Pattern(旁路缓存模式) 来聊聊。

Cache Aside Pattern 中遇到写申请是这样的:更新 DB,而后间接删除 cache。

如果更新数据库胜利,而删除缓存这一步失败的状况的话,简略说两个解决方案:

  1. 缓存生效工夫变短(不举荐,治标不治本):咱们让缓存数据的过期工夫变短,这样的话缓存就会从数据库中加载数据。另外,这种解决办法对于先操作缓存后操作数据库的场景不实用。
  2. 减少 cache 更新重试机制(罕用):如果 cache 服务以后不可用导致缓存删除失败的话,咱们就隔一段时间进行重试,重试次数能够本人定。如果多次重试还是失败的话,咱们能够把以后更新失败的 key 存入队列中,等缓存服务可用之后,再将缓存中对应的 key 删除即可。

相干文章举荐:缓存和数据库一致性问题,看这篇就够了 – 水滴与银弹

Redis 集群

Redis Sentinel

  1. 什么是 Sentinel?有什么用?
  2. Sentinel 如何检测节点是否下线?主观下线与主观下线的区别?
  3. Sentinel 是如何实现故障转移的?
  4. 为什么倡议部署多个 sentinel 节点(哨兵集群)?
  5. Sentinel 如何抉择出新的 master(选举机制)?
  6. 如何从 Sentinel 集群中抉择出 Leader?
  7. Sentinel 能够避免脑裂吗?

Redis Cluster

  1. 为什么须要 Redis Cluster?解决了什么问题?有什么劣势?
  2. Redis Cluster 是如何分片的?
  3. 为什么 Redis Cluster 的哈希槽是 16384 个?
  4. 如何确定给定 key 的应该散布到哪个哈希槽中?
  5. Redis Cluster 反对重新分配哈希槽吗?
  6. Redis Cluster 扩容缩容期间能够提供服务吗?
  7. Redis Cluster 中的节点是怎么进行通信的?

参考答案:Redis 集群详解(付费)。

参考

  • 《Redis 开发与运维》
  • 《Redis 设计与实现》
  • Redis 命令手册:https://www.redis.com.cn/comm…
  • WHY Redis choose single thread (vs multi threads): https://medium.com/@jychen7/sharing-redis-single-thread-vs-multi-threads-5870bd44d153
  • The difference between AOF and RDB persistence:https://www.sobyte.net/post/2…
正文完
 0