1. SpringDataRedis配置RedisTemplate介绍
-
RedisTemplate 介绍(推荐使用序列化、性能更好、业界都默认json序列化的)
- ValueOperations:简单K-V操作
- SetOperations:set类型数据操作
- ZSetOperations:zset类型数据操作
- HashOperations:针对map类型的数据操作
- ListOperations:list类型的数据操作
-
RedisTemplate和StringRedisTemplate的区别
- RedisTemplate和StringRedisTemplate的区别
- 两者的数据是不共通的(默认的序列化机制导致key不 ⼀样)
- StringRedisTemplate默认采⽤的是String的序列化策 略
- RedisTemplate默认采⽤的是JDK的序列化策略,会将 数据先序列化成字节数组然后在存⼊Redis数据库
-
总结
- 当redis数据库⾥⾯本来操作的是字符串数据的时 候,那使⽤StringRedisTemplate即可
- 数据是复杂的对象类型,那么使⽤RedisTemplate是 更好的选择
- 两者都是 String 结构
- 存储对象
2. RedisTemplate的序列化和反序列化机制
问题:同个key为啥获取不到值,核⼼就是序列化机制导致key 不⼀样
-
什么是序列化
- 把对象转换为字节序列的过程称为对象的序列化
- 把字节序列恢复为对象的过程称为对象的反序列化
- 对象的序列化主要有两种⽤途
- 把对象的字节序列永久地保存到硬盘上,通常存放 在⼀个⽂件中
- 在⽹络上传送对象的字节序列
-
Redis 为什么要序列化
- 性能可以提⾼,不同的序列化⽅式性能不⼀样
- 可视化⼯具更好查看
- 采⽤默认的jdk⽅式会乱码(POJO类需要实现 Serializable接⼝)
- 采⽤JSON⽅式则不⽤,且可视化⼯具更好查看
-
自定义Redis序列化方式,提供了多种可选择策略
- jdkSerializationRedisSerializer
- POJO对象的存取场景,使⽤JDK本身序列化机制
- 默认机制 ObjectInputStream/ObjectOutputStream进⾏序 列化操作
- StringRedisSerializer
- Key或者value为字符串
- Jackson2JsonRedisSerializer
- 利⽤jackson-json⼯具,将pojo实例序列化成json格 式存储
- GenericFastJsonRedisSerializer(阿里)
- 另一种javabean与json之间的转换,同时也需要指定Class类型
- jdkSerializationRedisSerializer
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1JbDj26F-1623767260757)(C:\Users\heng\AppData\Roaming\Typora\typora-user-images\image-20210608085549793.png)]
起 RedisTemplate配置类
@Configuration
public class RedisTemplateConfiguration {
/**
* @param redisConnectionFactory
* @return
*/
@Bean
public RedisTemplate<Object, Object>
redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(redisConnectionFactory);
// 使⽤Jackson2JsonRedisSerialize 替换默认序列化
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
// 对象转json的包
ObjectMapper objectMapper = new ObjectMapper();
// 配置基本属性 all 转换 json
objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
jackson2JsonRedisSerializer.setObjectMapper(objectMapper);
// 设置key和value的序列化规则
// 1. key改为String类型
redisTemplate.setKeySerializer(new StringRedisSerializer());
// 2. value改为String类型
redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
// 设置hashKey 和 hashValue的序列化规则
redisTemplate.setHashKeySerializer(jackson2JsonRedisSerializer);
// 设置支持事务
redisTemplate.setEnableTransactionSupport(true);
// 配置
redisTemplate.afterPropertiesSet();
// 返回 加入IOP容器
return redisTemplate;
}
}
3. SpringBoot 整合Jedis + Lettuce 客户端连接池配置
<!-- Spring2.X 集成redis 所需common-pool2-->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
<version>2.6.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<!--切换jedis 移除lettuce-->
<!-- <exclusions>-->
<!-- <exclusion>-->
<!-- <groupId>io.lettuce</groupId>-->
<!-- <artifactId>lettuce-core</artifactId>-->
<!-- </exclusion>-->
<!-- </exclusions>-->
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
</dependency>
application.yml 配置
spring:
redis:
#Redis 服务器地址
host: x.x.x.x
#Redis 服务器连接端口
port: 3366
password: 密码
max-redirects: 2 #获取失败 最大重定向次数
# 密码
#Redis 数据库索引(默认为0)
database: 0
#连接超时时间(毫秒)
timeout: 1800000
#连接池最大连接数(使用负值表示没有限制)
client-tyep: jedis
lettuce:
pool:
max-active: 20
#最大阻塞等待时间(负数表示没有限制)
max-wait: 1
#连接池中的最大空闲连接
max-idle: 5
#连接池中的最小空闲连接
min-idle: 0
jedis:
pool:
max-active: 20
#最大阻塞等待时间(负数表示没有限制)
max-wait: 1
#连接池中的最大空闲连接
max-idle: 5
#连接池中的最小空闲连接
min-idle: 0
分布式缓存实战-String数据结构案例
1.图形验证码 + 谷歌开源 Kaptcha
简介:
-
注册-登录-修改密码⼀般需要发送验证码,但是容易被 攻击恶意调⽤
-
公司带来的损失
- 短信⼀条5分钱,被⼤盗刷,带宽、连接等都被占 ⽤, 导致⽆法正常使⽤
-
如何避免⾃⼰的⽹站成为”⾁鸡“或者被刷呢
- 增加图形验证码(开发⼈员)
- 单IP请求次数限制(开发⼈员)
- 限制号码发送(⼀般短信提供商会做)
-
Kaptcha 框架介绍
- ⾕歌开源的⼀个可⾼度配置的实⽤验证码⽣成⼯具
- 验证码的字体/⼤⼩/颜⾊
- 验证码内容的范围(数字,字⺟,中⽂汉字!)
- 验证码图⽚的⼤⼩,边框,边框粗细,边框颜⾊ 验证码的⼲扰线
- 验证码的样式(⻥眼样式、3D、普通 模糊)
栗子:
<!--kaptcha依赖包-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>kaptcha-spring-boot-starter</artifactId>
<version>1.0.0</version>
</dependency>
配置类:
@Configuration
public class CaptchaConfig {
/**
* 验证码配置
* Kaptcha配置类名
* * @return
*/
@Bean
@Qualifier("captchaProducer")
public DefaultKaptcha kaptcha() {
DefaultKaptcha kaptcha = new DefaultKaptcha();
Properties properties = new Properties();
//验证码个数
properties.setProperty(Constants.KAPTCHA_TEXTPRODUCER_CHAR_LENGTH, "5");
//字体间隔
properties.setProperty(Constants.KAPTCHA_TEXTPRODUCER_CHAR_SPACE,"8");
//⼲扰线颜⾊
//⼲扰实现类
properties.setProperty(Constants.KAPTCHA_NOISE_IMPL, "com.google.code.kaptcha.impl.NoNoise");
//图⽚样式
properties.setProperty(Constants.KAPTCHA_OBSCURIFICATOR_IMPL, "com.google.code.kaptcha.impl.WaterRipple"); //⽂字来源
String Idcode = getCode();
properties.setProperty(Constants.KAPTCHA_TEXTPRODUCER_CHAR_STRING, Idcode);
Config config = new Config(properties);
kaptcha.setConfig(config);
return kaptcha;
}
public static String getCode(){
Random random = new Random();
String code = "";
for(int i=0;i<5;i++){
int rand = random.nextInt(10);
code += rand;
}
return code;
}
}
起 CaptchaController :
获取二维码图形
@Controller
public class CaptchaController {
@Autowired
private StringRedisTemplate redisTemplate;
// 和 CaptchaConfig 配置类的 名称一样
@Autowired
private Producer captchaProducer;
@GetMapping("/get_captchar")
public void getCaptchar(HttpServletRequest request, HttpServletResponse response){
// 生成验证码
String text = captchaProducer.createText();
// 获取 统一key
String key = getCaptcharKey(request);
System.out.println(key);
// 设置10分钟过期
redisTemplate.opsForValue().set(key,text,10, TimeUnit.MINUTES);
// 拿到图片
BufferedImage image = captchaProducer.createImage(text);
ServletOutputStream outputStream = null;
try {
// 输出流
outputStream = response.getOutputStream();
// 参 1.图片 2. 图片格式 3.输出流
ImageIO.write(image,"jpg",outputStream);
// 关闭
outputStream.flush();
outputStream.close();
} catch (Exception e){
e.printStackTrace();
}
}
// 把key避免过长 统一转MD5字符串
private String getCaptcharKey(HttpServletRequest request){
String ipAddr = CommonUtil.getIpAddr(request);
String userAgent = request.getHeader("User-Agent");
String key = "user-service:captchar:"+CommonUtil.MD5(ipAddr+userAgent);
return key;
}
}
验证二维码:
起 验证码工具类
public class JsonData {
/**
* 状态码 0 表示成功
*/
private Integer code;
/**
* 数据
*/
private Object data;
/**
* 描述
*/
private String msg;
public JsonData(int code,Object data,String msg){
this.code = code;
this.msg = msg;
this.code = code;
}
/**
* 成功,不传入数据
* @return
*/
public static JsonData buildSuccess() {
return new JsonData(0, null, null);
}
/**
* 成功,传入数据
* @param data
* @return
*/
public static JsonData buildSuccess(Object data) {
return new JsonData(0, data, null);
}
/**
* 失败,传入描述信息
* @param msg
* @return
*/
public static JsonData buildError(String msg) {
return new JsonData(-1, null, msg);
}
public Integer getCode() {
return code;
}
public void setCode(Integer code) {
this.code = code;
}
public Object getData() {
return data;
}
public void setData(Object data) {
this.data = data;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
}
在控制层 加入:
/**
*
* 发送验证码
* @param request
* @return
*
*
*/
@GetMapping("/getCode")
public JsonData sendCode(@RequestParam(value = "to",required = true) String to,
@RequestParam(value = "captchar",required = true) String captchar,
HttpServletRequest request){
String key = getCaptcharKey(request);
// 获取key
String cacheCaptchar = redisTemplate.opsForValue().get(key);
// 判断 一样删除
if(captchar != null && cacheCaptchar != null && cacheCaptchar.equals(captchar)){
redisTemplate.delete(key);
//TODO 发送验证码
return JsonData.buildSuccess();
} else {
return JsonData.buildError("验证码错误");
}
}
2. Jmeter5.x
热点数据接口压测
-
QPS: (Query Per Second): 每秒请求数,就是说服务器在 ⼀秒的时间内处理了多少个请求
-
新增聚合报告:线程组->添加->监听器->聚合报告 (Aggregate Report)
lable: sampler的名称
Samples: ⼀共发出去多少请求,例如10个⽤户,循环10 次,则是 100
Average: 平均响应时间
Median: 中位数,也就是 50% ⽤户的响应时间 90%
Line : 90% ⽤户的响应不会超过该时间 (90% of the samples took no more than this time. The remaining samples at least as long as this) 95%
Line : 95% ⽤户的响应不会超过该时间 99%
Line : 99% ⽤户的响应不会超过该时间
min : 最⼩响应时间
max : 最⼤响应时间
Error%:错误的请求的数量/请求的总数
Throughput: 吞吐量——默认情况下表示每秒完成的请求数 (Request per Second) 可类⽐为qps、tps KB/Sec: 每秒接收数据量
3. Hash 类型案例
-
背景
- 电商购物⻋实现,⽀持买多件商品,每个商品可以买不 同数量
- ⽀持⾼性能处理
-
购物⻋常⻅实现⽅式
- 实现⽅式⼀:存储到数据库
- 性能存在瓶颈
- 实现⽅式⼆:前端本地存储-localstorage-sessionstorag
- localstorage在浏览器中存储 key/value 对,没有过期时间
- sessionstorage在浏览器中存储 key/value 对,在关闭会话窗⼝后将会删除这些数据
- 实现⽅式三:后端存储到缓存如redis
- 可以开启AOF持久化防⽌重启丢失(推荐)
- 实现⽅式⼀:存储到数据库
-
购物车数据结构介绍
- ⼀个购物⻋⾥⾯,存在多个购物项
- 所以 购物⻋结构是⼀个双层Map:
- Map<String,Map<String,String>>
- 第一层Map, Key是用户id
- 第二层Map,Key是购物车中商品id,值是购物车数据
-
对应redis⾥⾯的存储
- redis⾥⾯有多种数据结构,应该使⽤哪种?
- 答案是 hash结构
4. SpringCache框架常⽤之Cacheable
Cacheable注解
-
标记在⼀个⽅法上,也可以标记在⼀个类上
-
缓存标注对象的返回结果,标注在⽅法上缓存该⽅法的 返回值,标注在类上缓存该类所有的⽅法返回值
-
value 缓存名称,可以有多个
-
key 缓存的key规则,可以⽤springEL表达式,默认是 ⽅法参数组合
-
condition 缓存条件,使⽤springEL编写,返回true才 缓存
案例:
//@Cacheable(value = {"product_page"},key = "#root.methodName+'_'+#page+'_'+#size") key麻烦 自定义规则 二选一
- spEL表达式
- methodName 当前被调⽤的⽅法名
- root.methodname
- args 当前被调⽤的⽅法的参数列表
- root.args[0]
- result ⽅法执⾏后的返回值
- result
- methodName 当前被调⽤的⽅法名
⾃定义CacheManager配置和过期 时间
- 修改redis缓存序列化器和配置manager过期时间
/**
* 配置过期时间 1分钟
* @param connectionFactory
* @return
*/
@Bean
public RedisCacheManager cacheManager1Minute(RedisConnectionFactory connectionFactory) {
RedisCacheConfiguration config = instanceConfig(60L);
return RedisCacheManager.builder(connectionFactory).cacheDefaults(config).transactionAware().build();
}
/**
* 配置过期时间 1小时
* @param connectionFactory
* @return
*/
@Bean
@Primary
public RedisCacheManager cacheManager1Hour(RedisConnectionFactory connectionFactory) {
RedisCacheConfiguration config = instanceConfig(3600L);
return RedisCacheManager.builder(connectionFactory).cacheDefaults(config).transactionAware().build();
}
/**
* 过期数据1天
* @param connectionFactory
* @return
*/
@Bean
public RedisCacheManager cacheManager1Day(RedisConnectionFactory connectionFactory) {
RedisCacheConfiguration config = instanceConfig(3600 * 24L);
return RedisCacheManager.builder(connectionFactory)
.cacheDefaults(config)
.transactionAware()
.build();
}
private RedisCacheConfiguration instanceConfig(Long ttl) {
Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
objectMapper.registerModule(new JavaTimeModule());
// 去掉各种@JsonSerialize注解的解析
objectMapper.configure(MapperFeature.USE_ANNOTATIONS, false);
// 只针对⾮空的值进⾏序列化
objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
// 将类型序列化到属性json字符串中
objectMapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);
jackson2JsonRedisSerializer.setObjectMapper(objectMapper);
//ofSeconds 是秒 disableCachingNullValues 禁止缓存null值
return RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofSeconds(ttl))
.disableCachingNullValues()
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer));
}
SpringCache框架⾃定义缓存KeyGenerator
/**
* Cache框架⾃定义缓存KeyGenerator
* @return
*/
@Bean
public KeyGenerator springCacheDefaultKeyGenerator(){
return new KeyGenerator() {
@Override
public Object generate(Object o, Method method, Object... objects) {
String key = o.getClass().getSimpleName()+"_"+method.getName()+"_"+ StringUtils.arrayToDelimitedString(objects, "_");
System.out.println(key);
return key;
}
};
}
使用:
- key 属性和keyGenerator属性只能⼆选⼀
@Cacheable(value = {"product_page"},keyGenerator = "springCacheDefaultKeyGenerator")
常⽤注解CachePut
- CachePut
- 根据⽅法的请求参数对其结果进⾏缓存,每次都会触发 真实⽅法的调⽤
- value 缓存名称,可以有多个
- key 缓存的key规则,可以⽤springEL表达式,默认是 ⽅法参数组合
- condition 缓存条件,使⽤springEL编写,返回true才 缓存
案例:
@CachePut(value = {"product"}, key="#productDO.id", cacheManager = "cacheManager1Minute")
常⽤之CacheEvict
- CacheEvict
- 从缓存中移除相应数据, 触发缓存删除的操作
- value 缓存名称,可以有多个
- key 缓存的key规则,可以⽤springEL表达式,默认是 ⽅法参数组合
- beforeInvocation = false
- 缓存的清除是否在⽅法之前执⾏ ,默认代表缓存清除 操作是在⽅法执⾏之后执⾏;
- 如果出现异常缓存就不会清除
- beforeInvocation = true
- 代表清除缓存操作是在⽅法运⾏之前执⾏,⽆论⽅ 法是否出现异常,缓存都清除
案例:
@CacheEvict(value = {"product"},key = "#root.args[0]")
多注解组合Caching
- Caching
- 组合多个Cache注解使⽤
- 允许在同⼀⽅法上使⽤多个嵌套的@Cacheable、 @CachePut和@CacheEvict注释
@Caching(
cacheable = {
@Cacheable(value = {"product"},key = "#root.args[0]"),
@Cacheable(value = {"product"},key = "'heng'+#root.args[0]"),
},
put = {
@CachePut(value = {"product_test"}, key="#id", cacheManager = "cacheManager1Minute")
}
)
分布式缓存必考题之缓存击穿 +解决⽅案
-
缓存击穿 (某个热点key缓存失效了)
- 缓存中没有但数据库中有的数据,假如是热点数据,那 key在缓存过期的⼀刻,同时有⼤量的请求,这些请求 都会击穿到DB,造成瞬时DB请求量⼤、压⼒增⼤。
- 和缓存雪崩的区别在于这⾥针对某⼀key缓存,后者则 是很多key。
-
预防
- 设置热点数据不过期
- 定时任务定时更新缓存
- 设置互斥锁
-
SpringCache解决⽅案
- 缓存的同步 sync
- sync 可以指示底层将缓存锁住,使只有⼀个线程可以 进⼊计算,⽽其他线程堵塞,直到返回结果更新到缓存 中
@Cacheable(value = {"product"},keyGenerator = "springCacheDefaultKeyGenerator",cacheManager = "cacheManager1Minute",sync = true)
分布式缓存必考题之缓存雪崩 +解决⽅案
-
缓存雪崩 (多个热点key都过期)
- ⼤量的key设置了相同的过期时间,导致在缓存在同⼀ 时刻全部失效,造成瞬时DB请求量⼤、压⼒骤增,引 起雪崩
-
预防
- 存数据的过期时间设置随机,防⽌同⼀时间⼤量数 据过期现象发⽣
- 设置热点数据永远不过期,定时任务定时更新
-
SpringCache解决⽅案
- 设置差别的过时时间
- ⽐如CacheManager配置多个过期时间维度
- 配置⽂件 time-to-live 配置
cache:
#使⽤的缓存类型
type: redis
#过时时间
redis:
time-to-live: 3600000
# 开启前缀,默以为true
use-key-prefix: true
# 键的前缀,默认就是缓存名cacheNames
key-prefix: XD_CACHE
# 是否缓存空结果,防⽌缓存穿透,默以为true
cache-null-values: true
分布式缓存必考题之缓存穿透 +解决⽅案
-
缓存穿透(查询不存在数据)
- 查询⼀个不存在的数据,由于缓存是不命中的,并且出 于容错考虑,如发起为id为“-1”不存在的数据
- 如果从存储层查不到数据则不写⼊缓存这将导致这个不 存在的数据每次请求都要到存储层去查询,失去了缓存 的意义。存在⼤量查询不存在的数据,可能DB就挂掉 了,这也是⿊客利⽤不存在的key频繁攻击应⽤的⼀种 ⽅式
-
预防
- 接⼝层增加校验,数据合理性校验
- 缓存取不到的数据,在数据库中也没有取到,这时 也可以将key-value对写为key-null,设置短点的过 期时间,防⽌同个key被⼀直攻击
-
SpringCache解决⽅案
- 空结果也缓存,默认不配置condition或者unless就 ⾏
cache:
#使⽤的缓存类型
type: redis
#过时时间
redis:
time-to-live: 3600000
# 开启前缀,默以为true
use-key-prefix: true
# 键的前缀,默认就是缓存名cacheNames
key-prefix: XD_CACHE
# 是否缓存空结果,防⽌缓存穿透,默以为true
cache-null-values: true
RDB 和 AOF 使用
- RDB持久化与AOF持久化⼀起使⽤
- 如果Redis中的数据并不是特别敏感或者可以通过其它 ⽅式重写⽣成数据
- 集群中可以关闭AOF持久化,靠集群的备份⽅式保证可 ⽤性
- ⾃⼰制定策略定期检查Redis的情况,然后可以⼿动触 发备份、重写数据;
- 采⽤集群和主从同步
Redis4.0后开始的rewrite⽀持混合模式
-
RDB和AOF 双⽤
-
直接将rdb持久化的⽅式来操作将⼆进制内容覆盖到aof ⽂件中,rdb是⼆进制,所以很⼩
-
有写⼊的话还是继续append追加到⽂件原始命令,等 下次⽂件过⼤的时候再次rewrite
-
默认是开启状态
-
好处
- 混合持久化结合了RDB持久化 和 AOF 持久化的优 点,采取了rdb的⽂件⼩易于灾难恢复
- 同时结合AOF,增量的数据以AOF⽅式保存了,数据 更少的丢失
-
坏处
- 前部分是RDB格式,是⼆进制,所以阅读性较差
-
数据恢复
- 先看是否存在aof⽂件,若存在则先按照aof⽂件恢 复,aof⽐rdb全,且aof⽂件也rewrite成rdb⼆进制 格式
- 若aof不存在,则才会查找rdb是否存在
Redis6的key过期时间删除策略
-
redis key过期策略
- 定期删除+惰性删除
-
Redis如何淘汰过期的keys:
- 定期删除:
- 隔⼀段时间,就随机抽取⼀些设置了过期时间的 key,检查其是否过期,如果过期就删除
- 定期删除可能会导致很多过期 key 到了时间并没有 被删除掉,那咋整呢,所以就是惰性删除
- 惰性删除 :
- 概念:当⼀些客户端尝试访问它时,key会被发现并 主动的过期
- 放任键过期不管,但是每次从键空间中获取键时, 都检查取得的键是否过期,如果过期的话,就删除 该键
- 定期删除:
内存不⾜时-Redis的Key淘 汰策略
- 简介
redis在占⽤的内存超过指定的maxmemory之后, 通过maxmemory_policy确定redis是否释放内存以及 如何释放内存 提供多种策略
-
策略
- volatile-lru(least recently used)
- 最近最少使⽤算法,从设置了过期时间的键中选择 空转时间最⻓的键值对清除掉;
- volatile-lfu(least frequently used)
- 最近最不经常使⽤算法,从设置了过期时间的键中 选择某段时间之内使⽤频次最⼩的键值对清除掉;
- volatile-ttl
- 从设置了过期时间的键中选择过期时间最早的键值 对清除 (删除即将过期的)
- volatile-random
- 从设置了过期时间的键中,随机选择键进⾏清除;
- allkeys-lru
- 最近最少使⽤算法,从所有的键中选择空转时间最 ⻓的键值对清除;
- allkeys-lfu
- 最近最不经常使⽤算法,从所有的键中选择某段时 间之内使⽤频次最少的键值对清除;
- allkeys-random
- 所有的键中,随机选择键进⾏删除;
- noeviction
- 不做任何的清理⼯作,在redis的内存超过限制之 后,所有的写⼊操作都会返回错误;但是读操作都 能正常的进⾏
- volatile-lru(least recently used)
-
config配置的时候 下划线_的key需要⽤中横线-
127.0.0.1:6379> config set maxmemory_policy
volatile-lru
(error) ERR Unsupported CONFIG parameter:
maxmemory_policy
127.0.0.1:6379> config set maxmemory-policy
volatile-lru
OK
主从 哨兵
主从 加速复制
- 完全重新同步需要在磁盘上创建⼀个RDB⽂件,然后加载这个⽂件以便为从服务器发送数据
- 在⽐较低速的磁盘,这种操作会给主服务器带来较⼤的 压⼒
- 新版⽀持⽆磁盘的复制,⼦进程直接将RDB通过⽹络发 送给从服务器,不使⽤磁盘作为中间存储
- repl-diskless-sync yes (默认是no
Sentinel(哨兵)三⼤⼯作任务
-
监控(Monitoring)
- Sentinel 会不断地检查你的主服务器和从服务器是 否运作正常
-
提醒(Notification)
- 当被监控的某个 Redis 服务器出现问题时, Sentinel 可以通过 API 向管理员或者其他应⽤程序 发送通知
-
自动故障迁移(Automatic failover)
- 当⼀个主服务器不能正常⼯作时, Sentinel 会开始 ⼀次⾃动故障迁移操作, 它会将失效主服务器的其 中⼀个从服务器升级为新的主服务器, 并让失效主 服务器的其他从服务器改为复制新的主服务器
- 当客户端试图连接失效的主服务器时, 集群也会向 客户端返回新主服务器的地址, 使得集群可以使⽤ 新主服务器代替失效服务器
-
问题
- ⼀个哨兵进程对Redis服务器进⾏监控,可能会出现问题
- ⼀般是使⽤多个哨兵进⾏监控,各个哨兵之间还会进⾏ 监控,形成多哨兵模式
-
多哨兵模式下线名称介绍
- 主观下线(Subjectively Down, 简称 SDOWN)
- 是单个 Sentinel 实例对服务器做出的下线判断,⽐ 如⽹络问题接收不到通知等
- ⼀个服务器没有在 down-after-milliseconds 选项所 指定的时间内, 对向它发送 PING 命令的 Sentinel 返回⼀个有效回复(valid reply), 那么 Sentinel 就会将这个服务器标记为主观下线
- 客观下线(Objectively Down, 简称 ODOWN)
- 指的是多个 Sentinel 实例在对同⼀个服务器做出 SDOWN 判断, 并且通过 SENTINEL is-masterdown-by-addr 命令互相交流之后, 得出的服务器 下线判断
- ⼀个 Sentinel 可以通过向另⼀个 Sentinel 发送 SENTINEL is-master-down-by-addr 命令来询问对 ⽅是否认为给定的服务器已下线
- 客观下线条件只适⽤于主服务器
- 主观下线(Subjectively Down, 简称 SDOWN)
-
仲裁 qurum
- Sentinel 在给定的时间范围内, 从其他 Sentinel 那 ⾥接收到了【⾜够数量】的主服务器下线报告, 那么 Sentinel 就会将主服务器的状态从主观下线改变 为客观下线
- 这个【⾜够数量】就是配置⽂件⾥⾯的值,⼀般是 Sentinel个数的⼀半加1,⽐如3个Sentinel则就设置 为2
- down-after-milliseconds 是⼀个哨兵在超过规定时 间依旧没有得到响应后,会⾃⼰认为主机不可⽤
- 当拥有认为主观下线的哨兵达到sentinel monitor所 配置的数量时,就会发起⼀次投票,进⾏failover
节点⾼可⽤之Cluster数据分⽚ 和虚拟哈希槽介
背景:
- 主节点的写能⼒和存储能⼒受限
- 单台机器⽆法满⾜需求,因此把数据分散存储到多个机 器
- 类似案例:mysql分库分表
常⻅的数据分区算法
- 哈希取模
- 对选择的 partitioning key 计算其哈希值,得到的哈 希值就是对应的分区
- 范围分⽚
- 通过确定分区键是否在某个范围内来选择分区
- ⼀致性Hash分区
- redis cluster集群没有采⽤⼀致性哈希⽅案,⽽是采⽤ 【数据分⽚】中的哈希槽来进⾏数据存储与读取的
什么是Redis的哈希槽 slot
-
Redis集群预分好16384个槽,当需要在 Redis 集群中 放置⼀个 key-value 时,根据 CRC16(key) mod 16384 的值,决定将⼀个key放到哪个桶中
-
假设主节点的数量为3,将16384个槽位按照【⽤户⾃ ⼰的规则】去分配这3个节点,每个节点复制⼀部分槽 位
- 节点1的槽位区间范围为0-5460
- 节点2的槽位区间范围为5461-10922
- 节点3的槽位区间范围为10923-16383
- 注意:从节点是没有槽位的,只有主节点才有
-
存储查找
- 对要存储查找的键进⾏crc16哈希运算,得到⼀个值, 并取模16384,判断这个值在哪个节点的范围区间
- 假设crc16(“test_key”)%16384=3000,就是节点⼀
- crc16算法不是简单的hash算法,是⼀种校验算法
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RKtg7TyJ-1623767260758)(C:\Users\heng\AppData\Roaming\Typora\typora-user-images\image-20210611165627241.png)]
- 使⽤哈希槽的好处就在于可以⽅便的添加或移除节点
- 当需要增加节点时,只需要把其他节点的某些哈希槽挪 到新节点就可以了;
- 当需要移除节点时,只需要把移除节点上的哈希槽挪到其他节点就⾏了