持久化
当要插入一个新的数据的时候,就需要把这个数据,同时写入到内存和硬盘。当查询某个数据时,直接从内存读取,硬盘的数据只是在redis重启的时候,用来恢复内存中的数据。
代价就是消耗了更多的空间,同一份数据,存储了两遍。
Redis实现持久化的策略
RDB=》Redis DataBase
-
定期备份:定期把我们Redis内存中的所有数据,都给写入到硬盘中,生成一个快照,一旦Reids重启,就可以根据刚刚的快照就能把内存中的数据给恢复过来
-
手动触发:通过Redis客户端执行特定的命令,来触发快照生成
- save:执行save的时候,Redis就会全力以赴的进行快照生成,此时会阻塞Redis客户端的其它命令
- bgsave:background save,不会影响Redis服务器处理其他客户端的请求和命令。
-
自动触发
- 在Redis配置文件中,设置一下,让Redis每隔多长时间/每产生多少次修改就触发
- 通过shutdown命令关闭服务器也会触发
- redis进行主从复制的时候,主节点也会自动生成rdb快照,然后把rdb快照文件内容传输给从节点。
-
-
Redis生成的rdb文件是在Redis的工作目录下,也可以通过配置文件指定,rdb文件是一个二进制文件,把内存中的数据,以压缩的形式保存到这个二进制文件中。
-
Redis提供了检查rdb文件完整性的工具,
redis-check-rdb
-
当执行生成rdb镜像操作的时候,此时就会把要生成的快照数据,先保存到一个临时文件中,当这个快照生成完毕之后,再删除之前的rdb文件,把新生成的临时的rdb文件名字改成
dump.rdb
-
手动破坏rdb文件,如果通过正常流程重新启动redis服务器,此时redis服务器会在退出的时候,自动触发rdb操作,但是如果是异常重启(kill -9或者服务器断电),此时redis来不及生成rdb,内存中尚未保存到快照中的数据,就会随着重启而丢失。
-
flashall
:也会删除rdb文件 -
当redis服务器挂了之后,可以查看redis日志,默认路径在
/var/log/redis/
- RDB是一个紧凑压缩的二进制文件,代表Redis在某个时间点上的数据快照。非常适用于备份,全量复制等场景。比如每6小时执行bgsave备份,并把RDB文件复制到远程机器或者文件系统中
- Redis加载RDB的恢复速度远快于AOF的方式。
- RDB方式数据没有办法做到实时持久化/秒级持久化。因为bgsave每次运行都要执行fork创建子进程,属于重量级操作,频繁执行成本过高
- RDB文件使用特定二进制格式保存,Redis版本演进过程中有多个RDB版本,兼容性可能有风险。
最大的问题:不能实时的持久化保存数据,两次生成快照之间实时的数据可能会丢失
AOF=》Append Only File
类似mysql中的binlog,会把用户的每个操作,都记录到文件中,当redis重新启动的时候,就会读取AOF文件中的内容,用来恢复数据,当开启AOF,就不会读取RDB了,以AOF为主。
在配置文件中更改
appendonly
为yes
即可开始
使用AOF之后,又要写内存,又要写硬盘,还能和之前一样快吗?
- AOF机制并非直接让工作线程把数据写入硬盘,而实现写入一个内存中的缓冲区,积累到一定的数据之后,才写一次硬盘,大大减少了写硬盘的次数。并且只是顺序写,而不是随机写。
- Redis给出了一些选项,让程序员,根据实际情况来决定怎么取舍(也就是提供了不同的缓冲区刷新策略)。
- 刷新频率越高,性能影响就越大,刷新频率越低,性能影响就越大
always
:命令写入aof_buf后调用fsync同步,每一条命令保存一次erversec
:每一秒保存一次no
:由操作系统来写入,redis不管
- Redis就存在一个机制,能够针对aof文件进行整理操作。这个整理就是能够剔除其中的冗余操作,并且合并了一些操作,达到给aof文件瘦身。
AOF的触发时机
- 手动触发:调用
bgrewriteaof
命令 - 自动触发:根据配置文件中的参数自动确定触发时机
- auto-aof-rewrite-min-size:表示触发重写时AOF的最小文件大小,默认为64MB。
- auto-aof-rewrite-percentage:表示当前AOF占用大小相比较上次重写时增加的比例。
AOF重写流程
父进程仍然负责接受请求,子进程负责针对aof文件进行重写,重写的时候,不关心aof文件中原来都有啥,只是关心内存中最终的数据状态,子进程只需要把内存中当前的数据,获取出来,以AOF的格式写入到一个新的AOF文件中。
子进程只包含了fork之前的数据,fork之后的数据是没有的。
子进程写新的AOF文件的同时,父进程会把新接收到的请求产生的AOF数据先写入到缓冲区再刷新到旧的AOF文件中,并且父进程里又准备了一个aof_rewrite_buf,专门存放fork之后的数据,也就是父进程要写两个缓冲区。等子进程把fork之前的数据写完之后,会通过一个信号通知父进程告诉父进程写完了,父进程此时就会将aof_rewrite_buf中的数据写到新的AOF文件中。
都写完之后就可以用新的AOF文件代替旧的AOF文件。
- 如果,在执行bgrewriteaof的时候,当前redis正在进行aof重写,此时不会再执行aof重写。
- 如果,在执行bgrewriteaof的时候,当发现redis正在生成rdb文件,此时aof重写操作就会等待rdb快照生成完毕之后,再进行aof重写。
rdb对于fork之后的新数据,直接置之不理。aof则对于fork之后新数据,采用aof_rewrite_buf缓冲区来处理。
混合持久化
AOF本来是按照文本的方式来写入文件的。但是文本的方式写入文件,后续加载的成本是比较高的,所以redis就引入了“混合持久化”的方式,结合了rdb和aof的特点。
按照aof的方式,每一个请求/操作,都记录入文件。在触发aof重写之后,就会把当前内存的状态按照rdb二进制格式写入到新的aof文件中。后续再进行的操作,仍然是按照aof文本的方式追加到文件后面。
当redis上同时存在aof文件和rdb快照的时候,以aof为主,rdb直接被忽略。aof包含的数据比rdb更全。
事务
Redis的事务,主要意义是为了打包,避免其它客户端的命令,插队插到中间。Redis如果是按照集群模式部署,不支持事务。
Redis中的ACID和MySQLACID的区别
- 不具备原子性(原子性以MySQL的原子性为主,不仅要能保证一次全部执行,还要能够保证执行成功和回滚操作),把多个操作打包到一起,要么全都执行,要么全都不执行,不保证成功。
- 不具备一致性,redis没有约束,也没有回滚机制,事务执行过程中如果某个修改操作失败,就可能引起不一样的情况。
- 不具备持久性,redis本身就是内存数据库,数据是存储早内存的,虽然redis提供了持久化操作,但是和事务没有什么直接关系。
- 不涉及隔离性,redis是一个单线程模型的服务器程序,所有的请求/事务,都是串行执行的。
Redis中实现事务,是引入了队列(每个客户端都有一个),开启驶入的时候,此时客户端输入的命令,就会发给服务器并且进入这个队列中(而不是执行),当遇到了“执行事务”命令的时候,此时就会把队列中这些任务按照顺序依次执行。
Redis中事务的命令
multi
:开启事务
exec
:执行事务
discard
:放弃当前事务
watch
:必须搭配事务使用,并且必须在multi
之前使用,watch
一个key,会给key设定一个版本号,在执行事务的时候,会进行判定,如果当前key在事务开启到最终执行这个过程中,没有别的客户端修改,才能真正设置,key的版本号与最开始watch时不一样,则表明其它客户端修改过了,因此丢弃当前事务,exec返回nil
Redis分布式部署方式
主从模式
在若干个redis节点中,有的是“主”节点,有的是“从”节点,从节点上的数据要跟随主节点变化,从节点的数据要和主节点的数据保持一致。就是把主节点上的数据复制到从节点,主节点上有任何修改都会同步到从节点中。
从节点就是主节点的副本。在redis主从模式中,从节点上的数据,不允许修改,只能读取数据。
主从模式主要是针对读操作,进行并发量和可用性的提高,实际业务中读操作往往比写操作更频繁。
部署主从
- 配置从节点:配置文件中加上
slaveof masterip masterport
,然后从节点使用该配置文件启动,即可成为从节点。 - 查看主从结构信息:在redis-cli中使用
info replication
命令即可看到当前的角色,从节点上可以看到主节点的ip,port,主节点的id。主节点可以看到当有多少个从节点,数据同步的进度,以及网络的延迟。 - 断开主从复制关系:
slave no one
,当从节点断开主从数据,它就不属于其它节点了,里面有的数据不会抛弃,但是主节点进行修改,从节点就不会继续修改了。
主从复制的基本流程
redis提供了一个psync
命令,完成数据同步的过程,这个命令不需要手动执行,redis会在建立好主从关系之后,自动执行psync
,从节点负责执行psync
,从节点从主节点这边拉去数据。
语法:psync replicationid offset
replicationid
:主节点在启动的时候生成的一个复制节点,即使是同一个主节点,每次重启生成的replicationid
,从节点和主节点建立了复制关系,就会从主节点这边获取到repilicationid
。使用info replication
,即可看到。
offset
:主节点和从节点都会维护这个偏移量,主节点会把修改命令,每个命令的字节数,进行累加。从节点的偏移量,描述了现在从节点数据同步到哪里了,具体的同步进度。
pysnc
:可以从主节点获取全量数据,也可以获取一部分数据,如果offset是-1,就是获取全量数据,如果写一个具体的正整数,则是从当前偏移量位置来进行获取。
不是从节点索要那部分,主节点就给那部分,得看主节点方不方便,如果主节点不方便,就只能全量返回。
全量复制:
在复制的时候,如果主节点收到了新的数据,那么这部分新数据也会通过一系列手段,传输给从节点。
如果从节点开启了aof,在加载数据的过程中,从节点会产生大量的aof日志,由于从节点收到的是大批量的数据,此时产生的aof日志,可能存在一定的冗余信息,因此针对aof日志进行整理,也是必要的过程。
新版redis也支持diskless的方式来传输数据,但是也不能完美的解决复制时的问题。
部分复制:全量复制的特殊情况,优化手段,目的和全量复制一样。
实时复制:从节点,已经和主节点同步好了数据,但是之后,主节点这边也会源源不断的收到新的修改数据的请求,主节点上的数据就会随之改变。也需要能够同步给从节点。
主从复制的缺点:
- 从机多了,复制数据的延时非常明显。
- 主机挂了,从机不会升级成主机,只能通过人工干预的方式更换主节点。
从节点和主节点之间断开连接的两种情况:
- 从节点使用
slaveof no one
主动和主节点断开连接,这个时候,从节点就能够晋升成主节点 - 主节点挂掉了,这个时候从节点不会晋升成主节点,必须通过人工干预的方式,恢复主节点(哨兵机制就是为了解决这个问题)
主从+哨兵(sentinel)
哨兵机制是通过独立的进程来体现的,和之前的redis server是不同的进程。redis-sentinel不负责存储数据,只是对其他的redis-server进程起到监控的作用。
主节点无法启动,手动恢复节点:
- 把选中的从节点,通过slaveof no one,将从节点转换为主节点
- 把其他的从节点,修改slaveof的主节点ip port,连上新的主节点
- 告知客户端,客户端能够连接新的主节点,用来完成修改数据的操作
- 把之前挂了的主节点,修改之后,就可以作为一个新的从节点,挂到这组机器当中
自动恢复:
- 多个sentinel节点,分布在不同的服务器上监听所有的redis服务器
- 如果从节点挂了没关系, 如果主节点挂了,哨兵就要发挥作用了,但是为了防止误判(可能由于sentinel节点网络波动,没有收到主节点的心跳包,误判主节点挂掉了),所以需要多个sentinel节点共同判断是否挂掉。
- 主节点真的挂掉了,此时多个sentinel节点中选择出一个leader,由这个leader去执行主节点的更变。
- 这个sentinel的leader,会根据一定规则从从节点中选择一个节点执行slaveof no one,将该从阶段转换为主节点,然后将其它从节点的主节点更变为这个新的主节点。
哨兵的核心功能:
- 监控
- 自动的故障转移
在分布式系统中,要避免使用”单点”
哨兵配置
port 26379
dir /tmp
# 主节点名称 主节点IP 端口 法定票数
sentinel monitor mymaster 172.31.0.3 6379 2
sentinel auth-pass mymaster redis_pwd
sentinel down-after-milliseconds mymaster 30000
sentinel parallel-syncs mymaster 1
sentinel failover-timeout mymaster 180000
sentinel deny-scripts-reconfig yes
每个哨兵都需要不同的配置文件,因为哨兵在运行过程中会重写配置文件
哨兵重新选取主节点的流程
- 主观下线:哨兵通过心跳包,判定redis服务器是否正常工作,如果心跳包没有如约而至,那么当前哨兵就认为redis服务器挂了,但是此时还不能排除网络波动的影响,因此就只能单方面认为这个redis节点挂了。
- 客观下线:多个哨兵都认为主节点挂了,认为挂了的哨兵节点数达到法定票数,哨兵们就认为这个主节点是客观下线。
- 多个哨兵节点,选出一个leader节点,由这个leader节点进行选择一个从节点,作为新的主节点。
- leader选举完毕,需要挑选一个从节点,作为新的主节点
- 优先级:每个redis数据节点,都会在配置文件中,有一个优先级的设置,slave-priority 优先级高的从节点就会胜出
- offset 最大:offset表明从节点从主节点同步数据的进度,数值越大说明从节点的数据和主节点越接近
- run id:redis启动时随机生成的一串数字,但是走到这一步,就说明优先级和offset都一样,那么选谁都可以。
注意事项
- 哨兵节点不能只有一个
- 哨兵节点最好是奇数个,方便选举leader
- 哨兵节点不负责存储数据
- 哨兵+主从复制解决的问题是“提高可用性”,不能解决“数据极端情况下写丢失”的问题
- 哨兵+主从复制不能提高数据的存储容量,当我们需要存的数据接近或者超过机器的物理内存,这样的结构就难以胜任。
集群模式
广义的集群:只要是多个机器,构成了分布式系统,都可以称为一个“集群”
狭义的集群:redis提供的集群模式,这个集群模式之下,主要是解决存储空间不足的问题。
哨兵模式提高了系统的可用性,但是本质上哈市redis主从节点存储数据,其中就要求一个主/从节点,就得存储整个数据的全集。此处的问题的关键,就是引入多台机器,每台机器存储一部分数据。
把数据分成多份,具体怎么分:
数据分片算法
- 哈希求余
- 一致性哈希
- 哈希槽分区
集群模式故障处理
- 故障判定,需要识别出某个节点是否是挂了。主观下线、客观下线。
- 如果下线的是从节点,就不需要进行处理,如果下线的是主节点,那么该节点的从节点就会触发故障迁移
- 从节点会进行休眠,休眠时间为500ms+随机时间[0,500ms]+排名*1000ms,排名的规则,offset越大排名越高
- 当从节点从休眠中恢复过来,会对集群中的所有节点发起拉票,但是只有集群中的主节点可以投票,每个主节点只有一票。
- 投票完毕,票数最多的从节点会成为新的主节点
以下三种情况会出现集群宕机:
某个分片,所有的从节点和主节点都挂了
某个分片,主节点挂了,但是没有从节点
超过半数的master节点都挂了
前两种情况,都是分片无法提供数据服务了。最后一种情况表示集群遇到了很大的故障问题。
集群扩容
集群扩容操作,是一件风险较高,成本较大的操作
在搬运slows/key的过程中,客户端是否能访问redis集群?
搬运key的时候大部分key是不用搬运的,针对这些未搬运的key,此时可以正常访问的,针对这些正在搬运中的key,是有可能出现访问出错的情况的。
缓存
缓存使用的注意事项:
- 缓存穿透:访问的key在redis和数据库中都不存在
- 缓存雪崩:大量的key在短时间内一起过期
- 缓存击穿:缓存雪崩的特殊情况,热点key过期,大量请求打到数据库上
分布式锁
- value设置为服务器编号
- 看门狗
- redlock