diff --git a/README.md b/README.md index 16a2677..86a4bad 100644 --- a/README.md +++ b/README.md @@ -37,10 +37,10 @@ ### [搜索引擎](./docs/high-concurrency/es-introduction.md) -* [es 的分布式架构原理能说一下么(es 是如何实现分布式的啊)?](./docs/high-concurrency/es-architecture.md) -* [es 写入数据的工作原理是什么啊?es 查询数据的工作原理是什么啊?底层的 lucene 介绍一下呗?倒排索引了解吗?](./docs/high-concurrency/es-write-query-search.md) -* [es 在数据量很大的情况下(数十亿级别)如何提高查询效率啊?](./docs/high-concurrency/es-optimizing-query-performance.md) -* [es 生产集群的部署架构是什么?每个索引的数据量大概有多少?每个索引大概有多少个分片?](./docs/high-concurrency/es-production-cluster.md) +* [ES 的分布式架构原理能说一下么(ES 是如何实现分布式的啊)?](./docs/high-concurrency/es-architecture.md) +* [ES 写入数据的工作原理是什么啊?ES 查询数据的工作原理是什么啊?底层的 Lucene 介绍一下呗?倒排索引了解吗?](./docs/high-concurrency/es-write-query-search.md) +* [ES 在数据量很大的情况下(数十亿级别)如何提高查询效率啊?](./docs/high-concurrency/es-optimizing-query-performance.md) +* [ES 生产集群的部署架构是什么?每个索引的数据量大概有多少?每个索引大概有多少个分片?](./docs/high-concurrency/es-production-cluster.md) ### 缓存 @@ -51,7 +51,7 @@ * [如何保证 Redis 高并发、高可用?Redis 的主从复制原理能介绍一下么?Redis 的哨兵原理能介绍一下么?](./docs/high-concurrency/how-to-ensure-high-concurrency-and-high-availability-of-redis.md) * [Redis 的持久化有哪几种方式?不同的持久化机制都有什么优缺点?持久化机制具体底层是如何实现的?](./docs/high-concurrency/redis-persistence.md) * [Redis 集群模式的工作原理能说一下么?在集群模式下,Redis 的 key 是如何寻址的?分布式寻址都有哪些算法?了解一致性 hash 算法吗?如何动态增加和删除一个节点?](./docs/high-concurrency/redis-cluster.md) -* [了解什么是 redis 的雪崩、穿透和击穿?Redis 崩溃之后会怎么样?系统该如何应对这种情况?如何处理 Redis 的穿透?](./docs/high-concurrency/redis-caching-avalanche-and-caching-penetration.md) +* [了解什么是 Redis 的雪崩、穿透和击穿?Redis 崩溃之后会怎么样?系统该如何应对这种情况?如何处理 Redis 的穿透?](./docs/high-concurrency/redis-caching-avalanche-and-caching-penetration.md) * [如何保证缓存与数据库的双写一致性?](./docs/high-concurrency/redis-consistence.md) * [Redis 的并发竞争问题是什么?如何解决这个问题?了解 Redis 事务的 CAS 方案吗?](./docs/high-concurrency/redis-cas.md) * [生产环境中的 Redis 是怎么部署的?](./docs/high-concurrency/redis-production-environment.md) diff --git a/docs/high-concurrency/README.md b/docs/high-concurrency/README.md index 9b83cc3..20eb73e 100644 --- a/docs/high-concurrency/README.md +++ b/docs/high-concurrency/README.md @@ -12,10 +12,10 @@ ## [搜索引擎](/docs/high-concurrency/es-introduction.md) -* [es 的分布式架构原理能说一下么(es 是如何实现分布式的啊)?](/docs/high-concurrency/es-architecture.md) -* [es 写入数据的工作原理是什么啊?es 查询数据的工作原理是什么啊?底层的 lucene 介绍一下呗?倒排索引了解吗?](/docs/high-concurrency/es-write-query-search.md) -* [es 在数据量很大的情况下(数十亿级别)如何提高查询效率啊?](/docs/high-concurrency/es-optimizing-query-performance.md) -* [es 生产集群的部署架构是什么?每个索引的数据量大概有多少?每个索引大概有多少个分片?](/docs/high-concurrency/es-production-cluster.md) +* [ES 的分布式架构原理能说一下么(ES 是如何实现分布式的啊)?](/docs/high-concurrency/es-architecture.md) +* [ES 写入数据的工作原理是什么啊?ES 查询数据的工作原理是什么啊?底层的 Lucene 介绍一下呗?倒排索引了解吗?](/docs/high-concurrency/es-write-query-search.md) +* [ES 在数据量很大的情况下(数十亿级别)如何提高查询效率啊?](/docs/high-concurrency/es-optimizing-query-performance.md) +* [ES 生产集群的部署架构是什么?每个索引的数据量大概有多少?每个索引大概有多少个分片?](/docs/high-concurrency/es-production-cluster.md) ## 缓存 diff --git a/docs/high-concurrency/redis-caching-avalanche-and-caching-penetration.md b/docs/high-concurrency/redis-caching-avalanche-and-caching-penetration.md index cca43e2..30c6a1f 100644 --- a/docs/high-concurrency/redis-caching-avalanche-and-caching-penetration.md +++ b/docs/high-concurrency/redis-caching-avalanche-and-caching-penetration.md @@ -1,5 +1,5 @@ ## 面试题 -了解什么是 redis 的雪崩、穿透和击穿?redis 崩溃之后会怎么样?系统该如何应对这种情况?如何处理 redis 的穿透? +了解什么是 Redis 的雪崩、穿透和击穿?Redis 崩溃之后会怎么样?系统该如何应对这种情况?如何处理 Redis 的穿透? ## 面试官心理分析 @@ -18,13 +18,13 @@ 缓存雪崩的事前事中事后的解决方案如下: -* 事前:redis 高可用,主从+哨兵,redis cluster,避免全盘崩溃。 +* 事前:Redis 高可用,主从+哨兵,Redis cluster,避免全盘崩溃。 * 事中:本地 ehcache 缓存 + hystrix 限流&降级,避免 MySQL 被打死。 -* 事后:redis 持久化,一旦重启,自动从磁盘上加载数据,快速恢复缓存数据。 +* 事后:Redis 持久化,一旦重启,自动从磁盘上加载数据,快速恢复缓存数据。 ![redis-caching-avalanche-solution](./images/redis-caching-avalanche-solution.png) -用户发送一个请求,系统 A 收到请求后,先查本地 ehcache 缓存,如果没查到再查 redis。如果 ehcache 和 redis 都没有,再查数据库,将数据库中的结果,写入 ehcache 和 redis 中。 +用户发送一个请求,系统 A 收到请求后,先查本地 ehcache 缓存,如果没查到再查 Redis。如果 ehcache 和 Redis 都没有,再查数据库,将数据库中的结果,写入 ehcache 和 Redis 中。 限流组件,可以设置每秒的请求,有多少能通过组件,剩余的未通过的请求,怎么办?**走降级**!可以返回一些默认的值,或者友情提示,或者空值。 @@ -53,5 +53,5 @@ 不同场景下的解决方式可如下: * 若缓存的数据是基本不会发生更新的,则可尝试将该热点数据设置为永不过期。 -* 若缓存的数据更新不频繁,且缓存刷新的整个流程耗时较少的情况下,则可以采用基于 redis、zookeeper 等分布式中间件的分布式互斥锁,或者本地互斥锁以保证仅少量的请求能请求数据库并重新构建缓存,其余线程则在锁释放后能访问到新缓存。 +* 若缓存的数据更新不频繁,且缓存刷新的整个流程耗时较少的情况下,则可以采用基于 Redis、zookeeper 等分布式中间件的分布式互斥锁,或者本地互斥锁以保证仅少量的请求能请求数据库并重新构建缓存,其余线程则在锁释放后能访问到新缓存。 * 若缓存的数据更新频繁或者在缓存刷新的流程耗时较长的情况下,可以利用定时线程在缓存过期前主动地重新构建缓存或者延后缓存的过期时间,以保证所有的请求能一直访问到对应的缓存。 diff --git a/docs/high-concurrency/redis-cas.md b/docs/high-concurrency/redis-cas.md index 8e5f823..2d96ab7 100644 --- a/docs/high-concurrency/redis-cas.md +++ b/docs/high-concurrency/redis-cas.md @@ -1,11 +1,11 @@ ## 面试题 -redis 的并发竞争问题是什么?如何解决这个问题?了解 redis 事务的 CAS 方案吗? +Redis 的并发竞争问题是什么?如何解决这个问题?了解 Redis 事务的 CAS 方案吗? ## 面试官心理分析 这个也是线上非常常见的一个问题,就是**多客户端同时并发写**一个 key,可能本来应该先到的数据后到了,导致数据版本错了;或者是多客户端同时获取一个 key,修改值之后再写回去,只要顺序错了,数据就错了。 -而且 redis 自己就有天然解决这个问题的 CAS 类的乐观锁方案。 +而且 Redis 自己就有天然解决这个问题的 CAS 类的乐观锁方案。 ## 面试题剖析 diff --git a/docs/high-concurrency/redis-cluster.md b/docs/high-concurrency/redis-cluster.md index 8f80d5b..54403a5 100644 --- a/docs/high-concurrency/redis-cluster.md +++ b/docs/high-concurrency/redis-cluster.md @@ -1,41 +1,41 @@ ## 面试题 -redis 集群模式的工作原理能说一下么?在集群模式下,redis 的 key 是如何寻址的?分布式寻址都有哪些算法?了解一致性 hash 算法吗? +Redis 集群模式的工作原理能说一下么?在集群模式下,Redis 的 key 是如何寻址的?分布式寻址都有哪些算法?了解一致性 hash 算法吗? ## 面试官心理分析 -在前几年,redis 如果要搞几个节点,每个节点存储一部分的数据,得**借助一些中间件**来实现,比如说有 `codis` ,或者 `twemproxy` ,都有。有一些 redis 中间件,你读写 redis 中间件,redis 中间件负责将你的数据分布式存储在多台机器上的 redis 实例中。 +在前几年,Redis 如果要搞几个节点,每个节点存储一部分的数据,得**借助一些中间件**来实现,比如说有 `codis` ,或者 `twemproxy` ,都有。有一些 Redis 中间件,你读写 Redis 中间件,Redis 中间件负责将你的数据分布式存储在多台机器上的 Redis 实例中。 -这两年,redis 不断在发展,redis 也不断有新的版本,现在的 redis 集群模式,可以做到在多台机器上,部署多个 redis 实例,每个实例存储一部分的数据,同时每个 redis 主实例可以挂 redis 从实例,自动确保说,如果 redis 主实例挂了,会自动切换到 redis 从实例上来。 +这两年,Redis 不断在发展,Redis 也不断有新的版本,现在的 Redis 集群模式,可以做到在多台机器上,部署多个 Redis 实例,每个实例存储一部分的数据,同时每个 Redis 主实例可以挂 Redis 从实例,自动确保说,如果 Redis 主实例挂了,会自动切换到 Redis 从实例上来。 -现在 redis 的新版本,大家都是用 redis cluster 的,也就是 redis 原生支持的 redis 集群模式,那么面试官肯定会就 redis cluster 对你来个几连炮。要是你没用过 redis cluster,正常,以前很多人用 codis 之类的客户端来支持集群,但是起码你得研究一下 redis cluster 吧。 +现在 Redis 的新版本,大家都是用 Redis cluster 的,也就是 Redis 原生支持的 Redis 集群模式,那么面试官肯定会就 Redis cluster 对你来个几连炮。要是你没用过 Redis cluster,正常,以前很多人用 codis 之类的客户端来支持集群,但是起码你得研究一下 Redis cluster 吧。 -如果你的数据量很少,主要是承载高并发高性能的场景,比如你的缓存一般就几个 G,单机就足够了,可以使用 replication,一个 master 多个 slaves,要几个 slave 跟你要求的读吞吐量有关,然后自己搭建一个 sentinel 集群去保证 redis 主从架构的高可用性。 +如果你的数据量很少,主要是承载高并发高性能的场景,比如你的缓存一般就几个 G,单机就足够了,可以使用 replication,一个 master 多个 slaves,要几个 slave 跟你要求的读吞吐量有关,然后自己搭建一个 sentinel 集群去保证 Redis 主从架构的高可用性。 -redis cluster,主要是针对**海量数据+高并发+高可用**的场景。redis cluster 支撑 N 个 redis master node,每个 master node 都可以挂载多个 slave node。这样整个 redis 就可以横向扩容了。如果你要支撑更大数据量的缓存,那就横向扩容更多的 master 节点,每个 master 节点就能存放更多的数据了。 +Redis cluster,主要是针对**海量数据+高并发+高可用**的场景。Redis cluster 支撑 N 个 Redis master node,每个 master node 都可以挂载多个 slave node。这样整个 Redis 就可以横向扩容了。如果你要支撑更大数据量的缓存,那就横向扩容更多的 master 节点,每个 master 节点就能存放更多的数据了。 ## 面试题剖析 -### redis cluster 介绍 +### Redis cluster 介绍 * 自动将数据进行分片,每个 master 上放一部分数据 * 提供内置的高可用支持,部分 master 不可用时,还是可以继续工作的 -在 redis cluster 架构下,每个 redis 要放开两个端口号,比如一个是 6379,另外一个就是 加1w 的端口号,比如 16379。 +在 Redis cluster 架构下,每个 Redis 要放开两个端口号,比如一个是 6379,另外一个就是 加1w 的端口号,比如 16379。 16379 端口号是用来进行节点间通信的,也就是 cluster bus 的东西,cluster bus 的通信,用来进行故障检测、配置更新、故障转移授权。cluster bus 用了另外一种二进制的协议, `gossip` 协议,用于节点间进行高效的数据交换,占用更少的网络带宽和处理时间。 ### 节点间的内部通信机制 #### 基本通信原理 -集群元数据的维护有两种方式:集中式、Gossip 协议。redis cluster 节点间采用 gossip 协议进行通信。 +集群元数据的维护有两种方式:集中式、Gossip 协议。Redis cluster 节点间采用 gossip 协议进行通信。 **集中式**是将集群元数据(节点信息、故障等等)几种存储在某个节点上。集中式元数据集中存储的一个典型代表,就是大数据领域的 `storm` 。它是分布式的大数据实时计算引擎,是集中式的元数据存储的结构,底层基于 zookeeper(分布式协调的中间件)对所有元数据进行存储维护。 ![zookeeper-centralized-storage](./images/zookeeper-centralized-storage.png) -redis 维护集群元数据采用另一个方式, `gossip` 协议,所有节点都持有一份元数据,不同的节点如果出现了元数据的变更,就不断将元数据发送给其它的节点,让其它节点也进行元数据的变更。 +Redis 维护集群元数据采用另一个方式, `gossip` 协议,所有节点都持有一份元数据,不同的节点如果出现了元数据的变更,就不断将元数据发送给其它的节点,让其它节点也进行元数据的变更。 -![redis-gossip](./images/redis-gossip.png) +![Redis-gossip](./images/redis-gossip.png) **集中式**的**好处**在于,元数据的读取和更新,时效性非常好,一旦元数据出现了变更,就立即更新到集中式的存储中,其它节点读取的时候就可以感知到;**不好**在于,所有的元数据的更新压力全部集中在一个地方,可能会导致元数据的存储有压力。 @@ -52,7 +52,7 @@ gossip 协议包含多种消息,包含 `ping` , `pong` , `meet` , `fail` 等 * meet:某个节点发送 meet 给新加入的节点,让新节点加入集群中,然后新节点就会开始与其它节点进行通信。 ``` bash -redis-trib.rb add-node +Redis-trib.rb add-node ``` 其实内部就是发送了一个 gossip meet 消息给新加入的节点,通知那个节点去加入我们的集群。 @@ -73,7 +73,7 @@ ping 时要携带一些元数据,如果很频繁,可能会加重网络负担 * hash 算法(大量缓存重建) * 一致性 hash 算法(自动缓存迁移)+ 虚拟节点(自动负载均衡) -* redis cluster 的 hash slot 算法 +* Redis cluster 的 hash slot 算法 #### hash 算法 @@ -93,19 +93,19 @@ ping 时要携带一些元数据,如果很频繁,可能会加重网络负担 ![consistent-hashing-algorithm](./images/consistent-hashing-algorithm.png) -#### redis cluster 的 hash slot 算法 +#### Redis cluster 的 hash slot 算法 -redis cluster 有固定的 `16384` 个 hash slot,对每个 `key` 计算 `CRC16` 值,然后对 `16384` 取模,可以获取 key 对应的 hash slot。 +Redis cluster 有固定的 `16384` 个 hash slot,对每个 `key` 计算 `CRC16` 值,然后对 `16384` 取模,可以获取 key 对应的 hash slot。 -redis cluster 中每个 master 都会持有部分 slot,比如有 3 个 master,那么可能每个 master 持有 5000 多个 hash slot。hash slot 让 node 的增加和移除很简单,增加一个 master,就将其他 master 的 hash slot 移动部分过去,减少一个 master,就将它的 hash slot 移动到其他 master 上去。移动 hash slot 的成本是非常低的。客户端的 api,可以对指定的数据,让他们走同一个 hash slot,通过 `hash tag` 来实现。 +Redis cluster 中每个 master 都会持有部分 slot,比如有 3 个 master,那么可能每个 master 持有 5000 多个 hash slot。hash slot 让 node 的增加和移除很简单,增加一个 master,就将其他 master 的 hash slot 移动部分过去,减少一个 master,就将它的 hash slot 移动到其他 master 上去。移动 hash slot 的成本是非常低的。客户端的 api,可以对指定的数据,让他们走同一个 hash slot,通过 `hash tag` 来实现。 任何一台机器宕机,另外两个节点,不影响的。因为 key 找的是 hash slot,不是机器。 ![hash-slot](./images/hash-slot.png) -### redis cluster 的高可用与主备切换原理 +### Redis cluster 的高可用与主备切换原理 -redis cluster 的高可用的原理,几乎跟哨兵是类似的。 +Redis cluster 的高可用的原理,几乎跟哨兵是类似的。 #### 判断节点宕机 @@ -131,4 +131,4 @@ redis cluster 的高可用的原理,几乎跟哨兵是类似的。 #### 与哨兵比较 -整个流程跟哨兵相比,非常类似,所以说,redis cluster 功能强大,直接集成了 replication 和 sentinel 的功能。 +整个流程跟哨兵相比,非常类似,所以说,Redis cluster 功能强大,直接集成了 replication 和 sentinel 的功能。 diff --git a/docs/high-concurrency/redis-data-types.md b/docs/high-concurrency/redis-data-types.md index abc06df..abf57e5 100644 --- a/docs/high-concurrency/redis-data-types.md +++ b/docs/high-concurrency/redis-data-types.md @@ -1,5 +1,5 @@ ## 面试题 -redis 都有哪些数据类型?分别在哪些场景下使用比较合适? +Redis 都有哪些数据类型?分别在哪些场景下使用比较合适? ## 面试官心理分析 @@ -7,22 +7,24 @@ redis 都有哪些数据类型?分别在哪些场景下使用比较合适? 其实问这个问题,主要有两个原因: -* 看看你到底有没有全面的了解 redis 有哪些功能,一般怎么来用,啥场景用什么,就怕你别就会最简单的 KV 操作; -* 看看你在实际项目里都怎么玩儿过 redis。 +* 看看你到底有没有全面的了解 Redis 有哪些功能,一般怎么来用,啥场景用什么,就怕你别就会最简单的 KV 操作; +* 看看你在实际项目里都怎么玩儿过 Redis。 要是你回答的不好,没说出几种数据类型,也没说什么场景,你完了,面试官对你印象肯定不好,觉得你平时就是做个简单的 set 和 get。 ## 面试题剖析 -redis 主要有以下几种数据类型: +Redis 主要有以下几种数据类型: -* string -* hash -* list -* set -* sorted set +* Strings +* Hashes +* Lists +* Sets +* Sorted Sets -### string +> Redis 除了这 5 种数据类型之外,还有 Bitmaps、HyperLogLogs、Streams 等。 + +### Strings 这是最简单的类型,就是普通的 set 和 get,做简单的 KV 缓存。 @@ -30,9 +32,9 @@ redis 主要有以下几种数据类型: set college szu ``` -### hash +### Hashes -这个是类似 map 的一种结构,这个一般就是可以将结构化的数据,比如一个对象(前提是**这个对象没嵌套其他的对象**)给缓存在 redis 里,然后每次读写缓存的时候,可以就操作 hash 里的**某个字段**。 +这个是类似 map 的一种结构,这个一般就是可以将结构化的数据,比如一个对象(前提是**这个对象没嵌套其他的对象**)给缓存在 Redis 里,然后每次读写缓存的时候,可以就操作 hash 里的**某个字段**。 ``` bash hset person name bingo @@ -49,13 +51,13 @@ person = { } ``` -### list +### Lists -list 是有序列表,这个可以玩儿出很多花样。 +Lists 是有序列表,这个可以玩儿出很多花样。 比如可以通过 list 存储一些列表型的数据结构,类似粉丝列表、文章的评论列表之类的东西。 -比如可以通过 lrange 命令,读取某个闭区间内的元素,可以基于 list 实现分页查询,这个是很棒的一个功能,基于 redis 实现简单的高性能分页,可以做类似微博那种下拉不断分页的东西,性能高,就一页一页走。 +比如可以通过 lrange 命令,读取某个闭区间内的元素,可以基于 list 实现分页查询,这个是很棒的一个功能,基于 Redis 实现简单的高性能分页,可以做类似微博那种下拉不断分页的东西,性能高,就一页一页走。 ``` bash # 0开始位置,-1结束位置,结束位置为-1时,表示列表的最后一个位置,即查看所有。 @@ -73,11 +75,11 @@ lpush mylist 3 4 5 rpop mylist ``` -### set +### Sets -set 是无序集合,自动去重。 +Sets 是无序集合,自动去重。 -直接基于 set 将系统里需要去重的数据扔进去,自动就给去重了,如果你需要对一些数据进行快速的全局去重,你当然也可以基于 jvm 内存里的 HashSet 进行去重,但是如果你的某个系统部署在多台机器上呢?得基于 redis 进行全局的 set 去重。 +直接基于 set 将系统里需要去重的数据扔进去,自动就给去重了,如果你需要对一些数据进行快速的全局去重,你当然也可以基于 jvm 内存里的 HashSet 进行去重,但是如果你的某个系统部署在多台机器上呢?得基于 Redis 进行全局的 set 去重。 可以基于 set 玩儿交集、并集、差集的操作,比如交集吧,可以把两个人的粉丝列表整一个交集,看看俩人的共同好友是谁?对吧。 @@ -118,9 +120,9 @@ sunion yourSet mySet sdiff yourSet mySet ``` -### sorted set +### Sorted Sets -sorted set 是排序的 set,去重但可以排序,写进去的时候给一个分数,自动根据分数排序。 +Sorted Sets 是排序的 set,去重但可以排序,写进去的时候给一个分数,自动根据分数排序。 ``` bash zadd board 85 zhangsan diff --git a/docs/high-concurrency/redis-expiration-policies-and-lru.md b/docs/high-concurrency/redis-expiration-policies-and-lru.md index 3acf3b6..7fccd94 100644 --- a/docs/high-concurrency/redis-expiration-policies-and-lru.md +++ b/docs/high-concurrency/redis-expiration-policies-and-lru.md @@ -1,44 +1,44 @@ ## 面试题 -redis 的过期策略都有哪些?内存淘汰机制都有哪些?手写一下 LRU 代码实现? +Redis 的过期策略都有哪些?内存淘汰机制都有哪些?手写一下 LRU 代码实现? ## 面试官心理分析 -如果你连这个问题都不知道,上来就懵了,回答不出来,那线上你写代码的时候,想当然的认为写进 redis 的数据就一定会存在,后面导致系统各种 bug,谁来负责? +如果你连这个问题都不知道,上来就懵了,回答不出来,那线上你写代码的时候,想当然的认为写进 Redis 的数据就一定会存在,后面导致系统各种 bug,谁来负责? 常见的有两个问题: -* 往 redis 写入的数据怎么没了? +* 往 Redis 写入的数据怎么没了? -可能有同学会遇到,在生产环境的 redis 经常会丢掉一些数据,写进去了,过一会儿可能就没了。我的天,同学,你问这个问题就说明 redis 你就没用对啊。redis 是缓存,你给当存储了是吧? +可能有同学会遇到,在生产环境的 Redis 经常会丢掉一些数据,写进去了,过一会儿可能就没了。我的天,同学,你问这个问题就说明 Redis 你就没用对啊。Redis 是缓存,你给当存储了是吧? -啥叫缓存?用内存当缓存。内存是无限的吗,内存是很宝贵而且是有限的,磁盘是廉价而且是大量的。可能一台机器就几十个 G 的内存,但是可以有几个 T 的硬盘空间。redis 主要是基于内存来进行高性能、高并发的读写操作的。 +啥叫缓存?用内存当缓存。内存是无限的吗,内存是很宝贵而且是有限的,磁盘是廉价而且是大量的。可能一台机器就几十个 G 的内存,但是可以有几个 T 的硬盘空间。Redis 主要是基于内存来进行高性能、高并发的读写操作的。 -那既然内存是有限的,比如 redis 就只能用 10G,你要是往里面写了 20G 的数据,会咋办?当然会干掉 10G 的数据,然后就保留 10G 的数据了。那干掉哪些数据?保留哪些数据?当然是干掉不常用的数据,保留常用的数据了。 +那既然内存是有限的,比如 Redis 就只能用 10G,你要是往里面写了 20G 的数据,会咋办?当然会干掉 10G 的数据,然后就保留 10G 的数据了。那干掉哪些数据?保留哪些数据?当然是干掉不常用的数据,保留常用的数据了。 * 数据明明过期了,怎么还占用着内存? -这是由 redis 的过期策略来决定。 +这是由 Redis 的过期策略来决定。 ## 面试题剖析 -### redis 过期策略 -redis 过期策略是:**定期删除+惰性删除**。 +### Redis 过期策略 +Redis 过期策略是:**定期删除+惰性删除**。 -所谓**定期删除**,指的是 redis 默认是每隔 100ms 就随机抽取一些设置了过期时间的 key,检查其是否过期,如果过期就删除。 +所谓**定期删除**,指的是 Redis 默认是每隔 100ms 就随机抽取一些设置了过期时间的 key,检查其是否过期,如果过期就删除。 -假设 redis 里放了 10w 个 key,都设置了过期时间,你每隔几百毫秒,就检查 10w 个 key,那 redis 基本上就死了,cpu 负载会很高的,消耗在你的检查过期 key 上了。注意,这里可不是每隔 100ms 就遍历所有的设置过期时间的 key,那样就是一场性能上的**灾难**。实际上 redis 是每隔 100ms **随机抽取**一些 key 来检查和删除的。 +假设 Redis 里放了 10w 个 key,都设置了过期时间,你每隔几百毫秒,就检查 10w 个 key,那 Redis 基本上就死了,cpu 负载会很高的,消耗在你的检查过期 key 上了。注意,这里可不是每隔 100ms 就遍历所有的设置过期时间的 key,那样就是一场性能上的**灾难**。实际上 Redis 是每隔 100ms **随机抽取**一些 key 来检查和删除的。 -但是问题是,定期删除可能会导致很多过期 key 到了时间并没有被删除掉,那咋整呢?所以就是惰性删除了。这就是说,在你获取某个 key 的时候,redis 会检查一下 ,这个 key 如果设置了过期时间那么是否过期了?如果过期了此时就会删除,不会给你返回任何东西。 +但是问题是,定期删除可能会导致很多过期 key 到了时间并没有被删除掉,那咋整呢?所以就是惰性删除了。这就是说,在你获取某个 key 的时候,Redis 会检查一下 ,这个 key 如果设置了过期时间那么是否过期了?如果过期了此时就会删除,不会给你返回任何东西。 > 获取 key 的时候,如果此时 key 已经过期,就删除,不会返回任何东西。 -但是实际上这还是有问题的,如果定期删除漏掉了很多过期 key,然后你也没及时去查,也就没走惰性删除,此时会怎么样?如果大量过期 key 堆积在内存里,导致 redis 内存块耗尽了,咋整? +但是实际上这还是有问题的,如果定期删除漏掉了很多过期 key,然后你也没及时去查,也就没走惰性删除,此时会怎么样?如果大量过期 key 堆积在内存里,导致 Redis 内存块耗尽了,咋整? 答案是:**走内存淘汰机制**。 ### 内存淘汰机制 -redis 内存淘汰机制有以下几个: +Redis 内存淘汰机制有以下几个: * noeviction: 当内存不足以容纳新写入数据时,新写入操作会报错,这个一般没人用吧,实在是太恶心了。 * **allkeys-lru**:当内存不足以容纳新写入数据时,在**键空间**中,移除最近最少使用的 key(这个是**最常用**的)。 diff --git a/docs/high-concurrency/redis-master-slave.md b/docs/high-concurrency/redis-master-slave.md index 7adb9f5..47fe1b4 100644 --- a/docs/high-concurrency/redis-master-slave.md +++ b/docs/high-concurrency/redis-master-slave.md @@ -1,14 +1,14 @@ # Redis 主从架构 -单机的 redis,能够承载的 QPS 大概就在上万到几万不等。对于缓存来说,一般都是用来支撑**读高并发**的。因此架构做成主从(master-slave)架构,一主多从,主负责写,并且将数据复制到其它的 slave 节点,从节点负责读。所有的**读请求全部走从节点**。这样也可以很轻松实现水平扩容,**支撑读高并发**。 +单机的 Redis,能够承载的 QPS 大概就在上万到几万不等。对于缓存来说,一般都是用来支撑**读高并发**的。因此架构做成主从(master-slave)架构,一主多从,主负责写,并且将数据复制到其它的 slave 节点,从节点负责读。所有的**读请求全部走从节点**。这样也可以很轻松实现水平扩容,**支撑读高并发**。 -![redis-master-slave](./images/redis-master-slave.png) +![Redis-master-slave](./images/redis-master-slave.png) -redis replication -> 主从架构 -> 读写分离 -> 水平扩容支撑读高并发 +Redis replication -> 主从架构 -> 读写分离 -> 水平扩容支撑读高并发 -## redis replication 的核心机制 +## Redis replication 的核心机制 -* redis 采用**异步方式**复制数据到 slave 节点,不过 redis2.8 开始,slave node 会周期性地确认自己每次复制的数据量; +* Redis 采用**异步方式**复制数据到 slave 节点,不过 Redis2.8 开始,slave node 会周期性地确认自己每次复制的数据量; * 一个 master node 是可以配置多个 slave node 的; * slave node 也可以连接其他的 slave node; * slave node 做复制的时候,不会 block master node 的正常工作; @@ -19,17 +19,17 @@ redis replication -> 主从架构 -> 读写分离 -> 水平扩容支撑读高并 另外,master 的各种备份方案,也需要做。万一本地的所有文件丢失了,从备份中挑选一份 rdb 去恢复 master,这样才能**确保启动的时候,是有数据的**,即使采用了后续讲解的[高可用机制](/docs/high-concurrency/redis-sentinel.md),slave node 可以自动接管 master node,但也可能 sentinel 还没检测到 master failure,master node 就自动重启了,还是可能导致上面所有的 slave node 数据被清空。 -## redis 主从复制的核心原理 +## Redis 主从复制的核心原理 当启动一个 slave node 的时候,它会发送一个 `PSYNC` 命令给 master node。 如果这是 slave node 初次连接到 master node,那么会触发一次 `full resynchronization` 全量复制。此时 master 会启动一个后台线程,开始生成一份 `RDB` 快照文件,同时还会将从客户端 client 新收到的所有写命令缓存在内存中。 `RDB` 文件生成完毕后, master 会将这个 `RDB` 发送给 slave,slave 会先**写入本地磁盘,然后再从本地磁盘加载到内存**中,接着 master 会将内存中缓存的写命令发送到 slave,slave 也会同步这些数据。slave node 如果跟 master node 有网络故障,断开了连接,会自动重连,连接之后 master node 仅会复制给 slave 部分缺少的数据。 -![redis-master-slave-replication](./images/redis-master-slave-replication.png) +![Redis-master-slave-replication](./images/redis-master-slave-replication.png) ### 主从复制的断点续传 -从 redis2.8 开始,就支持主从复制的断点续传,如果主从复制过程中,网络连接断掉了,那么可以接着上次复制的地方,继续复制下去,而不是从头开始复制一份。 +从 Redis2.8 开始,就支持主从复制的断点续传,如果主从复制过程中,网络连接断掉了,那么可以接着上次复制的地方,继续复制下去,而不是从头开始复制一份。 master node 会在内存中维护一个 backlog,master 和 slave 都会保存一个 replica offset 还有一个 master run id,offset 就是保存在 backlog 中的。如果 master 和 slave 网络连接断掉了,slave 会让 master 从上次 replica offset 开始继续复制,如果没有找到对应的 offset,那么就会执行一次 `resynchronization` 。 @@ -56,7 +56,7 @@ slave node 启动时,会在自己本地保存 master node 的信息,包括 m slave node 内部有个定时任务,每秒检查是否有新的 master node 要连接和复制,如果发现,就跟 master node 建立 socket 网络连接。然后 slave node 发送 `ping` 命令给 master node。如果 master 设置了 requirepass,那么 slave node 必须发送 masterauth 的口令过去进行认证。master node **第一次执行全量复制**,将所有数据发给 slave node。而在后续,master node 持续将写命令,异步复制给 slave node。 -![redis-master-slave-replication-detail](./images/redis-master-slave-replication-detail.png) +![Redis-master-slave-replication-detail](./images/redis-master-slave-replication-detail.png) ### 全量复制 @@ -88,7 +88,7 @@ master 默认每隔 10秒 发送一次 heartbeat,slave node 每隔 1秒 发送 master 每次接收到写命令之后,先在内部写入数据,然后异步发送给 slave node。 -## redis 如何才能做到高可用 +## Redis 如何才能做到高可用 如果系统在 365 天内,有 99.99% 的时间,都是可以哗哗对外提供服务的,那么就说系统是高可用的。 @@ -96,8 +96,8 @@ master 每次接收到写命令之后,先在内部写入数据,然后异步 但是,如果 master node 死掉了,会怎么样?没法写数据了,写缓存的时候,全部失效了。slave node 还有什么用呢,没有 master 给它们复制数据了,系统相当于不可用了。 -redis 的高可用架构,叫做 `failover` **故障转移**,也可以叫做主备切换。 +Redis 的高可用架构,叫做 `failover` **故障转移**,也可以叫做主备切换。 -master node 在故障时,自动检测,并且将某个 slave node 自动切换为 master node 的过程,叫做主备切换。这个过程,实现了 redis 的主从架构下的高可用。 +master node 在故障时,自动检测,并且将某个 slave node 自动切换为 master node 的过程,叫做主备切换。这个过程,实现了 Redis 的主从架构下的高可用。 -后面会详细说明 redis [基于哨兵的高可用性](/docs/high-concurrency/redis-sentinel.md)。 +后面会详细说明 Redis [基于哨兵的高可用性](/docs/high-concurrency/redis-sentinel.md)。 diff --git a/docs/high-concurrency/redis-persistence.md b/docs/high-concurrency/redis-persistence.md index 492343a..7dba2cf 100644 --- a/docs/high-concurrency/redis-persistence.md +++ b/docs/high-concurrency/redis-persistence.md @@ -1,42 +1,42 @@ ## 面试题 -redis 的持久化有哪几种方式?不同的持久化机制都有什么优缺点?持久化机制具体底层是如何实现的? +Redis 的持久化有哪几种方式?不同的持久化机制都有什么优缺点?持久化机制具体底层是如何实现的? ## 面试官心理分析 -redis 如果仅仅只是将数据缓存在内存里面,如果 redis 宕机了再重启,内存里的数据就全部都弄丢了啊。你必须得用 redis 的持久化机制,将数据写入内存的同时,异步的慢慢的将数据写入磁盘文件里,进行持久化。 +Redis 如果仅仅只是将数据缓存在内存里面,如果 Redis 宕机了再重启,内存里的数据就全部都弄丢了啊。你必须得用 Redis 的持久化机制,将数据写入内存的同时,异步的慢慢的将数据写入磁盘文件里,进行持久化。 -如果 redis 宕机重启,自动从磁盘上加载之前持久化的一些数据就可以了,也许会丢失少许数据,但是至少不会将所有数据都弄丢。 +如果 Redis 宕机重启,自动从磁盘上加载之前持久化的一些数据就可以了,也许会丢失少许数据,但是至少不会将所有数据都弄丢。 -这个其实一样,针对的都是 redis 的生产环境可能遇到的一些问题,就是 redis 要是挂了再重启,内存里的数据不就全丢了?能不能重启的时候把数据给恢复了? +这个其实一样,针对的都是 Redis 的生产环境可能遇到的一些问题,就是 Redis 要是挂了再重启,内存里的数据不就全丢了?能不能重启的时候把数据给恢复了? ## 面试题剖析 -持久化主要是做灾难恢复、数据恢复,也可以归类到高可用的一个环节中去,比如你 redis 整个挂了,然后 redis 就不可用了,你要做的事情就是让 redis 变得可用,尽快变得可用。 +持久化主要是做灾难恢复、数据恢复,也可以归类到高可用的一个环节中去,比如你 Redis 整个挂了,然后 Redis 就不可用了,你要做的事情就是让 Redis 变得可用,尽快变得可用。 -重启 redis,尽快让它对外提供服务,如果没做数据备份,这时候 redis 启动了,也不可用啊,数据都没了。 +重启 Redis,尽快让它对外提供服务,如果没做数据备份,这时候 Redis 启动了,也不可用啊,数据都没了。 -很可能说,大量的请求过来,缓存全部无法命中,在 redis 里根本找不到数据,这个时候就死定了,出现**缓存雪崩**问题。所有请求没有在 redis 命中,就会去 mysql 数据库这种数据源头中去找,一下子 mysql 承接高并发,然后就挂了... +很可能说,大量的请求过来,缓存全部无法命中,在 Redis 里根本找不到数据,这个时候就死定了,出现**缓存雪崩**问题。所有请求没有在 Redis 命中,就会去 mysql 数据库这种数据源头中去找,一下子 mysql 承接高并发,然后就挂了... -如果你把 redis 持久化做好,备份和恢复方案做到企业级的程度,那么即使你的 redis 故障了,也可以通过备份数据,快速恢复,一旦恢复立即对外提供服务。 +如果你把 Redis 持久化做好,备份和恢复方案做到企业级的程度,那么即使你的 Redis 故障了,也可以通过备份数据,快速恢复,一旦恢复立即对外提供服务。 -### redis 持久化的两种方式 +### Redis 持久化的两种方式 -* RDB:RDB 持久化机制,是对 redis 中的数据执行**周期性**的持久化。 -* AOF:AOF 机制对每条写入命令作为日志,以 `append-only` 的模式写入一个日志文件中,在 redis 重启的时候,可以通过**回放** AOF 日志中的写入指令来重新构建整个数据集。 +* RDB:RDB 持久化机制,是对 Redis 中的数据执行**周期性**的持久化。 +* AOF:AOF 机制对每条写入命令作为日志,以 `append-only` 的模式写入一个日志文件中,在 Redis 重启的时候,可以通过**回放** AOF 日志中的写入指令来重新构建整个数据集。 -通过 RDB 或 AOF,都可以将 redis 内存中的数据给持久化到磁盘上面来,然后可以将这些数据备份到别的地方去,比如说阿里云等云服务。 +通过 RDB 或 AOF,都可以将 Redis 内存中的数据给持久化到磁盘上面来,然后可以将这些数据备份到别的地方去,比如说阿里云等云服务。 -如果 redis 挂了,服务器上的内存和磁盘上的数据都丢了,可以从云服务上拷贝回来之前的数据,放到指定的目录中,然后重新启动 redis,redis 就会自动根据持久化数据文件中的数据,去恢复内存中的数据,继续对外提供服务。 +如果 Redis 挂了,服务器上的内存和磁盘上的数据都丢了,可以从云服务上拷贝回来之前的数据,放到指定的目录中,然后重新启动 Redis,Redis 就会自动根据持久化数据文件中的数据,去恢复内存中的数据,继续对外提供服务。 -如果同时使用 RDB 和 AOF 两种持久化机制,那么在 redis 重启的时候,会使用 **AOF** 来重新构建数据,因为 AOF 中的**数据更加完整**。 +如果同时使用 RDB 和 AOF 两种持久化机制,那么在 Redis 重启的时候,会使用 **AOF** 来重新构建数据,因为 AOF 中的**数据更加完整**。 #### RDB 优缺点 -* RDB 会生成多个数据文件,每个数据文件都代表了某一个时刻中 redis 的数据,这种多个数据文件的方式,**非常适合做冷备**,可以将这种完整的数据文件发送到一些远程的安全存储上去,比如说 Amazon 的 S3 云服务上去,在国内可以是阿里云的 ODPS 分布式存储上,以预定好的备份策略来定期备份 redis 中的数据。 -* RDB 对 redis 对外提供的读写服务,影响非常小,可以让 redis **保持高性能**,因为 redis 主进程只需要 fork 一个子进程,让子进程执行磁盘 IO 操作来进行 RDB 持久化即可。 -* 相对于 AOF 持久化机制来说,直接基于 RDB 数据文件来重启和恢复 redis 进程,更加快速。 +* RDB 会生成多个数据文件,每个数据文件都代表了某一个时刻中 Redis 的数据,这种多个数据文件的方式,**非常适合做冷备**,可以将这种完整的数据文件发送到一些远程的安全存储上去,比如说 Amazon 的 S3 云服务上去,在国内可以是阿里云的 ODPS 分布式存储上,以预定好的备份策略来定期备份 Redis 中的数据。 +* RDB 对 Redis 对外提供的读写服务,影响非常小,可以让 Redis **保持高性能**,因为 Redis 主进程只需要 fork 一个子进程,让子进程执行磁盘 IO 操作来进行 RDB 持久化即可。 +* 相对于 AOF 持久化机制来说,直接基于 RDB 数据文件来重启和恢复 Redis 进程,更加快速。 -* 如果想要在 redis 故障时,尽可能少的丢失数据,那么 RDB 没有 AOF 好。一般来说,RDB 数据快照文件,都是每隔 5 分钟,或者更长时间生成一次,这个时候就得接受一旦 redis 进程宕机,那么会丢失最近 5 分钟的数据。 +* 如果想要在 Redis 故障时,尽可能少的丢失数据,那么 RDB 没有 AOF 好。一般来说,RDB 数据快照文件,都是每隔 5 分钟,或者更长时间生成一次,这个时候就得接受一旦 Redis 进程宕机,那么会丢失最近 5 分钟的数据。 * RDB 每次在 fork 子进程来执行 RDB 快照数据文件生成的时候,如果数据文件特别大,可能会导致对客户端提供的服务暂停数毫秒,或者甚至数秒。 #### AOF 优缺点 @@ -46,11 +46,11 @@ redis 如果仅仅只是将数据缓存在内存里面,如果 redis 宕机了 * AOF 日志文件即使过大的时候,出现后台重写操作,也不会影响客户端的读写。因为在 `rewrite` log 的时候,会对其中的指令进行压缩,创建出一份需要恢复数据的最小日志出来。在创建新日志文件的时候,老的日志文件还是照常写入。当新的 merge 后的日志文件 ready 的时候,再交换新老日志文件即可。 * AOF 日志文件的命令通过可读较强的方式进行记录,这个特性非常**适合做灾难性的误删除的紧急恢复**。比如某人不小心用 `flushall` 命令清空了所有数据,只要这个时候后台 `rewrite` 还没有发生,那么就可以立即拷贝 AOF 文件,将最后一条 `flushall` 命令给删了,然后再将该 `AOF` 文件放回去,就可以通过恢复机制,自动恢复所有数据。 * 对于同一份数据来说,AOF 日志文件通常比 RDB 数据快照文件更大。 -* AOF 开启后,支持的写 QPS 会比 RDB 支持的写 QPS 低,因为 AOF 一般会配置成每秒 `fsync` 一次日志文件,当然,每秒一次 `fsync` ,性能也还是很高的。(如果实时写入,那么 QPS 会大降,redis 性能会大大降低) +* AOF 开启后,支持的写 QPS 会比 RDB 支持的写 QPS 低,因为 AOF 一般会配置成每秒 `fsync` 一次日志文件,当然,每秒一次 `fsync` ,性能也还是很高的。(如果实时写入,那么 QPS 会大降,Redis 性能会大大降低) * 以前 AOF 发生过 bug,就是通过 AOF 记录的日志,进行数据恢复的时候,没有恢复一模一样的数据出来。所以说,类似 AOF 这种较为复杂的基于命令日志 / merge / 回放的方式,比基于 RDB 每次持久化一份完整的数据快照文件的方式,更加脆弱一些,容易有 bug。不过 AOF 就是为了避免 rewrite 过程导致的 bug,因此每次 rewrite 并不是基于旧的指令日志进行 merge 的,而是**基于当时内存中的数据进行指令的重新构建**,这样健壮性会好很多。 ### RDB 和 AOF 到底该如何选择 * 不要仅仅使用 RDB,因为那样会导致你丢失很多数据; * 也不要仅仅使用 AOF,因为那样有两个问题:第一,你通过 AOF 做冷备,没有 RDB 做冷备来的恢复速度更快;第二,RDB 每次简单粗暴生成数据快照,更加健壮,可以避免 AOF 这种复杂的备份和恢复机制的 bug; -* redis 支持同时开启开启两种持久化方式,我们可以综合使用 AOF 和 RDB 两种持久化机制,用 AOF 来保证数据不丢失,作为数据恢复的第一选择; 用 RDB 来做不同程度的冷备,在 AOF 文件都丢失或损坏不可用的时候,还可以使用 RDB 来进行快速的数据恢复。 +* Redis 支持同时开启开启两种持久化方式,我们可以综合使用 AOF 和 RDB 两种持久化机制,用 AOF 来保证数据不丢失,作为数据恢复的第一选择; 用 RDB 来做不同程度的冷备,在 AOF 文件都丢失或损坏不可用的时候,还可以使用 RDB 来进行快速的数据恢复。 diff --git a/docs/high-concurrency/redis-production-environment.md b/docs/high-concurrency/redis-production-environment.md index e110907..f743b7c 100644 --- a/docs/high-concurrency/redis-production-environment.md +++ b/docs/high-concurrency/redis-production-environment.md @@ -1,21 +1,21 @@ ## 面试题 -生产环境中的 redis 是怎么部署的? +生产环境中的 Redis 是怎么部署的? ## 面试官心理分析 -看看你了解不了解你们公司的 redis 生产集群的部署架构,如果你不了解,那么确实你就很失职了,你的 redis 是主从架构?集群架构?用了哪种集群方案?有没有做高可用保证?有没有开启持久化机制确保可以进行数据恢复?线上 redis 给几个 G 的内存?设置了哪些参数?压测后你们 redis 集群承载多少 QPS? +看看你了解不了解你们公司的 Redis 生产集群的部署架构,如果你不了解,那么确实你就很失职了,你的 Redis 是主从架构?集群架构?用了哪种集群方案?有没有做高可用保证?有没有开启持久化机制确保可以进行数据恢复?线上 Redis 给几个 G 的内存?设置了哪些参数?压测后你们 Redis 集群承载多少 QPS? 兄弟,这些你必须是门儿清的,否则你确实是没好好思考过。 ## 面试题剖析 -redis cluster,10 台机器,5 台机器部署了 redis 主实例,另外 5 台机器部署了 redis 的从实例,每个主实例挂了一个从实例,5 个节点对外提供读写服务,每个节点的读写高峰qps可能可以达到每秒 5 万,5 台机器最多是 25 万读写请求/s。 +Redis cluster,10 台机器,5 台机器部署了 Redis 主实例,另外 5 台机器部署了 Redis 的从实例,每个主实例挂了一个从实例,5 个节点对外提供读写服务,每个节点的读写高峰qps可能可以达到每秒 5 万,5 台机器最多是 25 万读写请求/s。 -机器是什么配置?32G 内存+ 8 核 CPU + 1T 磁盘,但是分配给 redis 进程的是10g内存,一般线上生产环境,redis 的内存尽量不要超过 10g,超过 10g 可能会有问题。 +机器是什么配置?32G 内存+ 8 核 CPU + 1T 磁盘,但是分配给 Redis 进程的是10g内存,一般线上生产环境,Redis 的内存尽量不要超过 10g,超过 10g 可能会有问题。 5 台机器对外提供读写,一共有 50g 内存。 -因为每个主实例都挂了一个从实例,所以是高可用的,任何一个主实例宕机,都会自动故障迁移,redis 从实例会自动变成主实例继续提供读写服务。 +因为每个主实例都挂了一个从实例,所以是高可用的,任何一个主实例宕机,都会自动故障迁移,Redis 从实例会自动变成主实例继续提供读写服务。 你往内存里写的是什么数据?每条数据的大小是多少?商品数据,每条数据是 10kb。100 条数据是 1mb,10 万条数据是 1g。常驻内存的是 200 万条商品数据,占用内存是 20g,仅仅不到总内存的 50%。目前高峰期每秒就是 3500 左右的请求量。 diff --git a/docs/high-concurrency/redis-sentinel.md b/docs/high-concurrency/redis-sentinel.md index c660283..0564368 100644 --- a/docs/high-concurrency/redis-sentinel.md +++ b/docs/high-concurrency/redis-sentinel.md @@ -2,14 +2,14 @@ ## 哨兵的介绍 -sentinel,中文名是哨兵。哨兵是 redis 集群架构中非常重要的一个组件,主要有以下功能: +sentinel,中文名是哨兵。哨兵是 Redis 集群架构中非常重要的一个组件,主要有以下功能: -* 集群监控:负责监控 redis master 和 slave 进程是否正常工作。 -* 消息通知:如果某个 redis 实例有故障,那么哨兵负责发送消息作为报警通知给管理员。 +* 集群监控:负责监控 Redis master 和 slave 进程是否正常工作。 +* 消息通知:如果某个 Redis 实例有故障,那么哨兵负责发送消息作为报警通知给管理员。 * 故障转移:如果 master node 挂掉了,会自动转移到 slave node 上。 * 配置中心:如果故障转移发生了,通知 client 客户端新的 master 地址。 -哨兵用于实现 redis 集群的高可用,本身也是分布式的,作为一个哨兵集群去运行,互相协同工作。 +哨兵用于实现 Redis 集群的高可用,本身也是分布式的,作为一个哨兵集群去运行,互相协同工作。 * 故障转移时,判断一个 master node 是否宕机了,需要大部分的哨兵都同意才行,涉及到了分布式选举的问题。 * 即使部分哨兵节点挂掉了,哨兵集群还是能正常工作的,因为如果一个作为高可用机制重要组成部分的故障转移系统本身是单点的,那就很坑爹了。 @@ -17,8 +17,8 @@ sentinel,中文名是哨兵。哨兵是 redis 集群架构中非常重要的 ## 哨兵的核心知识 * 哨兵至少需要 3 个实例,来保证自己的健壮性。 -* 哨兵 + redis 主从的部署架构,是**不保证数据零丢失**的,只能保证 redis 集群的高可用性。 -* 对于哨兵 + redis 主从这种复杂的部署架构,尽量在测试环境和生产环境,都进行充足的测试和演练。 +* 哨兵 + Redis 主从的部署架构,是**不保证数据零丢失**的,只能保证 Redis 集群的高可用性。 +* 对于哨兵 + Redis 主从这种复杂的部署架构,尽量在测试环境和生产环境,都进行充足的测试和演练。 哨兵集群必须部署 2 个以上节点,如果哨兵集群仅仅部署了 2 个哨兵实例,quorum = 1。 @@ -57,7 +57,7 @@ sentinel,中文名是哨兵。哨兵是 redis 集群架构中非常重要的 配置 `quorum=2` ,如果 M1 所在机器宕机了,那么三个哨兵还剩下 2 个,S2 和 S3 可以一致认为 master 宕机了,然后选举出一个来执行故障转移,同时 3 个哨兵的 majority 是 2,所以还剩下的 2 个哨兵运行着,就可以允许执行故障转移。 -## redis 哨兵主备切换的数据丢失问题 +## Redis 哨兵主备切换的数据丢失问题 ### 导致数据丢失的两种情况 主备切换的过程,可能会导致数据丢失: @@ -74,7 +74,7 @@ sentinel,中文名是哨兵。哨兵是 redis 集群架构中非常重要的 此时虽然某个 slave 被切换成了 master,但是可能 client 还没来得及切换到新的 master,还继续向旧 master 写数据。因此旧 master 再次恢复的时候,会被作为一个 slave 挂到新的 master 上去,自己的数据会清空,重新从新的 master 复制数据。而新的 master 并没有后来 client 写入的数据,因此,这部分数据也就丢失了。 -![redis-cluster-split-brain](./images/redis-cluster-split-brain.png) +![Redis-cluster-split-brain](./images/redis-cluster-split-brain.png) ### 数据丢失问题的解决方案 @@ -106,7 +106,7 @@ sdown 达成的条件很简单,如果一个哨兵 ping 一个 master,超过 ## 哨兵集群的自动发现机制 -哨兵互相之间的发现,是通过 redis 的 `pub/sub` 系统实现的,每个哨兵都会往 `__sentinel__:hello` 这个 channel 里发送一个消息,这时候所有其他哨兵都可以消费到这个消息,并感知到其他的哨兵的存在。 +哨兵互相之间的发现,是通过 Redis 的 `pub/sub` 系统实现的,每个哨兵都会往 `__sentinel__:hello` 这个 channel 里发送一个消息,这时候所有其他哨兵都可以消费到这个消息,并感知到其他的哨兵的存在。 每隔两秒钟,每个哨兵都会往自己监控的某个 master+slaves 对应的 `__sentinel__:hello` channel 里**发送一个消息**,内容是自己的 host、ip 和 runid 还有对这个 master 的监控配置。 @@ -149,7 +149,7 @@ sdown 达成的条件很简单,如果一个哨兵 ping 一个 master,超过 ## configuration epoch -哨兵会对一套 redis master+slaves 进行监控,有相应的监控的配置。 +哨兵会对一套 Redis master+slaves 进行监控,有相应的监控的配置。 执行切换的那个哨兵,会从要切换到的新 master(salve->master)那里得到一个 configuration epoch,这就是一个 version 号,每次切换的 version 号都必须是唯一的。 diff --git a/docs/high-concurrency/redis-single-thread-model.md b/docs/high-concurrency/redis-single-thread-model.md index 65b3978..ff45a22 100644 --- a/docs/high-concurrency/redis-single-thread-model.md +++ b/docs/high-concurrency/redis-single-thread-model.md @@ -1,31 +1,31 @@ ## 面试题 -redis 和 memcached 有什么区别?redis 的线程模型是什么?为什么 redis 单线程却能支撑高并发? +Redis 和 Memcached 有什么区别?Redis 的线程模型是什么?为什么 Redis 单线程却能支撑高并发? ## 面试官心理分析 -这个是问 redis 的时候,最基本的问题吧,redis 最基本的一个内部原理和特点,就是 redis 实际上是个**单线程工作模型**,你要是这个都不知道,那后面玩儿 redis 的时候,出了问题岂不是什么都不知道? +这个是问 Redis 的时候,最基本的问题吧,Redis 最基本的一个内部原理和特点,就是 Redis 实际上是个**单线程工作模型**,你要是这个都不知道,那后面玩儿 Redis 的时候,出了问题岂不是什么都不知道? -还有可能面试官会问问你 redis 和 memcached 的区别,但是 memcached 是早些年各大互联网公司常用的缓存方案,但是现在近几年基本都是 redis,没什么公司用 memcached 了。 +还有可能面试官会问问你 Redis 和 Memcached 的区别,但是 Memcached 是早些年各大互联网公司常用的缓存方案,但是现在近几年基本都是 Redis,没什么公司用 Memcached 了。 ## 面试题剖析 -### redis 和 memcached 有啥区别? +### Redis 和 Memcached 有啥区别? -#### redis 支持复杂的数据结构 +#### Redis 支持复杂的数据结构 -redis 相比 memcached 来说,拥有[更多的数据结构](/docs/high-concurrency/redis-data-types.md),能支持更丰富的数据操作。如果需要缓存能够支持更复杂的结构和操作, redis 会是不错的选择。 +Redis 相比 Memcached 来说,拥有[更多的数据结构](/docs/high-concurrency/redis-data-types.md),能支持更丰富的数据操作。如果需要缓存能够支持更复杂的结构和操作, Redis 会是不错的选择。 -#### redis 原生支持集群模式 +#### Redis 原生支持集群模式 -在 redis3.x 版本中,便能支持 cluster 模式,而 memcached 没有原生的集群模式,需要依靠客户端来实现往集群中分片写入数据。 +在 Redis3.x 版本中,便能支持 cluster 模式,而 Memcached 没有原生的集群模式,需要依靠客户端来实现往集群中分片写入数据。 #### 性能对比 -由于 redis 只使用**单核**,而 memcached 可以使用**多核**,所以平均每一个核上 redis 在存储小数据时比 memcached 性能更高。而在 100k 以上的数据中,memcached 性能要高于 redis。虽然 redis 最近也在存储大数据的性能上进行优化,但是比起 memcached,还是稍有逊色。 +由于 Redis 只使用**单核**,而 Memcached 可以使用**多核**,所以平均每一个核上 Redis 在存储小数据时比 Memcached 性能更高。而在 100k 以上的数据中,Memcached 性能要高于 Redis。虽然 Redis 最近也在存储大数据的性能上进行优化,但是比起 Memcached,还是稍有逊色。 -### redis 的线程模型 +### Redis 的线程模型 -redis 内部使用文件事件处理器 `file event handler` ,这个文件事件处理器是单线程的,所以 redis 才叫做单线程的模型。它采用 IO 多路复用机制同时监听多个 socket,将产生事件的 socket 压入内存队列中,事件分派器根据 socket 上的事件类型来选择对应的事件处理器进行处理。 +Redis 内部使用文件事件处理器 `file event handler` ,这个文件事件处理器是单线程的,所以 Redis 才叫做单线程的模型。它采用 IO 多路复用机制同时监听多个 socket,将产生事件的 socket 压入内存队列中,事件分派器根据 socket 上的事件类型来选择对应的事件处理器进行处理。 文件事件处理器的结构包含 4 个部分: @@ -36,23 +36,23 @@ redis 内部使用文件事件处理器 `file event handler` ,这个文件事 多个 socket 可能会并发产生不同的操作,每个操作对应不同的文件事件,但是 IO 多路复用程序会监听多个 socket,会将产生事件的 socket 放入队列中排队,事件分派器每次从队列中取出一个 socket,根据 socket 的事件类型交给对应的事件处理器进行处理。 -来看客户端与 redis 的一次通信过程: +来看客户端与 Redis 的一次通信过程: -![redis-single-thread-model](./images/redis-single-thread-model.png) +![Redis-single-thread-model](./images/redis-single-thread-model.png) 要明白,通信是通过 socket 来完成的,不懂的同学可以先去看一看 socket 网络编程。 -首先,redis 服务端进程初始化的时候,会将 server socket 的 `AE_READABLE` 事件与连接应答处理器关联。 +首先,Redis 服务端进程初始化的时候,会将 server socket 的 `AE_READABLE` 事件与连接应答处理器关联。 -客户端 socket01 向 redis 进程的 server socket 请求建立连接,此时 server socket 会产生一个 `AE_READABLE` 事件,IO 多路复用程序监听到 server socket 产生的事件后,将该 socket 压入队列中。文件事件分派器从队列中获取 socket,交给**连接应答处理器**。连接应答处理器会创建一个能与客户端通信的 socket01,并将该 socket01 的 `AE_READABLE` 事件与命令请求处理器关联。 +客户端 socket01 向 Redis 进程的 server socket 请求建立连接,此时 server socket 会产生一个 `AE_READABLE` 事件,IO 多路复用程序监听到 server socket 产生的事件后,将该 socket 压入队列中。文件事件分派器从队列中获取 socket,交给**连接应答处理器**。连接应答处理器会创建一个能与客户端通信的 socket01,并将该 socket01 的 `AE_READABLE` 事件与命令请求处理器关联。 -假设此时客户端发送了一个 `set key value` 请求,此时 redis 中的 socket01 会产生 `AE_READABLE` 事件,IO 多路复用程序将 socket01 压入队列,此时事件分派器从队列中获取到 socket01 产生的 `AE_READABLE` 事件,由于前面 socket01 的 `AE_READABLE` 事件已经与命令请求处理器关联,因此事件分派器将事件交给命令请求处理器来处理。命令请求处理器读取 socket01 的 `key value` 并在自己内存中完成 `key value` 的设置。操作完成后,它会将 socket01 的 `AE_WRITABLE` 事件与命令回复处理器关联。 +假设此时客户端发送了一个 `set key value` 请求,此时 Redis 中的 socket01 会产生 `AE_READABLE` 事件,IO 多路复用程序将 socket01 压入队列,此时事件分派器从队列中获取到 socket01 产生的 `AE_READABLE` 事件,由于前面 socket01 的 `AE_READABLE` 事件已经与命令请求处理器关联,因此事件分派器将事件交给命令请求处理器来处理。命令请求处理器读取 socket01 的 `key value` 并在自己内存中完成 `key value` 的设置。操作完成后,它会将 socket01 的 `AE_WRITABLE` 事件与命令回复处理器关联。 -如果此时客户端准备好接收返回结果了,那么 redis 中的 socket01 会产生一个 `AE_WRITABLE` 事件,同样压入队列中,事件分派器找到相关联的命令回复处理器,由命令回复处理器对 socket01 输入本次操作的一个结果,比如 `ok` ,之后解除 socket01 的 `AE_WRITABLE` 事件与命令回复处理器的关联。 +如果此时客户端准备好接收返回结果了,那么 Redis 中的 socket01 会产生一个 `AE_WRITABLE` 事件,同样压入队列中,事件分派器找到相关联的命令回复处理器,由命令回复处理器对 socket01 输入本次操作的一个结果,比如 `ok` ,之后解除 socket01 的 `AE_WRITABLE` 事件与命令回复处理器的关联。 这样便完成了一次通信。关于 Redis 的一次通信过程,推荐读者阅读《[Redis 设计与实现——黄健宏](https://github.com/doocs/technical-books#database)》进行系统学习。 -### 为啥 redis 单线程模型也能效率这么高? +### 为啥 Redis 单线程模型也能效率这么高? * 纯内存操作。 * 核心是基于非阻塞的 IO 多路复用机制。