Redis 原理

简介

本文档介绍Redis的原理知识,是从设计Redis的角度看Redis。主要包括如下几个方面:

为什么开发Redis

为了弄明白为什么要开发Redis,我们不妨先来看看Redis是什么,用官方的一句话:

 Redis是一个开源的、分布式的基于内存的数据结构化存储,常备用作内存数据库,消息缓存和代理

redis数据持久化

rdb

 1. 执行bgsave命令,Redis父进程判断当前是否存在正在执行的子进程,如RDB/AOF子进程,如果存在bgsave命令直接返回。

 2. 父进程执行fork操作创建子进程,fork操作过程中父进程被阻塞。

 3. 父进程fork完成后,bgsave命令返回“* Background saving started by pid xxx”信息,并不再阻塞父进程,可以继续响应其他命令。

 4. 父进程创建RDB文件,根据父进程内存生成临时快照文件,完成后对原有文件进行原子替换。根据lastsave命令可以获取最近一次生成RDB的时间,对应info Persistence中的rdb_last_save_time。

 5. 进程发送信号给父进程表示完胜,父进程更新统计信息。

 对于大多数操作系统来说,fork都是个重量级操作,虽然创建的子进程不需要拷贝父进程的物理内存空间,但是会复制父进程的空间内存页表。

 子进程通过fork操作产生,占用内存大小等同于父进程,理论上需要两倍的内存来完成持久化操作,但Linux有写时复制机制(copy-on-write)。父子进程会共享相同的物理内存页,当父进程处理写请求时会把要修改的页创建副本,而 子进程在fork操作过程中会共享父进程的内存快照。
 1. 手动触发

    包括save和bgsave命令。

     因为save会阻塞当前Redis节点,所以,Redis内部所有涉及RDB持久化的的操作都通过bgsave方式,save方式已废弃。

 2. 自动触发

     1> 使用save的相关配置。

     2> 从节点执行全量复制操作。

     3> 执行debug reload命令。

     4> 执行shutdown命令时,如果没有开启AOF持久化功能则会自动执行bgsave。

aof

 1. 所有的写入命令追加到aof_buf缓冲区中。

 2. AOF会根据对应的策略向磁盘做同步操作。刷盘策略由appendfsync参数决定。

 3. 定期对AOF文件进行重写。重写策略由auto-aof-rewrite-percentage,auto-aof-rewrite-min-size两个参数决定。

 appendfsync参数有如下取值:

 no: don't fsync, just let the OS flush the data when it wants. Faster. 只调用系统write操作,不对AOF文件做fsync操作,同步硬盘操作由操作系统负责,通常同步周期最长为30s。

 always: fsync after every write to the append only log. Slow, Safest. 命令写入到aof_buf后,会调用系统fsync操作同步到文件中。

 everysec: fsync only one time every second. Compromise. 只调用系统write操作,fsync同步文件操作由专门进程每秒调用一次。

 默认值为everysec,也是建议值。

 为什么要重写?重写后可以加快节点启动时的加载时间。

 重写后的文件为什么可以变小?

 1. 进程内超时的数据不用再写入到AOF文件中。

 2. 存在删除命令。

 3. 多条写命令可以合并为一个。
 1. 手动触发

      直接调用bgrewriteaof命令。

 2. 自动触发。

     与auto-aof-rewrite-percentage,auto-aof-rewrite-min-size两个参数有关。

     触发条件,aof_current_size > auto-aof-rewrite-min-size 并且 (aof_current_size  - aof_base_size) / aof_base_size >= auto-aof-rewrite-percentage。

     其中,aof_current_size是当前AOF文件大小,aof_base_size 是上一次重写后AOF文件的大小,这两部分的信息可从info Persistence处获取。
 1. 执行AOF重写请求。

     如果当前进程正在执行bgsave操作,重写命令会等待bgsave执行完后再执行。

 2. 父进程执行fork创建子进程。

 3. fork操作完成后,主进程会继续响应其它命令。所有修改命令依然会写入到aof_buf中,并根据appendfsync策略持久化到AOF文件中。

 4. 因fork操作运用的是写时复制技术,所以子进程只能共享fork操作时的内存数据,对于fork操作后,生成的数据,主进程会单独开辟一块aof_rewrite_buf保存。

 5. 子进程根据内存快照,按照命令合并规则写入到新的AOF文件中。每次批量写入磁盘的数据量由aof-rewrite-incremental-fsync参数控制,默认为32M,避免单次刷盘数据过多造成硬盘阻塞。

 6. 新AOF文件写入完成后,子进程发送信号给父进程,父进程更新统计信息。

 7. 父进程将aof_rewrite_buf(AOF重写缓冲区)的数据写入到新的AOF文件中。

 8. 使用新AOF文件替换老文件,完成AOF重写。

 实际上,当Redis节点执行完一个命令后,它会同时将这个写命令发送到AOF缓冲区和AOF重写缓冲区。
 1.  创建一个不带网络连接的伪客户端。因为Redis的命令只能在客户端上下文中执行。

 2. 从AOF文件中分析并读取一条命令。

 3. 使用伪客户端执行该命令。

 4. 反复执行步骤2,3,直到AOF文件中的所有命令都被处理完。 



 注意:AOF的持久化也可能会造成阻塞。

 AOF常用的持久化策略是everysec,在这种策略下,fsync同步文件操作由专门线程每秒调用一次。当系统磁盘较忙时,会造成Redis主线程阻塞。

 1. 主线程负责写入AOF缓冲区。

 2. AOF线程负责每秒执行一次同步磁盘操作,并记录最近一次同步时间。

 3. 主线程负责对比上次AOF同步时间。

    1> 如果距上次同步成功时间在2s内,主线程直接返回。

    2> 如果距上次同步成功时间超过2s,主线程会阻塞,直到同步操作完成。每出现一次阻塞,info Persistence中aof_delayed_fsync的值都会加1。

 所以,使用everysec策略最多会丢失2s数据,而不是1s

redis 的lua脚本

什么是lua脚本

使用lua脚本好处

Redis 事件通知

事件的类型

键空间频道的订阅者将接收到被执行的事件的名字,在这个例子中,就是 del

键事件频道的订阅者将接收到被执行事件的键的名字,在这个例子中,就是 mykey

配置

 K    键空间通知,以__keyspace@<db>__为前缀  db为:0-15
 E    键事件通知,以__keysevent@<db>__为前缀  db为:0-15

 g    del , expipre , rename 等类型无关的通用命令的通知, ...  
 $    String命令  
 l    List命令  
 s    Set命令  
 h    Hash命令  
 z    有序集合命令  
 x    过期事件(每次key过期时生成)  
 e    驱逐事件(当key在内存满了被清除时生成)  
 A    g$lshzxe的别名,因此”AKE”意味着所有的事件

如: notify-keyspace-events "Ex" 表示对过期事件进行通知发送; notify-keyspace-events "kx" 表示想监控某个 key 的失效事件。将参数设为字符串 AKE 表示发送所有类型的通知。

HyperLogLog

结语

【回首页】