写在前面
Xmind文件获取:GitHub 持续更新中,别忘了 star 喔~
「Java学习+面试指南」思维导图,计算机自学指南,包括Java基础、JVM、数据库、mysql、redis、计算机网络、算法、数据结构、操作系统等,后台技术栈/架构师之路/全栈开发社区,阿里,腾讯,百度,美团,头条等春招/秋招/校招/面试
思维导图(png格式可下载放大)
Redis
优缺点
优点
- 读写性能优异
- 支持数据持久化
- 支持事务,所有操作都是原子性的,支持对几个操作合并后的原子性执行。
- 数据结构丰富
- 主从复制,进行读写分离
缺点
- 较小数据量的高性能操作和运算
不具备自动容错和恢复功能
- 需要等待机器重启或者手动切换前端的IP才能恢复
主机宕机,宕机前有部分数据未能及时同步到从机
- 降低了系统的可用性
- 较难支持在线扩容
不用 map/guava
本地缓存
- 生命周期随着 jvm 的销毁而结束
- 每个实例都需要各自保存一份缓存,缓存不具有一致性
快的原因
完全基于内存
- 查找和操作的时间复杂度都是O(1)
- 数据结构简单
单线程
- 避免了不必要的上下文切换和竞争条件,也不存在多进程或者多线程导致的切换而消耗 CPU,不用去考虑各种锁的问题,不存在加锁释放锁操作,没有因为可能出现死锁而导致的性能消耗
- 多路 I/O 复用模型,非阻塞 IO
memcached
- (1) memcached所有的值均是简单的字符串,redis作为其替代者,支持更为丰富的数据类型
- (2) redis的速度比memcached快很多
- (3) redis可以持久化其数据
- 都支持分布式
基础
数据类型
- STRING
- LIST
- SET
- HASH
ZSET
jumplist
- 多层链表+二分法
应用场景
- 计数器
- 缓存
会话缓存
- 统一存储多台应用服务器的会话信息
查找表
- DNS 记录
消息队列(发布/订阅功能)
- List 是一个双向链表,可以通过 lpush 和 rpop 写入和读取消息
分布式锁
SETNX
- 随机value,比较相同才删除解锁
持久化
RDB(默认)快照
- dump.rdb
- fork 子进程来完成写操作,让主进程继续处理命令,所以是 IO 最大化
- 比 AOF 的启动效率更高
AOF
- 将Redis执行的每次写命令记录到单独的日志文件中
- 会优先选择AOF恢复
- 每进行一次 命令操作就记录到 aof 文件中一次
- AOF 文件比 RDB 文件大,且恢复速度慢
- rdb 启动效率低
- AOF文件比RDB更新频率高,优先使用AOF还原数据
过期键的删除策略
- 用于处理过期的缓存数据
定时过期
- 占用大量的CPU资源
惰性过期 用
- 当访问一个key
定期过期 用
- 扫描一定数量的数据库的expires字典中一定数量的key
- 过期时间和永久有效 EXPIRE和PERSIST
内存淘汰策略
- allkeys-lru
- 用于处理内存不足时的需要申请额外空间的数据
线程模型
文件事件处理器
- 多个套接字、IO多路复用程序、文件事件分派器(单线程)、事件处理器
- 使用 I/O 多路复用(multiplexing)程序来同时监听多个套接字
根据套接字目前执行的任务来为套接字关联不同的事件处理器
事务
- 要么全部被执行,要么全部都不执行
MULTI、EXEC、DISCARD、WATCH等一组命令的集合,一个事务中所有命令都会被序列化
WATCH
- 为 Redis 事务提供 check-and-set (CAS)行为。 可以监控一个或多个键,一旦其中有一个键被修改(或删除),之后的事务就不会执行,监控一直持续到EXEC命令。
DISCARD
- 客户端可以清空事务队列,并放弃执行事务
三个阶段
事务开始 MULTI
- 命令不会立即被执行,而是被放到一个队列中
- 命令入队
事务执行 EXEC
- 执行所有事务块内的命令
不支持回滚
- 在事务失败时不进行回滚,而是继续执行余下的命令
隔离性
- 单进程程序,并且它保证在执行事务时,不会对事务进行中断
不支持原子性
- 不保证原子性,且没有回滚
异步队列
- 使用list类型保存数据信息,rpush生产消息,lpop消费消息,当lpop没有消息时,可以sleep一段时间,然后再检查有没有信息,
如果不想sleep的话,可以使用blpop, 在没有信息的时候,会一直阻塞,直到信息的到来。 - redis可以通过pub/sub主题订阅模式实现一个生产者,多个消费者,当然也存在一定的缺点,当消费者下线时,生产的消息会丢失。
集群方案
哨兵模式
- 集群监控:负责监控 redis master 和 slave
- 消息通知:如果某个 redis 实例有故障,通知给管理员。
故障转移:如果 master node 挂掉了,会自动转移到 slave node 上。
- 判断一个 master node 是否宕机了,大部分的哨兵都同意,分布式选举
- 配置中心:故障转移,通知 client 客户端新的 master 地址。
核心知识
- 至少需要 3 个实例
- 哨兵 + redis 主从的部署架构,是不保证数据零丢失的,只能保证高可用性。
Cluster 方案(服务端路由查询)
分布式寻址算法
- hash 算法(大量缓存重建)
- 一致性 hash 算法(自动缓存迁移)+ 虚拟节点(自动负载均衡解决数据倾斜问题)
- redis cluster 的 hash slot哈希槽 算法 16384
主从架构
- 每份数据分片会存储在多个互为主从的多节点
- 多个节点间的数据不保持一致性
- 先写主节点,再同步到从节点(支持配置为阻塞同步)
- 当客户端操作的key没有分配在该节点上时,redis会返回转向指令,指向正确的节点
- 扩容时时需要需要把旧节点的数据迁移一部分到新节点
优点
- 支持动态扩容
- 具备Sentinel的监控和自动Failover(故障转移)能力
- 免去了proxy代理的损耗
- 连接集群中任何一个可用节点
基于客户端分配
- 采用哈希算法将Redis数据的key进行散列,通过hash函数,特定的key会映射到特定的Redis节点上
- Redis实例彼此独立,相互无关联
- 不支持动态增删节点,每个客户端都需要更新调整
- 连接不能共享,当应用规模增大时,资源浪费制约优化
基于代理服务器分片
- 请求到一个代理组件,代理解析客户端的数据,并将请求转发至正确的节点
- 切换成本低
- 代理层多了一次转发,性能有所损耗
主从架构
- 一主多从,主负责写,并且将数据复制到其它的 slave 节点,从节点负责读。所有的读请求全部走从节点。支撑读高并发。
- redis replication -> 主从架构 -> 读写分离 -> 水平扩容支撑读高并发
核心机制
异步方式复制
- 发送一个 PSYNC
- full resynchronization 全量复制,master 会启动一个后台线程,开始生成一份 RDB 快照文件
- 将这个 RDB 发送给 slave,slave 会先写入本地磁盘,然后再从本地磁盘加载到内存中
- master 会将内存中缓存的写命令发送到 slave
必须开启 master node 的持久化
- 在 master 宕机重启的时候数据是空的,然后可能一经过复制, slave node 的数据也丢了
从备份中挑选一份 rdb 去恢复 master,这样才能确保启动的时候,是有数据的
- lave node 可以自动接管 master node,但也可能 sentinel 还没检测到 master failure,master node 就自动重启了,还是可能导致上面所有的 slave node 数据被清空
分区缺点
涉及多个key的操作通常不会被支持
不能对两个集合求交集
- 被存储到不同的Redis实例
- 同时操作多个key,则不能使用Redis事务.
数据处理会非常复杂
- 必须从不同的Redis实例和主机同时收集RDB / AOF文件
动态扩容或缩容可能非常复
- Redis集群在运行时增加或者删除Redis节点,能做到最大程度对用户透明地数据再平衡,但其他一些客户端分区或者代理分区方法则不支持这种特性。
分布式锁
SETNX SET if Not eXists
- 当且仅当 key 不存在,将 key 的值设为 value。给定的 key 已经存在,则 SETNX 不做任何动作
解决 Redis 的并发竞争 Key
分布式锁(zookeeper 和 redis 都可以实现分布式锁)
- 每个客户端对某个方法加锁时,在zookeeper上的与该方法对应的指定节点的目录下,生成一个唯一的瞬时有序节点。
判断是否获取锁的方式很简单,只需要判断有序节点中序号最小的一个。 当释放锁的时候,只需将这个瞬时节点删除即可。
同时,其可以避免服务宕机导致的锁无法释放,而产生的死锁问题。完成业务流程后,删除对应的子节点释放锁。 Redlock
- 互斥访问,即永远只有一个 client 能拿到锁
- 最终 client 都可能拿到锁,不会出现死锁的情况
- 只要大部分 Redis 节点存活就可以正常提供服务
- 每个客户端对某个方法加锁时,在zookeeper上的与该方法对应的指定节点的目录下,生成一个唯一的瞬时有序节点。
缓存异常
缓存雪崩
缓存同一时间大面积的失效
- 过期时间设置随机,防止同一时间大量数据过期现象发生
- 加锁排队
缓存穿透
缓存和数据库中都没有的数据
- 接口层增加校验,id<=0的直接拦截
- 将key-value对写为key-null,缓存有效时间可以设置短点,如30秒。防止反复用同一个id暴力攻击
布隆过滤器判断在不在数据库
哈希表,多次hash
- 有一定的误识别率和删除困难
- 所有的Hash函数告诉我们该元素在集合中,才能确定在集合中
缓存击穿
热点数据缓存时间到期
- 设置热点数据永远不过期
- 加互斥锁
缓存预热
- 将相关的缓存数据直接加载到缓存系统
缓存降级
根据一些关键数据进行自动降级,也可以配置开关实现人工降级
- Redis出现问题,不去数据库查询,而是直接返回默认值给用户
数据库双写时的数据一致性
先更新数据库,然后再删除缓存
一致性:缓存删除失败,数据不一致
- 高并发下:A去查突然失效查脏数据,B更新数据库,B写入缓存,A写入缓存->数据不一致
先删除缓存,然后再更新数据库
- 高并发下:A删除,B读数据库脏数据,B写入,A更新数据库->数据不一致
不更新缓存
- 有大量冷数据占用资源,用到缓存才去算缓存