详解redis高级特性和工作原理

目标

1、学习Redis的一些高级特性,包括发布订阅、事务、Lua脚本等
2、掌握Redis的底层原理,包括单线程工作机制、内存回收、持久化

发布订阅模式

列表的局限

前面我们说通过队列的rpush和lpop可以实现消息队列(队尾进队头出),但是消费者需要不停地调用lpop查看List中是否有等待处理的消息(比如写一个while循环)。为了减少通信的消耗,可以sleep()一段时间再消费,但是会有两个问题:
1、如果生产者生产消息的速度远大于消费者消费消息的速度,List会占用大量的内存。
2、消息的实时性降低。

list还提供了一个阻塞的命令:blpop,没有任何元素可以弹出的时候,连接会被阻
塞。

blpop queue 5

基于list实现的消息队列,不支持一对多的消息分发。

发布订阅模式

除了通过list实现消息队列之外,Redis还提供了一组命令实现发布/订阅模式。
这种方式,发送者和接收者没有直接关联(实现了解耦),接收者也不需要持续尝试获取消息。

订阅频道

首先,我们有很多的频道(channel),我们也可以把这个频道理解成 queue。订阅者可以订阅一个或者多个频道。消息的发布者(生产者)可以给指定的频道发布消息。只要有消息到达了频道,所有订阅了这个频道的订阅者都会收到这条消息。

需要注意的注意是,发出去的消息不会被持久化,因为它已经从队列里面移除了,所以消费者只能收到它开始订阅这个频道之后发布的消息。

下面我们来看一下发布订阅命令的使用方法。
订阅者订阅频道:可以一次订阅多个,比如这个客户端订阅了3个频道。

subscribe channel-1 channel-2 channel-3

发布者可以向指定频道发布消息(并不支持一次向多个频道发送消息):

publish channel-1 2673

取消订阅(不能在订阅状态下使用):

unsubscribe channel-1
按规则(Pattern)订阅频道

支持?和*占位符。?代表一个字符,*代表0个或者多个字符。
消费端1,关注运动信息:

psubscribe *sport

消费端2,关注所有新闻:

psubscribe news*

消费端3,关注天气新闻:

psubscribe news-weather

生产者,发布3条信息

publish news-sport yaoming 
publish news-music jaychou 
publish news-weather rain

在这里插入图片描述

Redis 事务

先看官网
https://redis.io/topics/transactions/
http://redisdoc.com/topic/transaction.html

为什么要用事务

我们知道Redis 的单个命令是原子性的(比如 get set mget mset),如果涉及到多个命令的时候,需要把多个命令作为一个不可分割的处理序列,就需要用到事务。

例如我们之前说的用setnx实现分布式锁,我们先set,然后设置对key设置expire,防止del发生异常的时候锁不会被释放,业务处理完了以后再del,这三个动作我们就希望它们作为一组命令执行。
Redis的事务有两个特点:
1、按进入队列的顺序执行。
2、不会受到其他客户端的请求的影响。
Redis 的事务涉及到四个命令:multi(开启事务),exec(执行事务),discard
(取消事务),watch(监视)

事务的用法

案例场景:tom和mic各有1000元,tom需要向mic转账100元。
tom的账户余额减少100元,mic的账户余额增加100元。
tracantion
tracantion
通过multi的命令开启事务。事务不能嵌套,多个multi命令效果一样。
multi执行后,客户端可以继续向服务器发送任意多条命令, 这些命令不会立即被执行, 而是被放到一个队列中, 当 exec 命令被调用时, 所有队列中的命令才会被执行。

通过exec的命令执行事务。如果没有执行exec,所有的命令都不会被执行。
如果中途不想执行事务了,怎么办?
可以调用discard可以清空事务队列,放弃执行。
give up excu

watch 命令

在Redis中还提供了一个watch命令。

它可以为 Redis 事务提供 CAS 乐观锁行为(Check and Set / Compare and Swap),也就是多个线程更新变量的时候,会跟原值做比较,只有它没有被其他线程修改的情况下,才更新成新的值。

我们可以用watch监视一个或者多个key,如果开启事务之后,至少有一个被监视key键在 exec 执行之前被修改了, 那么整个事务都会被取消(key提前过期除外)。可以用unwatch取消。
redis transection

事务可能遇到的问题

我们把事务执行遇到的问题分成两种,一种是在执行exec之前发生错误,一种是在
执行exec之后发生错误。

在执行 exec 之前发生错误

比如:入队的命令存在语法错误,包括参数数量,参数名等等(编译器错误)。

在这里插入图片描述
在这种情况下事务会被拒绝执行,也就是队列中所有的命令都不会得到执行。

在执行 exec 之后发生错误

比如,类型错误,比如对String使用了Hash的命令,这是一种运行时错误。
在这里插入图片描述
最后我们发现setk11的命令是成功的,也就是在这种发生了运行时异常的情况下,
只有错误的命令没有被执行,但是其他命令没有受到影响
这个显然不符合我们对原子性的定义,也就是我们没办法用Redis 的这种事务机制
来实现原子性,保证数据的一致。
思考:
为什么在一个事务中存在错误,Redis不回滚?

Lua 脚本

Lua/ˈluə/是一种轻量级脚本语言,它是用C语言编写的,跟数据的存储过程有点类
似。 使用Lua脚本来执行Redis命令的好处:
1、一次发送多个命令,减少网络开销。
2、Redis会将整个脚本作为一个整体执行,不会被其他请求打断,保持原子性。
3、对于复杂的组合命令,我们可以放在文件中,可以实现程序之间的命令集复
用。

在 Redis 中调用 Lua 脚本

使用eval /ɪ’væl/ 方法,语法格式:

redis>eval lua-script key-num[key1 key2 key3....][value1 value2 value3....]

 eval 代表执行 Lua 语言的命令。
 lua-script 代表 Lua 语言脚本内容。
 key-num 表示参数中有多少个 key,需要注意的是 Redis 中 key 是从 1 开始的,如果没有 key 的参数,那么写 0。
 [key1 key2 key3…]是 key 作为参数传递给 Lua 语言,也可以不填,但是需要和 key-num 的个数对应起来。
 [value1 value2 value3 ….]这些参数传递给 Lua 语言,它们是可填可不填的。

示例,返回一个字符串,0个参数:

127.0.0.1:0>eval "return 'leon'" 0
在 Lua 脚本中调用 Redis 命令

使用redis.call(command, key [param1, param2…])进行操作。语法格式:

redis>eval "redis.call('set',KEYS[1],ARGV[1])" 1 lua-key lua-value

 command 是命令,包括 set、get、del 等。
 key 是被操作的键。
 param1,param2…代表给 key 的参数。
注意跟Java不一样,定义只有形参,调用只有实参。
Lua是在调用时用key表示形参,argv表示参数值(实参)。

设置键值对

在Redis中调用Lua脚本执行Redis命令

redis>eval "return redis.call('set',KEYS[1],ARGV[1])" 1 gupao 2673 
redis>get gupao

以上命令等价于set gupao 2673。

在redis-cli中直接写Lua脚本不够方便,也不能实现编辑和复用,通常我们会把脚
本放在文件里面,然后执行这个文件。

在 Redis 中调用 Lua 脚本文件中的命令,操作 Redis

创建Lua脚本文件:

cd /usr/local/soft/redis5.0.5/src 
vim gupao.lua

Lua脚本内容,先设置,再取值:

redis.call('set','gupao','lua666') 
return redis.call('get','gupao')

在Redis客户端中调用Lua脚本

cd /usr/local/soft/redis5.0.5/src 
redis-cli --eval gupao.lua 0

得到返回值:
[root@localhostsrc]# redis-cli --eval gupao.lua 0
“lua666”

案例:对 IP 进行限流

需求:在X秒内只能访问Y次。
设计思路:用key记录IP,用value记录访问次数。
拿到IP以后,对IP+1。如果是第一次访问,对key设置过期时间(参数1)。否则
判断次数,超过限定的次数(参数2),返回0。如果没有超过次数则返回1。超过时间,
key过期之后,可以再次访问。
KEY[1]是IP, ARGV[1]是过期时间X,ARGV[2]是限制访问的次数Y。
在这里插入图片描述
6秒钟内限制访问10次,调用测试(连续调用10次):

./redis-cli--eval "ip_limit.lua" app:ip:limit:192.168.8.111 , 6 10

 app:ip:limit:192.168.8.111 是key值 ,后面是参数值,中间要加上一个空格 和
一个逗号,再加上一个 空格 。
即:./redis-cli –eval [lua脚本] [key…]空格,空格[args…]
 多个参数之间用一个 空格 分割 。

缓存 Lua 脚本

为什么要缓存
在脚本比较长的情况下,如果每次调用脚本都需要把整个脚本传给 Redis服务端,会产生比较大的网络开销。为了解决这个问题,Redis提供了EVALSHA命令,允许开发者通过脚本内容的SHA1摘要来执行脚本。
如何缓存
Redis在执行script load命令时会计算脚本的SHA1摘要并记录在脚本缓存中,执EVALSHA 命令时 Redis 会根据提供的摘要从脚本缓存中查找对应的脚本内容,如果找到了则执行脚本,否则会返回错误:“NOSCRIPT No matching script. Please use EVAL.”
在这里插入图片描述
自乘案例
Redis有incrby这样的自增命令,但是没有自乘,比如乘以3,乘以5。
我们可以写一个自乘的运算,让它乘以后面的参数:
在这里插入图片描述
把这个脚本变成单行,语句之间使用分号隔开

在这里插入图片描述
script load ‘命令’
在这里插入图片描述
在这里插入图片描述

脚本超时

Redis的指令执行本身是单线程的,这个线程还要执行客户端的Lua脚本,如果Lua脚本执行超时或者陷入了死循环,是不是没有办法为客户端提供服务了呢?

eval 'while(true)do end' 0

为了防止某个脚本执行时间过长导致 Redis 无法提供服务,Redis 提供了lua-time-limit参数限制脚本的最长运行时间,默认为5秒钟。
lua-time-limit 5000(redis.conf配置文件中)

当脚本运行时间超过这一限制后,Redis将开始接受其他命令但不会执行(以确保脚本的原子性,因为此时脚本并没有被终止),而是会返回“BUSY”错误。
Redis提供了一个script kill的命令来中止脚本的执行。新开一个客户端:
script kill

如果当前执行的Lua 脚本对 Redis 的数据进行了修改(SET、DEL 等),那么通过
script kill命令是不能终止脚本运行的。

127.0.0.1:6379>eval "redis.call('set','gupao','666') while true do end" 0

因为要保证脚本运行的原子性,如果脚本执行了一部分终止,那就违背了脚本原子
性的要求。最终要保证脚本要么都执行,要么都不执行。

127.0.0.1:6379>script kill 
(error) UNKILLABLE Sorry the script already executed write commands against the dataset. You can either wait the script 

遇到这种情况,只能通过shutdown nosave命令来强行终止redis。
shutdown nosave 和 shutdown 的区别在于 shutdown nosave 不会进行持久化
操作,意味着发生在上一次快照后的数据库修改都会丢失。
思考:Redis不是只有一个线程吗?它已经卡死了,怎么接受spriptkill指
令的?
总结:如果我们有一些特殊的需求,可以用Lua来实现,但是要注意那些耗时的操
作。

Redis 为什么这么快?
Redis 到底有多快?

https://redis.io/topics/benchmarks

cd /usr/local/soft/redis-5.0.5/src 
redis-benchmark -t set,lpush -n 100000 -q

结果(本地虚拟机):
SET:51813.47 requests per second —— 每秒钟处理 5 万多次 set 请求
LPUSH:51706.31 requests per second —— 每秒钟处理 5 万多次 lpush 请求

redis-benchmark -n 100000 -q  script load  "redis.call('set','foo','bar')"

结果(本地虚拟机):
script load redis.call(‘set’,‘foo’,‘bar’):46816.48 requests per second—— 每秒钟 46000 次 lua 脚本调用
在这里插入图片描述
根据官方的数据,Redis的QPS可以达到10万左右(每秒请求数)。

Redis 为什么这么快?

总结:1)纯内存结构、2)单线程、3)多路复用

内存

KV结构的内存数据库,时间复杂度O(1)。
第二个,要实现这么高的并发性能,是不是要创建非常多的线程?
恰恰相反,Redis是单线程的。

单线程

单线程有什么好处呢?
1、没有创建线程、销毁线程带来的消耗
2、避免了上线文切换导致的CPU消耗
3、避免了线程之间带来的竞争问题,例如加锁释放锁死锁等等

异步非阻塞

异步非阻塞I/O,多路复用处理并发连接。

Redis 为什么是单线程的?

不是白白浪费了CPU的资源吗?
https://redis.io/topics/faq#redis-is-single-threaded-how-can-i-exploit-multiple-cpu–cores
因为单线程已经够用了,CPU不是redis的瓶颈。Redis的瓶颈最有可能是机器内存或者网络带宽。既然单线程容易实现,而且CPU不会成为瓶颈,那就顺理成章地采用单线程的方案了。

单线程为什么这么快?

因为Redis是基于内存的操作,我们先从内存开始说起。

虚拟存储器(虚拟内存 Vitual Memory)

名词解释:主存:内存;辅存:磁盘(硬盘)

计算机主存(内存)可看作一个由M个连续的字节大小的单元组成的数组,每个字节有一个唯一的地址,这个地址叫做物理地址(PA)。早期的计算机中,如果CPU需要内存,使用物理寻址,直接访问主存储器。
在这里插入图片描述
这种方式有几个弊端:
1、在多用户多任务操作系统中,所有的进程共享主存,如果每个进程都独占一块物理地址空间,主存很快就会被用完。我们希望在不同的时刻,不同的进程可以共用同一块物理地址空间。
2、如果所有进程都是直接访问物理内存,那么一个进程就可以修改其他进程的内存
数据,导致物理地址空间被破坏,程序运行就会出现异常。为了解决这些问题,我们就想了一个办法,在CPU和主存之间增加一个中间层。 CPU不再使用物理地址访问,而是访问一个虚拟地址,由这个中间层把地址转换成物理地址,
最终获得数据。这个中间层就叫做虚拟存储器(Virtual Memory)。
具体的操作如下所示:

在这里插入图片描述
在每一个进程开始创建的时候,都会分配一段虚拟地址,然后通过虚拟地址和物理地址的映射来获取真实数据,这样进程就不会直接接触到物理地址,甚至不知道自己调用的哪块物理地址的数据。

目前,大多数操作系统都使用了虚拟内存,如 Windows 系统的虚拟内存、 Linux 系统的交换空间等等。Windows的虚拟内存(pagefile.sys)是磁盘空间的一部分。

在 32 位的系统上,虚拟地址空间大小是 2^32bit=4G。在64位系统上,最大虚拟地址空间大小是多少?是不是 2^64bit=1024*1014TB=1024PB=16EB?
实际上没有用到64位,因为用不到这么大的空间,而且会造成很大的系统开销。Linux一般用低48位来表示虚拟地址空间,也就是2^48bit=256T。

cat/proc/cpuinfo

address sizes : 40 bits physical, 48 bits virtual
实际的物理内存可能远远小于虚拟内存的大小。

总结:引入虚拟内存,可以提供更大的地址空间,并且地址空间是连续的,使得程序编写、链接更加简单。并且可以对物理内存进行隔离,不同的进程操作互不影响。还可以通过把同一块物理内存映射到不同的虚拟地址空间实现内存共享。

用户空间和内核空间

为了避免用户进程直接操作内核,保证内核安全,操作系统将虚拟内存划分为两部 分,一部分是内核空间(Kernel-space)/ˈkɜːnl/,一部分是用户空间(User-space)。
在这里插入图片描述
内核是操作系统的核心,独立于普通的应用程序,可以访问受保护的内存空间,也有访问底层硬件设备的权限。

内核空间中存放的是内核代码和数据,而进程的用户空间中存放的是用户程序的代码和数据。不管是内核空间还是用户空间,它们都处于虚拟空间中,都是对物理地址的映射。

在Linux系统中, 内核进程和用户进程所占的虚拟内存比例是1:3。

在这里插入图片描述
当进程运行在内核空间时就处于内核态,而进程运行在用户空间时则处于用户态。

进程在内核空间以执行任意命令,调用系统的一切资源;在用户空间只能执行简单
的运算,不能直接调用系统资源,必须通过系统接口(又称 systemcall),才能向内核
发出指令。

top命令:
在这里插入图片描述
us代表CPU消耗在 User space 的时间百分比;
sy代表CPU消耗在 Kernel space 的时间百分比。

进程切换(上下文切换)

多任务操作系统是怎么实现运行远大于CPU数量的任务个数的?当然,这些任务实际上并不是真的在同时运行,而是因为系统通过时间片分片算法,在很短的时间内,将CPU 轮流分配给它们,造成多任务同时运行的错觉。

在这里插入图片描述
为了控制进程的执行,内核必须有能力挂起正在CPU上运行的进程,并恢复以前挂起的某个进程的执行。这种行为被称为进程切换。

什么叫上下文?
在每个任务运行前,CPU 都需要知道任务从哪里加载、又从哪里开始运行,也就是说,需要系统事先帮它设置好 CPU 寄存器和程序计数器(ProgramCounter),这个叫做CPU的上下文。

而这些保存下来的上下文,会存储在系统内核中,并在任务重新调度执行时再次加载进来。这样就能保证任务原来的状态不受影响,让任务看起来还是连续运行。在切换上下文的时候,需要完成一系列的工作,这是一个很消耗资源的操作。

进程的阻塞

正在运行的进程由于提出系统服务请求(如I/O 操作),但因为某种原因未得到操作系统的立即响应,该进程只能把自己变成阻塞状态,等待相应的事件出现后才被唤醒。进程在阻塞状态不占用CPU资源。

文件描述符 FD

Linux系统将所有设备都当作文件来处理,而Linux用文件描述符来标识每个文件对象。
文件描述符(FileDescriptor)是内核为了高效管理已被打开的文件所创建的索引,用于指向被打开的文件,所有执行 I/O操作的系统调用都通过文件描述符;文件描述符是一个简单的非负整数,用以表明每个被进程打开的文件。
Linux系统里面有三个标准文件描述符。
0:标准输入(键盘);1:标准输出(显示器);2:标准错误输出(显示器)。

传统 I/O 数据拷贝

以读操作为例:
当应用程序执行 read 系统调用读取文件描述符(FD)的时候,如果这块数据已经存在于用户进程的页内存中,就直接从内存中读取数据。如果数据不存在,则先将数据从磁盘加载数据到内核缓冲区中,再从内核缓冲区拷贝到用户进程的页内存中。(两次拷贝,两次user和kernel的上下文切换)。

在这里插入图片描述
I/O的阻塞到底阻塞在哪里?

Blocking I/O

当使用read或write对某个文件描述符进行过读写时,如果当前FD不可读,系统就不会对其他的操作做出响应。从设备复制数据到内核缓冲区是阻塞的,从内核缓冲区拷贝到用户空间,也是阻塞的,直到 copycomplete,内核返回结果,用户进程才解除block的状态。
在这里插入图片描述
为了解决阻塞的问题,我们有几个思路。

1、在服务端创建多个线程或者使用线程池,但是在高并发的情况下需要的线程会很多,系统无法承受,而且创建和释放线程都需要消耗资源。
2、由请求方定期轮询,在数据准备完毕后再从内核缓存缓冲区复制数据到用户空间(非阻塞式I/O),这种方式会存在一定的延迟。
能不能用一个线程处理多个客户端请求?

I/O 多路复用(I/O Multiplexing)

I/O指的是网络I/O。
多路指的是多个TCP连接(Socket或Channel)。
复用指的是复用一个或多个线程。
它的基本原理就是不再由应用程序自己监视连接,而是由内核替应用程序监视文件描述符。

客户端在操作的时候,会产生具有不同事件类型的socket。在服务端,I/O 多路复用程序(I/OMultiplexingModule)会把消息放入队列中,然后通过文件事件分派器(File event Dispatcher),转发到不同的事件处理器中。

在这里插入图片描述
多路复用有很多的实现,以select为例,当用户进程调用了多路复用器,进程会被阻塞。内核会监视多路复用器负责的所有socket,当任何一个socket的数据准备好了,多路复用器就会返回。这时候用户进程再调用read操作,把数据从内核缓冲区拷贝到用户空间。

在这里插入图片描述
所以,I/O 多路复用的特点是通过一种机制一个进程能同时等待多个文件描述符,
而这些文件描述符(套接字描述符)其中的任意一个进入读就绪(readable)状态, select()函数就可以返回。
Redis 的多路复用, 提供了 select, epoll, evport, kqueue 几种选择,在编译的时
候来选择一种。源码ae.c
在这里插入图片描述
evport 是Solaris系统内核提供支持的;
epoll 是LINUX系统内核提供支持的;
kqueue是Mac 系统提供支持的;
select是POSIX提供的,一般的操作系统都有支撑(保底方案);
源码ae_epoll.c、ae_select.c、ae_kqueue.c、ae_evport.c

内存回收

Reids所有的数据都是存储在内存中的,在某些情况下需要对占用的内存空间进行回收。内存回收主要分为两类,一类是key过期,一类是内存使用达到上限(max_memory)触发内存淘汰。

过期策略

要实现key过期,我们有几种思路。

定时过期(主动淘汰)

每个设置过期时间的key都需要创建一个定时器,到过期时间就会立即清除。该策略可以立即清除过期的数据,对内存很友好;但是会占用大量的CPU资源去处理过期的数据,从而影响缓存的响应时间和吞吐量。

惰性过期(被动淘汰)

只有当访问一个key时,才会判断该key是否已过期,过期则清除。该策略可以最大化地节省CPU资源,却对内存非常不友好。极端情况可能出现大量的过期key没有再次被访问,从而不会被清除,占用大量内存。

例如 String,在getCommand里面会调用expireIfNeeded

server.c expireIfNeeded(redisDb *db, robj *key)

第二种情况,每次写入key时,发现内存不够,调用activeExpireCycle释放一部分
内存。
expire.c activeExpireCycle(int type)

定期过期

源码:server.h
在这里插入图片描述
每隔一定的时间,会扫描一定数量的数据库的expires字典中一定数量的key,并清除其中已过期的key。该策略是前两者的一个折中方案。通过调整定时扫描的时间间隔和每次扫描的限定耗时,可以在不同情况下使得CPU和内存资源达到最优的平衡效果。

Redis中同时使用了惰性过期和定期过期两种过期策略。
问题:如果都不过期,Redis内存满了怎么办?

淘汰策略

Redis的内存淘汰策略,是指当内存使用达到最大内存极限时,需要使用淘汰算法来决定清理掉哪些数据,以保证新数据的存入。

最大内存设置

redis.conf参数配置:
#maxmemory
如果不设置maxmemory 或者设置为0,64位系统不限制内存,32位系统最多使
用3GB内存。
动态修改:
redis>config set maxmemory 2GB
到达最大内存以后怎么办?

淘汰策略

https://redis.io/topics/lru-cache

redis.conf
#maxmemory-policy noeviction
在这里插入图片描述
先从算法来看:
LRU,Least Recently Used:最近最少使用。判断最近被使用的时间,目前最远的数据优先被淘汰。

LFU,Least Frequently Used,最不常用,4.0版本新增。
random,随机删除。

在这里插入图片描述
如果没有符合前提条件的 key 被淘汰,那么 volatile-lru、volatile-random 、volatile-ttl 相当于 noeviction(不做内存回收)。

动态修改淘汰策略:

redis>config set maxmemory-policy volatile-lru

建议使用 volatile-lru,在保证正常服务的情况下,优先删除最近最少使用的key。

持久化机制

https://redis.io/topics/persistence
Redis速度快,很大一部分原因是因为它所有的数据都存储在内存中。如果断电或者宕机,都会导致内存中的数据丢失。为了实现重启后数据不丢失,Redis提供了两种持久化的方案,一种是RDB快照(RedisDataBase),一种是AOF(AppendOnlyFile)。

RDB

RDB 是 Redis 默认的持久化方案。当满足一定条件的时候,会把当前内存中的数据写入磁盘,生成一个快照文件dump.rdb。Redis 重启会通过加载dump.rdb文件恢复数据。
什么时候写入rdb文件?

自动触发

a)配置规则触发。
redis.conf, SNAPSHOTTING,其中定义了触发把数据保存到磁盘的触发频率。
如果不需要RDB方案,注释save或者配置成空字符串""。

在这里插入图片描述

注意上面的配置是不冲突的,只要满足任意一个都会触发。
RDB文件位置和目录:
在这里插入图片描述
在这里插入图片描述
问题:为什么停止Redis服务的时候没有save,重启数据还在?

RDB还有两种触发方式:
b)shutdown触发,保证服务器正常关闭。
c)flushall,RDB文件是空的,没什么意义(删掉dump.rdb演示一下)。

手动触发

如果我们需要重启服务或者迁移数据,这个时候就需要手动触RDB快照保存。Redis
提供了两条命令:
a)save
save在生成快照的时候会阻塞当前Redis服务器, Redis不能处理其他命令。如果
内存中的数据比较多,会造成Redis长时间的阻塞。生产环境不建议使用这个命令。
为了解决这个问题,Redis提供了第二种方式
b)bgsave
执行bgsave时,Redis会在后台异步进行快照操作,快照同时还可以响应客户端请
求。
具体操作是Redis进程执行fork操作创建子进程(copy-on-write),RDB持久化过程由子进程负责,完成后自动结束。它不会记录fork之后后续的命令。阻塞只发生在fork阶段,一般时间很短。
用lastsave命令可以查看最近一次成功生成快照的时间。

未完
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值