• 22-06-28 西安 redis(02) 持久化机制、入门使用、事务控制、主从复制机制


    一、Redis持久化机制

    2种机制,4种选项

    redis的高性能是由于其将所有数据都存储在了内存中,为了使redis在重启之后仍能保证数据不丢失,需要将数据从内存中同步到硬盘(文件)中,这一过程就是持久化。redis 提供了一系列不同的持久化选项,包含: RDB、AOF、不持久以及RDB+AOF这四种选项

    RDB持久化机制:快照模式

    RDB 持久化以指定的时间间隔执行数据集的快照。RDB持久化方式是redis默认开启的,我们不配置也可以默认使用RDB持久化机制。

    我们要研究的问题:什么时候拍摄快照
    1.基于默认的时间间隔配置

    save <seconds><changes>

    redis.conf 配置文件中的默认配置:

     RDB默认配置的含义:

    配置含义
    save 900 1900秒(15分钟)内至少有一次修改则触发保存操作
    save 300 10300秒(5分钟)内至少有10次修改则触发保存操作
    save 60 1000060秒(1分钟)内至少有1万次修改则触发保存操作

    2.手动调用命令进行快照 save(前台执行) 或者bgsave(后台执行)

    2.1 save: 将redis内存中的数据采用快照的方式保存到磁盘文件中,它是一个同步操作,在它执行持久化的时候,所有redis客户端都不能连接redis服务器执行读写操作

    2.2 bgsave:将redis内存中的数据采用快照的方式保存到磁盘文件中,它是一个异步操作,在它执行持久化的时候是采用子线程执行的,不会影响主线程处理客户端读写请求

    3.调用flushall命令,清空redis16个数据库,并且对已清空的状态做一次快照(斩草除根)
    产生的dump.rdb文件,但里面是空的,没有意义


    4.正常关闭redis服务器,在redis客户端执行shutdown命令,退出之前redis就会执行一次持久化保存


    redis.conf中RDB的相关配置

    配置项取值作用
    save900 1设置RDB的快照时间点,如果为空则禁用RDB
    dbfilename文件名,例如:dump.rdb设置RDB机制下,指定快照文件的名字
    dirRedis工作目录路径指定存放持久化文件的目录(drb和aof都在这个目录)的路径。注意:这里指定的必须是目录不能是文件名

    rdb的优势和缺点

    优势:
    1.rdb文件的体积较小:相对aof的持久化文件
    2.rdb文件数据恢复速度快:相对aof持久化文件

    想听骚话吗?满足你

    1. RDB是一个简洁的按时间点生成的文件,RDB 文件非常适合备份。例如,你可能希望在最近 24 小时内每小时存档一次 RDB 文件,并在 30 天内每天保存一个 RDB 快照。这使您可以在发生灾难时轻松恢复不同版本的数据集。

    2. RDB 非常适合灾难恢复,它是一个可以传输到远程数据中心或者云服务器的简洁文件(文件体积不大),并且进行数据恢复时速度很快(便于数据恢复)

    3. RDB 最大限度地不影响 Redis 的性能,因为 Redis 父进程为了持久化需要做的唯一工作是创建一个能完成所有持久化工作子进程。父进程永远不会执行磁盘 I/O 或类似操作。

    4. 在副本上,RDB 支持重启和故障转移后的部分重新同步。

    redis要做几件事情:1.处理客户端的请求2.执行持久化

    父进程:处理客户端请求
    子进程:执行持久化

    rdb缺点:
    1.容易造成数据丢失
    2.fork 子进程

    想听骚话吗?满足你

    1. 容易造成数据丢失,因为RDB是在一定的时间点才会去进行持久化,例如( 300秒内至少有10次修改则触发保存操作),那么此时如果发生不可预期的关机或断电,则会导致这几分钟之内的数据丢失

    2. RDB 经常需要 fork() 以便使用子进程在磁盘上持久化。如果数据集很大,Fork() 可能会很耗时,如果数据集很大且 CPU 性能不是很好,则可能导致 Redis 停止为客户端服务几毫秒甚至一秒钟。 AOF 也需要 fork() 但你可以调整你想要重写日志的频率,而不会对持久性进行任何权衡。


    AOF持久化机制:追加模式

    它是在持久化文件中记录每一次写操作命令

    AOF 持久化记录服务器收到的每个写操作,这些操作将在服务器启动时再次播放,重建原始数据集。命令使用与 Redis 协议本身相同的格式以仅附加的方式记录。当日志变得太大时,Redis 能够在后台重写日志。

    redis.conf中AOF的基本配置: 

    配置项取值作用
    appendonlyyes启用AOF持久化机制
    no禁用AOF持久化机制[默认值no]
    appendfilename"文件名"AOF持久化文件名
    dirRedis工作目录路径指定存放持久化文件的目录的路径。注意:这里指定的必须是目录不能是文件名
    appendfsyncalways每一次数据修改后都将执行文件写入操作,是最安全的方式但是速度缓慢。
    everysec每秒执行一次写入操作。折中。
    no由操作系统在适当的时候执行写入操作,Redis性能最好,数据保存次数最少。


     
    注意: 当 AOF 和 RDB 机制并存时,Redis 会优先采纳 AOF 机制


    AOF的优势

    数据更难丢失,最多丢失1秒之内的数据

    1. 使用AOF可以让Redis更持久:你可以有不同的 fsync 策略:不进行fsync,每秒进行fsync,每次执行Redis命令都进行fsync。使用 fsync每秒写入性能的默认策略仍然很棒(fsync是使用后台线程执行的,当没有 fsync 正在进行时,主线程将努力执行写入。)但您只能丢失一秒钟的写入。

    2. AOF 日志是仅附加日志,因此在断电时不会出现寻道或损坏问题。即使日志由于某种原因(磁盘已满或其他原因)以半写命令结束,redis-check-aof 工具也能够轻松修复它。

    3. 当 AOF文件变大时,Redis 能够在后台自动重写 AOF。重写是完全安全的,因为当 Redis 继续追加到旧文件时,会使用创建当前数据集所需的最少操作集生成一个全新的文件,一旦第二个文件准备就绪,Redis 就会切换这两个文件并开始追加到新的。

    4. AOF以易于理解和解析的格式包含所有操作的日志。您甚至可以轻松导出 AOF 文件。例如,即使您不小心使用 FLUSHALL删除了所有内容,只要在此期间没有重写日志,您仍然可以通过停止服务器、删除最新命令并重新启动Redis 来保存您的数据集再次。

    AOF的缺点

    1. AOF文件通常比相同数据集的等效 RDB 文件大很多。

    2. AOF持久化数据的恢复速度比RDB要慢很多

    3. AOF可能在特定命令中遇到罕见的错误(例如,有一个涉及像 RPOPLPUSH 这样的阻塞命令)导致生成的 AOF在重新加载时无法重现完全相同的数据集。但这种错误是极其罕见的


    AOF文件重写

    原因是:随着写入操作的执行 ,aof文件体积会越来越大

    重写:经过计算以最少的命令表示当前redis内存中的字符集,从而达到减小AOF持久化文件体积的目的

    文件重写:
    1.得到的是硬盘空间
    2.消耗的是cpu和内存

    什么时候会进行文件重写?(2个条件都满足)

    Redis会根据AOF文件的体积来决定是否进行AOF重写。参考的配置项如下:

    配置项含义
    auto-aof-rewrite-percentage 100文件体积增大100%时执行AOF重写
    auto-aof-rewrite-min-size 64mb文件体积增长到64mb时执行AOF重写

    做如下假设:
    若第一次重写发生在64M,若重写后的文件大小为20M

    那么第二次重写发生在64M(同时满足>40M && >60M),若重写后的文件大小为40M

    那么第三次重写发生在80M(同时满足>80M && > 60M)

    但是呢,老师的配置可不是60MB,老师配置了5G


    修复损坏的AOF文件

    Redis服务器启动时如果读取了损坏的AOF持久化文件会导致启动失败,此时为了让Redis服务器能够正常启动,需要对损坏的持久化文件进行修复

    edis-check-aof --fix appendonly.aof  

    • 第一步:备份要修复的appendonly.aof文件

    • 第二步:执行修复程序

      /usr/local/redis/bin/redis-check-aof --fix /usr/local/redis/appendonly.aof

    • 第三步:重启Redis

    注意:所谓修复持久化文件仅仅是把损坏的部分去掉,而没法把受损的数据找回。


    如何选择Redis的持久化选项

    如果Redis仅仅作为缓存可以不使用任何持久化方式。

    RDB文件只用作后备用途,建议只在Slave上持久化RDB文件,而且只要15分钟备份一次就够了,只保留save 900 1这条规则。

    如果Enalbe AOF,好处是在最恶劣情况下也只会丢失不超过两秒数据,启动脚本较简单只load自己的AOF文件就可以了。代价一是带来了持续的IO,二是AOF rewrite的最后将rewrite过程中产生的新数据写到新文件造成的阻塞几乎是不可避免的。

    只要硬盘允许,应该尽量减少AOF rewrite的频率,AOF重写的基础大小默认值64M太小了,可以设到5G以上。默认超过原大小100%大小时重写可以改到适当的数值。

    如果不开启AOF,仅靠Master-Slave Replication 实现高可用性能也不错。能省掉一大笔IO也减少了rewrite时带来的系统波动。代价是如果Master/Slave同时挂掉,会丢失十几分钟的数据,启动脚本也要比较两个Master/Slave中的RDB文件,载入较新的那个。新浪微博就选用了这种架构。


    二、Jedis

    jedis就是java代码来操作redis。

    在实际项目中,redis 不是给用户直接访问的,而是给 java 程序访问的。

    Redis不仅是使用命令来操作,现在基本上主流的语言都有客户端支持,现在基本上主流的语言都有客户端支持,Java的客户端就有Jedis

    修改Redis配置文件中的bind配置项

    这个其实在之前连接redis的可视化工具的时候就配置过了,

    bind配置项的作用:

    bind后面跟的ip地址是客户端访问Redis时使用的IP地址。规则是:Redis要求客户端访问的地址,必须是 bind 配置项绑定的地址。

    注:将bind的值修改成0.0.0.0也可以实现本机和远程都能连接redis服务器

    当然还有另一种方式可以让Redis被远程客户端访问,那就是关闭Redis的安全模式: 在redis.conf配置文件中将protected-mode的值设置为no

    引入Jedis的依赖

    1. <dependency>
    2. <groupId>redis.clients</groupId>
    3. <artifactId>jedis</artifactId>
    4. <version>2.9.0</version>
    5. </dependency>
    6. <dependency>
    7. <groupId>junit</groupId>
    8. <artifactId>junit</artifactId>
    9. <version>4.13</version>
    10. <scope>test</scope>
    11. </dependency>

    编测试类:有一句很关键的话,jedis要调用的方法名是与redis的命令一一对应的

    1. public void testjedis() {
    2. //指定Redis服务器的IP地址和端口号
    3. Jedis jedis = new Jedis("192.168.2.108", 6379);
    4. //jedis要调用的方法名是与jedis的命令一一对应的
    5. //存储一个键值对
    6. jedis.set("username", "1234567");
    7. //根据key获取值
    8. System.out.println(jedis.get("username"));
    9. //存储一个set字符串集合,无序且唯一
    10. jedis.sadd("hobby","movie","mobile-games","brush-video");
    11. System.out.println(jedis.smembers("hobby"));
    12. //关闭连接
    13. jedis.close();
    14. }


    JedisPool的使用

    JedisPool是Jedis的连接池,依赖不需要额外引入,传递过来了

    测试jedisPool来获取连接

    1. public void testJedisPool(){
    2. //1. 我们可以对连接池进行配置
    3. JedisPoolConfig poolConfig = new JedisPoolConfig();
    4. //最大连接数
    5. poolConfig.setMaxTotal(20);
    6. //最大闲置连接数
    7. poolConfig.setMaxIdle(20);
    8. //最小闲置连接数
    9. poolConfig.setMinIdle(0);
    10. poolConfig.setMaxWaitMillis(3000);
    11. //2. 创建连接池
    12. JedisPool jedisPool = new JedisPool(poolConfig,"192.168.2.108",6379);
    13. //3. 使用连接池获取连接
    14. Jedis jedis = jedisPool.getResource();
    15. System.out.println(jedis.get("username"));
    16. //归还连接
    17. jedis.close();
    18. }


    三、Redis事务控制

    redis事务控制目的:不是为了回滚,而是为了防止别的队列命令插队执行

    redis事务控制相关的命令

    命令名作用
    MULTI表示开始收集命令,后面所有命令都不是马上执行,而是加入到一个队列中。
    EXEC执行MULTI后面命令队列中的所有命令。
    DISCARD放弃执行队列中的命令。
    WATCH“观察“、”监控“一个KEY,在当前队列外的其他命令操作这个KEY时,放弃执行自己队列的命令
    UNWATCH放弃监控一个KEY

    命令队列执行失败的俩种情况

    1.入队过程中某个命令出现了报告错误,执行时整个的所有队列都会被取消。

     2.错误在入队时检测不出来,整个队列执行时有错的命令执行失败,但是其他命令并没有回滚。 

    redis为什么不支持回滚呢?

    官方解释如下:

    如果你有使用关系式数据库的经验, 那么 “Redis 在事务失败时不进行回滚,而是继续执行余下的命令”这种做法可能会让你觉得有点奇怪。以下是这种做法的优点:

    1.Redis 命令只会因为错误的语法而失败(并且这些问题不能在入队时发现),或是命令用在了错误类型的键上面:这也就是说,从实用性的角度来说,失败的命令是由编程错误造成的,而这些错误应该在开发的过程中被发现,而不应该出现在生产环境中。

    2.因为不需要对回滚进行支持,所以 Redis 的内部可以保持简单且快速。 有种观点认为 Redis 处理事务的做法会产生 bug , 然而需要注意的是, 在通常情况下, 回滚并不能解决编程错误带来的问题。 举个例子, 如果你本来想通过 INCR 命令将键的值加上 1 , 却不小心加上了 2 , 又或者对错误类型的键执行了 INCR , 回滚是没有办法处理这些情况的。

    悲观锁和乐观锁

    悲观锁:当锁的持有者操作数据的时候,其它人都无法操作数据。传统的关系型数据库里边就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁。

    乐观锁:当我操作数据的时候,一点都不担心别人也会操作这个数据(不上锁)
    有可能发生多个用户同时操作某一个数据的情况。

    乐观锁版本号机制:

    当我最终要执行的时候,我会检查数据的版本号,如果数据的版本号和之前加入队列时候的版本号一致(说明在这个过程中该数据没有被别人操作过),那么我就真正执行队列。

    如果数据的版本号和之前加入队列时候的版本号不一致(说明在这个过程中有别人已经抢先执行操作这个数据了),那么我就将整个MULTI队列中的操作都被丢弃

    乐观锁:抢占机制,效率高。
    抢占机制:大家可以一起操作,谁先抢到就是谁的

    在执行multi之前,先执行watch key1 [key2],可以监视一个(或多个) key ,如果在事务执行之前这个(或这些) key 被其他命令所改动,那么事务将被打断。这就是乐观锁的具体体现


    四、Redis的主从复制机制

    集群内数据的一致性问题

    1. master主机负责所有写操作
    2. slave从机负责读操作

    搭建主从复制集群思路:

    Redis集群在运行时使用的是同一个可执行文件(redis-server),只是对应的配置文件(redis.conf)不同

     每个配置文件相同的参数是:

    #表示以后台进程的方式启动redis
    daemonize yes
    # 设置redis的工作目录
    dir /usr/local/cluster-redis

     每个配置文件不同的参数是:

    配置项名称作用取值
    portRedis服务器启动后监听的端口号6000 7000 8000
    dbfilenameRDB文件存储文件名dump6000.rdb dump7000.rdb dump8000.rdb
    logfile日志文件位置/var/logs/redis6000.log /var/logs/redis7000.log /var/logs/redis8000.log
    pidfilepid文件位置/var/run/redis6000.pid /var/run/redis7000.pid /var/run/redis8000.pid

    具体的搭建主从复制集群步骤:

    第一步:创建/usr/local/cluster-redis目录 

    mkdir /usr/local/cluster-redis

    第二步:把原始未经修改的redis.conf复制到/usr/local/cluster-redis目录,并且改名为redis6000.conf

    cp /usr/local/redis/redis.conf /usr/local/cluster-redis/redis6000.conf

    第三步:按照既定计划修改redis6000.conf中的相关配置项

    • daemonize yes

    • dir

    • port

    • dbfilename

    • logfile

    • pidfile

    第五步:复制redis6000.conf为redis7000.conf

     cp redis6000.conf redis7000.conf

    第六步:修改redis7000.conf中的相关配置项

    把6000全部替换为7000

    %s/6000/7000/g

    第七步:复制redis6000.conf为redis8000.conf

    第八步:修改redis8000.conf中的相关配置项


    启动Redis主从复制集群

    Redis集群在运行时使用的是同一个可执行文件(redis-server),只是对应的配置文件(redis.conf)不同

    /usr/local/redis/bin/redis-server /usr/local/cluster-redis/redis7000.conf

     使用redis-cli连接指定服务器的命令格式如下:

    /usr/local/redis/bin/redis-cli -h IP地址 -p 端口号

    /usr/local/redis/bin/redis-cli -h 127.0.0.1 -p 7000

    这样就创建好了3个redis客户端

    使用redis-cli停止指定服务器的命令格式如下:

    /usr/local/redis/bin/redis-cli -h IP地址 -p 端口号 shutdown

    /usr/local/redis/bin/redis-cli -h 127.0.0.1 -p 7000 shutdown


    主从关系

    查看主从关系

    刚刚启动的集群服务器中每一个节点服务器都认为自己是主服务器。需要建立主从关系。

    info replication

    配置主从关系

    在从机上指定主机的地址和端口号即可

    slaveof 127.0.0.1 6000  #此时表示本机的6000端口是主机

    取消主从关系

    只需要在从机上执行命令SLAVEOF NO ONE即可,那么此时从机就自己变成了主机

    slaveof no one

    我们可以在主机里写入数据,在从机里查看,当然主机也是可以查看的

    但是在从机里是不能写入数据的,会报如下错误: 

    1.主机挂掉以后,查看从机的状态:

    2.主机挂掉以后再重启从机的状态 

    3.从机SHUTDOWN,此时主机写入数据,从机恢复启动查看状态。重新设定主从关系后看新写入的数据是否同步。

    从机再次重启会认为自己是主机,很飘。

    重启后的从机里的数据

     我们把它变为6000的从机,看看数据是否同步


    五、哨兵模式

    上面有俩个问题:

    question1.当从机挂掉以后,重启从机,从机无法找到它原本的主机
    question2.当主机挂掉之后,从机中无法自动重新选举出一台新的主机,从而导致整个集群都无法为客户端提供服务

    试想有这样一个监控者监控整个集群
    1.它监控到从机挂掉之后重启,就自动给从机找到主机
    2.监控到主机挂掉,就会组织整个集群中的所有节点重新选举出一台新的主机

    这个监控者就叫哨兵

    redis中的哨兵模式sentinel

    监控主机一定要用客观下线、监控从机只需要用主观下线

    通过哨兵服务器监控master/slave可以实现主从复制集群的自动管理

    下线:当哨兵监控到服务器节点挂掉之后,将该服务器节点的状态置为down(下线状态)

    主观下线:有一台哨兵监控到某个服务器节点挂掉了,就将这个服务器节点的状态置为down

    客观下线:
    当监控到服务器节点挂掉的哨兵数量达到了阈值,就将该服务器节点的状态置为down

    这个阈值后续会在哨兵的启动配置文件中指定

    心跳检查机制
    哨兵向服务器节点发送心跳数据包,服务器节点接收到心跳数据包之后,向哨兵响应心跳数据

    配置哨兵

    创建一个哨兵服务器运行所需要的配置文件。

    touch /usr/local/cluster-redis/sentinel.conf

    编辑sentinel.conf文件:

    格式例子
    sentinel monitor 为主机命名 主机IP 主机端口号 将主机判定为下线时需要Sentinel同意的数量sentinel monitor mymaster 127.0.0.1 6000 1

    启动哨兵 

    1.进入/usr/local/redis/bin目录中:

    cd /usr/local/redis/bin

    2.然后执行

    ./redis-server /usr/local/cluster-redis/sentinel.conf --sentinel

      

    剩下的这几个就不是我测试的了,我没测试出来。。各种恶心我呀

    直接用老师测试的结果吧

    从机下线,则哨兵中打印的是

    +sdown主观下线

     再把7000启动,哨兵的监测结果

    -sdown 取消下线状态,并把7000转为6000的从机


    主机6000挂掉,则哨兵中监控的是

    +odown 客观下线

    切换主机以后,把6000和7000变为8000的从机 ,并让6000下线

    6000再上线以后就变成8000的从机了

  • 相关阅读:
    基于.Net Core实现的飞书所有文档一键导出服务(支持多系统)
    Python:让我教你如何让电脑永不息屏
    代码随想录52——动态规划:300最长递增子序列、674最长连续递增序列、 718最长重复子数组
    第四章 路由基础
    结尾:编程指南
    JAVA电商平台免费搭建 B2B2C商城系统 多用户商城系统 直播带货 新零售商城 o2o商城 电子商务 拼团商城 分销商城
    Django笔记十五之in查询及date日期相关过滤操作
    大厂超全安全测试--关于安全测试的分类及如何测试
    封装公共组件中在main.js中通过插件统一注册
    利用SoapUI工具生成Java WebService客户端代码
  • 原文地址:https://blog.csdn.net/m0_56799642/article/details/125501035