redis 缓存


一、缓存更新策略、缓存穿透、雪崩、击穿

redis 集群登陆

背景:保存用户信息和验证码,用的session,由于session数据不共享,所以启动redis

1、redis 字符串格式。set  15201672234 2345,以手机号作为key

2、输入登录码,到数据库验证是否有该手机号,没有则新增;不管新增还是不新增,保存hset 用户信息。eg: hset token userinfo name tom tel 1336767676 code 2344

3、设置过期时间30分钟,30分钟没有请求,就过期用户信息。后端有拦截器,只要有请求,就更新过期时间

 

redis 缓存

1、缓存的作用:降低后端负载、提高读写速度

2、缓存的成本:数据不一致、开发、运维成本提升

 

 

 

3、缓存更新策略:

自带的内存淘汰机制,内存充足,发生长时间不淘汰。

超时删除,也会发生数据不一致的情况。

主动更新,修改数据的时候,更新缓存。

先更新数据库,再删除缓存。对失效的缓存会出现短暂的数据不一致的情况。单体系统,把缓存和更新数据放在一个事务中,保持其原子性。分布式系统,tcc分布式事务。

 

4、缓存穿透

访问一个不存在的数据,缓存没有,数据库也没有,加大对数据库的压力,导致数据库奔溃。

解决方案

1、缓存空对象

缺点:大量空对象,可以设置过期时间减少大量空数据的缓存。数据不一致,访问为空,实际有数据,设置过期时间解决。

在逻辑处理的时候,判断缓存对象是否为空,例如,不空返回信息,否则,返回不存在。

2、布隆过滤器,将所有数据库ID,进行哈希算法以二进制的形式存储在数组里。并不是百分百的准确,有误判情况。优点空间占用少。

 

 

 

5、缓存雪崩

大量缓存同时失效或者redis服务宕机,导致大量请求到达数据库,给数据库造成压力。

解决方案

1、大量缓存同时失效。对数据设置随机过期时间。

2、redis服务宕机。部署redis集群。

3、给缓存业务,降级限流。

4、给业务增加多级缓存。例如“浏览器缓存、Nginx服务缓存、redis缓存。

 

6、缓存击穿

高频次高并发的、业务复杂的sql,访问的缓存没了,给数据库造成压力。(高并发的热点key 失效)

重建数据的时间比较长,导致在这段时间内,获取数据的请求全跑到了数据库里。

解决方案

1、互斥锁

当请求获取数据,正在重建数据中,另外一个请求又来了,也去重建数据,导致大量资源全在重建同样的数据。

解决方案,第一次重建数据的过程中,开辟一个新线程,加锁,建完后,释放锁。原来的线程直接返回旧的数据(哪里来的旧数据?),缺点暂时性的数据不一致。另外一个请求来了获取互斥锁,加睡眠时间,过一段时间再来获取互斥锁。

缺点,所有请求都在等待数据重建完成。

互斥锁用的是setnx,  key 设置后,是不可被覆盖的。防止死锁要加过期时间释放锁;设置key-value、过期时间,要保持其原子性,写法:set key value  nx ex 10

 

2、逻辑过期

 

二、卖超了(其实就是消费重复问题)

并发情况下,出现卖超了。乐观锁版本号解决方案。

悲观锁,添加同步锁了,业务串联进行。效率低下。

乐观锁,不加锁,更新之前判断是否有修改。性能好,成功率低。

先判断版本号,更新的时候where 的是版本号,如果一致,证明没有人更新,进行更新操作。

如果有人更新,where 条件查询失败。

可以将版本号简化为库存号。

 成功率低,会出现,100个库存,只抢了20多个,剩下70多个。因为stock=存库,造成sql 失败很多,修改成 stock >0,提高成功率。

三、一人一单

在修改库存之前,判断订单数据库有没有用户id,如果有,就返回,“用户只能抢一张优惠券”。还需要给用户ID加锁,一个用户只能抢一单。

流程:判断活动开始结束时间、库存——>给用户ID加锁,订单数据库有没有用户id——>版本号库存法更新库存,加事务

此方法用的是悲观锁。

以上适用于单机,如果是集群模式,会发生同一个用户抢2单的情况。因为是多台服务,每天服务是一把锁,每台机器的锁资源不共享。

 

四、分布式锁

 分布式锁:多线程可见、互斥、高可用。用redis

 

分布式锁还存在一个问题,阻塞时间太长,A业务没有执行完成,锁被释放,准备删除锁的时候,B线程乘虚而入正在执行,A将B线程删除。

问题:删除了被人的锁 

解决方案:业务是用线程ID+UUID(因为集群模式下,线程ID会一致)做的锁key,删除锁的时候,判断是否是自己的锁,如果是自己的锁则删除,如果不是自己的锁,不删除,防止误删别人的。

问题:以上方案还是不完美,判断是否是自己的锁,然后删除锁是两个动作,在删除锁的时候,遇到阻塞(垃圾回收导致阻塞),导致锁超时被删除,线程B获取锁执行,出现并发,数据重复。

解决方案:判断是否是自己的锁与删除锁,放在同一事务中,保持其原子性。

知识点:redis的原子性具有局限性,有执行成功的,有执行失败的,redis 不会回滚。用Lua脚本来保持redis的原子性,将判断与删除放在Lua 脚本里。

五、Lua脚本

 

 

总结
基于 Redis 的分布式锁实现思路:
  • 利用 set nx ex 获取锁,并设置过期时间,保存线程标示
  • 释放锁时先判断线程标示是否与自己一致,一致则删除锁
特性:
  • 利用 set nx 满足互斥性
  • 利用 set ex 保证故障时锁依然能释放,避免死锁,提高安全性
  • 利用 Redis 集群保证高可用和高并发特性

五、分布式锁——redisson 

以上方案较为成熟,可解决大部分问题。但是还存在以下问题,不过这些问题不解决也可用。

 redisson 可解决上面问题。

 

redis 重入原理

redis可重入,同一进程,可以多次获取锁,获取几次,最后就要释放几次。用hset lock thread 1 , hset lock thread 2,代表获取了2次锁。执行完业务,再删除锁 del lock

redisson 分布式锁原理:
  • 可重入:利用 hash 结构记录线程 id 和重入次数
  • 可重试:利用信号量和 PubSub 功能实现等待、唤醒,获取锁失败的重试机制
  • 超时续约:利用 watchDog,每隔一段时间(releaseTime / 3),重置超时时间
1)不可重入 Redis 分布式锁:
  • 原理:利用 setnx 的互斥性;利用 ex 避免死锁;释放锁时判断线程标示
  • 缺陷:不可重入、无法重试、锁超时失效
2)可重入的 Redis 分布式锁:
  • 原理:利用 hash 结构,记录线程标示和重入次数;利用 watchDog 延续锁时间;利用信号量控制锁重试等待
  • 缺陷:redis 宕机引起锁失效问题
3)Redisson 的 multiLock:
  • 原理:多个独立的 Redis 节点,没有主从,以防宕机主从数据未同步,必须在所有节点都获取重入锁,才算获取锁成功
  • 缺陷:运维成本高、实现复杂

六、分布式缓存——redis 的持久化

1、默认宕机前,会自动保存

2、异步保存。在conf 文件配置,每隔多长时间修改了多少次,进行异步保存bgsave

两种持久化

六、分布式缓存——redis 哨兵机制

搭建了redis的主从集群,master 挂了,需要从slever 中选一个当master。redis 哨兵的作用 就是自动恢复主从故障。

原理是,心跳机制,检测集群ping ,是否挂了。

 

posted @   东方不败--Never  阅读(13)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 零经验选手,Compose 一天开发一款小游戏!
· 一起来玩mcp_server_sqlite,让AI帮你做增删改查!!
点击右上角即可分享
微信分享提示