一、SpringBoot整合Redis

1.导入依赖

<!--存在Redis依赖-->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

2.application.yml

server:port: 8081
spring:session.store-type: redisredis:database: 0host: localhostport: 6379password: roottimeout: 300jedis:pool:max-active: 8max-wait: -1max-idle: 8min-idle: 0

3.使用方法

完成上述配置之后,会自动创建两个对象:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1WXTr721-1606313432296)(C:\Users\ThinkPad\AppData\Roaming\Typora\typora-user-images\image-20201111094810912.png)]

StringRedisTemplate:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RMFxzrHy-1606313432298)(C:\Users\ThinkPad\AppData\Roaming\Typora\typora-user-images\image-20201111094051072.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vd63ZUSQ-1606313432300)(C:\Users\ThinkPad\AppData\Roaming\Typora\typora-user-images\image-20201111094935637.png)]

RedisTemplate:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dZvEMS3F-1606313432302)(C:\Users\ThinkPad\AppData\Roaming\Typora\typora-user-images\image-20201111101857482.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TLeWvlhC-1606313432304)(C:\Users\ThinkPad\AppData\Roaming\Typora\typora-user-images\image-20201111101933208.png)]

4.Redis 配置类(直接用即可)

package com.sonnie.springbootredis_demo.config;import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.*;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;/*** Redis配置类* @program: redisdemo* @Author: david* @Description:*/
//RedisConfig中重写RedisTemplate类(why?)
@Configuration
@EnableCaching //开启注解
public class RedisConfig extends CachingConfigurerSupport {/*** retemplate相关配置* @param factory* @return*/@Beanpublic RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {RedisTemplate<String, Object> template = new RedisTemplate<>();// 配置连接工厂template.setConnectionFactory(factory);//使用Jackson2JsonRedisSerializer来序列化和反序列化redis的value值(默认使用JDK的序列化方式)Jackson2JsonRedisSerializer jacksonSeial = new Jackson2JsonRedisSerializer(Object.class);ObjectMapper om = new ObjectMapper();// 指定要序列化的域,field,get和set,以及修饰符范围,ANY是都有包括private和publicom.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);// 指定序列化输入的类型,类必须是非final修饰的,final修饰的类,比如String,Integer等会跑出异常om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);jacksonSeial.setObjectMapper(om);// 值采用json序列化template.setValueSerializer(jacksonSeial);//使用StringRedisSerializer来序列化和反序列化redis的key值template.setKeySerializer(new StringRedisSerializer());// 设置hash key 和value序列化模式template.setHashKeySerializer(new StringRedisSerializer());template.setHashValueSerializer(jacksonSeial);template.afterPropertiesSet();return template;}/*** 对hash类型的数据操作** @param redisTemplate* @return*/@Beanpublic HashOperations<String, String, Object> hashOperations(RedisTemplate<String, Object> redisTemplate) {return redisTemplate.opsForHash();}/*** 对redis字符串类型数据操作** @param redisTemplate* @return*/@Beanpublic ValueOperations<String, Object> valueOperations(RedisTemplate<String, Object> redisTemplate) {return redisTemplate.opsForValue();}/*** 对链表类型的数据操作** @param redisTemplate* @return*/@Beanpublic ListOperations<String, Object> listOperations(RedisTemplate<String, Object> redisTemplate) {return redisTemplate.opsForList();}/*** 对无序集合类型的数据操作** @param redisTemplate* @return*/@Beanpublic SetOperations<String, Object> setOperations(RedisTemplate<String, Object> redisTemplate) {return redisTemplate.opsForSet();}/*** 对有序集合类型的数据操作** @param redisTemplate* @return*/@Beanpublic ZSetOperations<String, Object> zSetOperations(RedisTemplate<String, Object> redisTemplate) {return redisTemplate.opsForZSet();}
}

5.Redis工具类( 封装redisTemplate–有需要再加)

package com.sonnie.springbootredis_demo.util;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;/*** redisTemplate封装** @author david*/
@Component
public class RedisUtil {@Autowiredprivate RedisTemplate<String, Object> redisTemplate;public RedisUtil(RedisTemplate<String, Object> redisTemplate) {this.redisTemplate = redisTemplate;}/*** 指定缓存失效时间** @param key  键* @param time 时间(秒)* @return*/public boolean expire(String key, long time) {try {if (time > 0) {redisTemplate.expire(key, time, TimeUnit.SECONDS);}return true;} catch (Exception e) {e.printStackTrace();return false;}}/*** 根据key 获取过期时间** @param key 键 不能为null* @return 时间(秒) 返回0代表为永久有效*/public long getExpire(String key) {return redisTemplate.getExpire(key, TimeUnit.SECONDS);}/*** 判断key是否存在** @param key 键* @return true 存在 false不存在*/public boolean hasKey(String key) {try {return redisTemplate.hasKey(key);} catch (Exception e) {e.printStackTrace();return false;}}/*** 删除缓存** @param key 可以传一个值 或多个*/@SuppressWarnings("unchecked")public void del(String... key) {if (key != null && key.length > 0) {if (key.length == 1) {redisTemplate.delete(key[0]);} else {redisTemplate.delete(CollectionUtils.arrayToList(key));}}}//============================String=============================/*** 普通缓存获取** @param key 键* @return 值*/public Object get(String key) {return key == null ? null : redisTemplate.opsForValue().get(key);}/*** 普通缓存放入** @param key   键* @param value 值* @return true成功 false失败*/public boolean set(String key, Object value) {try {redisTemplate.opsForValue().set(key, value);return true;} catch (Exception e) {e.printStackTrace();return false;}}/*** 普通缓存放入并设置时间** @param key   键* @param value 值* @param time  时间(秒) time要大于0 如果time小于等于0 将设置无限期* @return true成功 false 失败*/public boolean set(String key, Object value, long time) {try {if (time > 0) {redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);} else {set(key, value);}return true;} catch (Exception e) {e.printStackTrace();return false;}}/*** 递增** @param key   键* @param delta 要增加几(大于0)* @return*/public long incr(String key, long delta) {if (delta < 0) {throw new RuntimeException("递增因子必须大于0");}return redisTemplate.opsForValue().increment(key, delta);}/*** 递减** @param key   键* @param delta 要减少几(小于0)* @return*/public long decr(String key, long delta) {if (delta < 0) {throw new RuntimeException("递减因子必须大于0");}return redisTemplate.opsForValue().increment(key, -delta);}//================================Map=================================/*** HashGet** @param key  键 不能为null* @param item 项 不能为null* @return 值*/public Object hget(String key, String item) {return redisTemplate.opsForHash().get(key, item);}/*** 获取hashKey对应的所有键值** @param key 键* @return 对应的多个键值*/public Map<Object, Object> hmget(String key) {return redisTemplate.opsForHash().entries(key);}/*** HashSet** @param key 键* @param map 对应多个键值* @return true 成功 false 失败*/public boolean hmset(String key, Map<String, Object> map) {try {redisTemplate.opsForHash().putAll(key, map);return true;} catch (Exception e) {e.printStackTrace();return false;}}/*** HashSet 并设置时间** @param key  键* @param map  对应多个键值* @param time 时间(秒)* @return true成功 false失败*/public boolean hmset(String key, Map<String, Object> map, long time) {try {redisTemplate.opsForHash().putAll(key, map);if (time > 0) {expire(key, time);}return true;} catch (Exception e) {e.printStackTrace();return false;}}/*** 向一张hash表中放入数据,如果不存在将创建** @param key   键* @param item  项* @param value 值* @return true 成功 false失败*/public boolean hset(String key, String item, Object value) {try {redisTemplate.opsForHash().put(key, item, value);return true;} catch (Exception e) {e.printStackTrace();return false;}}/*** 向一张hash表中放入数据,如果不存在将创建** @param key   键* @param item  项* @param value 值* @param time  时间(秒)  注意:如果已存在的hash表有时间,这里将会替换原有的时间* @return true 成功 false失败*/public boolean hset(String key, String item, Object value, long time) {try {redisTemplate.opsForHash().put(key, item, value);if (time > 0) {expire(key, time);}return true;} catch (Exception e) {e.printStackTrace();return false;}}/*** 删除hash表中的值** @param key  键 不能为null* @param item 项 可以使多个 不能为null*/public void hdel(String key, Object... item) {redisTemplate.opsForHash().delete(key, item);}/*** 判断hash表中是否有该项的值** @param key  键 不能为null* @param item 项 不能为null* @return true 存在 false不存在*/public boolean hHasKey(String key, String item) {return redisTemplate.opsForHash().hasKey(key, item);}/*** hash递增 如果不存在,就会创建一个 并把新增后的值返回** @param key  键* @param item 项* @param by   要增加几(大于0)* @return*/public double hincr(String key, String item, double by) {return redisTemplate.opsForHash().increment(key, item, by);}/*** hash递减** @param key  键* @param item 项* @param by   要减少记(小于0)* @return*/public double hdecr(String key, String item, double by) {return redisTemplate.opsForHash().increment(key, item, -by);}//============================set=============================/*** 根据key获取Set中的所有值** @param key 键* @return*/public Set<Object> sGet(String key) {try {return redisTemplate.opsForSet().members(key);} catch (Exception e) {e.printStackTrace();return null;}}/*** 根据value从一个set中查询,是否存在** @param key   键* @param value 值* @return true 存在 false不存在*/public boolean sHasKey(String key, Object value) {try {return redisTemplate.opsForSet().isMember(key, value);} catch (Exception e) {e.printStackTrace();return false;}}/*** 将数据放入set缓存** @param key    键* @param values 值 可以是多个* @return 成功个数*/public long sSet(String key, Object... values) {try {return redisTemplate.opsForSet().add(key, values);} catch (Exception e) {e.printStackTrace();return 0;}}/*** 将set数据放入缓存** @param key    键* @param time   时间(秒)* @param values 值 可以是多个* @return 成功个数*/public long sSetAndTime(String key, long time, Object... values) {try {Long count = redisTemplate.opsForSet().add(key, values);if (time > 0) {expire(key, time);}return count;} catch (Exception e) {e.printStackTrace();return 0;}}/*** 获取set缓存的长度** @param key 键* @return*/public long sGetSetSize(String key) {try {return redisTemplate.opsForSet().size(key);} catch (Exception e) {e.printStackTrace();return 0;}}/*** 移除值为value的** @param key    键* @param values 值 可以是多个* @return 移除的个数*/public long setRemove(String key, Object... values) {try {Long count = redisTemplate.opsForSet().remove(key, values);return count;} catch (Exception e) {e.printStackTrace();return 0;}}//===============================list=================================/*** 获取list缓存的内容** @param key   键* @param start 开始* @param end   结束  0 到 -1代表所有值* @return*/public List<Object> lGet(String key, long start, long end) {try {return redisTemplate.opsForList().range(key, start, end);} catch (Exception e) {e.printStackTrace();return null;}}/*** 获取list缓存的长度** @param key 键* @return*/public long lGetListSize(String key) {try {return redisTemplate.opsForList().size(key);} catch (Exception e) {e.printStackTrace();return 0;}}/*** 通过索引 获取list中的值** @param key   键* @param index 索引  index>=0时, 0 表头,1 第二个元素,依次类推;index<0时,-1,表尾,-2倒数第二个元素,依次类推* @return*/public Object lGetIndex(String key, long index) {try {return redisTemplate.opsForList().index(key, index);} catch (Exception e) {e.printStackTrace();return null;}}/*** 将list放入缓存** @param key   键* @param value 值* @return*/public boolean lSet(String key, Object value) {try {redisTemplate.opsForList().rightPush(key, value);return true;} catch (Exception e) {e.printStackTrace();return false;}}/*** 将list放入缓存** @param key   键* @param value 值* @param time  时间(秒)* @return*/public boolean lSet(String key, Object value, long time) {try {redisTemplate.opsForList().rightPush(key, value);if (time > 0) {expire(key, time);}return true;} catch (Exception e) {e.printStackTrace();return false;}}/*** 将list放入缓存** @param key   键* @param value 值* @return*/public boolean lSet(String key, List<Object> value) {try {redisTemplate.opsForList().rightPushAll(key, value);return true;} catch (Exception e) {e.printStackTrace();return false;}}/*** 将list放入缓存** @param key   键* @param value 值* @param time  时间(秒)* @return*/public boolean lSet(String key, List<Object> value, long time) {try {redisTemplate.opsForList().rightPushAll(key, value);if (time > 0) {expire(key, time);}return true;} catch (Exception e) {e.printStackTrace();return false;}}/*** 根据索引修改list中的某条数据** @param key   键* @param index 索引* @param value 值* @return*/public boolean lUpdateIndex(String key, long index, Object value) {try {redisTemplate.opsForList().set(key, index, value);return true;} catch (Exception e) {e.printStackTrace();return false;}}/*** 移除N个值为value** @param key   键* @param count 移除多少个* @param value 值* @return 移除的个数*/public long lRemove(String key, long count, Object value) {try {Long remove = redisTemplate.opsForList().remove(key, count, value);return remove;} catch (Exception e) {e.printStackTrace();return 0;}}//===============================list=================================/*** 赋值操作,存在值返回true,不存在返回false;** @param key   键* @param value 值* @param millisecond 有效期* @return 赋值结果*/public boolean setIfAbsent(String key,String value, long millisecond) {Boolean success = redisTemplate.opsForValue().setIfAbsent(key, value,millisecond, TimeUnit.MILLISECONDS);return success != null && success;}/*** 移除key对应的value。** @param key   键**/public void delete(String key) {redisTemplate.delete(key);}
}

二、缓存应用

1.缓存

1.1什么是缓存?

缓存就是存在于计算机内存中的一段数据;

针对于我们的程序而言,缓存就是存在于JVM(JVM也存在于内存中)中的一段数据。

1.2缓存/内存中数据的特点

a、读写快

b、断电既失

1.3使用缓存的好处

a、提高网站响应速度,优化网站的运行

b、减轻访问数据库时给数据库带来的压力

1.4缓存的应用环境

缓存一般应用于查询较多,增删极少的业务领域

1.5项目中开发缓存模块

项目结构

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uWeFzfjW-1606313432306)(C:\Users\ThinkPad\AppData\Roaming\Typora\typora-user-images\image-20201111160631566.png)]

1.5.1SpringBoot+Mybatis

——使用Mybatis框架提供二级缓存技术

——只需要在使用缓存模块mapper配置中加入标签即可

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"><!--sql映射文件-->
<mapper namespace="com.example.springbootredisdemo.dao.UserDao"><!--开启二级缓存;二级缓存作用域:namespace;Mybatis3中二级缓存默认开启--><cache/><select id="selectAll" resultType="com.example.springbootredisdemo.entity.User">select id,name,password from users</select><select id="selectById" resultType="com.example.springbootredisdemo.entity.User" parameterType="String">select id,name,password from users where id = #{id}</select><insert id="insert" parameterType="com.example.springbootredisdemo.entity.User">insert into users (id,name,password) values (#{id},#{name},#{password})</insert><update id="updateById" parameterType="com.example.springbootredisdemo.entity.User">update users set name # {name},password = #{password} where id = #{id}</update><delete id="deleteById" parameterType="com.example.springbootredisdemo.entity.User">delete from users where id = #{id}</delete></mapper>

——Mybatis自身缓存(也称作本地缓存)的缺点:

a、本地缓存存储在JVM内存中,如果数据过大,会影响JVM的性能

b、不能作为分布式缓存

1.5.2重写Mybatis cache实现Redis缓存
编写ApplicationContextUtil类
package com.example.springbootredisdemo.util;import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.Configuration;//在自定义的类中获取工厂中已经创建好的某些对象
@Configuration
public class ApplicationContextUtil implements ApplicationContextAware {private static ApplicationContext context;@Overridepublic void setApplicationContext(ApplicationContext applicationContext) throws BeansException {this.context = applicationContext;}//根据bean id获取对象public static Object getBean(String id){return context.getBean(id);}//根据bean 类型获取对象public static Object getBean(Class clazz){return context.getBean(clazz);}//根据bean id和类型获取对象public static Object getBean(String id,Class clazz){return context.getBean(id,clazz);}}
编写RedisCache类
package com.example.springbootredisdemo.config;import com.example.springbootredisdemo.util.ApplicationContextUtil;
import org.apache.ibatis.cache.Cache;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.StringRedisSerializer;import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;/*** 重写Mybatis cache实现redis缓存* 不能交给工厂管理* */
public class RedisCache implements Cache {//必须定义这个String类型的id,因为这个id表示当前加入缓存的namespace;private String id;public RedisCache(String id) {this.id = id;}@Overridepublic String getId() {return id;}//放入缓存@Overridepublic void putObject(Object key, Object value) {//获取redisTemplate对象RedisTemplate redisTemplate = (RedisTemplate) ApplicationContextUtil.getBean("redisTemplate");//存储缓存数据redisTemplate.setKeySerializer(new StringRedisSerializer());//设置HashKey序列化redisTemplate.setHashKeySerializer(new StringRedisSerializer());//hash模型redisTemplate.opsForHash().put(id,key.toString(),value);}//从缓存中获取@Overridepublic Object getObject(Object key) {//获取redisTemplate对象RedisTemplate redisTemplate = (RedisTemplate) ApplicationContextUtil.getBean("redisTemplate");redisTemplate.setKeySerializer(new StringRedisSerializer());//设置HashKey序列化redisTemplate.setHashKeySerializer(new StringRedisSerializer());return redisTemplate.opsForHash().get(id.toString(),key.toString());}//从缓存中移除//真正使用过程中,这个方法并不会被用到@Overridepublic Boolean removeObject(Object key) {return null;}//清除缓存@Overridepublic void clear() {//获取redisTemplate对象RedisTemplate redisTemplate = (RedisTemplate) ApplicationContextUtil.getBean("redisTemplate");redisTemplate.delete(id);}//缓存命中率计算@Overridepublic int getSize() {
//      获取redisTemplate对象RedisTemplate redisTemplate = (RedisTemplate) ApplicationContextUtil.getBean("redisTemplate");redisTemplate.setKeySerializer(new StringRedisSerializer());return redisTemplate.opsForHash().size(id.toString()).intValue();}/** ReadWriteLock读写锁 表示:读写之间互斥,读读之间不互斥,写写之间不互斥* 区别于Synchronized  表示:读读之间互斥,写写之阿互斥,读写之间互斥* 因此ReadWriteLock效率比Synchronized高* 对于缓存,只有写操作,没有写操作* */@Overridepublic ReadWriteLock getReadWriteLock() {return new ReentrantReadWriteLock();}
}
mapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"><!--sql映射文件-->
<mapper namespace="com.example.springbootredisdemo.dao.UserDao"><!--开启二级缓存;二级缓存作用域:namespace;Mybatis3中二级缓存默认开启参数:type:用来指定自定义cache的全限定名--><cache type="com.example.springbootredisdemo.config.RedisCache"/><select id="selectAll" resultType="com.example.springbootredisdemo.entity.User">select id,name,password from users</select><select id="selectById" resultType="com.example.springbootredisdemo.entity.User" parameterType="String">select id,name,password from users where id = #{id}</select><insert id="insert" parameterType="com.example.springbootredisdemo.entity.User">insert into users (id,name,password) values (#{id},#{name},#{password})</insert><update id="updateById" parameterType="com.example.springbootredisdemo.entity.User">update users set name # {name},password = #{password} where id = #{id}</update><delete id="deleteById" parameterType="com.example.springbootredisdemo.entity.User">delete from users where id = #{id}</delete></mapper>

1.6Redis集成Mybatis实现分布式缓存——总结

1.6.1为什么要这样做?

1.Mybatis本地缓存的缺点:

​ a、本地缓存存储在JVM内存中,如果数据过大,会影响JVM的性能

​ b、不能在分布式系统中实现共享

2.redis缓存的优点:

​ a、本身就是内存数据库,在内存中存储数据,读写块

​ b、可以在分布式系统中实现共享

1.6.2如何保证同一个查询中多次查询key始终一致?

MyBatis 中涉及到动态 SQL 的原因,缓存项的 key 不能仅仅通过一个 String 来表示,所以通过CacheKey 来封装缓存的 key 值,CacheKey 可以封装多个影响缓存项的因素

​ a、namespace.id

​ b、指定查询结果集的范围(分页信息)

​ c、查询所使用的 SQL 语句

​ d、用户传递给 SQL 语句的实际参数值

通过这种方式确保了cacheKey的唯一。

1.6.3Redis缓存模型如何设计?

a、初步设计:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-se6yHQX2-1606313432307)(C:\Users\ThinkPad\AppData\Roaming\Typora\typora-user-images\image-20201111174524613.png)]

缺点:在Redis中散列了很多的key,他们都被存储在一起;当进行清除操作时,会将其他模块的缓存一起清除掉。

b、优化改进:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yZw8J53R-1606313432308)(C:\Users\ThinkPad\AppData\Roaming\Typora\typora-user-images\image-20201111174958554.png)]

1.6.4谈谈缓存穿透问题、缓存击穿问题以及缓存雪崩问题?

缓存雪崩

​ **概念:**是指缓存中数据大批量到过期时间,而查询数据量巨大,引起数据库压力过大甚至down机。

​ 举个例子,目前电商首页以及热点数据都会去做缓存 ,一般缓存都是定时任务去刷新,或者是查不到之后 去更新的,定时任务刷新就有一个问题。如果所有首页的Key失效时间都是12小时,中午12点刷新的,我零点 有个秒杀活动大量用户涌入,假设当时每秒 6000 个请求,本来缓存在可以扛住每秒 5000 个请求,但是缓 存当时所有的Key都失效了。此时 1 秒 6000 个请求全部落数据库,数据库必然扛不住,它会报一下警,真 实情况可能DBA都没反应过来就直接挂了。此时,如果没用什么特别的方案来处理这个故障,DBA 很着 急,重启数据库,但是数据库立马又被新的流量给打死了。这就是我理解的缓存雪崩。

如何应对:

​ a、在业务数据量不是很大的情况下,可以设置缓存数据为永久存储

​ b、基于不同的业务数据,设置不同的超时时间,如下图:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-j6R33EBj-1606313432309)(C:\Users\ThinkPad\AppData\Roaming\Typora\typora-user-images\image-20201112092228645.png)]

缓存穿透

​ **概念:**是指缓存和数据库中都没有的数据,而用户不断发起请求,每次都能绕开Redis直接打到数据库,数据 库也查不到,每次都这样,并发高点就容易崩掉了。

​ 举个例子:我们数据库的 id 都是从1开始自增上去的,在不对参数做校验的情况下,如发起为id值为 -1 的 数据或 id 为特别大不存在的数据。这时的用户很可能是攻击者,攻击会导致数据库压力过大,严重会击垮数据 库。

如何处理:

​ a、其实,Redis集成Mybatis实现分布式缓存,已经解决了缓存穿透的问题。无论你查询的id在缓存和数 据库中是否存在,都会将其存入到缓存中,只不过value为null。当对上述的id值在数据库中做了添加操作 后,Redis缓存会将已经存在于缓存中id对应的数据清除。当再次发起对这个id的查询时,查询的自然也就是 刚存入数据库的数据。

​ b、在不使用Redis集成Mybatis实现分布式缓存的情况下,可以在接口层增加校验,比如用户鉴权校验, 参数做校验,不合法的参数直接代码Return,比如:id 做基础校验,id <=0的直接拦截等。

​ c、Redis还有一个高级用法==布隆过滤器(Bloom Filter)==这个也能很好的防止缓存穿透的发生,他的原 理也很简单就是利用高效的数据结构和算法快速判断出你这个Key是否在数据库中存在,不存在你return就好 了,存在你就去查了DB刷新KV再return。

缓存击穿

​ **概念:**是指缓存中数据大批量到过期时间,而查询数据量巨大,引起数据库压力过大甚至down机。是指一个Key非常热点,在不停的扛着大并发,大并发集中对这一个点进行访问,当这个Key在失效的瞬间,持续的大并发就穿破缓存,直接请求数据库,就像在一个完好无损的桶上凿开了一个洞。

如何处理:设置热点数据永远不过期,或者加上互斥锁

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-z7Sspax4-1606313432310)(C:\Users\ThinkPad\AppData\Roaming\Typora\typora-user-images\image-20201112085021200.png)]

1.7补充

标签作用:与指定dao共享同一个缓存

使用方法:

注意:和不能同时出现

三、使用Redis实现Session共享

1.memcache和Redis管理机制的区别:

a、memcache与tomcat整合,实现的是全局Session管理机制,也就是说整个服务器所有应用全部基于memcache管理。

b、Redis与应用整合,实现的是基于应用的Session管理,也就是说一个应用会话交给Redis管理。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CP759K1K-1606313432312)(C:\Users\ThinkPad\AppData\Roaming\Typora\typora-user-images\image-20201112093439765.png)]

2.使用Redis实现Session共享

2.1导入依赖

<!--全局Session管理依赖-->
<dependency><groupId>org.springframework.session</groupId><artifactId>spring-session-data-redis</artifactId>
</dependency>

2.2application.yml

spring:session.store-type: redis

2.3自定义配置类来开启整个应用的会话交给Redis管理

package com.example.springbootredisdemo.config;import org.springframework.context.annotation.Configuration;
import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession;@Configuration
@EnableRedisHttpSession
//@EnableRedisHttpSession开启Redis全局会话管理
public class RedisSessionConfig {}

2.4编写controller

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;@RestController
public class SessionController {@RequestMapping("/trs")public void testRedisSession(HttpServletRequest request, HttpServletResponse response) throws IOException {List<String> list = (List<String>) request.getSession().getAttribute("list");if(list==null){list = new ArrayList<>();}list.add("xxx");request.getSession().setAttribute("list",list);response.getWriter().println("Session:" + request.getSession().getId());response.getWriter().println("counts:" + list.size());}
}

2.5执行结果:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7rLfWoNo-1606313432313)(C:\Users\ThinkPad\AppData\Roaming\Typora\typora-user-images\image-20201112111019093.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-V6EZvR7R-1606313432314)(C:\Users\ThinkPad\AppData\Roaming\Typora\typora-user-images\image-20201112111055270.png)]

2.6全部代码

pom.xml
<?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.5.RELEASE</version><relativePath/> <!-- lookup parent from repository --></parent><groupId>com.example</groupId><artifactId>springboot-redis-demo</artifactId><version>0.0.1-SNAPSHOT</version><packaging>war</packaging><name>springboot-redis-demo</name><description>Demo project for Spring Boot</description><properties><java.version>1.8</java.version></properties><dependencies>
<!--        web支持--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency>
<!--        热部署--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-devtools</artifactId><scope>runtime</scope><optional>true</optional></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-configuration-processor</artifactId><optional>true</optional></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>
<!--            排除传导依赖--><exclusions><exclusion><groupId>org.junit.vintage</groupId><artifactId>junit-vintage-engine</artifactId></exclusion></exclusions></dependency>
<!--        redis--><dependency><groupId>org.springframework.session</groupId><artifactId>spring-session-data-redis</artifactId></dependency>
<!--        去掉内嵌的Tomcat--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-tomcat</artifactId><scope>provided</scope></dependency>
<!--引入JSP支持--><dependency><groupId>org.apache.tomcat.embed</groupId><artifactId>tomcat-embed-jasper</artifactId><scope>provided</scope></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId><exclusions><exclusion><groupId>io.lettuce</groupId><artifactId>lettuce-core</artifactId></exclusion></exclusions></dependency><dependency><groupId>org.springframework.data</groupId><artifactId>spring-data-redis</artifactId></dependency><dependency><groupId>redis.clients</groupId><artifactId>jedis</artifactId></dependency><!--        MySQL--><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><scope>runtime</scope></dependency>
<!--        Mybatis--><dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>1.3.2</version></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId>
<!--                使用热部署出现中文乱码解决方法--><configuration><fork>true</fork>
<!--                    增加JVM参数--><jvmArguments>-Dfile.encoding=UTF-8</jvmArguments>
<!--                    指定入口类--><mainClass>com.example.springbootredisdemo.SpringbootRedisDemoApplication</mainClass></configuration></plugin></plugins></build></project>
application.yml
spring:datasource:username: rootpassword:url: jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf-8&useSSL=true&serverTimezone=UTCdriver-class-name: com.mysql.cj.jdbc.Driverthymeleaf:prefix: classpath:/templates/suffix: .htmlmode: LEGACYHTML5encoding: UTF-8cache: falsesession.store-type: redisredis:database: 0host: localhostport: 6379password:timeout: 300jedis:pool:max-active: 8max-wait: -1max-idle: 8min-idle: 0
mybatis:mapper-locations: classpath:mapping/*Mapper.xmltype-aliases-package: com.example.entity
#showSql
logging:level:com:example:mapper : debug
cache.RedisCache.java
package com.example.springbootredisdemo.cache;import com.example.springbootredisdemo.util.ApplicationContextUtil;
import org.apache.ibatis.cache.Cache;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.StringRedisSerializer;import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;/*** 重写Mybatis cache实现redis缓存* 不能交给工厂管理* */
public class RedisCache implements Cache {//必须定义这个String类型的id,因为这个id表示当前加入缓存的namespace;private String id;public RedisCache(String id) {this.id = id;}@Overridepublic String getId() {return id;}//放入缓存@Overridepublic void putObject(Object key, Object value) {//获取redisTemplate对象RedisTemplate redisTemplate = (RedisTemplate) ApplicationContextUtil.getBean("redisTemplate");//存储缓存数据redisTemplate.setKeySerializer(new StringRedisSerializer());//设置HashKey序列化redisTemplate.setHashKeySerializer(new StringRedisSerializer());//hash模型redisTemplate.opsForHash().put(id,key.toString(),value);}//从缓存中获取@Overridepublic Object getObject(Object key) {//获取redisTemplate对象RedisTemplate redisTemplate = (RedisTemplate) ApplicationContextUtil.getBean("redisTemplate");redisTemplate.setKeySerializer(new StringRedisSerializer());//设置HashKey序列化redisTemplate.setHashKeySerializer(new StringRedisSerializer());return redisTemplate.opsForHash().get(id.toString(),key.toString());}//从缓存中移除//真正使用过程中,这个方法并不会被用到@Overridepublic Boolean removeObject(Object key) {return null;}//清除缓存@Overridepublic void clear() {//获取redisTemplate对象RedisTemplate redisTemplate = (RedisTemplate) ApplicationContextUtil.getBean("redisTemplate");redisTemplate.delete(id);}//缓存命中率计算@Overridepublic int getSize() {
//      获取redisTemplate对象RedisTemplate redisTemplate = (RedisTemplate) ApplicationContextUtil.getBean("redisTemplate");redisTemplate.setKeySerializer(new StringRedisSerializer());return redisTemplate.opsForHash().size(id.toString()).intValue();}/** ReadWriteLock读写锁 表示:读写之间互斥,读读之间不互斥,写写之间不互斥* 区别于Synchronized  表示:读读之间互斥,写写之阿互斥,读写之间互斥* 因此ReadWriteLock效率比Synchronized高* 对于缓存,只有写操作,没有写操作* */@Overridepublic ReadWriteLock getReadWriteLock() {return new ReentrantReadWriteLock();}
}
config.RedisConfig.java
package com.example.springbootredisdemo.config;import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.*;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;/*** redis配置类* @program: redisdemo* @Author: david* @Description:*/
//RedisConfig中重写RedisTemplate类(why?)
@Configuration
@EnableCaching //开启注解
public class RedisConfig extends CachingConfigurerSupport {/*** retemplate相关配置* @param factory* @return*/@Beanpublic RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {RedisTemplate<String, Object> template = new RedisTemplate<>();// 配置连接工厂template.setConnectionFactory(factory);//使用Jackson2JsonRedisSerializer来序列化和反序列化redis的value值(默认使用JDK的序列化方式)Jackson2JsonRedisSerializer jacksonSeial = new Jackson2JsonRedisSerializer(Object.class);ObjectMapper om = new ObjectMapper();// 指定要序列化的域,field,get和set,以及修饰符范围,ANY是都有包括private和publicom.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);// 指定序列化输入的类型,类必须是非final修饰的,final修饰的类,比如String,Integer等会跑出异常om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);jacksonSeial.setObjectMapper(om);// 值采用json序列化template.setValueSerializer(jacksonSeial);//使用StringRedisSerializer来序列化和反序列化redis的key值template.setKeySerializer(new StringRedisSerializer());// 设置hash key 和value序列化模式template.setHashKeySerializer(new StringRedisSerializer());template.setHashValueSerializer(jacksonSeial);template.afterPropertiesSet();return template;}/*** 对hash类型的数据操作** @param redisTemplate* @return*/@Beanpublic HashOperations<String, String, Object> hashOperations(RedisTemplate<String, Object> redisTemplate) {return redisTemplate.opsForHash();}/*** 对redis字符串类型数据操作** @param redisTemplate* @return*/@Beanpublic ValueOperations<String, Object> valueOperations(RedisTemplate<String, Object> redisTemplate) {return redisTemplate.opsForValue();}/*** 对链表类型的数据操作** @param redisTemplate* @return*/@Beanpublic ListOperations<String, Object> listOperations(RedisTemplate<String, Object> redisTemplate) {return redisTemplate.opsForList();}/*** 对无序集合类型的数据操作** @param redisTemplate* @return*/@Beanpublic SetOperations<String, Object> setOperations(RedisTemplate<String, Object> redisTemplate) {return redisTemplate.opsForSet();}/*** 对有序集合类型的数据操作** @param redisTemplate* @return*/@Beanpublic ZSetOperations<String, Object> zSetOperations(RedisTemplate<String, Object> redisTemplate) {return redisTemplate.opsForZSet();}}
config.RedisSessionConfig.java
package com.example.springbootredisdemo.config;import org.springframework.context.annotation.Configuration;
import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession;@Configuration
@EnableRedisHttpSession
//@EnableRedisHttpSession开启Redis全局会话管理
public class RedisSessionConfig {}
Util.ApplicationContextUtil.java
package com.example.springbootredisdemo.util;import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.Configuration;//在自定义的类中获取工厂中已经创建好的某些对象
@Configuration
public class ApplicationContextUtil implements ApplicationContextAware {private static ApplicationContext context;@Overridepublic void setApplicationContext(ApplicationContext applicationContext) throws BeansException {this.context = applicationContext;}//根据bean id获取对象public static Object getBean(String id){return context.getBean(id);}//根据bean 类型获取对象public static Object getBean(Class clazz){return context.getBean(clazz);}//根据bean id和类型获取对象public static Object getBean(String id,Class clazz){return context.getBean(id,clazz);}}
controller.RedisSessionController.java
package com.example.springbootredisdemo.controller;import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;@RestController
public class RedisSessionController {@RequestMapping("/trs")public void testRedisSession(HttpServletRequest request, HttpServletResponse response) throws IOException {List<String> list = (List<String>) request.getSession().getAttribute("list");if(list==null){list = new ArrayList<>();}list.add("xxx");request.getSession().setAttribute("list",list);response.getWriter().println("Session:" + request.getSession().getId());response.getWriter().println("counts:" + list.size());}
}

四、主从复制架构(master-slave)、哨兵机制(sentinal)、Redis Cluster

1.主从复制(master-slave)

1.1概念

主从复制,是指将一台Redis服务器的数据(主服务器master)复制到其他的Redis服务器(从服务器slave)。数据的复制是单向的,只能由主节点到从节点。默认情况下,每台Redis服务器都是主节点;且一个主节点可以有多个从节点(或没有从节点),但一个从节点只能有一个主节点。(主从之间是1 : n关系,n >= 0)

1.2解决的问题

数据冗余备份

但不能解决故障的自动转移。

1.3搭建

(1)开启主从复制

​ 主从复制的开启,完全是在从节点发起的,主节点被动接收从节点的请求并做出相应处理就好。从节点开启主从复制,有3种方式:

  • 配置文件:在从服务器的配置文件中加入:slaveof
  • 启动命令:redis-server启动命令后加入 --slaveof
  • 客户端命令:Redis服务器(记为A)启动后,直接通过客户端(连接到A服务器的客户端)执行命令:slaveof ,则该Redis实例成为从节点。

(2)关闭主从复制

​ 通过slaveof 命令建立主从复制关系以后,可以通过****slaveof no one****断开。从节点断开复制后,不会删除已有的数据,只是不再接受主节点新的数据变化。

1.4原理

主从复制包括三个阶段:

a、建立连接阶段:保存主节点信息、建立socket连接、发送ping命令、身份验证、发送从节点端口信息

b、数据同步阶段:全量复制和部分复制

c、命令传播阶段:发送写命令、维持心跳机制

2.哨兵机制(sentinel)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zW7Dh594-1606313432316)(C:\Users\ThinkPad\AppData\Roaming\Typora\typora-user-images\image-20201112135250873.png)]

2.1概念

Redis的哨兵(sentinel) 系统用于管理多个 Redis 服务器,该系统执行以下三个任务:
a、监控(Monitoring): 哨兵(sentinel) 会不断地检查你的Master和Slave是否运作正常。
b、提醒(Notification):当被监控的某个 Redis出现问题时, 哨兵(sentinel) 可以通过 API 向管理员或者其他应用程序发送通知。

​ c、自动故障迁移(Automatic failover):当一个Master不能正常工作时,哨兵(sentinel) 会开始一次自动故障迁移操作,它会将失效Master的其中一个Slave升级为新的Master, 并让失效Master的其他Slave改为复制新的Master; 当客户端试图连接失效的Master时,集群也会向客户端返回新Master的地址,使得集群可以使用Master代替失效Master。

哨兵(sentinel) 是一个分布式系统,你可以在一个架构中运行多个哨兵(sentinel) 进程,这些进程使用流言协议(gossipprotocols)来接收关于Master是否下线的信息,并使用投票协议(agreement protocols)来决定是否执行自动故障迁移,以及选择哪个Slave作为新的Master。
每个哨兵(sentinel) 会向其它哨兵(sentinel)、master、slave定时发送消息,以确认对方是否”活”着,如果发现对方在指定时间(可配置)内未回应,则暂时认为对方已挂(所谓的”主观认为宕机” Subjective Down,简称sdown).
若“哨兵群”中的多数sentinel,都报告某一master没响应,系统才认为该master"彻底死亡"(即:客观上的真正down机,Objective Down,简称odown),通过一定的vote算法,从剩下的slave节点中,选一台提升为master,然后自动修改相关配置。
虽然哨兵(sentinel) 释出为一个单独的可执行文件 redis-sentinel ,但实际上它只是一个运行在特殊模式下的 Redis 服务器,你可以在启动一个普通 Redis 服务器时通过给定 --sentinel 选项来启动哨兵(sentinel)。

2.2解决的问题

哨兵机制其实就是带有故障自动转移功能的主从式架构;

主要解决的是:

a、数据冗余备份;

b、故障自动转移

但不能解决现有系统单节点并发压力和物理上限的问题。

2.3搭建

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xW39Kt9f-1606313432317)(C:\Users\ThinkPad\AppData\Roaming\Typora\typora-user-images\image-20201112140207946.png)]

2.4修改application.yml

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SAEyZYvj-1606313432318)(C:\Users\ThinkPad\AppData\Roaming\Typora\typora-user-images\image-20201112140052873.png)]

2.5工作方式

1):每个Sentinel以每秒钟一次的频率向它所知的Master,Slave以及其他 Sentinel 实例发送一个 PING 命令。

2):如果一个实例(instance)距离最后一次有效回复 PING 命令的时间超过 down-after-milliseconds 选项所指定的值, 则这个实例会被 Sentinel 标记为主观下线。

3):如果一个Master被标记为主观下线,则正在监视这个Master的所有 Sentinel 要以每秒一次的频率确认Master的确进入了主观下线状态。

4):当有足够数量的 Sentinel(大于等于配置文件指定的值)在指定的时间范围内确认Master的确进入了主观下线状态, 则Master会被标记为客观下线 。

5):在一般情况下, 每个 Sentinel 会以每 10 秒一次的频率向它已知的所有Master,Slave发送 INFO 命令 。

6):当Master被 Sentinel 标记为客观下线时,Sentinel 向下线的 Master 的所有 Slave 发送 INFO 命令的频率会从 10 秒一次改为每秒一次 。

7):若没有足够数量的 Sentinel 同意 Master 已经下线, Master 的客观下线状态就会被移除。

​ 若 Master 重新向 Sentinel 的 PING 命令返回有效回复, Master 的主观下线状态就会被移除。

3.Redis集群(cluster)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JoAAndvH-1606313432319)(C:\Users\ThinkPad\AppData\Roaming\Typora\typora-user-images\image-20201112141856941.png)]

3.1解决的问题

a、数据冗余备份;

b、故障自动转移

c、单节点并发压力和物理上限的问题

3.2详情可见另一篇文章——《RedisCluster》

失效的Master时,集群也会向客户端返回新Master的地址,使得集群可以使用Master代替失效Master。

哨兵(sentinel) 是一个分布式系统,你可以在一个架构中运行多个哨兵(sentinel) 进程,这些进程使用流言协议(gossipprotocols)来接收关于Master是否下线的信息,并使用投票协议(agreement protocols)来决定是否执行自动故障迁移,以及选择哪个Slave作为新的Master。
每个哨兵(sentinel) 会向其它哨兵(sentinel)、master、slave定时发送消息,以确认对方是否”活”着,如果发现对方在指定时间(可配置)内未回应,则暂时认为对方已挂(所谓的”主观认为宕机” Subjective Down,简称sdown).
若“哨兵群”中的多数sentinel,都报告某一master没响应,系统才认为该master"彻底死亡"(即:客观上的真正down机,Objective Down,简称odown),通过一定的vote算法,从剩下的slave节点中,选一台提升为master,然后自动修改相关配置。
虽然哨兵(sentinel) 释出为一个单独的可执行文件 redis-sentinel ,但实际上它只是一个运行在特殊模式下的 Redis 服务器,你可以在启动一个普通 Redis 服务器时通过给定 --sentinel 选项来启动哨兵(sentinel)。

2.2解决的问题

哨兵机制其实就是带有故障自动转移功能的主从式架构;

主要解决的是:

a、数据冗余备份;

b、故障自动转移

但不能解决现有系统单节点并发压力和物理上限的问题。

2.3搭建

[外链图片转存中…(img-xW39Kt9f-1606313432317)]

2.4修改application.yml

[外链图片转存中…(img-SAEyZYvj-1606313432318)]

2.5工作方式

1):每个Sentinel以每秒钟一次的频率向它所知的Master,Slave以及其他 Sentinel 实例发送一个 PING 命令。

2):如果一个实例(instance)距离最后一次有效回复 PING 命令的时间超过 down-after-milliseconds 选项所指定的值, 则这个实例会被 Sentinel 标记为主观下线。

3):如果一个Master被标记为主观下线,则正在监视这个Master的所有 Sentinel 要以每秒一次的频率确认Master的确进入了主观下线状态。

4):当有足够数量的 Sentinel(大于等于配置文件指定的值)在指定的时间范围内确认Master的确进入了主观下线状态, 则Master会被标记为客观下线 。

5):在一般情况下, 每个 Sentinel 会以每 10 秒一次的频率向它已知的所有Master,Slave发送 INFO 命令 。

6):当Master被 Sentinel 标记为客观下线时,Sentinel 向下线的 Master 的所有 Slave 发送 INFO 命令的频率会从 10 秒一次改为每秒一次 。

7):若没有足够数量的 Sentinel 同意 Master 已经下线, Master 的客观下线状态就会被移除。

​ 若 Master 重新向 Sentinel 的 PING 命令返回有效回复, Master 的主观下线状态就会被移除。

3.Redis集群(cluster)

[外链图片转存中…(img-JoAAndvH-1606313432319)]

3.1解决的问题

a、数据冗余备份;

b、故障自动转移

c、单节点并发压力和物理上限的问题

3.2详情可见另一

SpringBoot整合Redis+Redis缓存应用+Redis实现Session共享+...相关推荐

  1. SpringBoot整合mybatis、shiro、redis实现基于数据库的细粒度动态权限管理系统实例(转)...

    SpringBoot整合mybatis.shiro.redis实现基于数据库的细粒度动态权限管理系统实例 shiro 目录(?)[+] 前言 表结构 maven配置 配置Druid 配置mybatis ...

  2. SpringBoot整合mybatis、shiro、redis实现基于数据库的细粒度动态权限管理系统实例...

    SpringBoot整合mybatis.shiro.redis实现基于数据库的细粒度动态权限管理系统实例 shiro 目录(?)[+] 1.前言 本文主要介绍使用SpringBoot与shiro实现基 ...

  3. C#session共享+redis_Shiro权限管理框架(二):Shiro结合Redis实现分布式环境下的Session共享...

    精品推荐 国内稀缺优秀Java全栈课程-Vue+SpringBoot通讯录系统全新发布! Docker快速手上视频教程(无废话版)[免费] 作者:夜月归途 转载自: https://www.cnblo ...

  4. Redis+Tomcat+Nginx集群实现Session共享,Tomcat Session共享

    转载自  Redis+Tomcat+Nginx集群实现Session共享,Tomcat Session共享 一.Session共享使用tomcat-cluster-redis-session-mana ...

  5. 如果redis哨兵宕机了怎么办_Spring集成Redis做缓存,Redis宕机时Spring处理的问题

    采用的是Spring自带的缓存管理,使用Redis做缓存,在Spring中配置如下 @Configuration @EnableCaching public class CachingConfig { ...

  6. Redis + Tomcat + Nginx 集群实现 Session 共享

    点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试文章 作者 | 蕃薯耀 链接 | www.cnblogs.com/fan ...

  7. Redis解决老项目集群Session共享案例与回顾

    老项目突然之间客户要用了而且用户量还不少,后端移动端都需要给升级.第一改进的时候做了移动端与后端的服务分流,这次升级为分布式集群模式.分布式集群模式需要解决Session共享问题和数据一致性分布式锁处 ...

  8. Nginx+tomcat+redis实现高可用负载均衡session共享集群+redis哨兵监控

    实验拓扑图``` 实验步骤: 一.做nginx和tomcat的代理 二.做keepalived+nginx的双机热备份,vip:192.168.10.100 三.做keepalived+redis的哨 ...

  9. SpringBoot整合canal实现缓存更新

    canal是阿里巴巴的开源组件,用于监听MySQL的binlog日志而实现消息的同步机制,提供增量数据订阅和消费. canal必须基于MySQL的主从架构才可使用,canal会伪装成MySQL的一个s ...

  10. Redis 解决 WebSocket 分布式场景下 Session共享问题

    点击上方"服务端思维",选择"设为星标" 回复"669"获取独家整理的精选资料集 回复"加群"加入全国服务端高端社群「后 ...

最新文章

  1. Java 运行时的内存划分
  2. Geomesa-Hbase单机部署及ingest、export shp文件数据
  3. 微服务架构与Spring Cloud Alibaba
  4. 树莓派AI视觉云台——8、WiringPi库函数
  5. collections求和方法_java集合求和最大值最小值示例分享
  6. iis7.5配置 html,windows server 2008R2系统 IIS7.5配置伪静态的方法(urlrewrite)
  7. mysql linux_linux下mysql下载安装
  8. linux 查找某目录下包含关键字内容的文件(文件内容、grep)
  9. 《人月神话》读后总结
  10. Windos环境用Nginx配置反向代理和负载均衡
  11. 如何访问局域网的Access数据库?
  12. mysql当执行delete语句时备份_mysql中,执行delete语句时出现Lock wait timeout exceeded问题...
  13. [SHELL]判断一个命令是否存在
  14. 华为微博抽奖头目两次中奖:大哥咱玩不起,不玩行不行?
  15. EXCEL VBA常用代码集
  16. 华罗庚杯数学竞赛考试时间
  17. Ubuntu安装Google Chrome浏览器
  18. 操作系统学习笔记:操作系统基础知识
  19. 单片机、微处理器的WiFi http协议 网页响应
  20. 编译内核报错 No rule to make target ‘debian/canonical-certs.pem‘ 或 ‘canonical-revoked-certs.pem‘ 的解决方法

热门文章

  1. Android调用 Webservice报org.ksoap2.serializa 异常
  2. 反距离权重插值(IDW)的python实现
  3. 安卓项目中so库选择(ndk abiFilters设置,armeabi,armeabi-v7a,arm64-v8a)
  4. 点击密码input框禁止浏览器弹出已经记录的账号密码
  5. 精品微信小程序springboot服装企业人事管理系统+后台管理系统
  6. 转——嵌入式Linux学习路线
  7. 小米手机能刷鸿蒙系统了!这操作太6了!
  8. 微信小程序推广技巧、营销方案
  9. python链式队列
  10. Android Studio实现MQTT客户端