Redis分片集群模式以及整合SpringBoot

1.分片集群结构

哨兵模式只有一个主节点,如果写操作频率过高,那么就会导致主节点出现宕机问题,就需要使用分片集群模式

分片集群结构图:这些主从都会存在哨兵模式

使用了分槽技术,默认集群槽的数量为16384个。而每个槽可以存放若干个数据。如果搭建redis集群模式会为主节点平均分配这些槽。

原理: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。

2.如何搭建redis分片集群

准备6台redis服务:3台主节点和3台从节点,这里为了操作就在一台虚拟机上启动6个redis服务,修改端口号分别为为7001、7002、7003、7004、7005、7006

修改redis集群中6台配置文件:如果是多台虚拟机的话就不必要修改名称,这里是为了分辨

1.修改端口号:

2.修改快照.rdb文件的名称防止一样

3.必须开启aof模式并修改.aof文件的名称

4.开启redis集群模式 cluster-enabled yes

5.修改集群文件名 cluster-config-file nodes-7001.conf

6.设置允许任意ip访问 bind * -::*

启动6台redis服务

为上面6台redis设置主从关系并分配槽:

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

中途输入yes

完成

分配了三主三从:

启动redis客户端:redis-cli -c -h 192.168.61.223 -p 7001 这个ip和端口输入开启6台的任意一台都可以

测试:可以看到通过crc16算法进入了12706所在的分区进入了7003

测试关掉7001主节点看7001的从节点(7006)会不会上位

发现7006接替了7001的主节点

3.java连接redis

可以使用jedis完成java与redis之间的连接

依赖:

<dependencies>
        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
            <version>3.6.0</version>
        </dependency>
    </dependencies>

3.1连接单机版:

public class Test01 {

    public static void main(String[] args) {
        //1.创建连接对象
        Jedis jedis = new Jedis("192.168.61.223",6379);
        //查看所有key
        Set<String> keys = jedis.keys("*");
        System.out.println("所有的key"+keys);
        //设置key的值为字符串类型
        jedis.set("k1","v1");
        //设置有效时间
        jedis.expire("k1",20L);
        //查看有效时间
        Long time = jedis.ttl("k1");
        System.out.println("有效时间"+time);

        //删除key为k1
//        Long flag = jedis.del("k1");
//        System.out.println(flag);
        //判断当前key是否存在
        System.out.println(jedis.exists("k1"));
        System.out.println("-----String类型-----");
        //设置key的值为字符串类型的value
        jedis.set("k1","v1");
        //根据key获取对应的value值
        String k1 = jedis.get("k1");
        System.out.println(k1);//v1
        //设置多个key-value
        jedis.mset("k1","v1","k2","v2");
        //获取多个key对应的value值
        List<String> mget = jedis.mget("k1", "k2", "k3", "k4"); //[v1, v2, null, null]
        System.out.println(mget);
        //如果指定的key存在则不存入,如果不存在才存入
        jedis.setnx("k1","v4");
        //为指定的key递增 必须为整数
        jedis.set("k3","2");
        jedis.incr("k3");
        //为指定的key递减 必须为整数
        jedis.decr("k3");
        //为指定的key递增指定的值
        jedis.incrBy("k3",2L);
        //为指定的key递减指定的值
        jedis.decrBy("k3",2L);
        System.out.println("-----hash类型-----");
        //存放hash类型的数据
        jedis.hset("k4","name","zs");
        jedis.hset("k4","age","20");
        //或者
        HashMap<String, String> map = new HashMap<>();
        map.put("name","ls");
        map.put("age","20");
        jedis.hset("k5",map);
        //获取相应key中field对应的数据
        System.out.println(jedis.hget("k5", "name")); //ls
        //获取key对应hash数据内容
        System.out.println(jedis.hgetAll("k4")); //{name=zs, age=20}
        //获取hash中所有field字段
        System.out.println(jedis.hkeys("k4"));//[name, age]
        //获取hash中所有value字段
        System.out.println(jedis.hvals("k4"));//[zs, 20]
        System.out.println("-----list列表类型-----");
        //从左边存放列表数据
        jedis.lpush("k6","v1","v2","v3","v4","v5");
        //从左边取出元素 (指最后存入的)
        String k6 = jedis.lpop("k6");//v5
        System.out.println(k6);
        // 从左边获取指定范围的元素
        System.out.println(jedis.lrange("k6", 0, -1)); //[v4, v3, v2, v1]
        System.out.println("-----set集合类型");
        //存放set类型的元素
        jedis.sadd("k7","v1","v2","v3","v4","v5");
        //获取指定key对应的所有元素。
        System.out.println(jedis.smembers("k7"));//[v1, v2, v3, v4, v5]
        //随机获取集合中一个或多个元素
        System.out.println(jedis.srandmember("k7"));//v2
        //求多个集合的交集
        jedis.sadd("k8","v3","v4","v5","v6","v7");
        System.out.println(jedis.sinter("k7","k8"));
        //随机移除一个或多个元素
        System.out.println(jedis.spop("k8"));//v7
        System.out.println("-----sorted set集合类型-----");
        // 添加有序集合
        jedis.zadd("k9",10,"v1");
        HashMap<String, Double> map1 = new HashMap<>();
        map1.put("v1",10.);
        map1.put("v2",20.);
        map1.put("v3",30.);
        map1.put("v4",40.);
        map1.put("v5",50.);
        jedis.zadd("k10",map1);
        //从小到大的顺序获取集合中的元素
        System.out.println(jedis.zrange("k10", 0, -1));//[v1, v2, v3, v4, v5]
        //从大到小的顺序获取集合中的元素
        Set<String> k10 = jedis.zrevrange("k10", 0, -1);//[v5, v4, v3, v2, v1]
        System.out.println(k10);
        //从小到大的顺序获取集合中的元素和分数
        Set<Tuple> k101 = jedis.zrevrangeWithScores("k10", 0, -1);//[[v5,50.0], [v4,40.0], [v3,30.0], [v2,20.0], [v1,10.0]]
        System.out.println(k101);
    }
}

3.2连接集群版:

public class Test02 {
    public static void main(String[] args) {
        //java连接redis集群
        HashSet<HostAndPort> hostAndPorts = new HashSet<>();
        hostAndPorts.add(new HostAndPort("192.168.61.223",7001));
        hostAndPorts.add(new HostAndPort("192.168.61.223",7002));
        hostAndPorts.add(new HostAndPort("192.168.61.223",7003));
        hostAndPorts.add(new HostAndPort("192.168.61.223",7004));
        hostAndPorts.add(new HostAndPort("192.168.61.223",7005));
        hostAndPorts.add(new HostAndPort("192.168.61.223",7006));
        //创建一个Jedis集群对象---需要传入redis集群服务的地址信息
        JedisCluster jedisCluster = new JedisCluster(hostAndPorts);
        jedisCluster.set("k1","v1");
        System.out.println(jedisCluster.get("k1"));
    }
}

4.使用springboot连接redis

springboot在整合redis时会自动封装了两个类:RedisTemplate和StringRedisTemplate. StringRedisTemplate它是RedisTemplate的子类,StringRedisTemplate它里面存储的key和value都是字符串类型。

依赖:springboot版本为:2.3.2.RELEASE

<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.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

4.1单机版

修改配置文件:

spring.redis.port=6379
spring.redis.host=192.168.61.223

4.1.1使用StringRedisTemplate

@SpringBootTest
class SpringbootRedis01ApplicationTests {

    @Autowired
    private StringRedisTemplate redisTemplate; //starter自动完成了redis的装配

    @Test
    void contextLoads() {
        //获取所有的key
        System.out.println(redisTemplate.keys("*")); //[]
        //是否存在指定的key
        System.out.println(redisTemplate.hasKey("k1"));//false
        //删除指定的key
        System.out.println(redisTemplate.delete("k1"));//false
        //设置有效时间
//        redisTemplate.expire("k1",20, TimeUnit.SECONDS);
        //查看有效时间
        System.out.println(redisTemplate.getExpire("k1"));//-2
        System.out.println("--------------操作String字符串类型---------------");
        ValueOperations<String, String> string = redisTemplate.opsForValue();
        //设置value为string的
        string.set("k1", "v1");
        //获取key为k1
        System.out.println(string.get("k1"));//v1
        //设置多个key-value
        HashMap<String, String> hashMap = new HashMap<>();
        hashMap.put("k1","v1");
        hashMap.put("k2","v2");
        hashMap.put("k3","v3");
        string.multiSet(hashMap);
        //获取多个key对应的value值
        ArrayList<String> list1 = new ArrayList<>();
        list1.add("k1");
        list1.add("k2");
        list1.add("k3");
        list1.add("k4");
        System.out.println(string.multiGet(list1));//[v1, v2, v3, null]
        //如果指定的key不存在存入,存在则不存入
        System.out.println(string.setIfAbsent("k1", "v1"));//false
        //为指定的key递增 必须为整数
        string.set("k2","1");
        System.out.println(string.increment("k2"));//2
        //为指定的key递增指定的数 必须为整数
        System.out.println(string.increment("k2", 3));//5
        //为指定的key递减 必须为整数
        System.out.println(string.decrement("k2"));//4
        //为指定的key递减指定的数 必须为整数
        System.out.println(string.decrement("k2",2));//2
        System.out.println("-------------------操作hash类型--------------------");
        HashOperations<String, Object, Object> hash = redisTemplate.opsForHash();
        //存放hash类型的数据
        hash.put("k4","name","张三");
        //存放多个
        HashMap<String, String> k4 = new HashMap<>();
        k4.put("age","20");
        k4.put("address","河南");
        hash.putAll("k4",k4);
        //获取相应key中field对应的数据
        System.out.println(hash.get("k4", "name"));//张三
        //获取hash中key对应的所有数据内容
        Map<Object, Object> k41 = hash.entries("k4");
        System.out.println(k41);//{name=张三, address=河南, age=20}
        //获取key对应的所有field
        System.out.println(hash.keys("k4"));//[name, address, age]
        //获取key对应的所有value字段
        System.out.println(hash.values("k4"));//[张三, 河南, 20]
        System.out.println("-------------------list列表类型-------------------");
        ListOperations<String, String> list = redisTemplate.opsForList();
        //从左边存放列表数据
        list.leftPushAll("k5","v1","v2");
        ArrayList<String> list2 = new ArrayList<>();
        list2.add("v2");
        list2.add("v3");
        list2.add("v4");
        //或者
        list.leftPushAll("k6",list2);
        //从左边取出一个元素 (最后存入的)
        System.out.println(list.leftPop("k5"));//v2
        //从左边获取指定范围的元素
        System.out.println(list.range("k6", 0, -1));//[v4, v3, v2]
        System.out.println("-----------------set集合类型-------------------");
        SetOperations<String, String> set = redisTemplate.opsForSet();
        //存放set类型的元素
        set.add("k7","v1","v2","v3","v4");
        //获取key对应的所有元素
        System.out.println(set.members("k7"));//[v1, v2, v3, v4]
        //随机获取集合中一个或多个元素
        System.out.println(set.randomMember("k7"));//v3
        //求多个集合的交集
        set.add("k8","v3","v4","v5","v6");//[v3, v4]
        System.out.println(set.intersect("k7", "k8"));
        //随机移除一个元素
        System.out.println(set.pop("k8"));//v6
        System.out.println("--------------------sorted set集合类型----------------------");
        ZSetOperations<String, String> zSet = redisTemplate.opsForZSet();
        //添加有序集合
        zSet.add("k9","v1",10);
        //添加多个
        Set<ZSetOperations.TypedTuple<String>> tuples = new HashSet<>();
        tuples.add(new DefaultTypedTuple<>("v1",10.));
        tuples.add(new DefaultTypedTuple<>("v2",20.));
        tuples.add(new DefaultTypedTuple<>("v3",30.));
        zSet.add("k9",tuples);
        //从小到大的顺序获取集合中的元素
        System.out.println(zSet.range("k9", 0, -1));//[v1, v2, v3]
        //从大到小的顺序获取集合中的元素
        System.out.println(zSet.reverseRange("k9",0,-1));//[v3, v2, v1]
        //从小到大的顺序获取元素和分数
        System.out.println(zSet.rangeWithScores("k9", 0, -1));//[org.springframework.data.redis.core.DefaultTypedTuple@c45c123c, org.springframework.data.redis.core.DefaultTypedTuple@c64c123d, org.springframework.data.redis.core.DefaultTypedTuple@c782123e]
        for (ZSetOperations.TypedTuple<String> k9 : zSet.rangeWithScores("k9", 0, -1)) {
            System.out.println(k9.getScore());
            System.out.println(k9.getValue());
        }
        //10.0
        //v1
        //20.0
        //v2
        //30.0
        //v3


    }

}

4.1.2使用RedisTemplate

@SpringBootTest
public class SpringbootApplicationTests {
    @Autowired
    private RedisTemplate redisTemplate;
    @Test
    void test01(){
        ValueOperations string = redisTemplate.opsForValue();
        string.set("k1","v1");
    }
}

在运行时发现存入的key和value都为乱码

这是因为RedisTemplate默认使用的是jdk序列化,而StringRedisTemplate使用的是StringRedisSerializer()序列化,所以我们需要设置序列化方式

因为不知道以后value要传入什么类型。所以value指定为json类型,Jackson2JsonRedisSerializer(Object.class));

hash中key和value也需要设置序列化:

@SpringBootTest
public class SpringbootApplicationTests {
    @Autowired
    private RedisTemplate redisTemplate;
    @Test
    void test01(){
        //指定了key的序列化
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        //指定了value的序列化
        redisTemplate.setValueSerializer(new Jackson2JsonRedisSerializer(Object.class));
        //指定hash的key的序列化
        redisTemplate.setHashKeySerializer(new StringRedisSerializer());
        //制定了hash的value的序列化
        redisTemplate.setHashValueSerializer(new Jackson2JsonRedisSerializer(Object.class));
        ValueOperations string = redisTemplate.opsForValue();
        string.set("k1","v1");
        //hash操作
        HashOperations hashOperations = redisTemplate.opsForHash();
        hashOperations.put("k55","name","张三");
    }
}

每次使用redistemplate时都需要配置这些序列化,所以我们可以将这些创建为一个配置类交于spring管理

@Configuration
public class RedisConfig {
    @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);
        template.setConnectionFactory(factory);
        //key序列化方式
        template.setKeySerializer(redisSerializer);
        //value序列化
        template.setValueSerializer(jackson2JsonRedisSerializer);
        //hashmap序列化key
        template.setHashKeySerializer(redisSerializer);
        //hashmap序列化value
        template.setHashValueSerializer(jackson2JsonRedisSerializer);
        return template;
    }
}

4.2集群版

# 集群redis
spring.redis.cluster.nodes=192.168.61.223:7001,\
  192.168.61.223:7002,\
  192.168.61.223:7003,\
  192.168.61.223:7004,\
  192.168.61.223:7005,\
  192.168.61.223:7006

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值