Redis的实战篇
1.短信的登入注册
可以使用RandomUtil.randomNumbers(6)生成6位随机数
1.1 使用session来存储验证码
题外话:session与Cookie的关系:
- 当用户登录的时候,服务器在Session中新增一个新记录,并把SessionID返回给客户端(通过HTTP响应的Set-Cookie字段返回)。
- 客户端后续再给服务器发送请求的时候,需要在请求中带上SessionID(通过HTTP请求中的Cookie字段带上)
- 服务器收到请求之后,根据SessionID在Session信息中获取到对应的用户信息,再进行后续操作,找不到则重新创建Session,并把SessionID返回。
1.1.1 验证用户的登入状态
使用拦截器(为什么要使用拦截器?)
1.保证用户的登入状态
2.在集群模式下,可以获得用户的信息,存储在ThreadLocal下
拦截器的使用 类 implements HandlerInterceptor(三个方法), 然后需要在Configuraion中配置addInterceptor
1.1.2 需要注意用户的隐藏信息
返回的数据要有限的,可以使用BeanUtils.copyProperties()
来复制对象,然后再返回
1.2.3问题
每个tomcat中都有一份属于自己的session,假设用户第一次访问第一台tomcat,并且把自己的信息存放到第一台服务器的session中,但是第二次这个用户访问到了第二台tomcat,那么在第二台服务器上,肯定没有第一台服务器存放的session,所以此时 整个登录拦截功能就会出现问题。
1.2 使用redis来存储session
使用UUID.randomUUID().toString()
来生成一个唯一的token
在去返回给前端,前端在请求的时候,需要在请求头中带上这个token
在经过拦截器的时候,我们可以通过这个token来获取到用户的信息
1.3 优化
有一些不用登入的接口,我们可以不用拦截器,直接放行
可以设置2个拦截器。(解决刷新登录token令牌的问题)
- 第一个拦截器,用来拦截token,把用户的信息存放到ThreadLocal中
- 第二个拦截器,用来判断用户是否登入,如果没有登入,就返回一个错误信息
2.商户查询缓存
缓存(Cache),就是数据交换的缓冲区,俗称的缓存就是缓冲区内的数据,一般从数据库中获取,存储于本地代码
浏览器缓存:主要是存在于浏览器端的缓存
**应用层缓存:**可以分为tomcat本地缓存,比如之前提到的map,或者是使用redis作为缓存
**数据库缓存:**在数据库中有一片空间是 buffer pool,增改查数据都会先加载到mysql的缓存中
2.1使用redis来缓存商户数据
较为常规,使用redis的String类型来存储数据
2.2 缓存更新策略
- 内存淘汰:redis自动进行,当redis内存达到咱们设定的max-memery的时候,会自动触发淘汰机制,淘汰掉一些不重要的数据(可以自己设置策略方式)
- 超时剔除: 设置一个过期时间,当过期时间到了,就会自动删除
- 主动更新:当数据发生变化的时候,我们可以主动去更新缓存
- 问题:数据库与redis出现数据不一致的情况
- 1.是写少读多的情况,可以使用以把缓存删除,等待再次查询时,将缓存中的数据加载出来
- 2.删除删除缓存还是更新缓存
- 3.何保证缓存与数据库的操作的同时成功或失败
- 需要先操作数据库,再删除缓存,防止高并发时候,出现了脏数据
2.3 缓存穿透
防止黑客通过一些特殊的字符,来绕过缓存,直接访问数据库
- 1.缓存空对象
- 就是当数据库中没有这个数据的时候,我们也把这个空对象存放到缓存中,访问时候,先去缓存中查找,如果没有,再去数据库中查找
- 2.布隆过滤器(可能会出现误判)
解决方案:- 缓存null值
- 布隆过滤
- 增强id的复杂度,避免被猜测id规律
- 做好数据的基础格式校验
- 加强用户权限校验
- 做好热点参数的限流
2.4缓存雪崩问题及解决思路
解决方案:
- 给不同的Key的TTL添加随机值(Random)
- 利用Redis集群提高服务的可用性
- 给缓存业务添加降级限流策略
- 给业务添加多级缓存
2.5 缓存击穿问题(热点Key问题)
就是在高并发的情况下,一个热点key过期了,导致大量的请求直接访问数据库,导致数据库压力过大
解决方案:
- 1.使用互斥锁
- 2.使用逻辑过期
2.5.1 使用互斥锁
-
1.在获取缓存的时候,先去获取锁,如果获取不到锁,就等待一段时间,再去获取锁,使用了串行的方式
-
2.获得锁的去查询数据库,然后再去更新缓存
问题:1.性能不行 2.可能有死锁的情况
如何实现:1.s使用redis的setIfabsent 来去设置锁private boolean tryLock(String key) { Boolean flag = stringRedisTemplate.opsForValue().setIfAbsent(key, “1”, 10, TimeUnit.SECONDS);
return BooleanUtil.isTrue(flag);
}
private void unlock(String key) {
stringRedisTemplate.delete(key);
}
2.5.2 使用逻辑过期
还没写完。。