常用技术栈01------Redis
注:此文用于学习自用
学习思路:
1.什么是Redis
2.Redis应用的主要场景
3.如何使用Redis
4.可能存在的扩展内容
学习前存在的疑惑:
说到Redis,第一反应就是“缓存”,那么就有一下几个问题。
1.Redis缓存的是什么?
2.Redis是怎么样实现缓存的或者说缓存机制是如何的?
3.Redis的缓存是否是可调优的?
4.Redis是否具备一些重要特性,可能会导致数据的安全问题?我们应该怎样处理它?
————————————————————————————————————————
1.什么是Redis?
首先,我们看看Redis在菜鸟教程中是怎么描述的
信息获取:
1.Redis是Remote Dictionary Server的缩写,顾名思义的理解,是一个远端的文件服务器。
2.Redis对文件的存储是基于key-value的,也就是以键值对的形式进行对文件数据的存储
3.Redis开源,由C语言编写,可以基于内存亦可持久化的日志型、Key-Value数据库
4.Redis所支持的存储的值有五种,分别是String、hash、list、sets、sorted sets
信息总结:
Redis是一个远端文件服务器,以键值对的形式存储数据,它支持的存储值的类型有五种,分别是String、hash、list、sets、sortedsets,并且它支持将数据缓存在内存中或者持久化到本地。
解决疑惑1:Redis缓存的是什么?
Redis缓存的是以key-value形式存储的数据,它存储的value值的数据结构可以是String、hash、sets、sortedSets、list,所以Redis也被称为数据结构型服务器。
————————————————————————————————————————
继续看网站中的描述:
信息获取+分析:
1.Redis的缓存有这样的特点,我们可以将内存中的数据持久化到本地,然后在重启Redis服务后再次读取本地的数据以实现缓存(思考:这是否意味着在持久化之后,在读取持久化的数据时,可以减少一次后台对Redis服务的访问)
2.Redis的性能很高(可以处理高并发)
3.Redis的操作特点:原子性,官网所述为要么成功执行要么失败完全不执行。
我的理解是,也就是说,Redis的每个操作都是不可再分的独立单元,那么显而易见,这个操作要么成功要么失败,而在多操作的情况下,支持事务,这意味着我们在执行多个操作时,要求它返回一个统一的结果。
再深入理解一下原子性,我的理解为,Redis对于它的操作是没有进行封装的,我们在操作Redis时,就是操作的这个方法的最基层的代码(即不存在方法里面套方法的情况)。
————————————————————————————————————————
2.Redis的安装与配置
二、Redis的配置
下面我会介绍几个我认为比较常用的配置项。
port 6379 -------6379为Redis的默认监听端口
bind 127.0.0.1--------绑定的主机地址
timeout 0--------当redis闲置n秒后,会自动关闭redis服务,而设置为0,表示将此功能关闭
loglevel notice--------redis的日志记录级别,有四种,debug–>verbose(冗长的)—>notice---->warning(记录范围由大至小,redis默认为verbose)
save seconds changes-------这表示在多少秒内,redis中执行了多少次操作,就会将数据更新到数据文件中
dbfilename dump.rdb--------持久化生成文件的默认名称为dump.rdb
dir ./-----------持久化生成文件的路径
maxclients 128-----设置redis服务器的最大连接数,如果设置为0,则表示无限制,默认也是无限制的,如果连接数超过设置,redis会关闭连接,并返回报错
maxmemory bytes-----设置redis内存存放数据的最大内存,达到最大内存时,redis会尝试取清除即将或者已到期的key,此操作之后如果内存还是触及所设置内存阈值,则不能再对redis进行写的操作,但可以进行读的操作
————————————————————————————————————————
3.Redis的命令
redis的操作命令
————————————————————————————————————————
4.Redis的持久化机制
1…持久化的具体机制是如何的,在redis持久化的执行流程中,执行到哪一步我们的数据才能称为已经被持久化的?
在reids作者的一篇博客中,他是这样描述的:
1.客户端向服务器发起写请求(数据位于客户端)
2.服务器处理写请求(数据位于服务端的内存中)
3.服务器调用write,OS准备向磁盘写入数据(数据在操作系统的内存中)
4.OS操作系统将数据向磁盘缓存写入(数据位于磁盘缓存中)
5.数据写入磁盘,实现持久化(数据写入磁盘中)
信息汇总:
第1步、第2步很好理解,就是简单的IO过程
第3步,可以看到数据首先放在了操作系统内存中,可以猜测,对硬盘的读写是必须通过操作系统来完成的,软件无法直接调用硬件层
第4步:这里涉及到一个磁盘缓存的概念,所谓的磁盘缓存,就是OS为了避免对硬盘反复读写而设置的一个数据缓存区,当传输到这个缓存区中的数据到达一定的体量时,才会去对硬盘执行读写操作,以增加硬盘的寿命
(注:磁盘缓存是可以关闭的,那么操作系统就会直接将数据写入磁盘)
分析:
问题:什么情况会导致redis持久化失败?
由redis的持久化过程可知,显而易见,任何导致第五步不能完整实现的情况都会导致redis的持久化失败或者数据丢失,这同样意味着有这样的情况:
1.客户端在向服务端传输完数据后断连,后面服务端正常传输
2.服务端在将数据传输到操作系统内存中后崩溃,后面操作系统正常运行
以上两种情况,都不会导致reids的持久化失败。
————————————————————————————————————————
5.Redis的持久化策略
1.首先,Redis提供了两种持久化技术:
RDB(Redis Database)与AOF(Append Only File )
RDB策略:
RDB—Redis Database,顾名思义,Redis的数据库,也是Redis的最开始设计的一种持久化策略。
那么RDB的持久化策略具体是指什么?
官网:RDB持久化方式能够在指定的时间间隔能对你的数据进行快照存储。
两个要点:
A.数据快照 B.指定时间间隔
解释:
redis的配置文件中有一个save配置项,save n seconds m changes,意味着在n秒内,如果redis中有m个key被修改,那么redis就会执行RDB的持久化策略,即把当前时刻的数据读取下来,然后持久化到本地磁盘中。
RDB的具体流程是怎么样的?
1.n秒内有m个键被改动
2.redis的主线程fork(分支)出一个子线程,主线程会继续处理来自client客户端的请求,子线程则会把当前数据执行持久化的操作,当子线程把这个时刻的数据本地化之后,会将新的数据替代旧的数据。
注1:
上面有提到,执行持久化时,主进程会fork出一个子进程,这个子进程其实是主进程的一个相同的副本,os为了节约内存,所以让这个两个进程共享同一个物理页面(也就是内存数据),但同时也修改了两个进程对当前物理页面的访问权限,这时两个进程对当前物理页面都只有可读权限,当主进程尝试对当前物理页面执行写操作时,os操作系统会创建一个当前物理页面的副本,以让主进程执行写操作,这也就是所谓的写时复制(copy on write)。同时,这也意味着,子进程仅会拥有共享的的物理页面,而不会继续与主进程执行写操作的副本进行关联,这也是RDB存储的数据为什么称作数据快照的原因。
注2:
我们可以使用save和bgsave来手动命令redis对当前内存数据进行持久化。
save和bgsave的区别在于:
save的方式,会让当前redis的主线程去执行持久化操作,因为redis只有一个主线程,这就意味着来自客户端的请求会被阻塞,因此不推荐使用,bgsave则是fork出子进程去执行持久化操作。
RDB特点分析+小结:
RDB特点分析:
要分析RDB的特点,我们需要回顾RDB在持久化的过程中完成了哪些操作。
redis在copy on write时,会分出子进程去进行数据快照,同时复制得到共享物理页面的一份副本,这意味着两点:第一,内存的消耗;第二,磁盘的IO。如果说redis内存中的数据体量相当大的话,复制一份共享物理页面所消耗的内存是很大的,并且子进程对于磁盘的IO也会影响系统的性能,可能会导致redis对客户端请求的片刻停滞响应的情况。但是,如果在硬件性能足够的情况下,RDB的机制可以充分发挥redis的性能,因为主进程仍然可以去处理来自客户端的请求。
小结:
RDB的优势:
1.因为RDB新的数据快照会替代旧的数据快照,所以你的数据都会保存在一个rdb文件中,这使得数据的备份和迁移十分方便
2.RDB和AOF对数据的写入速度存在区别,RDB快于AOF,大概存在25%及以上的速度差异,所以在持久化时RDB更快;而RDB和AOF在读数据时,RDB稍快,性能差异大概在10%左右,在恢复数据时会稍微占些优势。
RDB的缺点:
1.因为RDB进行数据快照时需要主进程fork出子进程,以及子进程实现对当前整个数据集进行数据快照,所以当出现意外时,可能会导致比save配置中的保存间隔更长的数据丢失。
2.在特点分析中说到,RDB需要复制共享物理页面,数据量足够大时,会消耗相当的内存,并且子进程对于共享物理页面的持久化也会消耗相当程度的磁盘性能,进而影响系统性能和redis主线程的运行性能。
===========
AOF策略:
AOF,append only file(只有追加操作的文件),简单地说,redis在执行aof策略时,会将对redis内存执行的写操作都通过调用write函数写到一个aof文件中,当我们重启redis后,只要执行这个aof文件,redis就会自动重建内存数据。
但是,我们上面讲过,由于在redis持久化的写入过程中,存在os内存缓存数据的情况,所以当redis将写操作写入到aof中时,可能不会直接写入到磁盘中,所以AOF策略提供了fsync的函数方法,通过配置appendfsync yes开启fsync同步写入,从而让我们可以将写操作强制写入到磁盘中。
fsync强制写入磁盘有三种方式:
- no fsync,这意味着不同步写入,写入时间交给操作系统来决定(比如os中缓存的数据到达了一定的量级)
- fysync always,始终同步写入,每有一条写入操作,redis都会强制写入到aof文件中,这种fsync’的方式性能比较差
- fysync everyseconds ,每一秒就将写操作写入磁盘aof文件,性能较fsync更好
AOF的劣势:
因为AOF的持久化策略是将对redis内存的写入操作存储起来,所以存在当多个操作操作同一个key时,然后redis还是会将每个关于这个key的写操作给追加到aof文件中,这也就意味着要重建相同的数据集,aof的文件要往往大于rdb文件,并且rdb的编码格式就是redis的编码格式,所以rdb文件可以直接被redis识别,而我们在使用aof文件进行重建redis数据库时,会消耗相当大的cpu性能。
为了避免aof文件过大,redis提供了这样的措施:
通过使用bgrewriteaof命令,来让redis新写一个新的aof文件,实现对aof文件的压缩,它的整个执行流程类似于rdb的快照模式。
第一步,redis的主进程fork出一个子进程
第二步,redis继续处理来自客户端的请求,继续对旧的aof文件追加写命令,同时缓存这些命令,而这时,子进程会对当时与主进程共享的物理数据页面,要实现的操作是用更少的命令集来重建当前的数据库,然后将这些命令集存入一个新的aof临时文件
第三步,当子进程的工作结束,子进程会向主进程发送一个通知,这时主进程会将缓存的命令写入这个新的aof文件中,并将之后的写入操作也追加到这个文件中,并且对它重命名的操作。