https://blog.csdn.net/qq_36299025/article/details/92851603
通常局限点来说,Redis也以消息队列的形式存在,作为内嵌的List存在,满足实时的高并发需求。在使用缓存的时候,redis比memcached具有更多的优势,并且支持更多的数据类型,把redis当作一个中间存储系统,用来处理高并发的数据库操作
什么是Redis持久化?Redis有哪几种持久化方式?优缺点是什么?
持久化就是把内存的数据写到磁盘中去,防止服务宕机了内存数据丢失。
Redis 提供了两种持久化方式:RDB(默认) 和AOF
RDB是Redis默认的持久化方式。按照一定的时间周期策略把内存的数据以快照的形式保存到硬盘的二进制文件。即Snapshot快照存储,对应产生的数据文件为dump.rdb,通过配置文件中的save参数来定义快照的周期。( 快照可以是其所表示的数据的一个副本,也可以是数据的一个复制品。)
AOF:Redis会将每一个收到的写命令都通过Write函数追加到文件最后,类似于MySQL的binlog。当Redis重启是会通过重新执行文件中保存的写命令来在内存中重建整个数据库的内容。
当两种方式同时开启时,数据恢复Redis会优先选择AOF恢复。
RDB的持久化方式是通过快照方式完成的,当符合某种规则时,会将内存中的数据全量生成一份副本存到硬盘行,这个过程称为“快照”。
快照执行原理
(1) Redis使用fork函数复制一份当前进程(父进程)的副本(子进程)
(2) 父进程继续处理来自客户端的请求,子进程开始将内存中的数据写入硬盘中的临时文件
(3) 当子进程写完所有的数据后,用该临时文件替换旧的RDB文件,至此,一次快照操作完成
(4) 注意:在执行fork的时候操作系统(类Unix操作系统)会使用写时复制(copy-on-write)策略,即fork函数发生的一刻,父进程和子进程共享同一块内存数据,当父进程需要修改其中的某片数据(如执行写命令)时,操作系统会将该片数据复制一份以保证子进程不受影响,所以RDB文件存储的是执行fork操作那一刻的内存数据。所以RDB方式理论上是会存在丢数据的情况的(fork之后修改的的那些没有写进RDB文件)
快照备份规则
(1) 根据配置规则进行自动快照:在配置文件redis/etc/redis.conf内,进行自定义快照条件。在快照条件缺省的条件下,快照会存放在简介中介绍的dump.rdb文件中。自定义快照条件则是通过「save time(时长, 单位s) num(变化key的个数) 」对进行快照备份的频率进行设置,例如「 save 900 1 」表示15分钟内1个key发生变化,进行快照备份。每个快照条件单独占一行,条件之间是‘or '或的关系,满足任何一个就进行自动快照备份
(2) 用户执行SAVE,BGSAVE命令:除了让Redis自动进行快照外,当我们需要重启,迁移,备份Redis时,我们也可以手动执行SAVE或BGSAVE命令主动进行快照操作。
(3) 执行FLUSHALL命令:当执行FLUSHALL命令时,Redis会清除数据库中的所有数据。需要注意的是:不论清空数据库的过程是否触发了自动快照的条件,只要自动快照条件不为空,Redis就会执行一次快照操作,当没有定义自动快照条件时,执行FLUSHALL命令不会进行快照操作。
(4) 执行复制(replication)操作:当设置了主从模式时,Redis会在复制初始化时进行自动快照。
数据迁移
问题:有三个Redis,分别命名为Redis1、Redis2、Redis3;现在希望将Redis1和Redis2中的数据迁移到Redis3中,同时不希望Reds3中rdb的数据丢失,那么如何将三个rdb文件融合为一个呢?
解决方式一:将Rdeis3先设置为Redis1的从数据库,进行数据同步;然后在将Redis3设置为Redis2的从数据库,进行数据同步。
解决方式二:(注意该方法适合用于临时恢复和导出数据,且需要关闭AOF持久化功能)
(1) 复制RDB文件:将Redis1的dump1.rdb、Redis2的dump2.rdb复制到Redis3的data目录下
(2) 获取RDB文件大小:dump1.rdb大小 = 27441041、dump2.rdb大小 = 37、dump3.rd大小 = 570214520
(3) 按照文件合并顺序截取文件:dump1.rdb为文件头,去掉尾部的9个字节:「dd bs=1 if=dump1.rdb of=dump.rdb count=27441032」
dump2.rdb为中间文件,去掉头部的11个字节,去掉尾部的9个字节:「dd bs=1 if=dump2.rdb of=res2.rdb count=17」
dump3.rdb为尾部文件,去掉头部的11个字节,去掉北部的8个字节:「dd bs=1 if=dump3.rdb of=res3.rdb count=570214501」
(4) 合并截取文件:cat res2.rdb >> dump.rdb
cat res3.rdb >> dump.rdb
(5) 验证备份文件:执行命令「redis-check-rdb dump.rdb 」
(6) 修改配置文件:将redis.conf中的rdbchecksum设置为no
(7) 重启Redis3,所有备份数据在数据库内生效
AOF的持久化方式是将Redsi执行的每一条命令追加写到硬盘文件中,这一过程坑你会降低Redis的性能,但大部分情况这个影响是可以接收的,默认情况下,AOF的持计划功能是关闭的,可以通过修改redis.conf文件的配置进行功能开启,本分文件的存储路径与dump.rdb是一致的。
实现过程
(1) 概述:AOF以纯文本的形式记录Redis执行的写命令,将Redis按照上面提到的配置打开AOF持久化功能,执行命令「set test ceshi」,打开appendonly.aof 查看持久化内容信息,其文本中记录命令的规则这里不展开细说,感兴趣可以深入学习一下
(2) AOF文件重写:AOF记录用户操作Redis的命令行,但如果每一条命令行都记录,那appendonly.aof文件会变的特别大,所以Redis设计了AOF文件重写机制。重写机制就是重新生成一份AOF文件,新的AOF文件中一条记录的操作只会有一次,而不像老文件中存在对同一数值进行多次操作的现象。重写的实现过程与RDB备份快照的过程类似,也是fork一个进程,直接遍历数据,写入新的AOF临时文件。在写入新文件的过程中,所有的写操作日志还是会写到原来老的AOF文件中,同时还会记录在内存缓冲区中。当重完操作完成后,会将所有缓冲区中的日志一次性写入到临时文件中。然后调用原子性的rename命令用新的 AOF文件取代老的AOF文件。
如下图中的操作,重写后的AOF文件只会记录最后一条命令「set test ceshi3」
AOF文件重写规则
(1) 根据配置规则进行自动重写:在配置文件redis/etc/redis.conf内,进行自定义重写条件。
通常以上两个配置是一起使用
(2) 用户执行BGREWRITEAOF命令进行重写:执行BGREWRITEAOF命令后,AOF文件中仅保留了「set test ceshi3」命令
同步硬盘
虽然每次执行更改数据库的内容时,AOF都会记录执行的命令,但是由于操作系统本身的硬盘缓存的缘故,AOF文件的内容并没有真正地写入硬盘,在默认情况下,操作系统会每隔30s将硬盘缓存中的数据同步到硬盘,但是为了防止系统异常退出而导致丢数据的情况发生,我们还可以在Redis的配置文件中配置这个同步的频率
二者选择的标准,就是看系统是愿意牺牲一些性能,换取更高的缓存一致性(AOF),还是愿意写操作频繁的时候,不启用备份来换取更高的性能,待手动运行save的时候,再做备份(RDB)
RDB的优势:(1) 数据可移植性好:仅存在一个数据备份文件,对数据压缩后可以轻松转移到其他存储介质上;(2) 性能最大化:RDB本分方式只需fork一个子进程,由子进程完成全部持久化工作,极大的避免了服务进程执行IO操作,当数据集很大时,其效率也高于AOF机制;
RDB的劣势:(1) 在数据未持久化之前出现宕机现象,那么未持久化的数据会丢失;(2) 当数据集较大时,fork子进程的备份工作会导致服务整体停止对外服务几百毫秒,甚至1秒
AOF的优势:(1) 数据安全性更高:写Redis命令采用append的模式记录,出现宕机,原有数据不会丢失,写入了一半数据就出现了系统崩溃问题,不用担心,在Redis下一次启动之前,我们可以通过redis-check-aof工具来帮助我们解决数据一致性的问题;
AOF的劣势:(1) 相同数据集,AOF文件大于RDB文件,且恢复速度慢于RDB;(2) 数据同步效率低于RDB
原理概述:一主多从 (1个master,多个slave) ;全量同步&增量同步。
一主多从:主数据库进行读写操作,当发生写操作时自动将数据同步到从数据库;从数据库一般只进行读操作,并接收主数据库同步过来的数据,一个主数据库可以有多个从数据库,但一个从数据库只能有一个主数据库。通过这种架构能够很好的实现读写分离,从而提高服务器的负载能力。
全量同步:
增量同步:
全量&增量同步选择:
redis主从配置:
(1)在slave的redis.conf中修改配置信息,ip : port对应master的ip : port,masterauth参数对应master的密码,有则配置,没有则不用关
(2)启动slave:./bin/redis-server etc/redis.conf
(3)登录master:./redis-cli -p 'port',输入info,查看slave是否配置成功;
(4)登录slave也可以查看:./redis-cli -p 'port',输入info
(5)如果master挂了,需要将slave切换成master登录slave:./redis-cli -p 'port',执行:SLAVEOF NO ONE,执行:info
(6)master修复后,可在slave上执行:SLAVEOF 127.0.0.1 'port'(SLAVEOF master_ip master_port),重新将其挂接在master上
原子性概念:一个事务中的所有操作,要么全部完成,要不全部不完成,不会结束在之间某个环节,Redis的原子性就是指其操作命令要么执行,要么不执行
Redis操作原子性的原因:Redis是单线程的,线程是操作系统运算调度的最小单元
MULTI, EXEC, DISCARD and WATCH 是Redis事务的基础。用来显式开启并控制一个事务,它们允许在一个步骤中执行一组命令。并提供两个重要的保证:
事务中的所有命令都会被序列化并按顺序执行。在执行Redis事务的过程中,不会出现由另一个客户端发出的请求。这保证 命令队列 作为一个单独的原子操作被执行。
队列中的命令要么全部被处理,要么全部被忽略。EXEC命令触发事务中所有命令的执行,因此,当客户端在事务上下文中失去与服务器的连接,
如果发生在调用MULTI命令之前,则不执行任何commands;
如果在此之前EXEC命令被调用,则所有的commands都被执行。
1、Redis是纯内存操作,需要的时候需要我们手动持久化到硬盘中
2、Redis是单线程,从而避开了多线程中上下文频繁切换的操作。
3、Redis数据结构简单、对数据的操作也比较简单
4、使用底层模型不同,它们之间底层实现方式以及与客户端之间通信的应用协议不一样,Redis直接自己构建了VM 机制 ,因为一般的系统调用系统函数的话,会浪费一定的时间去移动和请求
5、使用多路I/O复用模型,非阻塞I/O
多路I/O复用: I/O 多路复用技术是为了解决进程或线程阻塞到某个 I/O 系统调用而出现的技术,可以监视多个描述符,一旦某个描述符就绪(一般是读就绪或者写就绪,就是这个文件描述符进行读写操作之前),能够通知程序进行相应的读写操作
问题:比如你redis只能存5G数据,可是你写了10G,那会删5G的数据。怎么删的,这个问题思考过么?还有,你的数据已经设置了过期时间,但是时间到了,内存占用率还是比较高,有思考过原因么?
策略:定期删除 + 惰性删除策略 + 内存淘汰机制
定期删除策略的工作原理:定时删除,用一个定时器来负责监视key,过期则自动删除。虽然内存及时释放,但是十分消耗CPU资源。在大并发请求下,CPU要将时间应用在处理请求,而不是删除key,因此没有采用这一策略。
定期删除 + 惰性删除策略的工作原理:定期删除,redis默认每个100ms检查,是否有过期的key,有过期key则删除。需要说明的是,redis不是每个100ms将所有的key检查一次,而是随机抽取进行检查(如果每隔100ms,全部key进行检查,redis岂不是卡死)。因此,如果只采用定期删除策略,会导致很多key到时间没有删除。于是,惰性删除派上用场。也就是说在你获取某个key的时候,redis会检查一下,这个key如果设置了过期时间那么是否过期了?如果过期了此时就会删除。
内存淘汰机制:如果定时删除没有删除key,用户也没有及时请求key,即惰性删除也没有生效,这样redis的内存使用率会越来越高,那么就需要使用内存淘汰机制。在redis/etc/redis.conf中会有内存淘汰机制的配置规则:
该配置为redis内存淘汰策略的设定配置:
不推荐使用redis的事务机制。因为我们的生产环境,基本都是redis集群环境,做了数据分片操作。你一个事务中有涉及到多个key操作的时候,这多个key不一定都存储在同一个redis-server上。因此,redis的事务机制,十分鸡肋。
(1)如果对这个key操作,不要求顺序: 准备一个分布式锁,大家去抢锁,抢到锁就做set操作即可
(2)如果对这个key操作,要求顺序: 分布式锁+时间戳。 假设这会系统B先抢到锁,将key1设置为{valueB 3:05}。接下来系统A抢到锁,发现自己的valueA的时间戳早于缓存中的时间戳,那就不做set操作了。以此类推。
(3) 利用队列,将set方法变成串行访问也可以
redis遇到高并发,如果保证读写key的一致性
对redis的操作都是具有原子性的,是线程安全的操作,你不用考虑并发问题,redis内部已经帮你处理好并发的问题了。
1.哨兵(Sentinel)和复制(Replication)
Redis服务器毫无征兆的罢工是个麻烦事,如何保证备份的机器是原始服务器的完整备份呢?这时候就需要哨兵和复制。
哨兵Sentinel可以管理多个Redis服务器,它提供了监控,提醒以及自动的故障转移的功能,Replication则是负责让一个Redis服务器可以配备多个备份的服务器。
Redis也是利用这两个功能来保证Redis的高可用的。
2.事务
很多情况下我们需要一次执行不止一个命令,而且需要其同时成功或者失败。redis对事务的支持也是源自于这部分需求,即支持一次性按顺序执行多个命令的能力,并保证其原子性。
3.LUA脚本
在事务的基础上,如果我们需要在服务端一次性的执行更复杂的操作(包含一些逻辑判断),则lua就可以排上用场了。
4.持久化
redis的持久化指的是redis会把内存的中的数据写入到硬盘中,在redis重新启动的时候加载这些数据,从而最大限度的降低缓存丢失带来的影响。
5.集群(Cluster)
单台服务器资源的总是有上限的,CPU资源和IO资源我们可以通过主从复制,进行读写分离,把一部分CPU和IO的压力转移到从服务器上,这也有点类似mysql数据库的主从同步。
多线程处理会涉及到锁,而且多线程处理会涉及到线程切换而消耗CPU。因为CPU不是Redis的瓶颈,Redis的瓶颈最有可能是机器内存或者网络带宽。单线程无法发挥多核CPU性能,不过可以通过在单机开多个Redis实例来解决。
1、速度快,因为数据存在内存中,类似于HashMap,HashMap的优势就是查找和操作的时间复杂度都是O(1)
2、支持丰富数据类型,支持string,list,set,sorted set,hash
3、支持事务,操作都是原子性,所谓的原子性就是对数据的更改要么全部执行,要么全部不执行
4、丰富的特性:可用于缓存,消息,按key设置过期时间,过期后将会自动删除
通过增加Slave DB的数量,读的性能可以线性增长。为了避免Master DB的单点故障,集群一般都会采用两台Master DB做双机热备,所以整个集群的读和写的可用性都非常高。
读写分离架构的缺陷在于,不管是Master还是Slave,每个节点都必须保存完整的数据,如果在数据量很大的情况下,集群的扩展能力还是受限于单个节点的存储能力,而且对于Write-intensive类型的应用,读写分离架构并不适合。
为了解决读写分离模型的缺陷,可以将数据分片模型应用进来。
可以将每个节点看成都是独立的master,然后通过业务实现数据分片。
结合上面两种模型,可以将每个master设计成由一个master和多个slave组成的模型。
(1) Master最好不要做任何持久化工作,如RDB内存快照和AOF日志文件
(2) 如果数据比较重要,某个Slave开启AOF备份数据,策略设置为每秒同步一次
(3) 为了主从复制的速度和连接的稳定性,Master和Slave最好在同一个局域网内
(4) 尽量避免在压力很大的主库上增加从库
(5) 主从复制不要用图状结构,用单向链表结构更为稳定,即:Master <- Slave1 <- Slave2 <- Slave3...
这样的结构方便解决单点故障问题,实现Slave对Master的替换。如果Master挂了,可以立刻启用Slave1做Master,其他不变。