赞
踩
哨兵模式只有一个主节点,如果写操作频率过高,那么就会导致主节点出现宕机问题,就需要使用分片集群模式
分片集群结构图:这些主从都会存在哨兵模式
使用了分槽技术,默认集群槽的数量为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。
准备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的主节点
可以使用jedis完成java与redis之间的连接
依赖:
- <dependencies>
- <dependency>
- <groupId>redis.clients</groupId>
- <artifactId>jedis</artifactId>
- <version>3.6.0</version>
- </dependency>
- </dependencies>
- 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"));
- }
- }
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>
修改配置文件:
- spring.redis.port=6379
- spring.redis.host=192.168.61.223
- @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
-
-
- }
-
- }
- @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;
- }
- }
- # 集群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
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。