Redis


什么是Redis

Redis(remote dictionary server) 是一种用 c 语言写的,开源的,高性能 NoSql(非关系)键值对数据库。

可以存储健和五种不同数据结构之间的映射,但是健必须是字符串。

与传统数据库不同的是,redis 运行在内存,所以速度非常快,常用作缓存。也常用作分布式锁。

Redis 支持 事务,持久化,LUA 脚本,LRU驱动事件,多种集群方案。

支持的数据结构

  • 字符串:string
  • 集合:set
  • 列表:list
  • 散列表:hash
  • 有序集合:zset

优点

  • 读写速度快
  • 支持数据持久化,支持 AOF(写命令日志追加) 和 RDB(定期存储数据) 两种持久化方式
  • 支持事务,Redis 的操作都是原子的,并且支持几个操作合并后 也是原子操作。
  • 数据结构丰富
  • 支持主从复制,主机自动同步数据给从机,并支持读写分离。

缺点

  • 数据库容量受内存的限制,无法做海量数据的高性能读写,所以一般只作为缓存。
  • 不具备自动容错和恢复功能,主机从机宕机,都会导致读写失败。
  • 主机宕机,没有及时同步数据到从机,导致读写失败
  • 很难进行在线扩容。

缓存分类

  • 本地缓存:map,guava,随程序销毁而销毁,无法持久,并且多个实例有多个备份,无法数据一致。
  • 分布式缓存:redis,memcached,多实例公用一份备份,数据一致性。

为什么Redis快

  • 完全基于内存
  • 数据结构简单
  • 采用单线程,避免不必要的上下文切换和竞争条件,也不存在各个线程和进程切换,不用考虑锁等问题。
  • 使用多路 I/O 复用,非阻塞 I/O
  • Redis 使用自己构建的 VM 机制,减少系统调用。

过期策略

  • 定时过期:为每个健设定一个定时器,到达时间自动过期删除。
  • 惰性过期:每次查询健的时候查看是否过期,不查询不做过期检查。
  • 定期过期:一定时间全部健做过期检查。

redis 使用定期过期和惰性过期。

内存淘汰策略

线程模型

  • 使用的是 reactor 模型,I/O 复用。
  • 单线程。

事物

  • redis 一个事物内的命令顺序执行,并且其他客户端的请求也不会插入到事物中。并且支持 ACID 中的隔离性和一致性,持久性,其他不支持。
  • A 原子性:一个事物内的操作要发生就都会发生,redis 的命令支持隔离,但事物不支持隔离,一条命令失败,其他命令继续执行。
  • C:一致性,事物操作前后保持一致性
  • I:隔离性,多个事物并发时,一个事物不会影响其他事物。redis 是单线程,并且是顺序执行的,所以支持 I。
  • D:持久性,事物一旦提交,堆数据库的影响时持久性的。

集群方案

哨兵模式

  • 集群监控:负责监控redis master 和 slave 进程是否正常工作
  • 消息通知:如果某个 redis 实例有故障,哨兵会发送消息通知给管理员
  • 故障转移:如果 master node 挂掉了,会自动转移到 slave node 上。需要大量的 哨兵 node 一起投票选举新的 master node。
  • 配置中心:如果故障转移发生,通知 client 端新的 master 地址
  • 哨兵节点的个数 2n+1 ,保证投票选举可以执行。

服务器端数据共享

  • 一份数据被分为多个切片,分布在服务器的各个主从节点中
  • 同一个切分可能分布在不同节点上。
  • 写入数据时,先写在主节点上,再写到多个从节点上。
  • 读取数据时,如果该节点没有对应数据,会将请求转移到正确的节点上
  • 扩容时,把旧数据的切片分一部分给新节点。

客户端分配

代理服务器分配

Redis 主从架构

  • 一主多从
  • 主负责写,从负责读,读写分离
  • 好处是:轻松实现水平扩容,支持读高并发
  • 为什么不建议使用 slave 做 redis 备份:因为如果 master 不备份,数据丢了后,salve 复制了 0 数据,自身也丢了。
  • 复制数据过程:
    • 新加入 salve 节点,发送同步命令给 master 节点
    • master 开启后台线程生成 RDS 文件,并且把此时的写命令缓存到内存中。
    • RDS 文件生成好后发给 slave 节点进行本地磁盘数据库建立,并写入到 slave 内存中。还有缓存的写命令。
    • 之后 master 收到的写命令 都会发给 slave,同步操作。

分布式锁

  • 使用 setnx 实现分布式锁,只有 key 不存在时才设值。

缓存异常

缓存雪崩

  • 是指一段时间内缓存大面积失效,导致命令落在了数据库上,对数据库造成短时间内承受大量请求而崩溃。
  • 解决:
    • 缓存设置随机过期时间,防止一段时间内大面积过期。
    • 一般并发量不多的时候,使用加锁排队
    • 给每一个缓存加标记,记录缓存是否失效。

缓存穿透

  • 是指 缓存和数据库都没有数据
  • 解决:使用布隆过滤器,直接过滤掉,防止访问数据库

缓存击穿

  • 是指缓存没有,数据库有,但是与雪崩不同的是,击穿指的是一条数据。多次请求一条数据在数据库上。
  • 解决:设置热点数据永不过期。加互斥锁。

缓存预热

  • 系统上线后,自动将数据添加到缓存中。

缓存降级

  • 热点数据和冷数据

缓存热点key