头图

写在前面

📌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 节点存活就可以正常提供服务

缓存异常

缓存雪崩

  • 缓存同一时间大面积的失效

    • 过期时间设置随机,防止同一时间大量数据过期现象发生
    • 加锁排队

缓存穿透

  • 缓存和数据库中都没有的数据

    • 接口层增加校验,id<=0的直接拦截
    • 将key-value对写为key-null,缓存有效时间可以设置短点,如30秒。防止反复用同一个id暴力攻击
    • 布隆过滤器判断在不在数据库

      • 哈希表,多次hash

        • 有一定的误识别率和删除困难
      • 所有的Hash函数告诉我们该元素在集合中,才能确定在集合中

缓存击穿

  • 热点数据缓存时间到期

    • 设置热点数据永远不过期
    • 加互斥锁

缓存预热

  • 将相关的缓存数据直接加载到缓存系统

缓存降级

  • 根据一些关键数据进行自动降级,也可以配置开关实现人工降级

    • Redis出现问题,不去数据库查询,而是直接返回默认值给用户

数据库双写时的数据一致性

  • 先更新数据库,然后再删除缓存

    • 一致性:缓存删除失败,数据不一致

      • 高并发下:A去查突然失效查脏数据,B更新数据库,B写入缓存,A写入缓存->数据不一致
  • 先删除缓存,然后再更新数据库

    • 高并发下:A删除,B读数据库脏数据,B写入,A更新数据库->数据不一致
  • 不更新缓存

    • 有大量冷数据占用资源,用到缓存才去算缓存

HNUJSY
1 声望0 粉丝

湖南大学 计算机科学与技术硕士在读;腾讯PCG 后端开发实习生