redis的持久化和集群模式

1.redis实际开发的应用场景

1、热点数据的缓存: 减少对数据库的访问频率和减轻数据库的压力。
2. 限时业务的运用: 秒杀  存储登录者用户信息  存储短信验证码
3. 计数器相关问题: 点赞数 收藏数 播放量。
4. 排行榜相关问题: sort set 
5. 分布式锁: ---同步锁: 
6. 限量秒杀: ---decr key:

 2.redis的持久化

把内存中的数据持久化到磁盘中,防止数据丢失。---当redis服务器开启时,会把磁盘中的数据加载到内存中进行计算。

提高了两种持久化方式:

RDB和AOF.

 2.1 RDB持久化

它是每隔一段时间进行快照存储。--它是一个二进制文件,里面的内容打开后无法看懂。

触发机制:

[1]save---手动触发

[2]bgsave--手动触发

[3]通过配置文件--自动触发

 2.1.1save触发

该命令会阻塞当前Redis服务器,执行save命令期间,Redis不能处理其他命令,直到RDB过程完成为止。具体流程如下

 执行完成时候如果存在老的RDB文件,就把新的替代掉旧的。我们的客户端可能都是几万或者是几十万,这种方式显然不可取。

2.1.2 bgsave:

执行该命令时,Redis会在后台异步进行快照操作,快照同时还可以响应客户端请求。具体流程如下

 

 bgsave在执行该命令时会fork出一个新的线程,单独执行rdb持久化操作,而不影响其他客户对redis服务的操作。

 不管使用save还是bgsave都需要手动输入。我们也可以通过配置文件完成自动化rdb操作。

 

 

RDB快照持久化优缺点:

优点: ----数据恢复速度快。

缺点: ----数据完整性差--会丢失最后一段时间的数据。

 2.2 AOF持久化

日志追加持久化,当我们执行写操作,会触发一个函数write,把会把写操作的命令追加到一个日志文件appendfile中。当服务器启动时会把appendfile中的命令从新执行一遍。默认不开启。

手动开启aof模式。

 

 

AOF优缺点:

优点: 数据完整性高---丢失最后一条指令。

缺点: 数据恢复慢。

如果rdb和aof都使用,当服务器重启时会加载哪个文件?

先加载AOF的文件【它以数据完整性为主】。

 3.redis集群模式

提供了三种集群模式.

[1]主从模式

[2]哨兵模式

[3]集群模式

 为什么要使用集群模式:

[1]解决单机故障问题 [2]解决单机压力问题

 

 3.1主从模式

配从不配主:非常简单。

准备: 一台linux服务。 开三个redis服务----通过修改port----6380 [主] 6381 6382[从]。

修改: 
1.port端口号
2.rdb文件名dump638X.rbd
3.关闭aof 

启动上面三个redis服务

redis-server redis6380.conf
redis-server redis6381.conf
redis-server redis6382.conf

 进入相应的客户端 

 查看每个redis服务的角色:

 

 发现他们都是master角色,如何分配主从关系:

 配从不配主: slaveof 主节点ip 主节点port

 

 

主节点修改数据时,主节点的数据会同步到所有的从节点。

从节点只能进行读操作,不能执行写操作。

 

如果主节点宕机,从节点会不会篡位-----不会自动上位

如果增加一个从节点,该节点会不会把之前主节点的数据同步过来----一定同步

4.哨兵模式

命令:cd /usr/soft/redis6.版本/master

由于上面主从模式,主节点宕机后,从节点不会自动上位,这段时间内无法进行写操作

解决上面的问题---哨兵模式--->1.监控功能 2.故障恢复 3.选举一个master主节点

 4.1监控功能

 4.2master节点的选举

 4.3启动哨兵

修改哨兵的配置 vi sentinel.conf

 

 

启动哨兵模式 :redis-sentinel sentinel.conf

测试:使用shutdown让主节点宕机 观察sentinel的控制台

如果原来的master回来(重新启用6380),跟着现在的master走

 5.集群模式

不管是主从模式还是哨兵模式,始终只有一个master主节点。如果写操作并发高. 势必会导致master节点的压力过大。

 真正的集群:

 

 存储原理:

redis 集群中内置了 16384 个哈希槽,当需要在 Redis 集群中放置一个 key-value时,redis 先对 key 使用 crc16 算法算出一个整数结果,然后把结果对 16384 求余数,这样每个 key 都会对应一个编号在 0-16383 之间的哈希槽,redis 会根据节点数量大致均等的将哈希槽映射到不同的节点。

当你往Redis Cluster中加入一个Key时,会根据crc16(key) mod 16384计算这个key应该分布到哪个hash slot中,一个hash slot中会有很多key和value。你可以理解成表的分区,使用单节点时的redis时只有一个表,所有的key都放在这个表里;改用Redis Cluster以后会自动为你生成16384个分区表,你insert数据时会根据上面的简单算法来决定你的key应该存在哪个分区,每个分区里有很多key。

搭建三主三从:
7001~7006: 6台redis服务。
创建一个文件夹cluster存放6个redis的配置文件

 修改6台redis配置文件--必须redis服务是空的

 (1)端口号

 

 (2) bind绑定---任意

 

 (3)必须开启aof模式并修改文件名

 

 (4)开启集群模式

 

 

 开启上面6台redis服务

redis-server redisXXX.conf
ps -ef | grep redis (查看)
cluster(集群模式)

 

分槽,以及设置主从关系

redis-cli --cluster create --cluster-replicas 1 192.168.223.147:7001 192.168.223.147:7002 192.168.223.147:7003 192.168.223.147:7004 192.168.223.147:7005 192.168.223.147:7006 

 

访问:

redis-cli -c -h 192.168.245.223 -p 7001

 

 

 6.java连接redis

6.1java连接单机redis

6.1.1创建一个普通的maven工程

 1)引入redis的依赖
<!--导入redis的依赖-->
<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
    <version>3.8.0</version>
</dependency>
2)测试
package demo01;
import redis.clients.jedis.Jedis;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
public class Test01 {
    public static void main(String[] args) {
        //把所有关于对redis的操作都封装到一个类中Jedis
        //Jedis jedis = new Jedis();//jedis 依赖架包   无参构造函数,默认连接的是本地的redis。端口默认6379
        Jedis jedis = new Jedis("192.168.245.223", 6379);//有参函数
        /*String ping = jedis.ping();//测试连通性
        System.out.println(ping);*/
        Set<String> keys = jedis.keys("*");//查看端口6379的所有内容
        System.out.println(keys);
        Long del = jedis.del("k1","k2","k3");//删除
        System.out.println("删除的个数:"+del);
        //jedis.expire("k4",20);//k4过时20秒
        //关于字符串的操作
/*        jedis.setex("k5",20L,"霍梦齐");
        String k5 = jedis.get("k5");
        System.out.println(k5);
        Long setnx = jedis.setnx("k6","hmq");//k5有值是霍梦齐,结果是0没有存进去,改成k6就存进去了,因为k6没有东西。结果为1
        System.out.println(setnx);*/
        //关于hash的操作
        jedis.hset("k7","name","霍梦齐");
        String hget = jedis.hget("k7","name");
        System.out.println(hget);
        //存放多个
        HashMap<String, String> map = new HashMap<String,String>();
        map.put("name","霍梦齐真帅");
        map.put("age","24");
        map.put("sex","男");
        jedis.hset("k8",map);
        Map<String, String> map1 = jedis.hgetAll("k8");
        System.out.println(map1.get("name"));
        jedis.close();//关闭连接对象
    }
}
3)报错

没有这个端口号

 

 防火墙处于开启状态

 

 运行redis.配置文件

 

 特点: Jedis把对redis的操作都封装到Jedis类对象中了,而每个命令封装了一个对应的方法。

 6.2java通过连接池连接redis

连接池的作用:减少频繁创建和销毁连接对象

 

package demo01;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
public class Test02 {
    public static void main(String[] args) {
        //配置连接对象的信息
        JedisPoolConfig poolConfig = new JedisPoolConfig();
        poolConfig.setMinIdle(5);//设置最小的空闲数
        poolConfig.setMaxIdle(10);//设置最大空闲数,当没人连接redis服务器时,该池子中最多能空闲几个连接对象
        poolConfig.setMaxTotal(15);//当有人连接redis服务时,最大能生成多少个连接对象
        //poolConfig.setMaxWait();
        //创建连接池对象
        JedisPool jedisPool = new JedisPool(poolConfig, "192.168.245.223", 6379);
        long start = System.currentTimeMillis();
        //从连接池获取连接对象
        for (int i=0;i<100;i++){
            Jedis jedis = jedisPool.getResource();
            jedis.ping();
            jedis.close();//把该对象还给连接池
        }
        long end = System.currentTimeMillis();
        System.out.println("耗时:"+(end-start));
    }
}

6.3java连接redis集群

package demo01;
import redis.clients.jedis.HostAndPort;
import redis.clients.jedis.JedisCluster;
import java.util.HashSet;
import java.util.Set;
public class Test04 {
    public static void main(String[] args) {
        //必须时所有redis服务器
        HostAndPort hostAndPort1=new HostAndPort("192.168.245.223",7001);
        HostAndPort hostAndPort2=new HostAndPort("192.168.245.223",7002);
        HostAndPort hostAndPort3=new HostAndPort("192.168.245.223",7003);
        HostAndPort hostAndPort4=new HostAndPort("192.168.245.223",7004);
        HostAndPort hostAndPort5=new HostAndPort("192.168.245.223",7005);
        HostAndPort hostAndPort6=new HostAndPort("192.168.245.223",7006);
        Set<HostAndPort> sets=new HashSet<HostAndPort>();
        sets.add(hostAndPort1);
        sets.add(hostAndPort2);
        sets.add(hostAndPort3);
        sets.add(hostAndPort4);
        sets.add(hostAndPort5);
        sets.add(hostAndPort6);
        JedisCluster jedisCluster=new JedisCluster(sets);
        jedisCluster.set("k1","v1");
        jedisCluster.set("k2","v2");
        jedisCluster.set("k3","v3");
        jedisCluster.set("k4","v4");
        jedisCluster.set("k5","v5");
        jedisCluster.set("k6","v6");
        jedisCluster.set("k7","v7");
    }
}

 7.springboot整合redis

springboot对redis的操作封装到模板类中RedisTemplate和StringRedisTemplate。StringRedisTemplate是Redistemplate的子类,它只能往redis中存放字符串类型。

7.1创建一个springboot工程

7.2引入依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.12.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.hmq</groupId>
    <artifactId>hmq-redis</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>hmq-redis</name>
    <description>hmq-redis</description>
    <properties>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

7.3配置信息

#redis的配置信息--单机
spring.redis.host=192.168.245.223
spring.redis.port=6379

7.4测试

package com.hmq;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.redis.connection.StringRedisConnection;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.StringRedisTemplate;
import java.util.Map;
import java.util.Set;
@SpringBootTest
class HmqRedisApplicationTests {
    //springboot创建好该类对象 并交于IOC容器管理
    @Autowired
    private StringRedisTemplate redisTemplate;
    @Test
    void contextLoads() {
        //操作key
        //查看所有的key
/*        Boolean k1 = redisTemplate.hasKey("k1");//查看看是否存在
        System.out.println("判断k1是否存在:"+k1);
        Set<String> keys = redisTemplate.keys("*");//查看所有的keys
        System.out.println("所有的key:"+keys);
        Boolean k11 = redisTemplate.delete("k1");//删除key
        System.out.println("是否删除成功:"+k11);*/
        //2.字符串的操作---redisTemplate类中对于每一种数据类型的操作,单独封装了相应的类。
        //        ValueOperations<String, String> forValue = redisTemplate.opsForValue();
//        forValue.set("k1","ldh");
//        String value = forValue.get("k1");
//        System.out.println("获取k1对于的value值:"+value);
//        //setnx----
//        forValue.setIfAbsent("k1","张学友");
//        forValue.setIfAbsent("k2","刘德华");
//        System.out.println(forValue.get("k1"));
//        System.out.println(forValue.get("k2"));
        //3.hash类型操作
        HashOperations<String, Object, Object> forHash = redisTemplate.opsForHash();
        forHash.put("k3","name","hmq");
        forHash.put("k3","age","18");
        Object o = forHash.get("k3", "name");
        System.out.println("name的值:"+o);
        Map<Object, Object> k3 = forHash.entries("k3");
        System.out.println(k3);
    }
}

总结:

1. 哨兵模式: 哨兵监控redis集群中每台服务器的状态,通过心跳机制发送ping命令。 

2. 集群模式: 把16384个槽平均分配到主节点。 

3. java连接redis.----Jedis---把操作redis的方法都封装到Jedis中,而每个方法对应redis中相应的命令。

4. springboot操作redis.封装了俩个类RedisTemplate和StringRedisTemplate,而StringRedistemplate它是redisTemplate的子类,它只能对字符串操作。如果想通过该StringRedistemplate放入一个对象时,需要把该对象转换为json字符串才能放入

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值