Redis 单线程?如何持久化?

157 阅读5分钟

Redis 只有单线程吗

Redis 是单线程的,主要是指 Redis 的网络 IO 线程。Redis 的持久化,集群同步等操作,则是由另外的线程来执行

Redis 采用单线程为什么还这么快

redis 4.0 版本之前

Redis 的大部分操作都在内存中完成

使用了多种优秀的数据结构

单线程模型避免了多线程之间的竞争

Redis 采用了 IO 多路复用机制处理大量的客户端 Socket 请求

redis 6.0 版本之后新增了多线程 IO 的读写并发能力

  • 网络硬件不断升级,性能瓶颈出现在单线程的 IO 处理上
  • 网络请求采用多线程并行,但是读写命令依然是单线程处理

Redis 数据持久化

  • AOF 日志:记录所有的操作命令,并以文本的形式追加到文件中
  • RDB 快照:将某一时刻的内存数据,以二进制的方式写入磁盘
  • 混合持久化方式:Redis 4.0 新增混合之久话的方式,集成了 RDB 和 AOF 的优点

AOF 日志如何实现的?

通常情况下,关系型数据库的日志都是“写前日志”,而 AOF 里记录的是 Redis 收到的每一条命令,这些命令时以文本形式保存的,不同的是,Redis 的 AOF 日志的记录顺序与传统关系型数据库正好相反,它是写后日志,“写后”是指 Redis 要先执行命令,把数据写入内存,把数据写入内存,然后再记录日志到文件

  1. 执行命令写入内存
  2. 记录日志

why:因为 Redis 在写入日志之前,不对命令进行语法检查。所以,只记录执行成功的命令,避免了出现记录错误命令的情况。并且,在命令执行完之后在记录,不会阻塞当前的写操作

but:数据可能会丢失(数据在写入内存时宕机);可能阻塞其他操作(在执行记录日志的操作的时候依然会阻塞将要写入内存的数据操作)

so:为了解决 AOF 方法做故障恢复时操作缓慢这个问题,Redis 增加了 RDB 内存快照的操作,它既可以保证可靠性,又能在宕机时实现快速恢复;和 AOF 不同的是,RDB 记录 Redis 某一时刻的数据,而不是操作。所以在做数据恢复的时候,只需要直接把 RDB 文件写入内存,完成快速恢复

RDB 主要原理主要考点

  1. RDB 做快照时会阻塞线程吗?

    1. 为了解决阻塞线程问题,Redis 提供了两个命令来生成 RDB 快照文件,分别是 save 和 bgsave。save命令在主线程中执行,会导致阻塞。而 bgsave 命令会创建一个子线程,用于写入 RDB 文件的操作,避免了对主线程的阻塞
  2. RDB 在做快照的时候数据能修改吗?

    question 1:如果此时可以执行写操作:意味着 Redis 还能正常处理写操作,就可能出现正在执行快照的数据是已经被修改了的情况

    question 2:如果此时不可以执行写操作:意味着 Redis 的所有写操作都得等到快照执行完成之后才能执行,那么就又出现了阻塞主线程的问题

    RDB 如何解决上述问题的呢?

    1. 如果主线程执行读操作,则主线程和 bgsave 子线程互相不影响
    2. 如果主线程执行写操作,则被修改的数据会复制一份副本,然后 bgsave 子线程会把该副本数据写入 RDB 文件,在这个过程中,主线程依然可以直接修改原来的数据

注意:Redis 对 RDB 的执行频率非常重要,因为这会影响快照数据的完整性以及 Redis 的稳定性,所以在 Redis 4.0 后,增加了 AOF 和 RDB 混合的数据持久化机制:把数据以 RDB 的方式写入文件,再将后续的操作命令以 AOF 的格式存入文件,即保证了 Redis 重启速度,又降低数据丢失风险

Redis 如何实现服务高可用?

  • 主从同步

    • 一主多从实现数据读写分离承担更多的并发操作
  • 哨兵模式 (Redis Sentinel)

    • 当 Redis 主从服务器出现故障宕机,需要手动进行恢复,Redis 增加了烧饼模式(哨兵模式做到了可以监控主从服务器,并且提供主动容灾恢复的功能)
  • Redis Cluster(集群)

    • Redis Cluster 是一种分布式去中心化的运行模式,它将数据分布在不同的服务器上,以此来降低系统对单节点的依赖,从而提高 Redis 服务的读写性能(多主多从)

    • Redis Cluster 方案采用哈希槽来处理数据与实例之间的映射关系,在 Redis Cluster 方案中,一个切片集群共有 16384 个哈希槽,这些哈希槽类似于数据分区,每个键值对都会根据它的 key,被映射到一个哈希槽中,具体执行过程分为两大步

      • 根据键值对的 key,按照 CRC16 算法计算一个 16bit 的值
      • 再用 16bit 值对 16384 取模,得到 0~16384 范围内的模数,每个模数代表一个对应编号的哈希槽

      哈希槽怎么被映射到具体的 Redis 实例上的呢?

      • 平均分配:在使用 cluster create 命令创建 Redis 集群时,Redis 会自动把所有哈希槽平均分布到集群实例上 (16384 / 实例个数)
      • 手动分配:可以使用 cluster meet 命令手动建立实例间的连接,组成集群,再使用 cluster addslots 命令,指定每个实例上的哈希槽个数