Redis 基础知识学习总结

Redis 是什么

Redis 是一个基于内存的高性能key-value数据库。

官方网站:https://redis.io/

推荐一本好书:《Redis开发与运维》

Redis 支持的数据类型

  • String
  • List
  • Set
  • Sorted Set
  • Hash
  • Bitmaps
  • HyperLogLog
  • GEO

Redis 相对 Memcached 的优势

  1. Memcached所有的值均是简单的字符串,Redis作为其替代者,支持更为丰富的数据类型
  2. Redis的速度比Memcached快很多
  3. Redis可以持久化其数据

Redis 的常见应用场景

  • 缓存(数据缓存、Session缓存、全页缓存等)
  • 排行榜系统
  • 计数器应用
  • 发布/订阅(可以实现简单的消息队列)

Redis 的持久化机制

RDB方式

是默认的持久化方式,这种方式是就是将内存中数据以快照的方式写入到二进制文件中,默认的文件名为dump.rdb。

RDB文件保存过程:

  1. redis调用fork,现在有了子进程和父进程。
  2. 父进程继续处理client请求,子进程负责将内存内容写入到临时文件。由于os的写时复制机制(copy on write)父子进程会共享相同的物理页面,当父进程处理写请求时os会为父进程要修改的页面创建副本,而不是写共享的页面。所以子进程的地址空间内的数 据是fork时刻整个数据库的一个快照。
  3. 当子进程将快照写入临时文件完毕后,用临时文件替换原来的快照文件,然后子进程退出。

优点:RDB是一个紧凑的二进制压缩文件,非常适用于备份、全脸复制等场景;恢复速度远快于AOF方式。

缺点:无法做到实时/秒级持久化,因为每次运行都要执行fork,属于重量级操作;存在老版本无法兼容新版本RDB二进制格式的问题。

AOF方式

将每一个收到的写命令都通过write函数追加到文件中(默认是 appendonly.aof)。

aof 的方式也同时带来了另一个问题。持久化文件会变的越来越大。例如我们调用incr test命令100次,文件中必须保存全部的100条命令,其实有99条都是多余的。因为要恢复数据库的状态其实文件中保存一条set test 100就够了。

为了压缩aof的持久化文件。redis提供了bgrewriteaof命令。收到此命令redis将使用与快照类似的方式将内存中的数据 以命令的方式保存到临时文件中,最后替换原来的文件。具体过程如下:

  1. redis调用fork ,现在有父子两个进程
  2. 子进程根据内存中的数据库快照,往临时文件中写入重建数据库状态的命令
  3. 父进程继续处理client请求,除了把写命令写入到原来的aof文件中。同时把收到的写命令缓存起来。这样就能保证如果子进程重写失败的话并不会出问题。
  4. 当子进程把快照内容写入已命令方式写到临时文件中后,子进程发信号通知父进程。然后父进程把缓存的写命令也写入到临时文件。
  5. 现在父进程可以使用临时文件替换老的aof文件,并重命名,后面收到的写命令也开始往新的aof文件中追加。
  6. 需要注意到是重写aof文件的操作,并没有读取旧的aof文件,而是将整个内存中的数据库内容用命令的方式重写了一个新的aof文件,这点和快照有点类似。

优点:AOF文件易读;可以追加;

缺点:文件体积通常较大;速度慢于RDB;

Redis常见性能问题和解决方案

  1. Master最好不要做任何持久化工作,如RDB内存快照和AOF日志文件
  2. (Master写内存快照,save命令调度rdbSave函数,会阻塞主线程的工作,当快照比较大时对性能影响是非常大的,会间断性暂停服务,所以Master最好不要写内存快照;AOF文件过大会影响Master重启的恢复速度)
  3. 如果数据比较重要,某个Slave开启AOF备份数据,策略设置为每秒同步一次
  4. 为了主从复制的速度和连接的稳定性,Master和Slave最好在同一个局域网内
  5. 尽量避免在压力很大的主库上增加从库
  6. 主从复制不要用图状结构,用单向链表结构更为稳定,即:Master <- Slave1 <- Slave2 <- Slave3...这样的结构方便解决单点故障问题,实现Slave对Master的替换。如果Master挂了,可以立刻启用Slave1做Master,其他不变。

Redis主从复制

Redis的主从复制模式可以将主节点的数据同步给从节点。Redis的复制拓扑结构可以支持单层或多层复制关系,根据拓扑复杂性可以分为:一主一从、一主多从、树桩主从结构。

这样从节点可以起到两个作用:

  1. 作为主节点的备份,一旦主节点出了故障,这样从节点就可以作为后备顶上来。
  2. 可以扩展主节点的读能力,一旦主节点不能支撑大量并发的读操作,从节点可以在一定程度上帮助主节点分担读压力。

Redis Sentinel (哨兵)

主从复制存在着以下问题:

  • 高可用问题:一旦主节点出现故障,需要人工手动将一个从节点晋升为主节点,同时需要修改应用方的主节点地址。
  • 分布式问题:主节点的写能力、存储能力受到单机的限制。

Redis Sentinel 的高可用性:当主节点出现故障时,Redis Sentinel 能自动完成故障发现和故障转移,并通知应用方,从而实现真正的高可用。

Redis Sentinel 具有以下几个功能:

  • 监控:哨兵节点会定期检测Redis数据节点、其余哨兵节点是否可达。
  • 通知:哨兵节点会将故障转移的结果通知给应用方。
  • 主节点故障转移:实现从节点晋升为主节点并维护后续正确定的主从关系。
  • 配置提供者:在Redis Sentinel结构中,客户端在初始化的时候连接的是哨兵节点集合,从中获取主节点信息。

Redis 缓存设计

缓存的收益和成本

收益:

  • 加速读写:因为Redis是全内存的,而存储层通常读写性能不够强悍,通过缓存可以有效地加速读写,优化用户体验。
  • 降低后端负载:帮助后端减少访问量和复杂计算(如很复杂的SQL语句)。

成本:

  • 数据不一致:缓存层和存储层的数据存在着一定的时间窗口的不一致性。
  • 代码维护成本:加入缓存后,同时需要处理缓存层和存储层的逻辑,增大了开发维护运维成本。

缓存更新策略

缓存中的数据通常都是有生命周期的,需要在指定时间后被删除和更新,这样可以保证缓存空间在一个可控的范围内。但是缓存中的数据会和数据源中的真实数据有一段时间的窗口不一致,需要利用某些策略进行更新。

下面是三种缓存更新策略:

  1. LRU/LFU/FIFO算法剔除:通常用于缓存使用量超过了预设的最大值时。
  2. 超时剔除:通过给数据设置过期时间,让其在过期时间后自动删除,如expire命令。
  3. 主动更新:在真实数据更新后,立即更新缓存数据。

三种策略从上到下一致性越来越好,但是维护成本越来越高。

缓存穿透的优化思路

缓存穿透是指查询一个根本不存在的数据,缓存层和存储层均不命中,不将空结果写回缓存并返回空结果。

缓存穿透将导致不存在的数据每次请求都要到存储层去查询,失去了缓存保护后端存储的意义,使后端存储负载加大。

解决缓存穿透问题有两种方案:

  1. 缓存空对象
  2. 使用BloomFilter(布隆过滤器)进行拦截

缓存空对象维护代码简单,但是需要过多的存储空间,适用于数据频繁变化实时性高的场合。

布隆过滤器维护代码复杂,占用空间少,适用于数据相对固定实时性低的场合。

缓存雪崩的优化思路

由于缓存层承载着大量请求,有效地保护了存储层,但是如果缓存层由于某些原因不能提供服务,于是所有的请求都会达到存储层,存储层的调用会暴增,造成存储层也会级联宕机的情况。

可以从三个方面入手解决:

  1. 保证缓存服务高可用性,如使用Redis Sentinel、Redis Cluster
  2. 依赖隔离组件为后端限流并降级,例如在推荐服务中,如果“个性化推荐”服务不可用,前端可以降级渲染“热点数据”服务
  3. 提前演练,演练缓存层宕机,观察可能出现的问题

1 条评论

发表评论

电子邮件地址不会被公开。