Redis的数据持久化
Redis是基于内存对数据操作的数据库,计算机重启后,内存中的数据就会丢失,所以redis提供了持久化的功能,可以将redis操作的内存中数据持久化到本地的硬盘中。在redis重启之后,会自动把硬盘的持久化好的数据加载到redis操作的内存中。redis的持久化机制有两种,RDB和AOF,下面我们分别描述。
01
RDB
RDB全称是Redis Database,RDB机制就是对当前Redis操作的内存中的数据做一次全量的备份(snap shotting),把数据从内存写入到硬盘中。在持久化的过程中,会涉及到比较多的知识点,比如在持久化的过程中,redis的服务进程还可以处理新的对数据的读写请求吗?在持久化的过程中,redis突然崩了,持久化中断了怎么办,等等问题。为了解决上述问题,我们有必要了解一下RDB持久化的原理。
准备知识—COW机制
COW(copy-on-write)写入时复制,它不是Redis中特定的概念,是一种计算机程序设计领域的优化策略。其核心思想是,如果有多个调用者(callers)同时请求相同资源(如内存或磁盘上的数据存储),他们会共同获取相同的指针指向相同的资源,直到某个调用者试图修改资源的内容时,系统才会真正复制一份专用副本(private copy)给该调用者,而其他调用者所见到的最初的资源仍然保持不变。这过程对其他的调用者都是透明的(transparently)。此做法主要的优点是如果调用者没有修改该资源,就不会有副本(private copy)被建立,因此多个调用者只是读取操作时可以共享同一份资源。
我们总结成一句话:“多个线程访问某个共享资源,当对这个资源有写操作的时候拷贝一份新的,在新的上面进行写入,写入完成后,再将新的结果赋值给旧的数据。”
RDB持久化的实现过程
在这里我们说的RDB持久化的过程其实就是后面我们要提到的使用bgsave命令后RDB持久化的过程(save命令基本上废弃不用了),从这个命令的名称bg(background)“后台”,可以看出,这个命令在持久化的过程中,redis主线程(或者叫服务器)仍然可以处理客户端发来的请求。我们来看具体过程:
1、执行bgsave命令,Redis父进程首先判断:当前是否在执行save,或bgsave/bgrewriteaof(aof文件重写命令)的子进程,如果在执行则bgsave命令直接返回。
2、父进程执行fork操作创建子进程,fork操作过程中父进程会阻塞的,Redis不能执行来自客户端的任何命令;通过info status命令查看lastest_fork_usee选项,可以获取最近一个fork操作的耗时,该过程时间极其的短,单位为微秒。
3、父进程fork完成后,bgsave命令返回”Background saving started”信息并不再阻塞父进程,并可以响应其他客户端命令。
4、子进程根据父进程内存快照生成临时快照文件,开始对数据进行持久化,完成后对原有的.rdb文件进行原子替换。
注:在这个过程中就会使用到COW机制,创建子进程的时,子进程与父进程共享父进程的内存数据,如果父进程有对数据的写操作,父进程会复制一份内存数据的副本进行操作,而不会影响到子进程对数据的持久化操作。
5、子进程持久化完毕后,子进程发送信号给父进程表示完成,父进程更新统计信息。
RDB持久化的触发方式
手动触发:
save命令:同步持久化,在主线程中保存快照;阻塞当前Redis服务器,直到RDB过程完成为止,对于内存比较大的实例会造成长时间阻塞。
bgsave命令:异步持久化,Redis进程执行fork操作创建子进程,RDB持久化过程由子进程负责,完成后自动结束。堵塞只发生在fork阶段,一般时间很短;BGSAVE命令是针对SAVE堵塞问题做的优化。因此Redis内部所有的设计RDB的操作都采用BGSAVE的方式,而save命令已经废弃。
自动触发:
命令格式:save m n
过程:指定当m秒内发生n次变化时,会自动触发bgsave
优缺点
优点:在重启时,对于保存了大数据集的实例,恢复时比 AOF 要快,适合大规模数据的恢复。
缺点:
1、主线程复制副本的时候增加的内存消耗
2、意外挂了主线程更新的数据没有备份进去,数据丢失的风险比较大
02
AOF
AOF(append only file)日志记录了Redis服务器中从实例创建之初“对内存修改指令”的所有指令序列。所以恢复数据时,只需要在一个空的Redis实例序列中AOF日志中所有的指令全部执行一遍,就会使redis的服务器状态恢复到最新。
AOF操作方式
开启AOF持久化:appendonly yes|no
AOF写数据策略:appendfsync always|everysec|no
always:每个redis的写命令都要同步写入硬盘
everysec:每秒执行一次同步
no:让操作系统决定。
缺点
AOF的文件体积过大,还原数据的时间较长。
解决:bgrewriteaof命令对AOF文件瘦身。
Redis中的事务
事务提供了一种将多个命令请求打包,然后一次性、按顺序地执行多个命令的机制,并且在事务执行期间,服务器不会中断事务而去执行其他客户端命令。Redis提供了“简单的”事务处理能力。
Redis的事务命令
一个事务从开始到执行会经历以下三个阶段:
1、开始事务:命令:multi
2、命令入队:mul命令之后对redis的操作会放入命令队列
3、事务执行:命令:exec
示例:
127.0.0.1:6379> multiOK127.0.0.1:6379> zadd nametable 100 zhangsanQUEUED127.0.0.1:6379> zadd nametable 99 lisiQUEUED127.0.0.1:6379> exec1) (integer) 12) (integer) 1
Redis事务中的其他命令:
discard:丢弃
Redis的事务是原子性的吗
事务的原子性:一个事务中的多条指令,要么全部执行,要么全都不执行,只要有一条指令的执行过程中出错,就会导致整个事务的回滚。但是在redis中,如果一个事务中的某条指令出错,其余指令仍然会执行,所以Redis中的事务并不满足原子性。仅仅是满足了事务的隔离性,也就是执行过程中不被打断的权力。示例如下:
127.0.0.1:6379> multiOK127.0.0.1:6379> set age 20QUEUED127.0.0.1:6379> incr ageQUEUED127.0.0.1:6379> set name zhangsanQUEUED127.0.0.1:6379> incr nameQUEUED127.0.0.1:6379> exec1) OK2) (integer) 213) OK4) (error) ERR value is not an integer or out of range127.0.0.1:6379> get age"21"
watch指令
watch指令用于监听一个变量,当事务执行时,服务器收到了exec命令时,会检查watch的变量是否做了更改,如果变量被更改,则返回null告诉客户端执行失败。注意:watch指令必须在multi之前,multi和exec之间不允许
127.0.0.1:6379> watch sexOK127.0.0.1:6379> set sex maleOK127.0.0.1:6379> multiOK127.0.0.1:6379> set sex femaleQUEUED127.0.0.1:6379> exec(nil)
end