前言

关于Redis的知识点总结了一个思维导图分享给大家:

简介

Redis是一种面向“key-value”类型数据的分布式NoSQL数据库系统,支持五种数据类型格式:**
String,hash,list,set ,socket set **。

字符串(String)

Redis中的字符串是一个字节序列。 常用操作命令有get、set等。

列表(list)

Redis列表知识字符串列表,按插入顺序排序。您可以向Redis列表的头部或尾部添加元素,也可以用作队列。

散列/哈希(hash)

Redis散列/哈希是键值对的集合。Redis散列/哈希是字符串字段和字符串值之间的映射,但字段值只能是字符串,不支持其他类型。因此,他们用于表示对象。应用场景:存储用户的基本信息等。

无序集合类型(set)

redis的set是String类型的无序集合,set元素最大可以包含2的32次方-1个元素。利用set集合类型,我们可以快速取出n个key之间的并集、交集、差集等.应用场景:取出两个QQ号中的共同的好友数。

有序集合类型(sorted set)

和set一样,sorted set也是string类型元素的集合,是一个没有重复元素的字符串集合。因为元素是有序的,所以使用有序集合你可以以非常快的速度(O(log(N)))添加,删除和更新元素,它也很擅长排序。应用场景:获取所有用户投票数最高的前10名。

Redis持久化方式

Redis由于支持非常丰富的内存数据结构类型,如何把这些复杂的内存组织方式持久化到磁盘上是一个难题,所以Redis的持久化方式与传统数据库的方式有比较多的差别,Redis一共支持四种持久化方式,分别是:

RDB定时快照方式(snapshot)

RDB将数据库的快照(snapshot)以二进制的方式保存到磁盘中。

AOF基于语句追加文件的方式

则以协议文本的方式,将所有对数据库进行过写入的命令(及其参数)记录到AOF文件,以此达到记录数据库状态的目的。

虚拟内存(vm)(被废弃)

Diskstore方式(被废弃)

在设计思路上,前两种是基于全部数据都在内存中,即小数据量下提供磁盘落地功能,而后两种方式则是作者在尝试存储数据超过物理内存时,即大数据量的数据存储,截止到本文,后两种持久化方式仍然是在实验阶段,并且vm方式基本已经被作者放弃,所以实际能在生产环境用的只有前两种,换句话说Redis目前还只能作为小数据量存储(全部数据能够加载在内存中),海量数据存储方面并不是Redis所擅长的领域。

很多人开始会想象两者是互相结合的,即dump出一个snapshot到RDB文件,然后在此基础上记录变化日志到AOF文件。实际上两者毫无关系,完全独立运行,因为作者认为简单才不会出错。如果使用了AOF,重启时只会从AOF文件载入数据,不会再管RDB文件。

正确关闭服务器:redis-cli shutdown或者 kill,都会graceful shutdown,保证写RDB文件以及将AOF文件fsync到磁盘,不会丢失数据。如果是粗暴的Ctrl+C,或者kill-9就可能丢失。

下面分别介绍下这几种持久化方式:

RDB定时快照方式(snapshot)

RDB持久化的触发分为手动触发和自动触发两种。

1、手动触发

通过redis的save命令和bgsave命令,都可以生成RDB文件。

(1)save保存数据到磁盘的方式:

Redis Save命令执行一个同步保存操作,将当前Redis实例的所有数据快照(snapshot)以RDB文件的形式保存到硬盘。

语法:redis 127.0.0.1:6379> SAVE
返回值:保存成功时返回 OK 。

(2)BGSAVE保存数据到磁盘的方式:

BGSAVE命令执行之后立即返回OK ,然后 Redis fork 出一个新子进程,原来的Redis进程(父进程)继续处理客户端请求,而子进程则负责将数据保存到磁盘,然后退出。

客户端可以通过LASTSAVE命令查看相关信息,判断BGSAVE命令是否执行成功。

bgsave命令执行过程中,只有fork子进程时会阻塞服务器,而对于save命令,整个过程都会阻塞服务器,因此save已基本被废弃,线上环境要杜绝save的使用;后文中也将只介绍bgsave命令。此外,在自动触发RDB持久化时,Redis也会选择bgsave而不是save来进行持久化;下面介绍自动触发RDB持久化的条件。

2、自动触发

自动触发最常见的情况是在配置文件中通过save m n,指定当m秒内发生n次变化时,会触发bgsave。

例如,查看redis的默认配置文件redis.conf,可以看到如下配置信息:

其中save 900 1的含义是:当时间到900秒时,如果redis数据发生了至少1次变化,则执行bgsave;

当三个save条件满足任意一个时,都会引起bgsave的调用。

3、save m n的实现原理

save m n的含义:当时间到m秒时,如果redis数据发生了至少n次变化,则执行bgsave;

是将数据先存储在内存,然后当数据累计达到某些设定的伐值的时候,就会触发一次DUMP操作,将变化的数据一次性写入数据文件(RDB文件)。

RDB实际是在Redis内部一个定时器事件,每隔固定时间去检查当前数据发生的改变次数与时间是否满足配置的持久化触发的条件,如果满足则通过操作系统fork调用来创建出一个子进程,这个子进程默认会与父进程共享相同的地址空间,这时就可以通过子进程来遍历整个内存来进行存储操作,而主进程则仍然可以提供服务,当有写入时由操作系统按照内存页(page)为单位来进行copy-on-write保证父子进程之间不会互相影响。

**具体实现:是通过serverCron函数、dirty计数器、和lastsave时间戳来实现的。 **

redis.c文件的周期性函数serverCron:

/* This is our timer interrupt, called server.hz times per second.* Here is where we do a number of things that need to be done asynchronously.* For instance:** - Active expired keys collection (it is also performed in a lazy way on*   lookup).* - Software watchdog.* - Update some statistic.* - Incremental rehashing of the DBs hash tables.* - Triggering BGSAVE / AOF rewrite, and handling of terminated children.* - Clients timeout of different kinds.* - Replication reconnection.* - Many more...** Everything directly called here will be called server.hz times per second,* so in order to throttle execution of things we want to do less frequently* a macro is used: run_with_period(milliseconds) { .... }*/int serverCron(struct aeEventLoop *eventLoop, long long id, void *clientData) {

serverCron是Redis服务器的周期性操作函数,默认每隔100ms执行一次;该函数对服务器的状态进行维护,其中一项工作就是检查 save m n 配置的条件是否满足,如果满足就执行bgsave。

dirty计数器是Redis服务器维持的一个状态,记录了上一次执行bgsave/save命令后,服务器状态进行了多少次修改(包括增删改);而当save/bgsave执行完成后,会将dirty重新置为0。

例如,如果Redis执行了set mykey helloworld,则dirty值会+1;如果执行了sadd myset v1 v2 v3,则dirty值会+3;注意dirty记录的是服务器进行了多少次修改,而不是客户端执行了多少修改数据的命令。

lastsave时间戳也是Redis服务器维持的一个状态,记录的是上一次成功执行save/bgsave的时间。

save m n的原理如下:

1)按定时执行

每隔100ms,执行serverCron函数;

2)遍历所有save m n配置

在serverCron函数中,遍历save m n配置的保存条件,只要有一个条件满足,就进行bgsave。

例如:

当三个save条件满足任意一个时,都会引起bgsave的调用。

3)对于每一个save m n条件,只有下面两条同时满足时才算满足:

(1)当前时间 - lastsave > m

(2)dirty >= n

4)save m n 执行日志

下图是save m n触发bgsave执行时,服务器打印日志的情况:

其他自动触发机制

除了save m n 以外,还有一些其他情况会触发bgsave:

在主从复制场景下,如果从节点执行全量复制操作,则主节点会执行bgsave命令,并将rdb文件发送给从节点执行shutdown命令时,自动执行rdb持久化,如下图所示:

4、bgsave实现流程:

1)Redis父进程首先判断

当前是否在执行save,或bgsave/bgrewriteaof(后面会详细介绍该命令)的子进程,如果在执行则bgsave命令直接返回。bgsave/bgrewriteaof的子进程不能同时执行,主要是基于性能方面的考虑:两个并发的子进程同时执行大量的磁盘写操作,可能引起严重的性能问题。

2)FORK子进程

RDB写入时,会连内存一起Fork出一个新进程(子进程默认会与父进程共享相同的地址空间),遍历新进程内存中的数据写文件,这样就解决了些Snapshot过程中又有新的写入请求进来的问题。这个过程中父进程是阻塞的,Redis不能执行来自客户端的任何命令 。父进程fork后,bgsave命令返回”Background saving started”信息并不再阻塞父进程,并可以响应其他命令

3)子进程创建RDB文件

RDB会先写到临时文件,完了再Rename成,这样外部程序对RDB文件的备份和传输过程是安全的。而且即使写新快照的过程中Server被强制关掉了,旧的RDB文件还在。

3)可配置是否进行压缩:

压缩方法是字符串的LZF算法,以及将string形式的数字变回int形式存储。

4)动态所有停止RDB保存规则的方法

redis-cli config set save “”

该持久化的主要缺点是定时快照只是代表一段时间内的内存映像,所以系统重启会丢失上次快照与重启之间所有的数据。

5. RDB文件

RDB文件是经过压缩的二进制文件,下面介绍关于RDB文件的一些细节。

存储路径

RDB文件的存储路径既可以在启动前配置,也可以通过命令动态设定。

配置:dir配置指定目录,dbfilename指定文件名。默认是Redis根目录下的dump.rdb文件。

动态设定:Redis启动后也可以动态修改RDB存储路径,在磁盘损害或空间不足时非常有用;执行命令为config set dir{newdir}和config set dbfilename{newFileName}

1) REDIS

常量,保存着”REDIS”5个字符。

2) db_version

RDB文件的版本号,注意不是Redis的版本号。

3) SELECTDB 0 pairs

表示一个完整的数据库(0号数据库),同理SELECTDB 3 pairs表示完整的3号数据库;只有当数据库中有键值对时,RDB文件中才会有该数据库的信息(上图所示的Redis中只有0号和3号数据库有键值对);如果Redis中所有的数据库都没有键值对,则这一部分直接省略。其中:SELECTDB是一个常量,代表后面跟着的是数据库号码;0和3是数据库号码;pairs则存储了具体的键值对信息,包括key、value值,及其数据类型、内部编码、过期时间、压缩信息等等。

4) EOF

常量,标志RDB文件正文内容结束。

5) check_sum

前面所有内容的校验和;Redis在载入RBD文件时,会计算前面的校验和并与check_sum值比较,判断文件是否损坏。

Redis默认采用LZF算法对RDB文件进行压缩。虽然压缩耗时,但是可以大大减小RDB文件的体积,因此压缩默认开启;可以通过命令关闭:

config set rdbcompression no

需要注意的是,RDB文件的压缩并不是针对整个文件进行的,而是对数据库中的字符串进行的,且只有在字符串达到一定长度(20字节)时才会进行。

6. 启动时加载

RDB文件的载入工作是在服务器启动时自动执行的,并没有专门的命令。但是由于AOF的优先级更高,因此当AOF开启时,Redis会优先载入AOF文件来恢复数据;只有当AOF关闭时,才会在Redis服务器启动时检测RDB文件,并自动载入。服务器载入RDB文件期间处于阻塞状态,直到载入完成为止。
Redis载入RDB文件时,会对RDB文件进行校验,如果文件损坏,则日志中会打印错误,Redis启动失败。

7. RDB常用配置总结

下面是RDB常用的配置项,以及默认值;前面介绍过的这里不再详细介绍。

默认是打开RDB持久化

save m n:bgsave自动触发的条件;如果没有save m n配置,相当于自动的RDB持久化关闭,不过此时仍可以通过其他方式触发
stop-writes-on-bgsave-error yes:

当bgsave出现错误时,Redis是否停止执行写命令;设置为yes,则当硬盘出现问题时,可以及时发现,避免数据的大量丢失;设置为no,则Redis无视bgsave的错误继续执行写命令,当对Redis服务器的系统(尤其是硬盘)使用了监控时,该选项考虑设置为no

rdbcompression yes:是否开启RDB文件压缩

rdbchecksum yes:是否开启RDB文件的校验,在写入文件和读取文件时都起作用;关闭checksum在写入文件和启动文件时大约能带来10%的性能提升,但是数据损坏时无法发现

dbfilename dump.rdb:RDB文件名

dir ./:RDB文件和AOF文件所在目录

8、关闭RDB:

1)*关闭rdb的命令:config set save “”

执行命令后,无需重启服务,即可生效。

2)修改Save配置注释掉,并打开save “”

#Save 900 1

#Save 300 10

#Save 60 10000

Save “”

修改完成后,重启Redis服务即可。

AOF基于语句追加方式

AOF方式实际类似mysql的基于语句的binlog方式,即每条会使Redis内存数据发生改变的命令都会追加到一个log文件中,也就是说这个log文件就是Redis的持久化数据。

AOF持久化的触发分为手动触发和自动触发两种。

1、手动触发

使用bgrewriteaof命令:Redis主进程fork子进程来执行AOF重写,这个子进程创建新的AOF文件来存储重写结果,防止影响旧文件。因为fork采用了写时复制机制,子进程不能访问在其被创建出来之后产生的新数据。Redis使用“AOF重写缓冲区”保存这部分新数据,最后父进程将AOF重写缓冲区的数据写入新的AOF文件中然后使用新AOF文件替换老文件。

127.0.0.1:6379> bgrewriteoaf
OK

2、自动触发

和RDB一样,配置在redis.conf文件里:

appendonly:是否打开AOF持久化功能,appendonly默认是 no, 改成appendonly yes。

appendfilename:AOF文件名称

appendfsync:同步频率

auto-aof-rewrite-min-size:如果文件大小小于此值不会触发AOF,默认64MB

auto-aof-rewrite-percentage:Redis记录最近的一次AOF操作的文件大小,如果当前AOF文件大小增长超过这个百分比则触发一次重写,默认100

启用AOF:appendonly yes

关闭aof:

命令行关闭:关闭aof的命令:config set appendfsync no

配置文件:将appendonly设置为no,默认是 appendonly no )

3、AOF 工作原理

是将数据也是先存在内存,但是在存储的时候会使用调用fsync来完成对本次写操作的日志记录,这个日志文件其实是一个基于Redis网络交互协议的文本文件。AOF调用fsync也不是说全部都是无阻塞的,在某些系统上可能出现fsync阻塞进程的情况,对于这种情况可以通过配置修改,但默认情况不要修改。AOF最关键的配置就是关于调用fsync追加日志文件的平率,有两种预设频率,always每次记录进来都添加,everysecond 每秒添加一次。

同步命令到AOF文件的整个过程可以分为三个阶段:

1、命令写入:Redis将执行完的命令、命令的参数、命令的参数个数等信息发送到AOF程序中。

2、追加AOF缓存:AOF程序根据接收到的命令数据, 然后每隔一定的时间(比如每隔一秒)将协议内容写入AOF缓冲区中。

3、文件写入保存:当达到AOF同步条件,fsync函数或者fdatasync函数会被调用,将OS Cache中的数据写入磁盘文件。

随着AOF文件越来越大,需要定期对AOF文件进行重写,达到压缩的目的。

第一阶段:命令写入

当一个 Redis 客户端需要执行命令时, 它通过网络连接, 将协议文本发送给Redis服务器。
比如说, 要执行命令SET KEY VALUE , 客户端将向服务器发送文本"*3\r\n$3\r\nSET\r\n$3\r\nKEY\r\n$5\r\nVALUE\r\n"。

服务器在接到客户端的请求之后, 它会根据协议文本的内容, 选择适当的命令函数, 并将各个参数从字符串文本转换为 Redis 字符串对象(StringObject)。

比如说, 针对上面的 SET 命令例子, Redis 将客户端的命令指针指向实现 SET 命令的 setCommand 函数, 并创建三个 Redis 字符串对象, 分别保存 SET 、 KEY 和 VALUE 三个参数(命令也算作参数)。

每当命令函数成功执行之后, 命令参数都会被传播到 AOF 程序, 以及 REPLICATION 程序(本节不讨论这个,列在这里只是为了完整性的考虑)。

这个执行并传播命令的过程可以用以下伪代码表示:

if (execRedisCommand(cmd, argv, argc) == EXEC_SUCCESS):if aof_is_turn_on():# 传播命令到 AOF 程序propagate_aof(cmd, argv, argc)if replication_is_turn_on():# 传播命令到 REPLICATION 程序propagate_replication(cmd, argv, argc)

第二阶段:追加到AOF缓存

所有的写入命令会追加到aof_buf中

当命令被传播到AOF程序之后, 程序会根据命令以及命令的参数, 将命令从字符串对象转换回原来的协议文本。
比如说,如果AOF程序接受到的三个参数分别保存着SET、KEY和VALUE三个字符串, 那么它将生成协议文本 “*3\r\n$3\r\nSET\r\n$3\r\nKEY\r\n$5\r\nVALUE\r\n” 。

协议文本生成之后,它会被追加到redis.h/redisServer结构的aof_buf末尾。

redisServer结构维持着Redis服务器的状态,aof_buf域则保存着所有等待写入到AOF文件的协议文本:

struct redisServer {// 其他域...sds aof_buf;// 其他域...
};

至此, 追加命令到缓存的步骤执行完毕。综合起来,整个缓存追加过程可以分为以下三步:

1.接受命令、命令的参数、以及参数的个数、所使用的数据库等信息。

2.将命令还原成 Redis 网络通讯协议。

3.将协议文本追加到 aof_buf 末尾。

第三阶段:文件写入和保存

AOF缓冲区根据对应的策略向硬盘做同步操作。每当服务器常规任务函数被执行、 或者事件处理器被执行时,aof.c/flushAppendOnlyFile函数都会被调用, 这个函数执行以下两个工作:系统调用write和fsync说明
WRITE:根据条件,将aof_buf中的缓存写入到AOF文件,即系统调用write写入os cache。

SAVE:根据条件,调用fsync或fdatasync函数,将AOF文件保存到磁盘中。

两个步骤都需要根据一定的条件来执行,而这些条件由AOF所使用的保存模式来决定, 以下小节就来介绍AOF所使用的三种保存模式,以及在这些模式下,步骤WRITE和SAVE的调用条件。

第四阶段:AOF文件重写(Rewrite)

AOF是redis的一种持久化方式,用来记录所有的写操作,但是随着时间增加,aof文件会越来越大,所以需要进行重写,将内存中的数据重新以命令的方式写入aof文件。

在重写的过程中,由于redis还会有新的写入,为了避免数据丢失,会开辟一块内存用于存放重写期间产生的写入操作,等到重写完毕后会将这块内存中的操作再追加到aof文件中。

为什么要重写

redis中的数据其实有限的,很多数据可能会自动过期,可能会被用户删除,可能会被redis用缓存清除的算法清理掉。redis中的数据会不断淘汰掉旧的,就一部分常用的数据会被自动保留在redis内存中,所以可能很多之前的已经被清理掉的数据, 随着写操作的不断增加, 对应的写日志还停留在AOF中,AOF日志文件就一个,AOF文件会越来越大。

例如你递增一个计数器100次,那么最终结果就是数据集里的计数器的值为最终的递增结果,但是AOF文件里却会把这100次操作完整的记录下来。而事实上要恢复这个记录,只需要1个命令就行了,也就是说AOF文件里那100条命令其实可以精简为1条。所以Redis支持这样一个功能:在不中断服务的情况下在后台重建AOF文件。

问题1:AOF文件会变得越来越大

AOF文件通过同步Redis服务器所执行的命令,从而实现了数据库状态的记录。

但是,这种同步方式会造成一个问题: 随着运行时间的流逝,AOF文件会变得越来越大。

举个例子,如果服务器执行了以下命令:

RPUSH list 1 2 3 4 // [1, 2, 3, 4]

RPOP list // [1, 2, 3]

LPOP list // [2, 3]

LPUSH list 1 // [1, 2, 3]

那么光是记录list键的状态,AOF文件就需要保存四条命令。

问题2:AOF文件的体积就会急速膨胀

另一方面,有些被频繁操作的键,对它们所调用的命令可能有成百上千、甚至上万条, 如果这样被频繁操作的键有很多的话,AOF文件的体积就会急速膨胀。对Redis、甚至整个系统的造成影响。

为了解决以上的问题:Redis需要对AOF文件自动在后台每隔一定时间做rewrite(重写)操作, 创建一个新的AOF文件来代替原有的AOF文件,新AOF文件和原有AOF文件保存的数据库状态完全一样,但新AOF文件的体积小于等于原有AOF文件的体积。比如日志里已经存放了针对100w数据的写日志了; redis内存只剩下10万; 基于内存中当前的10万数据构建一套最新的日志,到AOF中; 覆盖之前的老日志; 确保AOF日志文件不会过大,保持跟redis内存数据量一致。

AOF的rewrite实现:

rewrite操作对于老日志文件中对于Key的多次操作,只保留最终的值的那次操作记录到日志文件中,从而缩小日志文件的大小。
考虑这样一个情况, 如果服务器对键 list 执行了以下四条命令:

RPUSH list 1 2 3 4 // [1, 2, 3, 4]

RPOP list // [1, 2, 3]

LPOP list // [2, 3]

LPUSH list 1 // [1, 2, 3]

那么当前列表键 list 在数据库中的值就为 [1, 2, 3] 。

如果我们要保存这个列表的当前状态, 并且尽量减少所使用的命令数, 那么最简单的方式不是去 AOF 文件上分析前面执行的四条命令, 而是直接读取 list 键在数据库的当前值, 然后用一条 RPUSH 1 2 3 命令来代替前面的四条命令。

AOF的rewrite流程:

作为一种辅佐性的维护手段,Redis不希望AOF重写造成服务器无法处理请求, 所以Redis决定将AOF重写程序放到(后台)子进程里执行,这样处理的最大好处是:

1.子进程进行AOF重写期间,主进程可以继续处理命令请求。

2.子进程带有主进程的数据副本,使用子进程而不是线程,可以在避免锁的情况下,保证数据的安全性。

不过,使用子进程也有一个问题需要解决:因为子进程在进行AOF重写期间,主进程还需要继续处理命令,而新的命令可能对现有的数据进行修改,这会让当前数据库的数据和重写后的AOF文件中的数据不一致。

为了解决这个问题,Redis增加了一个AOF重写缓冲区,这个缓存在fork出子进程之后开始启用,Redis主进程在接到新的写命令之后,除了会将这个写命令的协议内容追加到现有的AOF文件之外,还会追加到这个AOF重写缓冲区。

AOF的重写rewrite流程如下:即是BGREWRITEAOF命令的工作原理

(1)触发重写:超过AOF的阈值,redis fork一个子进程,进行rewrite操作。子进程基于当前内存中的数据,构建日志,开始往一个新的临时AOF文件中写入日志:temp-rewriteaof-pid.aof,pid为子进程pid。

(2)开启 AOF 重写缓冲区:同时开启AOF重写缓冲区,保存这段时间内新增的写指令。

(3)主进程继续写旧文件:之前的AOF操作保持,继续正常工:redis主进程,接收到client新的写操作之后,写入AOF重写缓冲区,同时新的命令日志也继续写入旧的AOF文件appendonly.aof。

(4)AOF重写缓冲区的指令追加新文件: 重写子进程写入temp-rewriteaof-pid.aof文件结束,redis主进程将将 AOF重写缓冲区的指令追加至新AOF文件 temp-rewriteaof-pid.aof末尾。

(5)文件替换:最后用temp-rewriteaof-pid.aof替换旧AOF文件appendonly.aof。

在一般情况下在整个AOF后台重写过程中,只有最后的AOF重写缓冲区的指令追加新文件 和改名替换操作会造成主进程阻塞,在其他时候,AOF后台重写都不会对主进程造成阻塞,这将AOF重写对性能造成的影响降到了最低。

AOF的rewrite触发条件:

redis2.4之后,AOF重写可以由用户通过调用BGREWRITEAOF手动触发。也可以服务器在AOF功能开启的情况下自动进行, 源码里面会维持以下三个变量:

1.记录当前AOF文件大小的变量aof_current_size。

2.记录最后一次AOF重写之后,AOF文件大小的变量aof_rewrite_base_size。

3.增长百分比变量aof_rewrite_perc。

每次当serverCron函数执行时, 它都会检查以下条件是否全部满足,如果是的话,就会触发自动的AOF重写:

1.没有 BGSAVE 命令在进行。

2.没有 BGREWRITEAOF 在进行。

3.当前AOF文件大小大于server.aof_rewrite_min_size(默认值为 1 MB)。

4.当前AOF文件大小和最后一次AOF重写后的大小之间的比率大于等于指定的增长百分比。

在redis.conf中可以配置rewrite策略,两个条件需要同时满足。

auto-aof-rewrite-percentage 100 :当前写入日志文件的大小占到初始日志文件大小的某个百分比时触发Rewrite。Redis会记住自从上一次重写后AOF文件的大小(如果自Redis启动后还没重写过,则记住启动时使用的AOF文件的大小)。如果当前的文件大小比起记住的那个大小超过指定的百分比,则会触发重写。
auto-aof-rewrite-min-size 64mb :本次Rewrite最小的写入数据量。只有大于这个值文件才会重写,以防文件很小,但是已经达到百分比的情况。

默认情况下,增长百分比为100% , 也即是说,如果前面三个条件都已经满足,并且当前AOF文件大小比最后一次AOF重写时的大小要大一倍的话,那么触发自动AOF重写。

比如说上一次AOF rewrite重写之后,是128mb。然后就会接着128mb继续写AOF的日志,如果发现增长的比例,超过了之前的100%,256mb,就可能会去触发一次rewrite但是此时还要去跟min-size,64mb去比较,256mb>64mb,才会去触发rewrite。

bgrewriteaof机制,在一个子进程中进行aof的重写,从而不阻塞主进程对其余命令的处理,同时解决了aof文件过大问题。

no-appendfsync-on-rewrite参数

由于重写子进程和主进程写aof文件的操作,两者都会操作磁盘,而AOF重写往往会涉及大量磁盘操作,这样就可能造成主进程在写aof文件的时候出现阻塞的情形: 主进程 fsync()/write() 操作被阻塞, 最终导致 Redis 主进程阻塞了。

解决办法:

no-appendfsync-on-rewrite yes设置为yes表示重写期间对新写操作不fsync,暂时存在AOF缓冲区中,等重写完成后再写入,默认为no 。

如果该参数设置为no,是最安全的方式,不会丢失数据,但是要忍受阻塞的问题。如果设置为yes,这就相当于将appendfsync设置为no,这说明并没有执行磁盘操作,只是写入了缓冲区,因此这样并不会造成阻塞(因为没有竞争磁盘),但是如果这个时候redis挂掉,就会丢失数据。丢失多少数据呢?在linux的操作系统的默认设置下,最多会丢失30s的数据。

因此,如果应用系统无法忍受延迟,而可以容忍少量的数据丢失,则设置为yes。如果应用系统无法忍受数据丢失,则设置为no。

4、AOF缓冲区同步文件策略

Redis目前支持三种AOF保存模式,Redis的配置appendfsync 中存在三种同步方式,它们分别是:

1)AOF_FSYNC_NO :不保存。

2)AOF_FSYNC_EVERYSEC :每一秒钟保存一次、该策略为AOF的缺省策略。

3)AOF_FSYNC_ALWAYS :每执行一个命令保存一次

对应redis配置:

appendfsync no:写入aof文件,不等待磁盘同步。

appendfsync always:总是写入aof文件,并完成磁盘同步

appendfsync everysec:每一秒写入aof文件,并完成磁盘同步

从持久化角度讲,always是最安全的。

从效率上讲,no是最快的。而redis默认设置进行了折中,选择了everysec,合情合理。

appendfsync no不保存:

命令写入aof缓冲区后,在写入系统缓冲区直接返回(系统调用write写入os cache),然后有专门线程每秒执行写入磁盘(阻塞,系统调用fsync)后返回。
当设置appendfsync为no的时候,Redis不会主动调用fsync去将AOF日志内容同步到磁盘,所以这一切就完全依赖于操作系统的调试了。对大多数Linux操作系统,是每30秒进行一次fsync,将缓冲区中的数据写到磁盘上。

在这种模式下, 每次调用flushAppendOnlyFile函数,write(写入os cache)都会被执行,但save(fsync)会被略过。

在这种模式下, SAVE只会在以下任意一种情况中被执行:

1.Redis被关闭

2.AOF功能被关闭

3.系统的写缓存被刷新(可能是缓存已经被写满,或者定期保存操作被执行)

这三种情况下的SAVE操作都会引起Redis主进程阻塞。

appendfsync everysec 每一秒钟保存一次:

在这种模式中,SAVE原则上每隔一秒钟就会执行一次,因为SAVE操作是由后台子线程调用的,所以它不会引起服务器主进程阻塞。注意,在上一句的说明里面使用了词语“原则上”,在实际运行中,Redis主进程在这种模式下对fsync/fdatasync的调用并不是每秒一次,它和调用flushAppendOnlyFile函数时Redis所处的状态有关。

每当flushAppendOnlyFile函数被调用时, 可能会出现以下四种情况:

子线程正在执行SAVE,并且:

1、这个SAVE的执行时间未超过2秒,那么Redis主进程就会直接返回,并不执行WRITE或新的SAVE。

2、这个SAVE已经执行超过2秒,那么程序执行WRITE,但不执行新的SAVE。

注意:Redis主进程就会阻塞,因为这时 WRITE的写入必须等待子线程先完成(旧的)SAVE,因此这里WRITE会比平时阻塞更长时间。

子线程没有在执行SAVE,并且:

1、上次成功执行SAVE距今不超过1秒,那么程序执行WRITE,但不执行SAVE。

2、上次成功执行SAVE距今已经超过1秒,那么程序执行WRITE和SAVE。

可以用流程图表示这四种情况:

根据以上说明可以知道,在“每一秒钟保存一次”模式下,如果在情况1中发生故障停机,那么用户最多损失小于2秒内所产生的所有数据。

如果在情况2中发生故障停机,那么用户损失的数据是可以超过2秒的。

Redis官网上所说的,AOF在“每一秒钟保存一次”时发生故障,只丢失1秒钟数据的说法,实际上并不准确。

appendfsync always每执行一个命令保存一次:

在这种模式下,每次执行完一个命令之后,WRITE和SAVE都会被执行。

另外,因为SAVE是由Redis主进程执行的,所以在SAVE执行期间,主进程会被阻塞,不能接受命令请求。

6、Redis的保存模式对性能的影响

对于三种AOF保存模式, 它们对服务器主进程的阻塞情况如下:

1.appendfsync no不保存:写入和保存都由主进程执行,两个操作都会阻塞主进程。

2.appendfsync everysec每一秒钟保存一次:写入操作由主进程执行,阻塞主进程。保存操作由子线程执行,不直接阻塞主进程,但保存操作完成的快慢会影响写入操作的阻塞时长。

3.appendfsync always每执行一个命令保存一次:和模式1一样。

因为阻塞操作会让Redis主进程无法持续处理请求,所以一般说来, 阻塞操作执行得越少、完成得越快,Redis的性能就越好。

模式1的保存操作只会在AOF关闭或Redis关闭时执行,或者由操作系统触发,在一般情况下,这种模式只需要为写入阻塞,因此它的写入性能要比后面两种模式要高。

当然,这种性能的提高是以降低安全性为代价的:在这种模式下,如果运行的中途发生停机,那么丢失数据的数量由操作系统的缓存冲洗策略决定。

模式2在性能方面要优于模式3,并且在通常情况下,这种模式最多丢失不多于2秒的数据,所以它的安全性要高于模式 1,这是一种兼顾性能和安全性的保存方案。

模式3的安全性是最高的,但性能也是最差的,因为服务器必须阻塞直到命令信息被写入并保存到磁盘之后,才能继续处理请求。

综合起来,三种AOF模式的操作特性可以总结如下:

4、Redis AOF日志分析

25852:M 16 Dec 19:05:10.646 * Starting automatic rewriting of AOF on 100% growth:到达触发条件,开始执行AOF
25852:M 16 Dec 19:05:10.765 * Background append only file rewriting started by pid 31619:bgrewriteaof命令,会fork一个子进程(进程号31619)来执行保存;
25852:M 16 Dec 19:08:46.489 * AOF rewrite child asks to stop sending diffs.
31619:C 16 Dec 19:08:46.489 * Parent agreed to stop sending diffs. Finalizing AOF…
31619:C 16 Dec 19:08:46.489 * Concatenating 95.85 MB of AOF diff received from parent.
31619:C 16 Dec 19:08:49.050 * SYNC append only file rewrite performed: fsync将数据落地到磁盘,最后close文件,将临时文件重命名,确保生成的aof文件完全ok,避免出现aof不完整情况,最后输出这条日志;
31619:C 16 Dec 19:08:49.142 * AOF rewrite: 3864 MB of memory used by copy-on-write:fork子进程做AOF重写,将会耗用3864MB内存作copy on write
25852:M 16 Dec 19:08:49.377 * Background AOF rewrite terminated with success
25852:M 16 Dec 19:08:49.379 * Residual parent diff successfully flushed to the rewritten AOF (1.37 MB)
25852:M 16 Dec 19:08:49.379 * Background AOF rewrite finished successfully

两者比较

综上所述,RDB的数据不实时,同时使用两者时服务器重启也只会找AOF文件。那要不要只使用AOF呢?作者建议不要,因为RDB更适合用于备份数据库(AOF在不断变化不好备份),快速重启,而且不会有AOF可能潜在的bug,留着作为一个万一的手段。

1.性能

Snapshot方式的性能是要明显高于AOF方式的,原因有两点:

1)采用二进制方式存储数据,数据文件比较小,加载快速。

2)存储的时候是按照配置中的save策略来存储,每次都是聚合很多数据批量存储,写入的效率很好,而AOF则一般都是工作在实时存储或者准实时模式下。相对来说存储的频率高,效率却偏低。

  1. 对于Redis的服务进程而言,在开始持久化时,它唯一需要做的只是fork出子进程,之后再由子进程完成这些持久化的工作,这样就可以极大的避免服务进程执行IO操作了。

2.数据安全

AOF数据安全性高于Snapshot存储,原因:

Snapshot存储是基于累计批量的思想,也就是说在允许的情况下,累计的数据越多那么写入效率也就越高,但数据的累计是靠时间的积累完成的,那么如果在长时间数据不写入RDB,但Redis又遇到了崩溃,那么没有写入的数据就无法恢复了,但是AOF方式偏偏相反,根据AOF配置的存储频率的策略可以做到最少的数据丢失和较高的数据恢复能力。

RDB优势:

1)备份策略方便:一旦采用该方式,那么你的整个Redis数据库将只包含一个文件,这对于文件备份而言是非常完美的。

比如,你可能打算每个小时归档一次最近24小时的数据,同时还要每天归档一次最近30天的数据。通过这样的备份策略,一旦系统出现灾难性故障,我们可以非常容易的进行恢复。

2)灾难恢复方便:RDB是非常不错的选择。因为我们可以非常轻松的将一个单独的文件压缩后再转移到其它存储介质上。

3)性能最大化:对于Redis的服务进程而言,在开始持久化时,它唯一需要做的只是fork出子进程,之后再由子进程完成这些持久化的工作,这样就可以极大的避免服务进程执行IO操作了。

4)启动效率高:相比于AOF机制,如果数据集很大,RDB的启动效率会更高。

RDB缺点:

1)数据的可靠性不高,没办法做到实时持久化:如果你想保证数据的高可用性,即最大限度的避免数据丢失,那么RDB将不是一个很好的选择。因为系统一旦在定时持久化之前出现宕机现象,此前没有来得及写入磁盘的数据都将丢失。

2)影响性能:由于RDB是通过fork子进程来协助完成数据持久化工作的,因此,如果当数据集较大时,可能会导致整个服务器停止服务几百毫秒,甚至是1秒钟。

3)版本兼容RDB格式问题

AOF的优点:

1)数据安全性

该机制可以带来更高的数据安全性,即数据持久性。Redis中提供了3中同步策略,即每秒同步、每修改同步和不同步。事实上,每秒同步也是异步完成的,其效率也是非常高的,所差的是一旦系统出现宕机现象,那么这一秒钟之内修改的数据将会丢失。而每修改同步,我们可以将其视为同步持久化,即每次发生的数据变化都会被立即记录到磁盘中。可以预见,这种方式在效率上是最低的。

2)数据一致性

由于该机制对日志文件的写入操作采用的是append模式,因此在写入过程中即使出现宕机现象,也不会破坏日志文件中已经存在的内容。然而如果我们本次操作只是写入了一半数据就出现了系统崩溃问题,不用担心,在Redis下一次启动之前,我们可以通过redis-check-aof工具来帮助我们解决数据一致性的问题。

3)如果日志过大,Redis可以自动启用rewrite机制。

即Redis以append模式不断的将修改数据写入到老的磁盘文件中,同时Redis还会创建一个新的文件用于记录此期间有哪些修改命令被执行。因此在进行rewrite切换时可以更好的保证数据安全性。

4)AOF包含一个格式清晰、易于理解的日志文件用于记录所有的修改操作。

事实上,我们也可以通过该文件完成数据的重建。

AOF的缺点:

1)恢复速度慢

对于相同数量的数据集而言,AOF文件通常要大于RDB文件。RDB 在恢复大数据集时的速度比AOF的恢复速度要快。

2)性能低

根据同步策略的不同,AOF在运行效率上往往会慢于RDB。总之,每秒同步策略的效率是比较高的,同步禁用策略的效率和RDB一样高效。

二者选择的标准,就是看系统是愿意牺牲一些性能,换取更高的缓存一致性(aof),还是愿意写操作频繁的时候,不启用备份来换取更高的性能,待手动运行save的时候,再做备份(rdb)。rdb这个就更有些 eventually consistent的意思了。

Redis持久化磁盘IO方式及其带来的问题

1、RDB持久化可能导致系统内存不足导致redis被oomkill

发现Redis在物理内存使用比较多,但还没有超过实际物理内存总容量时就会发生不稳定甚至崩溃的问题。

redis持久化AOF重写和RDB写入都是通过fork产生子进程来操作,AOF重写和RDB写入都是通过fork产生子进程来操作,理论上需要两倍的内存来完成持久化操作,但Linux有写时复制机制(copy-on-write),父子进程会共享相同的物理内存页(只有有写入的脏页会被复制), 当父进程处理写请求时会把要修改的页创建副本,而子进程在fork操作过程中共享整个父进程内存快照。避免在大量写入时做子进程重写操作,这样将导致父进程维护大量页副本,造成内存消耗。

但是由于Redis的持久化使用了Buffer IO,所谓Buffer IO是指Redis对持久化文件的写入和读取操作都会使用物理内存的Page Cache, 而大多数数据库系统会使用Direct IO来绕过这层Page Cache并自行维护一个数据的Cache,而当Redis的持久化文件过大(尤其是快照RDB文件),并对其进行读写时,磁盘文件中的数据都会被加载到物理内存中作为操作系统对该文件的一层Cache,而这层Cache的数据与Redis内存中管理的数据实际是重复存储的,虽然内核在物理内存紧张时会做Page Cache的剔除工作,但内核很可能认为某块Page Cache更重要,而让你的进程开始Swap ,这时你的系统就会开始出现不稳定或者崩溃了。
我们的经验是当你的Redis物理内存使用超过内存总容量的3/5时就会开始比较危险了。

下图是Redis在读取或者写入快照文件dump.rdb后的内存数据图:

2、持久化Fork引起redis主进程阻塞

AOF重写和RDB写入都是通过fork产生子进程来操作,子进程占用内存大小等同于父进程,理论上需要两倍的内存来完成持久化操作,但Linux有写时复制机制(copy-on-write)。父子进程会共享相同的物理内存页,当父进程处理写请求时会把要修改的页创建副本,而子进程在fork操作过程中共享整个父进程内存快照。避免在大量写入时做子进程重写操作,这样将导致父进程维护大量页副本,造成内存消耗。

测试把benchmark的11G数据写成一个1.3的RDB文件,或者等大的AOF文件rewrite,需要80秒,在redis-cli info中可查看。启动时载入一个AOF或RDB文件的速度与上面写入时相同,在log中可查看。

Fork一个使用了大量内存的进程也要时间,大约10ms per GB的样子,但Xen在EC2上是让人郁闷的239ms (KVM和VMWare貌似没有这个毛病),各种系统的对比,Info指令里的latest_fork_usec显示上次花费的时间。

3、在AOF持久化重写时,AOF重写缓冲区的指令追加新文件和改名替换造成阻塞:

在AOF持久化过程中,所有新来的写入请求依然会被写入旧的AOF文件,同时放到buffer中,当rewrite完成后,会在主线程把这部分内容合并到临时文件中之后才rename成新的AOF文件,所以rewrite过程中会不断打印”Background AOF buffer size: xx MB, Background AOF buffer size: xxMB”,这个合并的过程是阻塞的,如果你产生了280MB的buffer,在100MB/s的传统硬盘 上,Redis就要阻塞2.8秒!!!

4、持久化的系统IO阻塞redis主进程的问题

持久化很容易造成阻塞,不管是AOF rewrite,还是RDB bgsave,都有可能会出现阻塞,原因是一般情况下,如果Redis服务设置了appendfsync everysec, 主进程每秒钟便会调用fsync(), 将数据写到存储磁盘里. 但由于服务器的重写子进程正在进行大量IO操作, 导致主进程fsync()/操作被阻塞, 最终导致Redis主进程阻塞.解决方案这里就不多说了

5、Redis持久化性能调整

因为RDB文件只用作后备用途,建议只在Slave上持久化RDB文件,而且只要15分钟备份一次就够了,只保留save 900 1这条规则。

如果启用AOF,好处是在最恶劣情况下也只会丢失不超过两秒数据,启动脚本较简单只load自己的AOF文件就可以了。代价一是带来了持续的IO,二是AOF rewrite的最后将rewrite过程中产生的新数据写到新文件造成的阻塞几乎是不可避免的。只要硬盘许可,应该尽量减少AOF rewrite的频率,AOF重写的基础大小默认值64M太小了,可以设到5G以上。

默认超过原大小100%大小时重写可以改到适当的数值,比如之前的benchmark每个小时会产生40G大小的AOF文件,如果硬盘能撑到半夜系统闲时才用cron调度bgaofrewrite就好了。

如 果不Enable AOF ,仅靠Master-Slave Replication实现高可用性也可以。能省掉一大笔IO也减少了rewrite时带来的系统波动。代价是如果Master/Slave同时倒掉,会丢失十几分钟的数据,启动脚本也要比较两个Master/Slave中的RDB文件,载入较新的那个。

实际问题

1、Trouble Shooting——Enable AOF可能导致整个Redis被Block住,在2.6.12版之前

现象描述:当AOF rewrite 15G大小的内存时,Redis整个死掉的样子,所有指令甚至包括slave发到master的ping,redis-cli info都不能被执行。

原因分析:

官方文档,由IO产生的Latency详细分析, 已经预言了悲剧的发生,但一开始没留意。
Redis为求简单,采用了单请求处理线程结构。

打开AOF持久化功能后, Redis处理完每个事件后会调用write(2)将变化写入kernel的buffer,如果此时write(2)被阻塞,Redis就不能处理下一个事件。

Linux规定执行write(2)时,如果对同一个文件正在执行fdatasync(2)将kernel buffer写入物理磁盘,或者有system wide sync在执行,write(2)会被block住,整个Redis被block住。

如果系统IO繁忙,比如有别的应用在写盘,或者Redis自己在AOF rewrite或RDB snapshot(虽然此时写入的是另一个临时文件,虽然各自都在连续写,但两个文件间的切换使得磁盘磁头的寻道时间加长),就可能导致 fdatasync(2)迟迟未能完成从而block住write(2),block住整个Redis。

为了更清晰的看到fdatasync(2)的执行时长,可以使用”strace -p (pid of redis server) -T -e -f trace=fdatasync”,但会影响系统性能。

Redis提供了一个自救的方式,当发现文件有在执行fdatasync(2)时,就先不调用write(2),只存在cache里,免得被block。但如果已经 超过两秒都还是这个样子,则会硬着头皮执行write(2),即使redis会被block住。此时那句要命的log会打印:“Asynchronous AOF fsync is taking too long (disk is busy?). Writing the AOF buffer without waiting for fsync to complete, this may slow down Redis.” 之后用redis-cli INFO可以看到aof_delayed_fsync的值被加1。

因此,对于fsync设为everysec时丢失数 据的可能性的最严谨说法是:如果有fdatasync在长时间的执行,此时redis意外关闭会造成文件里不多于两秒的数据丢失。如果fdatasync运行正常,redis意外关闭没有影响,只有当操作系统crash时才会造成少于1秒的数据丢失。

解决方法:

最后发现,原来是AOF rewrite时一直埋头的调用write(2),由系统自己去触发sync。在RedHat Enterprise 6里,默认配置vm.dirty_background_ratio=10,也就是占用了10%的可用内存才会开始后台flush,而我的服务器有64G 内存。很明显一次flush太多数据会造成阻塞,所以最后果断设置了sysctl vm.dirty_bytes=33554432(32M),问题解决。

然后提了个issue,AOF rewrite时定时也执行一下fdatasync嘛, antirez三分钟后就回复了,新版中,AOF rewrite时32M就会重写主动调用fdatasync。

2、Redis log一直在报错:background: fork: Cannot allocate memory

Redis数据回写机制

数据回写分同步和异步两种方式

1.同步回写(SAVE),主进程直接向磁盘回写数据,在数据量大的情况下会导致系统假死很长时间。

2.异步回写(BGSAVE),主进程fork后,复制自身并通过这个新的进程回写磁盘, 回写结束后新进程自行关闭。

由于BGSAVE不需要主进程阻塞,系统也不会假死,一般会采用BGSAVE来实现数据回写。

故障分析

在小内存的进程上做fork,不需要太多资源,但当这个进程的内存空间以G为单位时,fork就成为一件很恐怖的操作.
在16G内存的足迹上fork 14G的进程,系统肯定Cannot allocate memory。
主机的Redis改动的越频繁fork进程也越频繁,所以一直在Cannot allocate memory

解决方案

1、redis有个默认的选项

stop-writes-on-bgsave-error yes

这个选项默认情况下,如果在RDB snapshots持久化过程中出现问题,设置该参数后,Redis是不允许用户进行任何更新操作。

不彻底的解决方式是,将这个选项改为false,但是这样只是当redis写硬盘快照出错时,可以让用户继续做更新操作,但是写硬盘仍然是失败的;

2、彻底的解决方式

直接修改内核参数vm.overcommit_memory

直接修改内核参数vm.overcommit_memory=1,Linux内核会根据参数vm.overcommit_memory参数的设置决定是否放行。

编辑/etc/sysctl.conf,改vm.overcommit_memory=1

vm.overcommit_memory=1,直接放行
vm.overcommit_memory=0:则比较 此次请求分配的虚拟内存大小和系统当前空闲的物理内存加上swap决定是否放行。
vm.overcommit_memory=2:则会比较进程所有已分配的虚拟内存加上此次请求分配的虚拟内存和系统当前的空闲物理内存加上swap,决定是否放行。

执行sysctl-p使其生效;

vm.overcommit_memory的作用

Linux对大部分申请内存的请求都回复"yes",以便能跑更多更大的程序。因为申请内存后,并不会马上使用内存,将这些不会使用的空闲内存分配给其它程序使用,以提高内存利用率,这种技术叫做Overcommit。

一般情况下,当所有程序都不会用到自己申请的所有内存时,系统不会出问题,但是如果程序随着运行,需要的内存越来越大,在自己申请的大小范围内,不断占用更多内存,直到超出物理内存,当linux发现内存不足时,会发生OOM killer(OOM=out-of-memory)。它会选择杀死一些进程(用户态进程,不是内核线程,哪些占用内存越多,运行时间越短的进程越有可能被杀掉),以便释放内存。

当oom-killer发生时,linux会选择杀死哪些进程?选择进程的函数是oom_badness函数(在mm/oom_kill.c中),该函数会计算每个进程的点数(0~1000)。点数越高,这个进程越有可能被杀死。每个进程的点数跟(/proc//oom_adj)oom_score_adj有关,而且oom_score_adj可以被设置(-1000最低,1000最高)。

当发生oom killer时,会将记录在系统日志中/var/log/messages

最后

我这边整理了一份Redis资料文档、Spring全家桶系列,Java的系统化资料(包括Java核心知识点、面试专题和20年最新的互联网真题、电子书等)有需要的朋友可以关注公众号【程序媛小琬】即可获取。

一文带你详解Redis常用的数据类型以及面试常碰到的数据持久化机制原理相关推荐

  1. 如何黑掉一个宇宙?一文带你详解Meterpreter后渗透模块攻击(文末赠送免费资源哦~)

    如何黑掉一个宇宙?一文带你详解Meterpreter后渗透模块攻击(文末赠送免费资源哦~) 文末赠送超级干货哈 一.名词解释 exploit 测试者利用它来攻击一个系统,程序,或服务,以获得开发者意料 ...

  2. 一颗眼睛是怎么称霸整个安全界的?一文带你详解nmap安全利器,附赠全程实操视频!

    nmap常用命令+海量视频教程分享 十条nmap常用的扫描命令介绍(文末有免费福利!) A+ 所属分类:Linux NMap,也就是Network Mapper,是Linux下的网络扫描和嗅探工具包. ...

  3. 面试官:说说你项目的API如何封装的?你:欸,纳尼??什么是API【一文带你详解API】【Java养成】

    Java养成计划----学习打卡第五天 Java入门到精通(打卡第五天) 学习内容 接口interface,API应用程序接口应用 内容管理 Hello!我是C风,专注于算法以及Java养成,如果觉得 ...

  4. HTTP系列学习(笔记一):一文带你详解HTTP协议

    1.什么是协议 计算机中的协议和现实生活中的协议是一样的,一式多份,彼此都遵从共同的一个规范,这个规范就可以称之为协议. 2.HTTP协议的工作流程 3.HTTP请求信息和响应信息的格式 请求: 响应 ...

  5. 一文带你详解数据库安全性

    数据库安全性 随着计算机的发展,数据的不断增加数据库的重要性就更为明显,数据库作为数据的管理工具,管理保存着企业公司等核心数据文件,其安全性则要求更高,没有一个安全的保障又如何实现数据的安全呢,今天我 ...

  6. 数据库关于事务的详解分析(全)包含面试常问的细节

    目录 前言 1. 定义 2. 四个特性(ACID) 3. 面临的问题 4. 四个隔离级别 5. 四个基本术语 6. 实战 前言 在敲代码的时候,提交事务.事务回滚等,事务二字确官方笼统,不知道具体什么 ...

  7. 系统性详解Redis操作Hash类型数据(带源码分析及测试结果)

    1 缘起 系统讲解Redis的Hash类型CURD, 帮助学习者系统且准确学习Hash数据操作, 逐步养成测试的好习惯, 本文较长,Hash的操作比较多,请耐心看, 既可以集中时间看,亦可以碎片时间学 ...

  8. 一篇文章带你详解 HTTP 协议(下)

    文章目录,方便阅读: 一.概述(已讲) 二.HTTP 工作过程(已讲) 三.HTTP 协议基础(已讲) 四.HTTP 协议报文结构(已讲) 五.HTTP 报文首部之请求行.状态行(已讲) 六.HTTP ...

  9. 斯坦福NLP名课带学详解 | CS224n 第10讲 - NLP中的问答系统(NLP通关指南·完结)

    作者:韩信子@ShowMeAI,路遥@ShowMeAI,奇异果@ShowMeAI 教程地址:https://www.showmeai.tech/tutorials/36 本文地址:https://ww ...

最新文章

  1. t-sql使用小记2010-01-27
  2. 面对大规模系统工程,看Facebook如何处理故障排查(一)
  3. LDAP组的概念以及命令
  4. 奖金67万!2020 中国计算机学会大数据与计算智能大赛启动!
  5. MFC开发IM-第十篇、MFC改变static text颜色
  6. 聊一聊 “超 大 模 型”
  7. Matlab常用的标记符号和颜色
  8. vue 打包css路径不对_vue项目打包后css背景图路径不对的问题
  9. python os.access_Python用access判断文件是否被占用的实例方法
  10. 老罗Android开发 视频教程
  11. 服务器虚拟化思维导图,虚拟语气思维导图解析.ppt
  12. 加减乘除求余 利用 位运算实现(详细)
  13. Docker 如何安全地进入到容器内部
  14. 2019年系统架构师考试心得
  15. word打开文档很久很慢_打开Word文档出现假死或超慢的原因及解决方法
  16. 核心设计——多种电源设计应用分享
  17. python爬取微信好友信息
  18. 基于WIFI信号的呼吸和心率检测(论文总结)
  19. python无法读取excel文字_Python帮你做Excel——读取Excel文档
  20. soso竟然没有收录qq.com的任何一个网页

热门文章

  1. 安全HCIP之Agile Controller-Campus系统
  2. 双11秘笈大揭秘:今年天猫在家居行业怎么玩?
  3. 高德机器人的名字是怎么呼叫的_语音呼叫名字打电话怎么设置
  4. Chapter1 C语言的结构和运行过程
  5. python爬取电子病历_自然语言处理技术应用于电子病历后结构化系统对临床决策支持系统(CDSS)的支持...
  6. 键盘输入一个英制的身高(几英尺几英寸),计算对应的公制身高(米)
  7. 笔记本电脑开机密码忘记解决方法
  8. Flutter绘制布局工具
  9. Flex 3 布局容器学习笔记
  10. 2009的牛宝宝取名大全