不为有趣之事,何遣有涯之生
不失其所者久,死而不亡者寿

Redis之路系列原理篇(5) 问渠哪得清如许

5 原理篇—问渠哪得清如许

单线程模型

Redis的线程模型:基于NI/O、单线程、异步的线程模型
Redis基于Reactor模式开发了网络事件处理器,这个处理器叫做文件事件处理器(file event handler)。

这个文件事件处理器,是单线程的,采用I/O多路复用机制同时监听多个socket,它根据socket上的事件,来选择对应的事件处理器来处理这个事件。

下方是Redis单线程模型示意图:

文件事件处理器的结构包含4个部分:多个socket,I/O多路复用程序,文件事件分发器,事件处理器
客戶端与Redis通信的流程大致如下:

  • 1:客户端发出连接Redis的请求,产生 AE_READABLE事件,最后会关联到连接应答处理器,由它来负责真正处理跟客户端的连接
  • 2:客户端向Redis发出命令请求,产生 AE_READABLE事件,最后会关联到命令请求处理器,由它来从socket中获取相关数据,然后进行真正的执行处理
  • 3 :Redis处理完成请求过后,会准备好返回给客户端的数据,产生 AE_WRITABLE事件,最后会关联到命令回复处理器,由它来把数据写入到 socket,返回到客户端
  • 4 :命令回复处理器完成后,就会删除这个socket的 AE_WRITABLE事件和命令回复处理器之间的关联关系

事务

基本特点

Redis中的事务本质就是一组命令的集合,被依次顺序的执行,当然可以放弃事务的执行,那么所有事务里面的命令都不会执行。
Redis中的事务特点:

  • 单线程架构保证了执行完事务内所有指令前是不可能再去同时执行其他客户端的请求的
  • Redis的事务没有隔离级别的概念,不存在”事务内的查询要看到事务里的更新,在事务外查询不能看 到”这种问题了
  • Redis的事务不保证原子性,只有决定是否开始执行全部指令的能力,没有执行到一半进行回滚的能力

基本过程

  • 1 发送事务开始指令:multi
  • 2 依次发送执行命令
  • 3 依次执行命令:exec

注意:如果某命令有语法错误,那么所有命令都不会执行;如果某命令只是执行错误,其它命令会正常执行,之后返回错误信息,无法回滚

持久化

Redis持久化分成三种方式:RDB(Redis DataBase)、AOF (Append Only File)和AOF+RDB混合持久化

  • RDB:在不同的时间点,将Redis某一时刻的数据生成快照并存储到磁盘上(缺省打开)
  • AOF :只允许追加不允许改写的文件,是将Redis执行过的所有写指令记录下来,在下次Redis重新启动时,只要把这些写指令从前到后再重复执行一遍,就可以实现数据恢复了(缺省关闭)
  • 混合方式:混合方式是先使用RDB进行快照存储,然后使用AOF持久化记录所有的写操作(redis6后推荐使用,缺省开启依赖RDB和AOF)。

RDB

默认情况下就是开启的

RDB方式,Redis会单独创建(fork)一个子进程来进行持久化,会先将数据写入到一个临时文件中,待持久化过程都结束了, 再用这个临时文件替换上次持久化好的文件。

  • 主进程不进行任何的IO操作,确保了极高的性能

  • RDB是快照读取,大规模数据效率比AOF好,但可能会丢失最后一次持久化数据

  • 适合对数据完整性不是很敏感的场景,比如冷备份,可以高性能快速恢复数据

相关配置:

  • save * :保存快照的频率,第一个表示多长时间,单位是秒,第二个*表 示至少执行写操作的次数;在一定时间内至少执行一定数量的写操作时,就自动保存快照;可设置多个条件。

如果想禁用RDB持久化的策略,只要不设置任何save指令,或者给save传入一个空字符串参数也可以
如果用户开启了RDB快照功能,那么在Redis持久化数据到磁盘时如果出现失败,默认情况下,Redis会停止接受所有的写请求。

这样做的好处在于可以让用户很明确的知道内存中的数据和磁盘上的数据已经存在不一致 了。如果下一次RDB持久化成功,redis会自动恢复接受写请求。

  • dbfilename:数据快照文件名(只是文件名,不包括目录),默认dump.rdb,还可以把它当作冷备份使用
  • dir:数据快照的保存目录(这个是目录),默认是当前路径
  • stop-writes-on-bgsave-error:如果配置成no,表示你不在乎数据不一致或者有其他的手段发现和控制这种不一致,那么在快照写入失败时,也能确保 redis继续接受新的写请求
  • rdbcompression:对于存储到磁盘中的快照,可以设置是否进行压缩存储。 如果是的话,redis会采用LZF算法进行压缩。默认是开启的,如果你不想消耗CPU来进行压缩的话,可以设置为关闭此功能
  • rdbchecksum:在存储快照后,还可以让redis使用CRC64算法来进行数据校验 ,但是这样做会增加大约10%的性能消耗,默认是开启的,如果希望获取到最大的性能提升 ,可以关闭此功能
  • rdb-del-sync-files:在没有持久性的情况下删除复制中使用的RDB文件启用 。默认情况下,此选项是禁用的。

RDB模式要注意的一些问题:

  • fork一个进程时,内存的数据也被复制了,即内存会是原来的两倍。
  • 每次快照持久化都是将内存数据完整写入到磁盘一次,并不是增量的同步数据。如果数据量大的话,而且写操作比较多,必然会 引起大量的磁盘io操作,可能会严重影响性能。
  • 由于快照方式是在一定间隔时间做一次的,所以如果redis意外 down掉的话,就会丢失最后一次快照后的所有修改。

触发快照情况:

  • 根据配置规则进行自动快照
  • 用户执行save(阻塞所有客户端请求)或bgsave(后台异步处理)命令
  • 执行flushall命令,会清除内存中所有数据,然后生产rdb文件(数据全没了)
  • 执行复制replication时

AOF

将配置项appendonly设置为yes就可以打开AOF功能,默认是关闭的。

默认的AOF持久化策略是每秒钟fsync一次,fsync是指把缓存中的写指令记录到磁盘中,在这种情况下,Redis仍可以保持很高的性能。

  • OS会在内核中缓存Redis的写操作

  • 缓存不会立即写到磁盘上,会有一定的延迟,理论上是有数据丢失的可能性

  • 你可以修改配置,强制不要缓存直接写入磁盘中

  • Fsync的方式有三种,推荐每秒启用一次的everysec

  • 优点:更好的保护数据不丢失 、性能高、可做紧急恢复

  • 缺点:文件比RDB文件大、写的QPS比RDB低

相关配置:

  • appendonly:是否开启AOF,默认关闭

  • appendfilename:设置AOF的日志文件名

  • appendfsync:设置AOF日志如何同步到磁盘,fsync()调用,用来告诉操作系统立即将缓存的指令写入磁盘,有三个选项always(一般不用),everysec(推荐使用),no(操作系统自己决定同步时间)

  • no-appendfsync-on-rewrite:设置当redis在rewrite的时候,是否允许appendsync。因为redis进程在进行AOF重写的时候,fsync()在主进程中的调用会被阻止,也就是redis的持久化功能暂时失效。默认为no ,这样能保证数据安全

  • auto-aof-rewrite-min-size:设置一个最小值,是为了防止在aof很小时就触发重写

  • auto-aof-rewrite-percentage:设置自动进行AOF重写的基准值,也就是重写启动时的AOF文件大小,假如redis自启动至今还没有进行过重写,那么启动时aof文件的大小会被作为基准值。这个基准值会和当前的aof大小进行比较。如果当前aof大小超出所设置的增长比例,则会 触发重写。如果设置auto-aof-rewrite-percentage为0,则会关闭此重写功能

  • AOF持久化采用文件追加的方式,这会导致AOF文件越来越大,因为AOF相当与和写相关的操作日志,所以AOF设置了重写机制

  • 重写机制就是当AOF超过一定大小,就会启动文件内容压缩,只保留可以恢复数据的最小指令集

  • 主动触发重写命令bgrewriteaof

重写基本流程:

  • 1 在重写开始前,redis会创建一个“重写子进程”,这个子进程会读取现有的 AOF文件,并将其包含的指令进行分析压缩并写入到一个临时文件中。
  • 2 与此同时,主进程会将新接收到的写指令一边累积到内存缓冲区中,一边继续写入到原有的AOF文件中,这样做是保证原有的AOF文件的可用性,避免在重写过程中出现意外。
  • 3 当“重写子进程”完成重写工作后,它会给父进程发一个信号,父进程收到信号后就会将内存中缓存的写指令追加到新AOF文件中
  • 4 当追加结束后,redis就会用新AOF文件来代替旧AOF文件,之后再有新的写指 令,就都会追加到新的AOF文件中
  • 5 重写aof文件的操作,并没有读取旧的aof文件,而是将整个内存中的数据库内容用命令的方式重写了一个新的aof文件,这点和快照有点类似

AOF日志恢复
如果在追加日志时,恰好遇到磁盘空间满或断电等情况,导致日志写入不完整,也没有关系,Redis提供了redis-check-aof工 具,可以用来进行日志修复,基本步骤如下:

  • 备份被写坏的AOF文件
  • 运行redis-check-aof –fix进行修复
  • 用diff -u来看下两个文件的差异,确认问题点
  • 重启redis,加载修复后的AOF文件

混合模式

先使用RDB进行快照存储,然后使用 AOF持久化记录所有的写操作,当重写策略满足或手动触发重写的时候,将最新的数据存储为新的RDB记录。

这样的话,重启服务的时候会从RDB和AOF两部分恢复数据, 既保证了数据完整性,又提高了恢复数据的性能。

通过 aof-use-rdb-preamble 配置项可以打开混合方式,默认是yes。
所以日志文件中会同时包含了RDB数据和AOF数据

数据恢复顺序:

  • 1 判断是否开启AOF持久化,若开启了AOF,则使用AOF持久化文件恢复数据
  • 2 如果AOF文件不存在,否则使用RDB持久化文件恢复数据
  • 3 如果AOF文件和RDB文件都不存在则直接启动Redis
  • 4 如果AOF或RDB文件出现错误,则启动失败返回错误信息

附录1:Redis事务基本命令

  • 1:multi:设置事务开始
  • 2:exec:执行事务
  • 3:discard:放弃事务
  • 4:watch:监控键值,如果键值被修改或删除,后面的一个事务就不 会执行
  • 5:unwatch:取消watch

关于watch:Redis使用Watch来提供乐观锁定,类似于CAS;可以被调用多次;当 EXEC 被调用后,所有的之前被监视的键值会被取消监视,不管事务是否被取消或者执行。并且当客户端连接丢失的时候,所有东 西都会被取消监视

未经允许不得转载:菡萏如佳人 » Redis之路系列原理篇(5)

欢迎加入极客江湖

进入江湖关于作者