Redis6(下)


Redis 与 Spring Boot 整合

整合步骤

1、创建springboot工程

2、引入redis依赖

<!-- redis -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency> <!-- spring2.X 集成 redis 所需 common-pool2-->
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-pool2</artifactId>
    <version>2.6.0</version>
</dependency>

3、配置参数

# Redis 服务器地址
spring.redis.host=192.168.140.136
# Redis 服务器连接端口
spring.redis.port=6379
# Redis 数据库索引(默认为 0)
spring.redis.database= 0
# 连接超时时间(毫秒)
spring.redis.timeout=1800000
# 连接池最大连接数(使用负值表示没有限制)
spring.redis.lettuce.pool.max-active=20
# 最大阻塞等待时间(负数表示没限制)
spring.redis.lettuce.pool.max-wait=-1
# 连接池中的最大空闲连接
spring.redis.lettuce.pool.max-idle=5

4、配置操作类

@EnableCaching
@Configuration
public class RedisConfig extends CachingConfigurerSupport {

    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        RedisSerializer<String> redisSerializer = new StringRedisSerializer();
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(om);
        template.setConnectionFactory(factory);
        //key序列化方式
        template.setKeySerializer(redisSerializer);
        //value序列化
        template.setValueSerializer(jackson2JsonRedisSerializer);
        //value hashmap序列化
        template.setHashValueSerializer(jackson2JsonRedisSerializer);
        return template;
    }

    @Bean
    public CacheManager cacheManager(RedisConnectionFactory factory) {
        RedisSerializer<String> redisSerializer = new StringRedisSerializer();
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
        //解决查询缓存转换异常的问题
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(om);
        // 配置序列化(解决乱码的问题),过期时间600秒
        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
                .entryTtl(Duration.ofSeconds(600))
                .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer))
                .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer))
                .disableCachingNullValues();
        RedisCacheManager cacheManager = RedisCacheManager.builder(factory)
                .cacheDefaults(config)
                .build();
        return cacheManager;
    }
}

Redis_事务_锁机制_秒杀

Redis 的事务定义

Redis 事务是一个单独的隔离操作:事务中的所有命令都会序列化、按顺序地执行。事 务在执行的过程中,不会被其他客户端发送来的命令请求所打断。
Redis 事务的主要作用就是串联多个命令防止别的命令插队。

Multi、Exec、discard

从输入 Multi 命令开始,输入的命令都会依次进入命令队列中,但不会执行,直到输 入 Exec 后,Redis 会将之前的命令队列中的命令依次执行。
组队的过程中可以通过 discard 来放弃组队。
在这里插入图片描述

在这里插入图片描述

事务的错误处理

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

在这里插入图片描述
如果执行阶段某个命令报出了错误,则只有报错的命令不会被执行,而其他的命令都 会执行,不会回滚。

在这里插入图片描述

事务冲突的问题

例子,三个人同时操作账户10000元,造成了账户金额的错误扣减

在这里插入图片描述

悲观锁

在这里插入图片描述
悲观锁(Pessimistic Lock), 顾名思义,就是很悲观,每次去拿数据的时候都认为别人会 修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会 block 直到它 拿到锁。传统的关系型数据库里边就用到了很多这种锁机制,比如行锁,表锁等,读 锁,写锁等,都是在做操作之前先上锁。MySql悲观锁

乐观锁

在这里插入图片描述
乐观锁(Optimistic Lock), 顾名思义,就是很乐观,每次去拿数据的时候都认为别人不 会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这 个数据,可以使用版本号等机制。乐观锁适用于多读的应用类型,这样可以提高吞吐 量Redis 就是利用这种 check-and-set 机制实现事务的。

WATCH key [key …]

相当于给要事务操作的key加上乐观锁
在执行 multi 之前,先执行 watch key1 [key2],可以监视一个(或多个) key ,如果在事务 执行之前这个(或这些) key 被其他命令所改动,那么事务将被打断。

unwatch

取消 WATCH 命令对所有 key 的监视。
如果在执行 WATCH 命令之后,EXEC 命令或 DISCARD 命令先被执行了的话,那么就 不需要再执行 UNWATCH 了。

Redis_事务_秒杀案例

Redis 持久化之 RDB

简介

在指定的时间间隔内将内存中的数据集快照写入磁盘, 也就是行话讲的 Snapshot 快 照,它恢复时是将快照文件直接读到内存里

备份是如何执行的

Redis 会单独创建(fork)一个子进程来进行持久化,会将数据入到 一个临时文件 中,待持久化过程都结束了,再用这个临时文件替换上次持久化好的文件。 整个过程 中,主进程是不进行任何 IO 操作的,这就确保了极高的性能 如果需要进行大规模数 据的恢复,且对于数据恢复的完整性不是非常敏感,那 RDB 方式要比 AOF 方式更加 的高效。RDB的缺点是最后一次持久化后的数据可能丢失

Fork

  • Fork 的作用是复制一个与当前进程一样的进程。新进程的所有数据(变量、环境变量、 程序计数器等) 数值都和原进程一致,但是是一个全新的进程,并作为原进程的子进程
  • 在 Linux 程序中,fork()会产生一个和父进程完全相同的子进程,但子进程在此后多 会 exec 系统调用,出于效率考虑,Linux 中引入了“写时复制技术” 复制临时文件相同空间的内容,在这个空间的基础追加数据,叫写时复制
  • 一般情况父进程和子进程会共用同一段物理内存,只有进程空间的各段的内容要 发生变化时,才会将父进程的内容复制一份给子进程。
  • 一般情况父进程和子进程会共用同一段物理内存,只有进程空间的各段的内容要 发生变化时,才会将父进程的内容复制一份给子进程。

RDB 持久化流程

在这里插入图片描述

dump.rdb 文件

持久化文件
在 redis.conf 中配置文件名称,默认为 dump.rdb
在这里插入图片描述
配置位置
rdb 文件的保存路径,也可以修改。默认为 Redis 启动时命令行所在的目录下
在这里插入图片描述

如何触发 RDB 快照;保持策略

配置文件中默认的快照配置,意味 3600秒内一个key发生变化,触发,或者30秒内10个key变化,或者60秒内10000个key变化
在这里插入图片描述

  • 参数 stop-writes-on-bgsave-error
    在这里插入图片描述
    当 Redis 无法写入磁盘的话,直接关掉 Redis 的写操作。推荐 yes.

  • 参数rdbcompression 压缩文件
    对于存储到磁盘中的快照,可以设置是否进行压缩存储。如果是的话,redis 会采用 LZF 算法进行压缩。
    如果你不想消耗 CPU 来进行压缩的话,可以设置为关闭此功能。推荐 yes.

  • 参数rdbchecksum 检查完整性
    在存储快照后,还可以让 redis 使用 CRC64 算法来进行数据校验, 但是这样做会增加大约 10%的性能消耗,如果希望获取到最大的性能提升,可以关 闭此功能 推荐 yes.

rdb 的备份

先通过 config get dir 查询 rdb 文件的目录
将*.rdb 的文件拷贝到别的地方

rdb 的恢复

  • 关闭 Redis
  • 先把备份的文件拷贝到工作目录下 cp dump2.rdb dump.rdb
  • 启动 Redis, 备份数据会直接加载

优势

  • 适合大规模的数据恢复
  • 对数据完整性和一致性要求不高更适合使用
  • 节省磁盘空间
  • 恢复速度快

劣势

  • Fork 的时候,内存中的数据被克隆了一份,大致 2 倍的膨胀性需要考虑
  • 虽然 Redis 在 fork 时使用了写时拷贝技术,但是如果数据庞大时还是比较消 耗性能。
  • 在备份周期在一定间隔时间做一次备份,所以如果 Redis 意外 down 掉的 话,就会丢失最后一次快照后的所有修改。

动态停止 RDB:redis-cli config set save “”#save 后给空值,表示禁用保存策略

Redis 持久化之 AOF

简介AOF(Append Only File)

以日志的形式来记录每个写操作(增量保存),将 Redis 执行过的所有写指令记录下 来(读操作不记录), 只许追加文件但不可以改写文件,redis 启动之初会读取该文件重 新构建数据,换言之,redis 重启的话就根据日志文件的内容将写指令从前到后执行一 次以完成数据的恢复工作

AOF 持久化流程

(1)客户端的请求写命令会被 append 追加到 AOF 缓冲区内;
(2)AOF 缓冲区根据 AOF 持久化策略[always,everysec,no]将操作 sync 同步到磁盘 的 AOF 文件中;
(3)AOF 文件大小超过重写策略或手动重写时,会对 AOF 文件 rewrite 重写,压缩 AOF 文件容量;
(4)Redis 服务重启时,会重新 load 加载 AOF 文件中的写操作达到数据恢复的目的;

在这里插入图片描述

  • AOF 默认不开启
    可以在 redis.conf 中配置文件名称,默认为appendonly.aof
    AOF 文件的保存路径,同 RDB 的路径一致。
  • AOF 和 RDB 同时开启,redis 听谁的?
    AOF 和 RDB 同时开启,系统默认取 AOF 的数据(数据不会存在丢失)

AOF 启动/修复/恢复

  • AOF 的备份机制和性能虽然和 RDB 不同, 但是备份和恢复的操作同 RDB 一样, 都是拷贝备份文件,需要恢复时再拷贝到 Redis 工作目录下,启动系统即加载。
  • 正常恢复
    修改默认的 appendonly no,改为 yes
    将有数据的 aof 文件复制一份保存到对应目录(查看目录:config get dir)
    恢复:重启 redis 然后重新加载
  • 异常恢复
    修改默认的 appendonly no,改为 yes
    如遇到 AOF文件损坏,通过/usr/local/bin/redis-check-aof--fix appendonly.aof进行恢复
    备份被写坏的 AOF 文件
    恢复:重启 redis,然后重新加载

AOF 同步频率设置

  • appendfsync always
    始终同步,每次 Redis 的写入都会立刻记入日志;性能较差但数据完整性比较好
  • appendfsync everysec
    每秒同步,每秒记入日志一次,如果宕机,本秒的数据可能丢失。
  • appendfsync no
    redis 不主动进行同步,把同步时机交给操作系统。

Redis_主从复制

主机数据更新后根据配置和策略, 自动同步到备机的 master/slaver 机制Master以 写为主,Slave 以读为主

能干嘛

  • 读写分离,性能扩展
  • 容灾快速恢复

在这里插入图片描述

配置主从复制

在这里插入图片描述

在这里插入图片描述
依次启动三台redis
在这里插入图片描述
查看系统进程,看看三台服务器是否启动
在这里插入图片描述
info replication 打印主从复制的相关信息
在 6380 和 6381 上执行: slaveof 127.0.0.1 6379

配置完成 ,在主机上写,在从机上可以读取数据,在从机上写数据报错。

  • 主机挂掉,重启就行,一切如初。
  • 从机重启需重设:slaveof 127.0.0.1 6379

主从复制原理

  1. 当从连接主后,向主发送同步数据请求
  2. 主接收到从到同步请求,把数据持久化为rdb,把rdb发送给从,从读取接收到的rdb
  3. 每次主服务器写操作,向从进行数据同步

全量复制:从 服务在接收到数据库文件数据后,将其存盘并加载到内存中。
增量复制:Master 继续将新的所有收集到的修改命令依次传给 slave,完成同步。

薪火相传

上一个 Slave 可以是下一个 slave 的 Master,Slave 同样可以接收其他 slaves 的连接和 同步请求,那么该 slave 作为了链条中下一个的 master, 可以有效减轻 master 的写压 力,去中心化降低风险。

用 slaveof
主从之间就形成了树状结构
在这里插入图片描述

反客为主

当一个 master 宕机后,后面的 slave 可以立刻升为 master,其后面的 slave 不用做任 何修改。
用 slaveof no one 将从机变为主机。

哨兵模式(sentinel)

反客为主的自动版,能够后台监控主机是否故障,如果故障了根据投票数自动将从库 转换为主库

使用步骤

  • 自定义的/myredis 目录下新建 sentinel.conf 文件,名 字绝不能错
    配置哨兵,填写内容
    sentinel monitor mymaster 127.0.0.1 6379 1
    其中 mymaster 为监控对象起的服务器名称, 1 为至少有多少个哨兵同意迁移的数量。
  • 启动哨兵
    执行 redis-sentinel /myredis/sentinel.conf

当主机挂掉,从机选举中产生新的主机
哪个从机会被选举为主机呢?根据优先级别:replica-priority
优先级在 redis.conf 中默认:replica-priority 100,值越小优先级越高

在java中配置

private static JedisSentinelPool jedisSentinelPool = null;

public static Jedis getJedisFromSentinel() {
    if (jedisSentinelPool == null) {
        Set<String> sentinelSet = new HashSet<>();
        sentinelSet.add("192.168.11.103:26379");
        JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
        jedisPoolConfig.setMaxTotal(10); //最大可用连接数
        jedisPoolConfig.setMaxIdle(5); //最大闲置连接数
        edisPoolConfig.setMinIdle(5); //最小闲置连接数
        jedisPoolConfig.setBlockWhenExhausted(true); //连接耗尽是否等待
        jedisPoolConfig.setMaxWaitMillis(2000); //等待时间
        jedisPoolConfig.setTestOnBorrow(true); //取连接的时候进行一下测试 ping pong 
        jedisSentinelPool = new JedisSentinelPool("mymaster", sentinelSet, jedisPoolConfig);
        return jedisSentinelPool.getResource();
    } else {
        return jedisSentinelPool.getResource();
    }
}

Redis 集群

容量不够,redis 如何进行扩容?
并发写操作, redis 如何分摊?
另外,主从模式,薪火相传模式,主机宕机,导致 ip 地址发生变化,应用程序中配置 需要修改对应的主机地址、端口等信息。
之前通过代理主机来解决,但是 redis3.0 中提供了解决方案。就是无中心化集群配置。

什么是集群
Redis 集群实现了对 Redis 的水平扩容,即启动 N 个 redis 节点,将整个数据库分布存 储在这 N 个节点中,每个节点存储总数据的 1/N。
Redis 集群通过分区(partition)来提供一定程度的可用性(availability): 即使集群 中有一部分节点失效或者无法进行通讯, 集群也可以继续处理命令请求。

集群搭建

在一台主机中,通过不同端口开启6个redis搭建集群

  • 删除rdb文件
    [root@localhost myredis]# rm -rf dum*

  • 制作 6 个实例,端口分别为 6379,6380,6381,6389,6390,6391

  • 给6个实例创建配置文件,注意对应端口号,关闭appendonly

include /myredis/redis.conf 
pidfile /var/run/redis_6379.pid 
port 6379 
dbfilename dump6379.rdb
cluster-enabled yes 
cluster-config-file nodes-6379.conf 
cluster-node-timeout 15000
  • 通过6个配置文件启动6个redis

在这里插入图片描述

  • 将六个节点合成一个集群
    组合之前,请确保所有 redis 实例启动后,nodes-xxxx.conf 文件都生成正常。
    在这里插入图片描述
  • 合体:进入redis的安装目录中的src目录下执行合体命令,使用真实IP地址
    cd /opt/redis-6.2.1/src

redis-cli --cluster create --cluster-replicas 1 192.168.22.10:6379 192.168.22.10:6380 192.168.22.10:6381 192.168.22.10:6389 192.168.22.10:6390 192.168.22.10:6391

–replicas 1 采用最简单的方式配置集群,一台主机,一台从机,正好三组。

  • 输入yes后,开启成功
    在这里插入图片描述
  • -c 采用集群策略连接,设置数据会自动切换到相应的 写主机
    redis-cli -p 6379
    通过 cluster nodes 命令查看集群信息
    在这里插入图片描述

什么是 slots

一个 Redis 集群包含 16384 个插槽(hash slot), 数据库中的每个键都属于这 16384 个插槽的其中一个,集群使用公式 CRC16(key) % 16384 来计算键 key 属于哪个槽

集群中的每个节点负责处理一部分插槽。 举个例子, 如果一个集群可以有主节点, 其中:
节点 A 负责处理 0 号至 5460 号插槽。
节点 B 负责处理 5461 号至 10922 号插槽。
节点 C 负责处理 10923 号至 16383 号插槽。

集群创建完毕是

在集群中录入值

在 redis-cli 每次录入、查询键值,redis 都会计算出该 key 应该送往的插槽,如果不是 该客户端对应服务器的插槽,redis 会报错,并告知应前往的 redis 实例地址和端口。

redis-cli 客户端提供了–c 参数实现自动重定向

redis-cli -c –p 6379 登入后,再录入、查询键值对可以自动重定向。

如图,在6379端口的redis设置k1,计算k1的插槽值为12706,该值属于6381端口的redis,而后自动切换至了6381端口redis
在这里插入图片描述

不在一个 slot 下的键值,是不能使用 mget,mset 等多键操作。

在这里插入图片描述
可以通过{}来定义组的概念,从而使 key 中{}内相同内容的键值对放到一个 slot 中去。

在这里插入图片描述

查询集群中的值

CLUSTER GETKEYSINSLOT <slot><count>返回 count 个 slot 槽中的键。

首先获取键cust的插槽值,然后根据插槽值获取value
在这里插入图片描述

故障恢复

如果主节点下线?从节点能否自动升为主节点?注意:15秒超时

主节点恢复后,主从关系会如何?主节点回来变成从机。
如果某一段插槽的主从都挂掉,而 cluster-require-full-coverage 为 yes ,那么 ,整个 集群都挂掉
如果某一段插槽的主从都挂掉,而 cluster-require-full-coverage 为 no ,那么,该插 槽数据全都不能使用,也无法存储。

redis.conf 中的参数 cluster-require-full-coverage

集群的 Jedis 开发

即使连接的不是主机,集群会自动切换主机存储。主机写,从机读。
无中心化主从集群。无论从哪台主机写的数据,其他主机上都能读到数据。

HostAndPort hostAndPort = new HostAndPort("192.168.22.10", 6379);
JedisCluster jedisCluster = new JedisCluster(hostAndPort);
jedisCluster.set("k1", "v1");
System.out.println(jedisCluster.get("k1"));

Redis 集群提供了以下好处

实现扩容
分摊压力
无中心配置相对简单

Redis 集群的不足

多键操作是不被支持的
多键的 Redis 事务是不被支持的。lua 脚本不被支持
由于集群方案出现较晚,很多公司已经采用了其他的集群方案,而代理或者客户端分 片的方案想要迁移至 redis cluster,需要整体迁移而不是逐步过渡,复杂度较大。

end

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值