4545
4646<!-- /MarkdownTOC -->
4747
48- # 1. Redis 集群以及应用
48+ # Redis 集群以及应用
4949
50- ## 1.1. 集群
50+ ## 集群
5151
52- ### 1.1.1. 主从复制
52+ ### 主从复制
5353
54- #### 1.1.1.1. 主从链(拓扑结构)
54+ #### 主从链(拓扑结构)
5555![ 主从] ( https://user-images.githubusercontent.com/26766909/67539461-d1a26c00-f714-11e9-81ae-61fa89faf156.png )
5656
5757![ 主从] ( https://user-images.githubusercontent.com/26766909/67539485-e0891e80-f714-11e9-8980-d253239fcd8b.png )
5858
59- #### 1.1.1.2. 复制模式
59+ #### 复制模式
6060- 全量复制: master 全部同步到 slave
6161- 部分复制: slave 数据丢失进行备份
6262
63- #### 1.1.1.3. 问题点
63+ #### 问题点
6464- 同步故障
6565 - 复制数据延迟(不一致)
6666 - 读取过期数据(Slave 不能删除数据)
7575 - 主从复制挤压缓冲区不足产生的问题(网络中断,部分复制无法满足),可增大复制缓冲区( rel_backlog_size 参数).
7676- 复制风暴
7777
78- ### 1.1.2. 哨兵机制
78+ ### 哨兵机制
7979
80- #### 1.1.2.1. 拓扑图
80+ #### 拓扑图
8181![ image] ( https://user-images.githubusercontent.com/26766909/67539495-f0086780-f714-11e9-9eab-c11a163ac6c0.png )
8282
83- #### 1.1.2.2. 节点下线
83+ #### 节点下线
8484- 客观下线
8585 - 所有 Sentinel 节点对 Redis 节点失败要达成共识,即超过 quorum 个统一。
8686- 主观下线
8787 - 即 Sentinel 节点对 Redis 节点失败的偏见,超出超时时间认为 Master 已经宕机。
8888
89- #### 1.1.2.3. leader选举
89+ #### leader选举
9090- 选举出一个 Sentinel 作为 Leader:集群中至少有三个 Sentinel 节点,但只有其中一个节点可完成故障转移.通过以下命令可以进行失败判定或领导者选举。
9191- 选举流程
9292 1 . 每个主观下线的 Sentinel 节点向其他 Sentinel 节点发送命令,要求设置它为领导者.
9393 2 . 收到命令的 Sentinel 节点如果没有同意通过其他 Sentinel 节点发送的命令,则同意该请求,否则拒绝。
9494 3 . 如果该 Sentinel 节点发现自己的票数已经超过 Sentinel 集合半数且超过 quorum,则它成为领导者。
9595 4 . 如果此过程有多个 Sentinel 节点成为领导者,则等待一段时间再重新进行选举。
9696
97- #### 1.1.2.4. 故障转移
97+ #### 故障转移
9898- 转移流程
9999 1 . Sentinel 选出一个合适的 Slave 作为新的 Master(slaveof no one 命令)。
100100 2 . 向其余 Slave 发出通知,让它们成为新 Master 的 Slave( parallel-syncs 参数)。
105105 2 . 选择复制偏移量最大的节点(同步数据最多)。
106106 3 . 选择 runId 最小的节点。
107107
108- #### 1.1.2.5. 读写分离
108+ #### 读写分离
109109
110- #### 1.1.2.6. 定时任务
110+ #### 定时任务
111111- 每 1s 每个 Sentinel 对其他 Sentinel 和 Redis 执行 ping,进行心跳检测。
112112- 每 2s 每个 Sentinel 通过 Master 的 Channel 交换信息(pub - sub)。
113113- 每 10s 每个 Sentinel 对 Master 和 Slave 执行 info,目的是发现 Slave 节点、确定主从关系。
114114
115- ### 1.1.3. 分布式集群(Cluster)
115+ ### 分布式集群(Cluster)
116116
117- #### 1.1.3.1. 拓扑图
117+ #### 拓扑图
118118
119119![ image] ( https://user-images.githubusercontent.com/26766909/67539510-f8f93900-f714-11e9-9d8d-08afdecff95a.png )
120120
121- #### 1.1.3.2. 通讯
121+ #### 通讯
122122
123- ##### 1.1.3.2.1. 集中式
123+ ##### 集中式
124124> 将集群元数据(节点信息、故障等等)几种存储在某个节点上。
125125- 优势
126126 1 . 元数据的更新读取具有很强的时效性,元数据修改立即更新
127127- 劣势
128128 1 . 数据集中存储
129129
130- ##### 1.1.3.2.2. Gossip
130+ ##### Gossip
131131![ image] ( https://user-images.githubusercontent.com/26766909/67539546-16c69e00-f715-11e9-9891-1e81b6af624c.png )
132132
133133- [ Gossip 协议] ( https://www.jianshu.com/p/8279d6fd65bb )
134134
135- #### 1.1.3.3. 寻址分片
135+ #### 寻址分片
136136
137- ##### 1.1.3.3.1. hash取模
137+ ##### hash取模
138138- hash(key)%机器数量
139139- 问题
140140 1 . 机器宕机,造成数据丢失,数据读取失败
141141 1 . 伸缩性
142142
143- ##### 1.1.3.3.2. 一致性hash
143+ ##### 一致性hash
144144- ![ image] ( https://user-images.githubusercontent.com/26766909/67539595-352c9980-f715-11e9-8e4a-9d9c04027785.png )
145145
146146- 问题
147147 1 . 一致性哈希算法在节点太少时,容易因为节点分布不均匀而造成缓存热点的问题。
148148 - 解决方案
149149 - 可以通过引入虚拟节点机制解决:即对每一个节点计算多个 hash,每个计算结果位置都放置一个虚拟节点。这样就实现了数据的均匀分布,负载均衡。
150150
151- ##### 1.1.3.3.3. hash槽
151+ ##### hash槽
152152- CRC16(key)%16384
153153-
154154![ image] ( https://user-images.githubusercontent.com/26766909/67539610-3fe72e80-f715-11e9-8e0d-ea58bc965795.png )
155155
156- ## 1.2. 使用场景
156+ ## 使用场景
157157
158- ### 1.2.1. 热点数据
158+ ### 热点数据
159159
160160存取数据优先从 Redis 操作,如果不存在再从文件(例如 MySQL)中操作,从文件操作完后将数据存储到 Redis 中并返回。同时有个定时任务后台定时扫描 Redis 的 key,根据业务规则进行淘汰,防止某些只访问一两次的数据一直存在 Redis 中。
161161> 例如使用 Zset 数据结构,存储 Key 的访问次数/最后访问时间作为 Score,最后做排序,来淘汰那些最少访问的 Key。
162162
163163如果企业级应用,可以参考:[ 阿里云的 Redis 混合存储版] [ 1 ]
164164
165- ### 1.2.2. 会话维持 Session
165+ ### 会话维持 Session
166166
167167会话维持 Session 场景,即使用 Redis 作为分布式场景下的登录中心存储应用。每次不同的服务在登录的时候,都会去统一的 Redis 去验证 Session 是否正确。但是在微服务场景,一般会考虑 Redis + JWT 做 Oauth2 模块。
168168> 其中 Redis 存储 JWT 的相关信息主要是留出口子,方便以后做统一的防刷接口,或者做登录设备限制等。
169169
170- ### 1.2.3. 分布式锁 SETNX
170+ ### 分布式锁 SETNX
171171
172172命令格式:` SETNX key value ` :当且仅当 key 不存在,将 key 的值设为 value。若给定的 key 已经存在,则 SETNX 不做任何动作。
173173
1741741 . 超时时间设置:获取锁的同时,启动守护线程,使用 expire 进行定时更新超时时间。如果该业务机器宕机,守护线程也挂掉,这样也会自动过期。如果该业务不是宕机,而是真的需要这么久的操作时间,那么增加超时时间在业务上也是可以接受的,但是肯定有个最大的阈值。
1751752 . 但是为了增加高可用,需要使用多台 Redis,就增加了复杂性,就可以参考 Redlock:[ Redlock分布式锁] ( Redlock分布式锁.md#怎么在单节点上实现分布式锁 )
176176
177- ### 1.2.4. 表缓存
177+ ### 表缓存
178178
179179Redis 缓存表的场景有黑名单、禁言表等。访问频率较高,即读高。根据业务需求,可以使用后台定时任务定时刷新 Redis 的缓存表数据。
180180
181- ### 1.2.5. 消息队列 list
181+ ### 消息队列 list
182182
183183主要使用了 List 数据结构。
184184List 支持在头部和尾部操作,因此可以实现简单的消息队列。
@@ -187,46 +187,46 @@ List 支持在头部和尾部操作,因此可以实现简单的消息队列。
187187
188188同时可以使用多个 List,来实现多个队列,根据不同的业务消息,塞入不同的 List,来增加吞吐量。
189189
190- ### 1.2.6. 计数器 string
190+ ### 计数器 string
191191
192192主要使用了 INCR、DECR、INCRBY、DECRBY 方法。
193193
194194INCR key:给 key 的 value 值增加一
195195DECR key:给 key 的 value 值减去一
196196
197- ## 1.3. 缓存设计
197+ ## 缓存设计
198198
199- ### 1.3.1. 更新策略
199+ ### 更新策略
200200- LRU、LFU、FIFO 算法自动清除:一致性最差,维护成本低。
201201- 超时自动清除(key expire):一致性较差,维护成本低。
202202- 主动更新:代码层面控制生命周期,一致性最好,维护成本高。
203203
204- ### 1.3.2. 更新一致性
204+ ### 更新一致性
205205- 读请求:先读缓存,缓存没有的话,就读数据库,然后取出数据后放入缓存,同时返回响应。
206206- 写请求:先删除缓存,然后再更新数据库(避免大量地写、却又不经常读的数据导致缓存频繁更新)。
207207
208- ### 1.3.3. 缓存粒度
208+ ### 缓存粒度
209209- 通用性:全量属性更好。
210210- 占用空间:部分属性更好。
211211- 代码维护成本。
212212
213- ### 1.3.4. 缓存穿透
213+ ### 缓存穿透
214214> 当大量的请求无命中缓存、直接请求到后端数据库(业务代码的 bug、或恶意攻击),同时后端数据库也没有查询到相应的记录、无法添加缓存。
215215> 这种状态会一直维持,流量一直打到存储层上,无法利用缓存、还会给存储层带来巨大压力。
216216
217- #### 1.3.4.1. 解决方案
217+ #### 解决方案
2182181 . 请求无法命中缓存、同时数据库记录为空时在缓存添加该 key 的空对象(设置过期时间),缺点是可能会在缓存中添加大量的空值键(比如遭到恶意攻击或爬虫),而且缓存层和存储层数据短期内不一致;
2192192 . 使用布隆过滤器在缓存层前拦截非法请求、自动为空值添加黑名单(同时可能要为误判的记录添加白名单).但需要考虑布隆过滤器的维护(离线生成/ 实时生成)。
220220
221- ### 1.3.5. 缓存雪崩
221+ ### 缓存雪崩
222222> 缓存崩溃时请求会直接落到数据库上,很可能由于无法承受大量的并发请求而崩溃,此时如果只重启数据库,或因为缓存重启后没有数据,新的流量进来很快又会把数据库击倒。
223223
224- #### 1.3.5.1. 出现后应对
224+ #### 出现后应对
225225- 事前:Redis 高可用,主从 + 哨兵,Redis Cluster,避免全盘崩溃。
226226- 事中:本地 ehcache 缓存 + hystrix 限流 & 降级,避免数据库承受太多压力。
227227- 事后:Redis 持久化,一旦重启,自动从磁盘上加载数据,快速恢复缓存数据。
228228
229- #### 1.3.5.2. 请求过程
229+ #### 请求过程
2302301 . 用户请求先访问本地缓存,无命中后再访问 Redis,如果本地缓存和 Redis 都没有再查数据库,并把数据添加到本地缓存和 Redis;
2312312 . 由于设置了限流,一段时间范围内超出的请求走降级处理(返回默认值,或给出友情提示)。
232232
0 commit comments