一、Redis 处理速度快的原因
1、纯内存结构
KV结构的内存数据库,时间复杂度O(1)。
2、请求处理单线程
1) 这里说的单线程其实指的是处理客户端的请求是单线程的,可以把它叫做主线程。从4.0的版本之后,还引入了一些线程处理其他的事情,比如清理脏数据、无用连接的释放、大key的删除。
2) 把处理请求的主线程设置成单线程的好处:a. 没有创建线程、销毁线程带来的消耗
b. 避免了上线文切换导致的CPU消耗
c. 避免了线程之间带来的竞争问题,例如加锁释放锁死锁等等
注意,因为请求处理是单线程的,不要在生产环境运行长命令,比如keys,flushall,flushdb。否则会导致请求被阻塞。
3、多路复用机制
同步非阻塞 I/O,多路复用处理并发连接.
I/O 多路复用: i/O指的是网络i/O。多路指的是多个TCP连接。复用指的是复用一个或多个线程.它的基本原理就是不再由应用程序自己监视连接,而是由内核替应用程序监视文件 描述符。客户端在操作的时候,会产生具有不同事件类型的socket。在服务端,i/O 多路复用程序会把消息放入队列中,然后通过文件事件分派器,转发到不同的事件处理器中。
二、内存回收
Redis本质上一个存储系统。所有的存储系统在数据量过大的情况下都会面临存储瓶颈,包括MySQL,RabbitMQ等等。这里我们解决要两个问题:首先,作为一个内存的KV系统,Redis服务肯定不是无限制地使用内存,应该设置一个上限(max_memory)。第二个,数据应该有过期属性,这样就能清除不再使用的key。
1.过期策略
1)立即过期(主动淘汰)
每个设置过期时间的key都需要创建一个定时器,到过期时间就会立即清除。该策略可以立即清除过期的数据,对内存很友好;但是会占用大量的CPU资源去处理过期的数据,从而影响缓存的响应时间和吞吐量。
2)惰性过期(被动淘汰)
a. 当访问一个key时,才会判断该key是否过期,过期则清除。该策略可以节省CPU资源,却对内存非常不友好。极端情况可能出现大量的过期key不会被清除,占用大量内存。
b.每次写入key时,若发现内存不够,调用activeExpireCycle释放一部分内存
3)定期过期
每隔一定的时间,会扫描一定数量的数据库的expires字典中一定数量的key,并清除其中已过期的key。该策略是前两者的一个折中方案。通过调整定时扫描的时间间隔和每次扫描的限定耗时,可以在不同情况下使得CPU和内存资源达到最优的平衡效果。
2.最大内存设置
1) redis.conf 参数配置:
#maxmemory<bytes> 如果不设置maxmemory或者设置为0, 32位系统最多使用3GB内存,64位系统不限制内存。
2) 动态修改
config set maxmemory 2GB
2.淘汰策略
Redis的内存淘汰策略,是指当内存使用达到最大内存极限时,需要使用淘汰算法来决定清理掉哪些数据,以保证新数据的存入。
1)策略 (建议使用volatile-Iru,在保证正常服务的情况下,优先删除最近最少使用的key。)
后缀: LRU, Least Recently Used:最近最少使用。 LFU,Least Frequently Used,最不常用,按照使用频率删除, random,随机删除
前缀: volatile是针对设置了ttl的key,allkeys是针对所有
策略 | 含义 |
noeviction | 默认策略,不会删除任何数据,拒绝所有写入操作并返回客户端错误信息(error)OOM command not allowed when used memory,此时 Redis只响应读操作。 |
volatile-lru | 根据LRU算法删除设置了超时属性(expire)的键,直到腾出足够内存为止。如果没有可删除的键对象,回退到noeviction策略。 |
allkeys-lru | 根据LRU算法删除键,不管数据有没有设置超时属性,直到腾出足够内存为止。 |
volatile-lfu | 在带有过期时间的键中选择最不常用的。 |
allkeys-lfu | 在所有的键中选择最不常用的,不管数据有没有设置超时属性。 |
volatile-random | 在带有过期时间的键中随机选择。 |
allkeys-random | 随机删除所有键,直到腾出足够内存为止。 |
volatile-ttl | 根据键值对象的ttl属性,删除最近将要过期数据。如果没有,回退到noeviction策略。 |
如果没有设置ttl或者没有符合前提条件的 key 被淘汰,那么 volatile-Iru、volatile-random、volatile-ttl 相当于 noeviction(不做内存回收)
2)修改策略
1.配置文件: redis.conf: # maxmemory-policy
2.动态修改:config set maxmemory-policy volatile-lru
三、持久化( 如果同时开启了AOF和RDB,优先用AOF)
1.RDB
RDB 是 Redis 默认的持久化方案。当满足一定条件的时候,会把当前内存中的数据写入磁盘,生成一个快照文件dump.rdb。Redis重启会通过加载dump.rdb文件恢复数据。
(1)RDB触发的不同情况
a 配置触发
在redis.conf,SNAPSHOTTING,其中定义了触发把数据保存到磁盘的触发频率。如果不需要rdb方案,注释save或者配置成空字符串””。下面的三个配置只要满足其中一个就会触发
save 800 1 #800秒内至少有一个key被修改(包括添加)
save 400 10 #400秒内至少有10个key被修改
save 100 1000
b.shutdown触发,保证服务器正常关闭
c.flushall. rdb文件会成空的
d.手动触发
如果我们需要重启服务或者迁移数据,这个时候就需要手动触RDB快照保存。Redis提供了两条命令:
save //save在生成快照的时候会阻塞当前Redis服务器,Redis不能处理其他命令。如果内存中的数据比较多,会造成Redis长时间的阻塞。生产环境不建议使用这个命令
bgsave //执行bgsave时,Redis进程执行fork操作创建子进程,在后台异步进行快照操作,RDB持久化过程由子进程负责,完成后自动结束。它不会记录fork之后产生的数据。阻塞只发生在fork阶段,一般时间很短。
(2)查看最近一次成功生成快照的时间: lastsave
(3)redis.conf 内其他相关配置
参数 | 默认参考值 | 说明 |
dir | ./ | rdb文件默认在启动目录下(相对路径 ) config get dir 可获取 |
dbfilename | dump.rdb | 文件名称 |
rdbcompression | yes | 开启压缩可以节省存储空间,但是会消耗一些CPU的计算时间,默认开启 |
rdbchecksum | yes | 使用CRC64算法来进行数据校验,但是这样做会增加大约10%的性能消耗,如果希望获取到最大的性能提升,可以关闭此功能。 |
2.AOF
Redis 默认不开启。AOF采用日志的形式来记录每个写操作,并追加到文件中。开启后,执行更改Redis数据的命令时,就会把命令写入到AOF文件中。Redis 重启时会根据日志文件的内容把写指令从前到后执行一次以完成数据的恢复工作。
1)配置
Redis默认只开启RDB持久化,开启AOF需要修改为yes。
appendonly yes
appendfilename "appendonly.aof" //文件名
2)持久化策略
由于操作系统的缓存机制,AOF数据并没有真正地写入硬盘,而是进入了系统的硬盘缓存。然后根据redis.conf中 appendfsync 的持久化策略把缓冲区的内容写入到AOF文件
策略 | 详细 |
everysec | 默认配置,表示每秒执行一次fsync,可能会导致丢失这1s数据。 |
no | 不执行fsync,由操作系统保证数据同步到磁盘,速度最快,但是不太安全; |
always | 每次写入都执行fsync,以保证数据同步到磁盘,效率很低; |
3)文件重写
由于AOF持久化是Redis不断将写命令记录到 AOF 文件中,随着Redis不断的进行,AOF的文件会越来越大,占用服务器内存越来越大, AOF 恢复要求时间越长。为了解决这个问题,Redis新增了重写机制,当AOF文件的大小超过所设定的阈值时,Redis就会启动AOF文件的内容压缩,只保留可以恢复数据的最小指令集。可以使用命令bgrewriteaof来重写。AOF 文件重写并不是对原文件进行重新整理,而是直接读取服务器现有的键值对,然后用一条命令去代替之前记录这个键值对的多条命令,生成一个新的文件后去替换原来的 AOF 文件。
参数 | 默认值 | 说明 |
auto-aof-rewrite-percentage | 100 | 当目前Aof文件大小超过上一次重写的aof文件大小的百分之多少,调用 bgrewriteaof 命令对日志文件进行重写进行重写, |
auto-aof-rewrite-min-size | 64M | 设置允许重写的最小aof文件大小,避免了达到约定百分比但尺寸仍然很小的情况还要重写。 |
no-appendfsync-on-rewrite | no | 在aof重写的时候,会执行大量 I/0,此时对于everysec和 always的 aof模式来说,执行fsync会造成阻塞过长时间。如果对延迟要求很高的应用,这个字段可以设置为yes,表示rewrite期间对新写操作不fsync,暂时存在内存中,等rewrite完成后再写入; |
aof-load-truncated | yes | 由于一切突发状况,aof文件可能在尾部是不完整的。如果选择的是yes,当截断的aof文件被导入的时候,会自动发布一个log给客户端然后load。如果是no,用户必须手动redis-check-aof 修复AOF文件才可以。 |
4)重写中的工作
a.处理命令请求。
b.将写命令追加到现有的AOF文件中。
c.将写命令追加到AOF重写缓存中。
注: b,c是为了确保重写过程中原AOF文件的改到引起的数据一致性问题
3.RDB和AOF对比
方式 | 优点 | 缺点 |
RDB | 1.RDB是一个非常紧凑(compact)的文件,它保存了redis 在某个时间点上的数据集。这种文件非常适合用于进行备份和灾难恢复。
2.生成RDB文件的时候,redis主进程会fork()一个子进程来处理所有保存工作,主进程不需要进行任何磁盘IO操作。
3.RDB 在恢复大数据集时的速度比AOF的恢复速度要快。 | 1、RDB方式数据没办法做到实时持久化/秒级持久化。因为bgsave每次运行都要执行fork操作创建子进程,频繁执行成本过高
2、在一定间隔时间做一次备份,所以如果redis意外down掉的话,就会丢失最后一次快照之后的所有修改(数据有丢失)。 |
AOF | 1. AOF 持久化的方法提供了多种的同步频率,即使使用默认的同步频率每秒同步一次,redis 最多也就丢失1秒的数据而已。 | 1. 对于具有相同数据的的Redis,AOF 文件通常会比 RDB文件体积更大(RDB存的是数据快照)。
2.虽然 AOF 提供了多种同步的频率,默认情况下,每秒同步一次的频率也会消耗较高的性能。在高并发的情况下,RDB比AOF 具好更好的性能保证 |