一篇文让你看懂Redis
一、Redis简介
1.1 Redis是什么?
Redis是一个完全开源免费的,用C语言编写的,是一个单线程,高性能(key/value)的内存数据库,基于内存运行并支持持久化的nosql数据库。
1.2 能干吗?
Redis主要用来做缓存,但是不仅仅可以做缓存,还有:redis计数器生成分布式唯一主键,分布式锁,队列,会话缓存等等。
二、Redis配置文件解读
#Redis默认只支持本地访问,将bind改为网卡ip代表只能通过某个ip进项访问redis,也可以啊将其注释,或者改为0.0.0.0表示支持所有ip访问
bind 127.0.0.1
# protected-mode表示保护模式,启用的条件有两个(两个要同时满足),1.没有使用bind,2.没有设置访问密码。
protected-mode yes
#Redis端口
port 6379
#数据库数量,默认16个,从0-15
databases 16
#rdb持久化策略,每900秒有一个改动,每300秒有10个改动,没60秒有10000个改动,则会出发rdb持久化机制
save 900 1
save 300 10
save 60 10000
# rdb持久化的文件名
dbfilename dump.rdb
# redis的rdb,aof,log文件的地址,默认启动目录,一般写死
dir ./
# 主节点密码,在做主从复制是使用,redis集群以及主从密码需要一样
masterauth <master-password>
#从节点只读
slave-read-only yes
# 开启密码 foobared为密码
requirepass foobared
#redis使用主机的主要内存,默认全部,生产环境一般50%以上
maxmemory <bytes>
#当达到最大内存时的删除策略
# volatile-lru -> remove the key with an expire set using an LRU algorithm
# allkeys-lru -> remove any key according to the LRU algorithm
# volatile-random -> remove a random key with an expire set
# allkeys-random -> remove a random key, any key
# volatile-ttl -> remove the key with the nearest expire time (minor TTL)
# noeviction -> don't expire at all, just return an error on write operations
#默认是不删除,直接报错
# maxmemory-policy noeviction
#当策略是LRU或TTL时,默认样本数,一次检查的样本数
# maxmemory-samples 5
#是否开启AOF持久化,默认不开启
appendonly no
#AOF持久化文件名
appendfilename "appendonly.aof"
#AOF持久化策略,有三种,no,everysec,always,默认everysec
appendfsync everysec
#AOF重写机制的百分率
auto-aof-rewrite-percentage 100
#AOF文件第一次达到多少大小的时候进行重写,生产环境一般5G起步
auto-aof-rewrite-min-size 64mb
#开启集群模式,默认关闭
cluster-enabled yes
#集群当前节点的配置信息(存放meet信心)
cluster-config-file nodes-6379.conf
#回收策略,一秒执行的次数,默认为10
hz 10
三、Redis持久化机制
什么是持久化?
持久化就是在指定的时间间隔内,将内存当中的数据集快照写入磁盘,它恢复时是将快照文件直接读到内存。
什么意思呢?我们都知道,内存当中的数据,如果我们一断电,那么数据必然会丢失,但是玩过redis的人应该都知道,我们一关机之后再启动的时候数据是还在的,所以它必然是在redis启动的时候重新去加载了持久化的文件。
redis提供两种方式进行持久化:
一种是RDB持久化默认。
另外一种是AOF(append only file)持久化。
3.1 RDB
1. 原理
虽然Redis是一个单进程nosql数据库,但是在进行RDB持久化的时候会单独创建(fork)一个与当前进程一摸一样的子进程来进行持久化,这个子进程的所有数据(变量。环境变量,程序程序计数器等)都和原进程一模一样,会先将数据写入到一个临时文件中,待持久化结束了,再用这个临时文件替换上次持久化好的文件,整个过程中,主进程不进行任何的io操作,这就确保了极高的性能。
2. 持久化文件在哪
根据dir配置
3. 什么时候fork子进程,或者什么时候触发rdb持久化机制
shutdown时,如果没有开启aof,会触发
配置文件中默认的快照配置
执行命令save或者bgsave
bgsave:会fork子进程,原理与rdb上面原理一样,系统默认触发rdb持久化都是调用的此命令
save:是不会fork子进程的,它使用主进程进行持久化,所以会导致客户端命令发送到我们服务端得不到及时处理,所以他是阻塞的
执行flushall命令 但是里面是空的,无意义
3.2 AOF
1. 原理:
将Reids的操作日志以追加的方式写入文件,读操作是不记录的,整体分为三步
1.数据写入内存
2.数据写入aof_buf
3.写入持久化文件。
注意:aof持久化不会fork子进程
2. 触发机制(根据配置文件配置项):
no:表示等操作系统进行数据缓存同步到磁盘(快,持久化没保证)
always:同步持久化,每次发生数据变更时,立即记录到磁盘(慢,安全)
everysec:表示每秒同步一次(默认值,很快,但可能会丢失一秒以内的数据)
3. AOF重写机制
为什么会出现重写?
aof是以日志追加的方式将命令字符串协议保存在aof 文件中,随着我们使用redis的时间越长,最redis的操作越多,这个aof文件会越来越大,如果不做处理,总有会撑爆磁盘,所以就出现了重写,重写就是专门给aof文件廋身的,
他的思想是:直接根据现在内存的数据,生成新的aof文件,然后去替换旧的aof文件,就可以把一下没用字符去掉,比如set k1 v1 ,然后我们del k1等等一些没用操作,这样我们的文件大小就会小很多
4. AOF重写触发条件
当AOF文件增长到一定大小的时候Redis能够调用 bgrewriteaof对日志文件进行重写 。当AOF文件大小的增长率大于该配置项时自动开启重写(这里指超过原大小的100%)。
auto-aof-rewrite-percentage 100
当AOF文件增长到一定大小的时候Redis能够调用 bgrewriteaof对日志文件进行重写 。当AOF文件大小大于该配置项时自动开启重写
auto-aof-rewrite-min-size 64mb
注意:重写操作是通过fork子进程来完成的,所以正常的aof不会fork子进程,触发了重写才会
3.3 混合持久化
4.0版本的混合持久化默认关闭的,通过aof-use-rdb-preamble配置参数控制,yes则表示开启,no表示禁用,5.0之后默认开启。
混合持久化是通过bgrewriteaof完成的,不同的是当开启混合持久化时,fork出的子进程先将共享的内存副本全量的以RDB方式写入aof文件,然后在将重写缓冲区的增量命令以AOF方式写入到文件,写入完成后通知主进程更新统计信息,并将新的含有RDB格式和AOF格式的AOF文件替换旧的的AOF文件。简单的说:新的AOF文件前半段是RDB格式的全量数据后半段是AOF格式的增量数据,
优点:混合持久化结合了RDB持久化 和 AOF 持久化的优点, 由于绝大部分都是RDB格式,加载速度快,同时结合AOF,增量的数据以AOF方式保存了,数据更少的丢失。
缺点:兼容性差,一旦开启了混合持久化,在4.0之前版本都不识别该aof文件,同时由于前部分是RDB格式,阅读性较差。
3.4 持久化问题总结
1.redis提供了rdb持久化方案,为什么还要aof?
优化数据丢失问题,rdb会丢失最后一次快照后的数据,aof丢失不会超过2秒的数据
2.如果aof和rdb同时存在,Redis重启会识别哪个持久化文件?
aof
3.rdb和aof优势劣势
rdb 适合大规模的数据恢复,对数据完整性和一致性不高 , 在一定间隔时间做一次备份,如果redis意外down机的话,就会丢失最后一次快照后的所有操作
aof 根据配置项而定,如果是默认配置,不会丢失超过两秒的数据
性能建议(这里只针对单机版redis持久化做性能建议):
rdb和aof同时存在时,因为RDB文件只用作后备用途,只要15分钟备份一次就够了,只保留save 900 1这条规则。
重写:只要硬盘许可,应该尽量减少AOF rewrite的频率,AOF重写的基础大小默认值64M太小了,可以设到5G以上。默认超过原大小100%大小时重写可以改到适当的数值。
四、Redis缓存问题
4.1缓存粒度控制
通俗来讲,缓存粒度问题就是我们在使用缓存时,是将所有数据缓存还是缓存部分数据?
数据类型 | 通用性 | 空间占用(内存空间+网络码率) | 代码维护 |
---|---|---|---|
全部数据 | 高 | 大 | 简单 |
部分数据 | 低 | 小 | 复杂 |
缓存粒度问题是一个容易被忽视的问题,如果使用不当,可能会造成很多无用空间的浪费,可能会造成网络带宽的浪费,可能会造成代码通用性较差等情况,必须学会综合数据通用性、空间占用比、代码维护性 三点评估取舍因素权衡使用。
4.2缓存穿透问题
缓存穿透是指查询一个一定不存在的数据,由于缓存不命中,并且出于容错考虑, 如果从存储层查不到数据则不写入缓存,这将导致这个不存在的数据每次请求都要到存储层去查询,失去了缓存的意义。
可能造成的原因:
1.业务代码自身问题
2.恶意攻击。爬虫等等
解决方案:
-
缓存空对象:可能会导致Redis缓存中空对象过多,撑爆内存。
-
布隆过滤器:
-
Google布隆过滤器
基于JVM内存的一种布隆过滤器,重启即失效。
本地缓存无法应用在分布式场景。
不支持大数据量存储。
-
Redis布隆过滤器
可扩展性布隆过滤器:一旦Bloom过滤器达到容量,就会在其上创建一个新的过滤器
不存在重启即失效或者定时任务维护的成本:基于Google实现的布隆过滤器需要启动之后初始化布隆过滤器
缺点:需要网络IO,性能比Google布隆过滤器低
-
4.3 缓存击穿:热点key重建缓存问题
缓存击穿是指缓存中没有但数据库中有的数据(一般是缓存时间到期),这时由于并发用户特别多,同时读缓存没读到数据,又同时去数据库去取数据,引起数据库压力瞬间增大,造成过大压力
我们知道,使用缓存,如果获取不到,才会去数据库里获取。但是如果是热点 key,访问量非常的大,数据库在重建缓存的时候,会出现很多线程同时重建的情况。因为高并发导致的大量热点的 key 在重建还没完成的时候,不断被重建缓存的过程,由于大量线程都去做重建缓存工作,导致服务器拖慢的情况。
解决方案:
1.互斥锁
第一次获取缓存的时候,加一个锁,然后查询数据库,接着是重建缓存。这个时候,另外一个请求又过来获取缓存,发现有个锁,这个时候就去等待,之后都是一次等待的过程,直到重建完成以后,锁解除后再次获取缓存命中。
互斥锁的优点是思路非常简单,具有一致性,但是互斥锁也有一定的问题,就是大量线程在等待的问题。存在死锁的可能性。
4.4缓存雪崩问题
缓存雪崩是指机器宕机或在我们设置缓存时采用了相同的过期时间,导致缓存在某一时刻同时失效,请求全部转发到DB,DB瞬时压力过重雪崩。
-
在缓存失效后,通过加锁或者队列来控制读数据库写缓存的线程数量。比如对某个key只允许一个线程查询数据和写缓存,其他线程等待。
-
做二级缓存,A1为原始缓存,A2为拷贝缓存,A1失效时,可以访问A2,A1缓存失效时间设置为短期,A2设置为长期
-
不同的key,设置不同的过期时间,让缓存失效的时间点尽量均匀。
-
如果缓存数据库是分布式部署,将热点数据均匀分布在不同搞得缓存数据库中。
-
搭建高可用集群,防止Redis宕机,造成无法用代码解决的问题。
五、Redis集群演变过程
5.1单机版Redis
核心技术:持久化
持久化是最简单的高可用方法(有时甚至不被归为高可用的手段),主要作用是数据备份,即将数据存储在硬盘,保证数据不会因进程退出而丢失。
5.2主从复制
复制是高可用Redis的基础,哨兵和集群都是在复制基础上实现高可用的。复制主要实现了数据的多机备份,以及对于读操作的负载均衡和简单的故障恢复。缺陷是故障恢复无法自动化;写操作无法负载均衡;存储能力受到单机的限制。
5.3哨兵
在复制的基础上,哨兵实现了自动化的故障恢复。缺陷是写操作无法负载均衡;存储能力受到单机的限制。
5.4高可用集群
通过集群,Redis解决了写操作无法负载均衡,以及存储能力受到单机限制的问题,实现了较为完善的高可用方案
六、Redis主从复制
6.1主从复制是什么?
单机有什么问题:
1.单机故障
2.容量瓶颈
3.qps瓶颈
主机数据更新后根据配置和策略,自动同步到备机的master/slaver机制,mester已写为主,slaver已读为主
好处:1.读写分离;2.容灾备份。
6.2主从复制工作流
主从复制总体分为3步
6.2.1 建立连接
1.设置master的地址和端口,发送slaveof ip port指令,master会返回响应客户端,根据响应信息保存master ip port信息 (连接测试)
2.根据保存的信息创建连接master的socket
3.周期性发送ping,master会响应pong
4.发送指令 auth password(身份验证),master验证身份
5.发送slave端口信息,master保存slave的端口号
6.2.2 数据同步
1.slave发送指令 psyn2
2.master 执行bgsave
3.在第一个salve连接时,创建命令缓存区
4.生成RDB文件,通过socket发送给slave
5.slave接收RDB,清空数据,执行RDB文件恢复过程
6.发送命令告知RDB恢复已经完成(告知全量复制完成)
7.master发送复制缓冲区信息
8.slave接收信息,执行重写后恢复数据
注意: master会保存slave从我这里拿走了多少数据,保存salve的偏移量
6.2.3 命令传播
slave心跳:replconf ack {offset} 汇报slave自己的offset,获取最新数据指令
命令传播阶段出现断网:
- 网络闪断闪连 忽略
- 段时间断网 增量
- 长时间断网 全量
全量复制核心三个要素
- 服务器运行id
用于服务器之间通信验证身份,master首次连接slave时,会将自己的run_id发送给slave,slave保存此ID - 主服务器积压的命令缓冲区
先进先出队列 - 主从服务器的复制偏移量
用于比对偏移量,然后判断出执行全量还是增量
6.3 全量复制消耗
1.bgsave时间
2.rdb文件网络传输
3.从节点请求请求数据时间
4.从节点加载rdb的时间
5.可能的aof重写时间
6.4 主从复制缺点
1.由于所有的写操作都是先在Master上操作,然后同步更新到Slave上,所以从Master同步到Slave机器有一定的延迟,当系统很繁忙的时候,延迟问题会更加严重,Slave机器数量的增加也会使这个问题更加严重。
2.当主机宕机之后,将不能进行写操作,需要手动将从机升级为主机,从机需要重新制定master。
简单总结:
一个master可以有多个Slave
一个slave只能有一个master
数据流向是单向的,只能从主到从
七、Redis扩展
1.Redis数据接口扩展
bitmaps
Bitmap本质是string,是一串连续的2进制数字(0或1),每一位所在的位置为偏移(offset)。string(Bitmap)最大长度是512 MB,所以它们可以表示2 ^ 32=4294967296个不同的位。
2.Redis删除策略
定时删除–>以CPU内存换redis内存
惰性删除–>以redis内存换CPU内存
定期删除
1.redis在启动的时候读取配置文件hz的值,默认为10
2.每秒执行hz次serverCron()–>databasesCron()—>actveEXpireCyle()
3.actveEXpireCyle()对每个expires[*]进行逐一检测,每次执行250ms/hz
4.对某个expires[*]检测时,随机挑选N个key检查
如果key超时,删除key
如果一轮中删除的key的数量>N25%,循环该过程
如果一轮中删除的key的数量小于等于N25%,检查下一个expires[ ]
current_db用于记录actveEXpireCyle()进入哪个expires[ * ] 执行,如果时间到了,那么下次根据current_db继续执行
redis使用:惰性删除+定期删除
3.Redis逐出策略和逐出算法
相关配置:
maxmemory: 最大可使用内存,占用物理内存的比例,默认值为0,,表示不限制。生产环境一般根据需求设置,通常50%以上
maxmemory-policy: 达到最大内存后,对挑选出来的数据进行删除策略
maxmemory-samples: 每次选取待删除数据的个数,选取数据时并不会全库扫描,采用随机获取数据的方式作为待检测删除数据。