Redis系列文章目录
@
目录一、Redis配置文件解析
INCLUDES 包含
- 作用:类似于Struct2 配置文件,可以通过 INCLUDES来包含其它配置文件,redis.conf 可以作为总闸。
GENERAL 通用
- Daemonize:是否作为守护线程运行,如果开启则开机自启
- Pidfile: 进程管道pid文件
- Port:redis运行的端口
- Tcp-backlog:backlog其实是一个连接队列,用来避免慢客户端连接问题。
- Timeout:在多长的空闲时间后会关闭连接,默认值为0,表示不关闭
- Bind: 绑定的ip地址
- Tcp-Keepalive:用于定时检测连接是否正常,单位为秒,如果设置为0,则不会进行Keepalive检测,建议设置为60
- Loglevel:日志级别,redis默认有4种日志级别 debug、verbose、notice、warning,日志级别由低到高主键上升。
- logfile:日志保存的路径;默认为标准输出,如果Redis设置为守护进程方式运行,而这里又配置为标准输出,则日志会发送给 /dev/null
- sys-log-enabled: 是否允许记录系统日志
- databases:redis含有的数据库,使用select xxx 来切换数据库
SECURITY
命令行语句:
config get dir //获取当前的工作目录
conifg get requirepass //获取登录密码
config set requirepass //设置登录密码
auth 密码 //使用密码登录
配置文件中:
requirepass xxxxx //设置登录密码
LIMITS 限制
- Maxclients:同一时间最大的用户连接数量,默认1
- Maxmemory:最大的缓存空间
- Maxmemory-samples :设置样本数量,LRU算法和最小TTL算法都不是精确的算法,而是估算值。redis默认会检查这么多个key并选择其中LRU的那个。
- Maxmemory-policy:清除缓存策略(当数据库存储的数据过多时)
Volatile-lru :最近最少算法移除key,只对设置了过期时间的键
Allkeys-lru :最近最少使用算法移除key
Volatile-lfu :使用频率最少算法移除key,只对设置了过期时间的键
Allkeys-lfu :使用频率最少使用算法移除key
Volatile-random :在过期集合中随机移除key,只对设置了过期时间的
Allkeys-random :随机移除的key
Volatile-ttl :移除最近将要过期的(TTL最小的)
Noeviction:永不移除(实际不会使用)
常用配置redis.conf 说明:
二、Redis持久化--- RDB方式
2.1 RDB(Redis DataBase):
-
概念:
在指定的时间间隔内将内存中的数据集快照写入磁盘,也就是Snapshot快照,他恢复时是将快照文件直接读入到内存里。
-
执行特点:Redis会单独创建(fork) 一个子进程来进行持久化,会先将数据写入到一个临时文件中(dump.rdb),等到持久化操作结束后再用这个临时文件去替换上次持久化好的文件。整个过程中主进程是不进行任何IO操作的,这就确保了极高的性能,如果需要进行大规模的数据恢复,且对于数据的完整性要求不高,那么RDB方式要比AOF方式更加的高效,缺点是最后一次持久化后的数据可能会丢失。
-
Fork:作用是复制一个与当前进程一样的进程。由该子进程进行 rdb 持久化操作,fork() 底层调用了
copy-on-write
写时复制机制。这个机制后续会详细介绍。 -
dump.rdb的恢复策略:
- 在操作满足对应持久化触发条件或者关闭Redis服务时,都会将内存数据写入磁盘(dump.rdb文件),最后替换上一次的持久化好的文件。 注意当关闭redis时会最后进行一次RDB持久化操作
- 最好的策略是主机和备份机分为2台机器,这样可以保障数据的安全,采用冷拷贝后重新使用的方式[
cp dump.rdb dump_new rdb
] - save和bgsave都是主动对数据进行备份。 Save时只管保存,其它不管,全部阻塞;BGSAVE时Redis会在后台异步进行快照操作,快照同时还可以响应客户端请求。可以通过lastsave命令获取最后一次成功执行快照的时间。
- Redis适合于大规模数据的恢复,对于数据一致性要求不高;
2.2 配置文件解读 (SNAPSHOTTING) 快照:
-
dir:指定本地数据库的存放目录
-
save
//触发持久化的条件
-
Stop-writes-on-bgsave-error:出现异常后立刻停止存储数据。如果配置成no,表示你不在乎数据不一致或者有其它的手段发现和控制。
-
rdbcompression xxx :对于存储到磁盘中的快照,可以设置是否进行压缩存储。如果的话,redis会采用LZF算法进行压缩。如果你不想消耗CPU来进行压缩的话,可以设置为关闭次功能。
-
rdbchecksum:在存储快照后,还可以让redis使用CRC64算法来进行数据校验。
-
dbfilename: RDB持久化后生成的持久化文件名
-
rdb文件异常恢复操作:
Redis-check-rdb --fix rdb文件名
-
dir:指定本地数据库存放的目录
2.3 save指令、bgsave指令、save配置:
-
save指令工作原理:
当同时有多个客户端向服务器发送请求时,由于Redis是单线程的,因此不同指令到达Redis内部是有一个执行顺序的,假设如下。
save指令的执行会阻塞当前的Redis服务器,直到当前RDB过程完成为止,有可能会造成长时间阻塞,线上环境不建议使用。
-
bgsave指令工作原理:
对于客户端发送的请求,服务器端不直接进行处理,而是通过Linux的fork函数来生成一个子进程来创建rdb文件同时返回消息。
bgsave命令是针对save阻塞问题做的优化。Redis内部所涉及到的RDB操作都采用bgsave方式,save命令可以放弃使用。
-
save配置:
save配置后台其实走的还是 bgsave指令的方式。 -
三种方式对比比较:
2.4 RDB总结:
(1)RDB优点:
- RDB是一个紧凑压缩的二进制文件,存储效率较高;
- RDB内部存储的是redis在某个时间点的数据快照(全部数据),非常适合用于数据备份,全量复制等场景;
- RDB在恢复大数据集的时候速度比AOF快很多;
- RDB可以最大优化Redis 的性能,父进程只需要fork出一个子进程,剩下的持久化操作交给子进程去处理,父进程无需进行任何IO操作;
- 应用:服务器中每X小时执行bgsave备份,并将RDB 文件拷贝到远程机器中,用于灾难恢复;
(2)RDB缺点:
- 如果需要尽量避免服务器在故障时丢失数据,那么RDB不合适。因为RDB单个文件需要保存整个数据集的状态,它需要存储的数据可能会很大。那么可能至少5分钟才完成RDB文件的保存,这种情况下如果出现故障就会丢失好几分钟的数据。
- fork()会占用较大性能。如果数据集比较大,fork()就会非常耗时,生成的子进程也会占用较大内存空间,如果CPU时间非常紧张的话,就会造成服务器宕机。
- 如果主进程经常发生写操作,那么就会产生大量的分页错误(由于
Copy-On-Write
机制),这样就需要花费不少时间在复制操作上。
2.5 RDB相关问题拓展
为什么Redis 进行 RDB持久化时,新起一个进程而不是在原进程中起一个线程?
答案: 主要是出于对 Redis 的性能考虑。由于Redis 的工作方式是单线程和单进程模式,Redis RDB持久化机制是会阻塞主进程的,那样就无法响应客户端的请求了;而如果在主线程中起一个线程,这样会造成对数据的竞争访问,解决竞争就可能会需要使用到锁,那样必然会造成性能的下降。为了避免性能问题,最好的办法就是另起一个进程来进行持久化操作。
详细解释一下 save和 bgsave区别?
首先了解一下 RDB 进行读写数据时的操作过程:
- 客户端向服务器发送写操作【数据在客户端内存中】;
- 服务器接收到写请求【数据在服务器内存中】;
- 服务器调用 write这个系统调用,将数据往缓冲区上写【数据在系统内存缓冲区中】;
- 操作系统将缓冲区中的数据转移到磁盘缓冲区【数据在磁盘缓冲区中】;
- 磁盘控制器将数据写入到物理介质中【数据真正同步到磁盘】。
Redis 通过调用 rdbSave()和 rdbLoad()函数来实现数据的同步和读取恢复。rdbSave() 命令是一个同步阻塞的操作。具体进行持久化时 rdbSave() 被两种方式调用,一种是 SAVE,一种是 BGSAVE:
SAVE
:直接调用 rdbSave(),阻塞Redis主进程,直到保存完成为止。在主进程阻塞期间不能处理服务端请求;
BGSAVE
:实现原理是copyonwrite()+fork()
,fork()操作复制了一个子进程,但是注意子进程和主进程是共享一块内存空间的,子进程调用 rdbSave()操作,并在数据保存完毕后向主进程发送信号通知持久化完毕。由于 rdbSave() 操作在子进程中被调用,所以不会阻塞主进程,主进程仍然能够处理客户端请求。
代码演示:
def SAVE():
rdbSave()
def BGSAVE():
pid = fork() # 注意会copy主进程的一份内存
if pid==0:
# 子进程保存RDB
rdbSave();
elif pid>0:
# 父进程继续处理请求,等待子进程完成同步
handle_request();
else:
# 处理fork 错误
handle_fork_error();
知道了RDB方式进行数据备份时不会阻塞,那么如果备份时有数据发生了更新改变,备份的是更新前的数据还是更新后的数据?
由于fork() 操作底层使用了 copy-on-write
机制,因此备份的是开始那个时刻的数据。
传统的普通进程的复制,会直接将父进程的数据拷贝到子进程中,父进程和子进程之间的数据段和堆栈是相互独立的。而 copy-on-write
机制复制的子进程和主进程是共享同一内存空间的,两者只是虚拟空间不同,实质上对应的物理空间相同。
什么是 copyonwrite()?解释一下
我们知道通过fork()
的子进程和主进程共享一块内存空间,但是两个进程对内存的修改操作彼此不可见。这就是采用了 copyonwrite
技术。
原理:
主进程fork()
子进程后,内核把主进程中的所有内存页权限设置为read-only
状态,然后子进程地址空间指向主进程。当主进程进行写操作时(这里只会有主进程写,因为子进程只进行rdb持久化),CPU硬件检测到当前页是只读的,于是会触发页异常中断(page-fault
),陷入内核中的一个中断例程。
中断例程中,内核就会把触发中断异常的那一页复制一份(注意只复制异常的那一页,不是全部复制),此时主进程和子进程就都会持有一份。
CopyOnWrite的优缺点:
优点:
- 减少分配和复制资源时带来的延迟;
- 减少了不必要的资源分配;
缺点:
- 如果子进程和主进程进行大量的写操作,就会产生很多分页错误,因此需要进行很多页的复制操作。.
总结: copyonwritefork()出来的子进程共享主进程的物理空间,当主子进程有内存写入操作时,read-only内存页发生中断,将触发的异常的内存页复制一份(其余的页还是共享主进程的)。但是对于Redis而言,大多数时候是读操作多于写操作,而读操作不会触发这一机制,因此 Bgsave
操作会更简单。
三、Redis持久化--- AOF方式
鉴于RDB方式的弊端如下:
- 存储数据量较大,效率较低:基于快照思想,每次读写都是全部数据,当数据量巨大时,效率非常低;
- 大数据量下IO性能较低;
- 基于fork创建子进程,内存产生额外消耗;
- 宕机带来的数据丢失风险;
解决思路:
- 不写全部数据,仅记录部分数据;
- 改记录数据为记录操作过程;
- 对所有操作均进行记录,排除丢失数据的风险;
3.1 AOF(Append Only File):
- 概念:
以日志的形式来记录每个写操作,将Redis执行过的所有写指令记录下来(读操作不记录),只许追加文件但不可以改写文件,redis启动之初会读取该文件重新构建数据,换言之,redis重启的话就根据日志文件内容将所有写指令从前到后执行一次来完成数据的恢复操作。
- 执行特点:
- 当AOF和RDB两种持久化方式都存在时优先执行AOF持久化方式,恢复数据也是一样的。
- AOF会记录所有的写操作,这也包括FLUSHDB、FLUSHALL等操作。
redis-check-aof --fix xxxx.aof
命令会对xxxx.aof文件进行修复操作。- AOF和RDB两种持久化操作可以同时执行,没有问题。
- Fork:作用是复制一个与当前进程一样的进程。新进程的所有数据等和原进程一样,但是是一个全新的进程,并作为原进程的子进程。
- AOF 写数据的三种策略:
3.2 配置文件解读 (APPEND ONLY MODE) :
- appendonly : 是否开启AOF持久化
- appendfilename : 持久化后的文件名
- Appendsync :持久化的频率。
- Always:每次接收到 写命令就立即写入磁盘,最慢的,但是保证完全持久化。不推荐。
- Everysec:出厂默认推荐,异步操作,每秒钟强制写入磁盘一次,对性能和持久化方面做了折中;
- no:完全依赖于os,性能最好但是持久化没有保证。
各持久化频率的出现情况:
- no:写磁盘只会在一下任意一种情况中被执行:Redis被关闭、AOF功能被关闭、系统的写缓存被刷新(可能是缓存占满或者写操作定期执行)。这三种情况下SAVE操作都会引起 Redis主进程阻塞。
- everysec:这种情况下每秒钟执行一次,由于SAVE是后台子线程调用的,不会引起主进程阻塞。
- always:这种情况下,每执行完一个命令后,写命令都会被持久化,同时主线程会阻塞,不能接收客户端命令请求。
- AOF异常恢复命令:
Redis-check-aof --fix aof文件名
- Rewrite:命令:
redis-cli -h ip -p port bgrewriteaof
AOF采用文件追加方式,文件会越来越大为了避免此种情况,新增了重写机制,在当前的快照保存工作结束后,fork一个子进程,将AOF文件进行重写,合并set命令等操作到一个临时文件,达到缩小文件大小的目的。重写结束后后将临时文件替换为新的AOF文件(重写过程中如果有新的redis操作命令,会提交到缓存中,重写结束后追加到AOF文件内)。 - Rewrite触发机制:Redis会记录上一次重写的AOF大小,默认配置是当AOF文件大小是上次rewrite后大小的一倍且文件大于64M时触发。
auto-aof-rewrite-pericentage xxx
:当目前的AOF文件大小超过上一次重写文件大小的百分之几时进行重写,如果没有重启过,则以启动时的AOF文件大小为依据auto-aof-rewrite-min-size
:允许重写的最小AOF文件大小No-appendsync-on-rewrite
:重写时是否可以运用 Appendsync,用默认no即可。保证数据的完整性。
3.3 AOF重写:
-
AOF重写的作用及介绍:
-
AOF重写的规则:
-
AOF的重写方式:
手动重写 ------------------------bgrewriteaof
自动重写 ------------------------auto-aof-rewrite-min-size size
auto-aof-rewrite-percentage percentage
-
AOF手动重写原理:
当输入bgrewriteaof指令时会直接触发AOF重写
- AOF自动重写原理:
自动重写需要满足两个触发条件之一
- AOF重写流程:
- redis 调用 fork 出一个子进程,现在有两个进程;
- 子进程根据内存中的数据库快照,往临时文件中写入重建数据库状态的命令;
- 父进程继续处理客户端请求,除了把写命令写入到原来的 aof文件中,还生成了一个 aof缓冲区,把收到的写命令缓存起来。这样就能保证即使子进程重写失败,数据也不会丢失。
- 子进程完成数据的重写后发送通知给父进程,父进程把缓存中的写命令也写入到临时文件中。
- 最后父进程用临时文件替换旧的aof文件,完成重写操作。
注意: 重写aof文件的操作,并没有读取旧的aof文件,而是将整个内存中的数据库内容用命令的方式重写一个新的aof文件,这点和快照十分相似。
3.4 AOF的优缺点:
优点:
使用AOF持久化会让Redis变得非常耐久,可以通过设置不同的持久化频率来进行持久化。AOF默认的是每秒钟持久化一次,由于是开启的子线程进行持久化不会造成阻塞,即使宕机了也只会损失1秒钟的数据。
缺点:
对于相同的数据集来说 AOF文件的体积会大于 RDB文件的体积。
根据所选择的 fsync策略,AOF的速度可能会慢于 RDB。一般情况下每秒钟持久化的性能也比较高。但是在写入巨大数据时RDB的速度更快。
3.5 AOF总结:
四、AOF与RDB性能建议:
参考:
https://blog.csdn.net/weixin_42683679/article/details/81092985
https://blog.csdn.net/lh87270202/article/details/106430154