【Java中操作Redis+Redis持久化+Redis淘汰策略+高可用】

1. Redis概述

  1. Redis的key都是字符串在这里插入图片描述
  • Redis通用命令

在这里插入图片描述

  • Redis底层数据结构:

在这里插入图片描述

  • 图解:
  • 在这里插入图片描述

2.Redis五种常见数据类型使用命令和使用场景

2.1 string

  • 字符串(string)类型:普通字符串,Redis中最简单的数据类型
  • 存储的形式: {key : value}
  • 应用场景:它通常适合做简单键值对的缓存.
  1. 验证码 ------ setnx
  2. 计数器 ----- incr------文章阅读数
  3. 热点数据
  4. 分布式锁(分布式session) ----- 登录
  • string常见命令

在这里插入图片描述

2.2 hash

  • 哈希(hash):也叫散列,类似于Java中的HashMap结构

  • 存储的形式: {key : [{field1 : value1} , {field2 : value2}]}

  • 应用场景:它通常适合存储结构化的数据,比如一个对象

  • 购物车,以用户ID为key,商品ID为field,商品数量为value

  • hash常见命令:

在这里插入图片描述

2.3 list

  • 列表(list):左右都可以存储数据,按照插入顺序排序,可以有重复元素,类似于栈结构,读取时,是从左到右
  • 存储的形式: {key : [value1 , value2]}
  • 应用场景:它通常适合存储顺序排序的集合数据,消息队列,保证顺序消费
  • list常见命令:

在这里插入图片描述

2.4 set

  • 集合(set):无序集合,没有重复元素,类似于Java中的HashSet
  • 存储的形式: {key : [value1 , value2]}
  • 应用场景:因为它是无序,不重复的,我们通常会用它来存储一些需要求交集,并集,差集操作的数据
  1. 微博的共同关注
  2. 微信朋友圈
  3. 公司抽奖
  • set常见命令:

在这里插入图片描述

2.5 zset

  • 有序集合(sorted set/zset):集合中每个元素关联一个分数(score),根据分数升序排序,没有重复元素

  • 存储的形式: {key : [{score1 , value1} , {score2 , value2}]}

  • 应用场景:因为它是根据分数进行排序的,往往用来存储需要排名的数据

  1. 排行榜
  2. 延时队列-----zset数据类型的去重有序(分数排序)特点进行延迟。例如:时间戳作为score进行排序
    在这里插入图片描述
  • zset常见命令:

在这里插入图片描述

2.6 Java中操作Redis

  • SpringDataRedis
  1. 概述:Spring官方基于Java开发的一个操作Redis的框架,底层对Redis命令做了封装
  2. 官网:https://spring.io/projects/spring-data-redis
  3. 核心类库:RedisTemplate ,StringRedisTemplate 专门操作字符串的模板
  • Java中所需依赖
		<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
  • Redis的序列化操作:
  1. 概述:SpringDataRedis在操作Redis时,会对操作的数据进行序列化操作,默认采用的序列化器为 JdkSerializationRedisSerializer。不方便通过可视化工具查看
/**
 * 定制redis序列化器
 * @Description
 * @Author Wcmy.m
 * @Date 2023-04-19 19:08:03
 */
@Configuration
@Slf4j
public class RedisConfiguration {

    @Bean
    public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory){
        log.info("开始创建redis模板对象...");
        RedisTemplate redisTemplate = new RedisTemplate();
        //设置redis的连接工厂对象
        redisTemplate.setConnectionFactory(redisConnectionFactory);
        //设置redis key的序列化器
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        return redisTemplate;
    }
}
  • 操作五种常见的数据类型
 @Autowired
    private StringRedisTemplate stringRedisTemplate;
    @Autowired
    private RedisTemplate redisTemplate;

    @Test
    public void stringRedisTemplateTest() {
        stringRedisTemplate.opsForValue().set("gender","femeal",50,TimeUnit.SECONDS);
        String gender = stringRedisTemplate.opsForValue().get("gender");
        System.out.println(gender);
    }

    /**
     * string测试
     */
    @Test
    public void stringTest(){
        //设置值
        redisTemplate.opsForValue().set("name","jack",600, TimeUnit.SECONDS);
        //获取值
        String name = (String) redisTemplate.opsForValue().get("name");
        System.out.println(name);

        /*
            这个方法等同于redis中的setnx
            1.存在,不做操作
            2.不存在,则设置
             用来做分布式锁的
         */
        redisTemplate.opsForValue().setIfAbsent("lock","one");
        redisTemplate.opsForValue().setIfAbsent("lock","two");
    }

    /**
     * hash测试
     */
    @Test
    public void hashTest(){
        //1.设置值 的时候也可以设置过期时间
        HashOperations hash = redisTemplate.opsForHash();
        //1.1放入单个键值对
        hash.put("product","name","荒天帝");
        hash.put("product","price",15000);

        HashMap<String, Object> map = new HashMap<>();
        map.put("address","石村");
        map.put("color","pink");

        //1.2放入一个集合
        hash.putAll("product",map);

        //2获取值
        //2.1获取单个key
        Object name = hash.get("product", "name");
        System.out.println(name);
        //2.2获取所有的key
        Set keys = hash.keys("product");
        System.out.println(keys);
        //2.3获取所有值
        List values = hash.values("product");
        System.out.println(values);
        //2.4获取所有的键值对
        Map entries = hash.entries("product");
        System.out.println(entries);
    }

    /**
     * 大多数是用来放列表的
     * list测试
     */
    @Test
    public void listTest() {
        ListOperations list = redisTemplate.opsForList();
        //1设置值
        //1.1左压栈
        list.leftPushAll("names","jack","rose","lucy");
        //2获取值
        List names = list.range("names", 0, -1);
        System.out.println("names = " + names);
    }

    /**
     * set(无序集合)测试
     */
    @Test
    public void setTest() {
        SetOperations set = redisTemplate.opsForSet();
        //1设置值
        set.add("hobbies","java","c","php");
        //2获取值
        System.out.println(set);
        //设置值
        set.add("set1","java","c","php","game");
        set.add("set2","java","c","php","study");
        //差集取得是set1
        Set difference = set.difference("set1", "set2");
        System.out.println(difference);
        //取交集
        Set intersect = set.intersect("set1", "set2");
        System.out.println(intersect);
        //取并集
        Set union = set.union("set1", "set2");
        System.out.println(union);
    }

    /**
     * zset(有序集合)测试
     */
    @Test
    public void zsetTest() {
        ZSetOperations zSet = redisTemplate.opsForZSet();
        //1设置值
        zSet.add("战力榜","张飞",99.1);
        zSet.add("战力榜","关羽",99.8);
        zSet.add("战力榜","赵云",99.7);
        zSet.add("战力榜","刘备",95);
        zSet.add("战力榜","吕布",99.9);
        //2获取一定范围的值
        Set<ZSetOperations.TypedTuple> zset = zSet.rangeWithScores("战力榜", 0, -1);
        for (ZSetOperations.TypedTuple typedTuple : zset) {
            System.out.println(typedTuple.getValue()+":"+typedTuple.getScore());
        }
        //只能得到key
        Set zset1 = zSet.range("战力榜", 0, -1);
        System.out.println(zset1);
    }

3. Redis持久化

  • Redis数据存储在内存,一般不是重要的数据,可能会丢失。持久化就是把内存的数据写到磁盘中去,防止服务宕机了内存数据丢失。

2.1Redis 的持久化机制

  • Redis 提供两种持久化机制 RDB(默认) 和 AOF 机制
  • RDB

RDB是 Redis DataBase 缩写快照,是 Redis 默认的持久化方式。按照一定的时间将内存的数据以快照的形式保 存到硬盘中,对应产生的数据文件为 dump.rdb。通过配置文件中的 save 参数来 定义快照的周期。
在这里插入图片描述

  • 优点:
  • 只有一个文件 dump.rdb,方便持久化。
  • 容灾性好,一个文件可以保存到安全的磁盘
  • 性能最大化,fork 子进程来完成写操作,让主进程继续处理命令,所 以是 IO 最大化。使用单独子进程来进行持久化,主进程不会进行任何 IO 操作,保证了 redis 的高性能
  • 相对于数据集大时,比 AOF 的启动效率更高。
  • 缺点:

数据安全性低。RDB 是间隔一段时间进行持久化,如果持久化之间 redis 发生故障,会发生数据丢失。所以这种方式更适合数据要求不严谨 的时候

  • AOF

AOF 持久化(即 Append Only File 持久化),是将 Redis 执行的每次写命令记录 到单独的日志文件中,当重启 Redis 会重新将持久化的日志中文件恢复数据。 当两种方式同时开启时,数据恢复 Redis 会优先选择 AOF 恢复。
在这里插入图片描述

  • 优点:
  • 数据安全,aof 持久化可以配置 appendfsync 属性,有 always,每 进行一次 命令操作就记录到 aof 文件中一次。
  • 通过 append 模式写文件,即使中途服务器宕机,可以通过 redischeck-aof 工具解决数据一致性问题。
  • AOF 机制的 rewrite 模式。AOF 文件没被 rewrite 之前(文件过大 时会对命令 进行合并重写),可以删除其中的某些命令(比如误操作的 flushall))
  • 缺点:
  • AOF 文件比 RDB 文件大,且恢复速度慢。
  • 数据集大的时候,比 rdb 启动效率低。
  • 数据恢复:先恢复AOF,再补充RDB

4. Redis淘汰策略

  • Redis数据存储在内存,如果内存被打满了?就会触发淘汰策略

  • 八大淘汰策略

  1. 一般会选择 volatile-lru在这里插入图片描述
  • Redis过期删除策略
  • 惰性删除
  1. 惰性删除指的是当我们查询key的时候才对key进⾏检测,如果已经达到过期时间,则删除。显然,他有⼀个缺点就是如果这些过期的key没有被访问,那么他就⼀直⽆法被删除,⽽且⼀直占⽤内存。
  • 定期删除
  1. 定期删除指的是Redis每隔⼀段时间对数据库做⼀次检查,删除⾥⾯的过期key。由于不可能对所有key去做轮询来删除,所以Redis会每次随机取⼀些key去做检查和删除。
  2. redis中的key如果过期了,是不是会立即被删除?
  3. 客户端来查询一个key,redis会先判断该key是否过期,不过期直接返回。过期就删除
  • 通过上面的介绍,相信大家对Redis的过期数据删除策略和内存淘汰策略有一定的了解了。这里总结一下:
  1. Redis过期删除策略是采用惰性删除和定期删除这两种方式组合进行的,惰性删除能够保证过期的数据我们在获取时一定获取不到,而定期删除设置合适的频率,则可以保证无效的数据及时得到释放,而不会一直占用内存数据。
  2. 但是我们说Redis是部署在物理机上的,内存不可能无限扩充的,当内存达到我们设定的界限后,便自动触发Redis内存淘汰策略,而具体的策略方式要根据实际业务情况进行选取。

5. Redis的高可用

5.1 主从模式:

在这里插入图片描述

  • 机器启动:首先启动主节点,然后一台一台启动从节点。
  • 主从复制的机制:
  1. 从数据库连接主数据库,发送SYNC命令;
  2. 主数据库接收到SYNC命令后,可以执行BGSAVE命令生成RDB文件并使用缓冲区记录此后执行的所有写命令;
  3. 主数据库BGSAVE执行完后,向所有从数据库发送快照文件,并在发送期间继续记录被执行的写命令;
  4. 从数据库收到快照文件后丢弃所有旧数据,载入收到的快照;
  5. 主数据库快照发送完毕后开始向从数据库发送缓冲区中的写命令;
  6. 从数据库完成对快照的载入,开始接受命令请求,并执行来自主数据库缓冲区的写命令;(从数据库初始化完成)
  7. 主数据库每执行一个写命令就会向从数据库发送相同的写命令,从数据库接收并执行收到的写命令(从数据库初始化完成后的操作)
  8. 出现断开重连后,2.8之后的版本会将断线期间的命令传给从数据库,增量复制。
  9. 主从刚刚连接的时候,进行全量同步;全同步结束后,进行增量同步。当然,如果有需要,slave在任何时候都可以发起全量同步。Redis的策略是,无论如何,首先会尝试进行增量同步,如不成功,要求从机进行全量同步。
    在这里插入图片描述
  • 主从模式的优缺点:
  • 优点
  1. 支持主从复制,主机会自动将数据同步到从机,可以进行读写分离;
  2. 为了分载Master的读操作压力,Slave服务器可以为客户端提供只读操作的服务,写服务依然必须由Master来完成;
  3. Slave同样可以接受其他Slaves的连接和同步请求,这样可以有效地分载Master的同步压力;
  4. Master是以非阻塞的方式为Slaves提供服务。所以在Master-Slave同步期间,客户端仍然可以提交查询或修改请求;
  5. Slave同样是以阻塞的方式完成数据同步。在同步期间,如果有客户端提交查询请求,Redis则返回同步之前的数据。
  • 缺点
  1. Redis不具备自动容错和恢复功能,主机从机的宕机都会导致前端部分读写请求失败,需要等待机器重启或者手动切换前端的IP才能恢复;
  2. 主机宕机,宕机前有部分数据未能及时同步到从机,切换IP后还会引入数据不一致的问题,降低了系统的可用性;
  3. 如果多个Slave断线了,需要重启的时候,尽量不要在同一时间段进行重启。因为只要Slave启动,就会发送sync请求和主机全量同步,当多个Slave重启的时候,可能会导致Master IO剧增从而宕机。
  4. Redis较难支持在线扩容,在集群容量达到上限时在线扩容会变得很复杂;
  5. redis的主节点和从节点中的数据是一样的,降低的内存的可用性

5.2 哨兵模式

在这里插入图片描述

  • 概述:
  1. 主从模式下,当主服务器宕机后,需要手动把一台从服务器切换为主服务器,这就需要人工干预,费事费力,还会造成一段时间内服务不可用。这种方式并不推荐,实际生产中,我们优先考虑哨兵模式。这种模式下,master宕机,哨兵会自动选举master并将其他的slave指向新的master。
  2. 在主从模式下,redis同时提供了哨兵命令redis-sentinel,哨兵是一个独立的进程,作为进程,它会独立运行。其原理是哨兵进程向所有的redis机器发送命令,等待Redis服务器响应,从而监控运行的多个Redis实例。
  3. 哨兵可以有多个,一般为了便于决策选举,使用奇数个哨兵。哨兵可以和redis机器部署在一起,也可以部署在其他的机器上。多个哨兵构成一个哨兵集群,哨兵直接也会相互通信,检查哨兵是否正常运行,同时发现master宕机哨兵之间会进行决策选举新的master
    在这里插入图片描述
  • 哨兵模式的作用 (哨兵很像kafka集群中的zookeeper的功能):
  1. 通过发送命令,让Redis服务器返回监控其运行状态,包括主服务器和从服务器;
  2. 当哨兵监测到master宕机,会自动将slave切换到master,然后通过发布订阅模式通过其他的从服务器,修改配置文件,让它们切换主机;
  3. 然而一个哨兵进程对Redis服务器进行监控,也可能会出现问题,为此,我们可以使用多个哨兵进行监控。各个哨兵之间还会进行监控,这样就形成了多哨兵模式。
  • 哨兵模式的优缺点:
  • 优点
  1. 哨兵模式是基于主从模式的,所有主从的优点,哨兵模式都具有。
  2. 主从可以自动切换,系统更健壮,可用性更高。
  • 缺点
  1. 具有主从模式的缺点,每台机器上的数据是一样的,内存的可用性较低。
  2. Redis较难支持在线扩容,在集群容量达到上限时在线扩容会变得很复杂。

5.3 集群模式

在这里插入图片描述

  1. 先说一个误区:Redis的集群模式本身没有使用一致性hash算法,而是使用slots插槽。这是很多人的一个误区。这里先留个坑,后面我会出一期《 redis系列之——一致性hash算法》。
  2. 跳转链接 https://blog.csdn.net/wuxiaolongah/article/details/107306679
  3. Redis 的哨兵模式基本已经可以实现高可用,读写分离 ,但是在这种模式下每台 Redis 服务器都存储相同的数据,很浪费内存,所以在redis3.0上加入了 Cluster 集群模式,实现了 Redis 的分布式存储,对数据进行分片,也就是说每台 Redis 节点上存储不同的内容;
    在这里插入图片描述
  • 集群模式的优缺点:
  • 优点:
  1. 采用去中心化思想,数据按照 slot 存储分布在多个节点,节点间数据共享,可动态调整数据分布;
  2. 可扩展性:可线性扩展到 1000 多个节点,节点可动态添加或删除;
  3. 高可用性:部分节点不可用时,集群仍可用。通过增加 Slave 做 standby 数据副本,能够实现故障自动 failover,节点之间通过 gossip 协议交换状态信息,用投票机制完成 Slave 到 Master 的角色提升;
  4. 降低运维成本,提高系统的扩展性和可用性。
  • 缺点:

    1. Redis Cluster是无中心节点的集群架构,依靠Goss协议(谣言传播)协同自动化修复集群的状态但 GosSIp有消息延时和消息冗余的问题,在集群节点数量过多的时候,节点之间需要不断进行PING/PANG通讯,不必须要的流量占用了大量的网络资源。虽然Reds4.0对此进行了优化,但这个问题仍然存在。
  • 2.数据迁移问题

    1. Redis Cluster可以进行节点的动态扩容缩容,这一过程,在目前实现中,还处于半自动状态,需要人工介入。在扩缩容的时候,需要进行数据迁移。
    1. 而 Redis为了保证迁移的一致性,迁移所有操作都是同步操作,执行迁移时,两端的 Redis均会进入时长不等的阻塞状态,对于小Key,该时间可以忽略不计,但如果一旦Key的内存使用过大,严重的时候会接触发集群内的故障转移,造成不必要的切换。
  • 总结:

  1. 主从模式:master节点挂掉后,需要手动指定新的master,可用性不高,基本不用。
  2. 哨兵模式:master节点挂掉后,哨兵进程会主动选举新的master,可用性高,但是每个节点存储的数据是一样的,浪费内存空间。数据量不是很多,集群规模不是很大,需要自动容错容灾的时候使用。
  3. 集群模式:数据量比较大,QPS要求较高的时候使用。 Redis Cluster是Redis 3.0以后才正式推出,时间较晚,目前能证明在大规模生产环境下成功的案例还不是很多,需要时间检验。

6. 附加Redis 面试题

https://www.cnblogs.com/three-fighter/p/16226366.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值