当前位置: 首页 > news >正文

黑马redis

Redis的多IO线程只是用来处理网络请求的,对于读写操作命令Redis仍然使用单线程来处理

Redisson分布式锁实现15问

文章目录

  • 主线程和IO线程是如何协作的
  • Unix网络编程中的五种IO模型
  • Linux世界一切皆文件
  • 生产上限制keys *、flushdb、flushall等危险命令
    • keys * 遍历查询100W数据花费时长
    • 配置禁用这些命令
  • BigKey案例
  • 缓存更新策略
    • Redis内存不足的缓存淘汰策略
    • 先删缓存再操作数据库
      • 理想情况
      • 多线程竟态条件下
      • 多线程竟态条件下
    • 先操作数据库再删除缓存【胜出】
      • 理想情况
    • 总结
  • 项目实践【黑马点评】
    • 目标
    • 缓存一致性
    • 缓存穿透
      • 缓存穿透解决方案调研
      • 实战解决商铺信息缓存穿透
      • 总结
    • 缓存雪崩
    • 缓存击穿
      • 缓存击穿解决方案调研
      • 实战解决缓存击穿
        • 互斥锁(setnx)
  • 优惠券秒杀-单机锁
    • 全局唯一ID
      • 自增ID存在的问题
      • 分布式ID的实现
    • 实战优惠券秒杀
      • 总结
  • 优惠券秒杀-分布式锁
    • 自定义的分布式锁
    • 将单机 synchronized 替换为自定义分布式锁
    • 分布式锁误删问题🍖
      • 问题原因分析
      • 代码实现
    • 判断锁标识和释放锁非原子性🥩
    • 存在的问题
      • 锁不可重入
      • 不可重试
      • 超时释放
      • 主从一致性
  • Redis集群方案
    • 主从复制—全量同步、增量同步
      • 全量同步
      • 增量同步
      • 面试题
    • 哨兵模式
      • 服务状态监控
      • redis集群(哨兵模式)脑裂
      • 面试题
    • 分片集群
      • 分片集群结构
      • 分片集群结构——数据读写
      • 存在的问题
      • 面试题1
      • 面试题2
  • Big Key
    • 大key的影响
    • 大key的查找
    • 删除大key注意事项
    • 大key的处理
    • 分拆方案
      • 一、单个简单的key存储的value很大
      • 二、value中存储过多的元素
      • 方案一:使用时间戳作为附加属性
      • 方案二:通过在 `key` 拼接上基于时间分拆
      • 代码解释
        • 方案一代码解释
        • 方案二代码解释

主线程和IO线程是如何协作的

  • 阶段一:服务端和客户端建立Socket连接,并分配处理线程
    首先,主线程负责接收建立连接请求,当有客户端请求和实例建立Socket连接时,主线程会创建和客户端的连接,并把 Socket放入全局等待队列中。紧接着,主线程通过轮询方法把Socket连接分配给IO线程

  • 阶段二:IO线程读取并解析请求
    主线程一旦把Socket分配给IO线程,就会进入阻塞状态,等待IO线程完成客户端请求读取和解析。因为有多个IO线程在并行处理,所以,这个过程很快就可以完成。

  • 阶段三:主线程执行请求操作
    等到IO线程解析完请求,主线程还是会以单线程的方式执行这些命令操作
    在这里插入图片描述

  • 阶段四:IO线程回写Socket和主线程清空全局队列
    当主线程执行完请求操作后,会把需要返回的结果写入缓冲区,然后,主线程会阻塞等待IO线程,把这些结果回写到Socket中,并返回给客户端。和IO线程读取和解析请求一样,IO线程回写Socket时,也是有多个线程在并发执行,所以回写Socket的速度也很快。等到IO线程回写Socket完毕,主线程会清空全局队列,等待客户端的后续请求。
    在这里插入图片描述

Unix网络编程中的五种IO模型

Blocking IO - 阻塞IO

NoneBlocking IO - 非阻塞IO

IO multiplexing - IO多路复用 ★★★

signal driven IO - 信号驱动IO(偏C)

asynchronous IO - 异步IO(偏C)

Linux世界一切皆文件

文件描述符、简称FD,句柄

FileDescriptor:
文件描述符(File descriptor)是计算机科学中的一个术语,是一个用于表述指向文件的引用的抽象化概念。文件描述符在形式上是一个非负整数。实际上,它是一个索引值,指向内核为每一个进程所维护的该进程打开文件的记录表。当程序打开一个现有文件或者创建一个新文件时,内核向进程返回一个文件描述符。在程序设计中,文件描述符这一概念往往只适用于UNIX、Linux这样的操作系统

I/O 的读和写本身是堵塞的,比如当 socket 中有数据时,Redis 会通过调用先将数据从内核态空间拷贝到用户态空间,再交给 Redis 调用,而这个拷贝的过程就是阻塞的,当数据量越大时拷贝所需要的时间就越多,而这些操作都是基于单线程完成的

生产上限制keys *、flushdb、flushall等危险命令

keys * 遍历查询100W数据花费时长

在这里插入图片描述

配置禁用这些命令

redis.conf 在 SECURITY 这一项中

rename-command keys ""
rename-command flushdb ""
rename-command FLUSHALL ""

BigKey案例

多大算Big
参考《阿里云Redis开发规范》

在这里插入图片描述

缓存更新策略

在这里插入图片描述

Redis内存不足的缓存淘汰策略

  • noeviction:当内存使用超过配置的时候会返回错误,不会驱逐任何键
  • allkeys-lru:加入键的时候,如果过限,首先通过LRU算法驱逐最久没有使用的键
  • volatile-lru:加入键的时候如果过限,首先从设置了过期时间的键集合中驱逐最久没有使用的键
  • allkeys-random:加入键的时候如果过限,从所有key随机删除
  • volatile-random:加入键的时候如果过限,从过期键的集合中随机驱逐
  • volatile-ttl:从配置了过期时间的键中驱逐马上就要过期的键
  • volatile-lfu:从所有配置了过期时间的键中驱逐使用频率最少的键 allkeys-lfu:从所有键中驱逐使用频率最少的键
    在这里插入图片描述
    在这里插入图片描述

先删缓存再操作数据库

理想情况

在这里插入图片描述

多线程竟态条件下

在这里插入图片描述

多线程竟态条件下

好巧不巧,缓存失效了,此时线程2要采用先更新数据库再删除缓存的策略,但由于更新数据库没有线程1查询数据库快,所以查到的还是未更新前的旧值10;
线程2更新完毕之后删除了redis缓存,线程1获取时间片后又将10写回了缓存,导致数据库缓存不一致的情况
在这里插入图片描述

先操作数据库再删除缓存【胜出】

理想情况

在这里插入图片描述
在这里插入图片描述

总结

给缓存设置过期时间,定期清理缓存并回写,是保证最终一致性的解决方案

我们可以对存入缓存的数据设置过期时间,所有的写操作以数据库为准,对缓存操作只是尽最大努力即可。
也就是说如果数据库写成功,缓存更新失败,那么只要到达过期时间,则后面的读请求自然会从数据库中读取新值然后回填缓存,达到一致性,切记,要以数据落库DB为准

项目实践【黑马点评】

目标

在这里插入图片描述

缓存一致性

com.sddp.service.impl.ShopServiceImpl#update
事务保证原子性,如果在微服务系统中,这两步不在一个方法当中,甚至不在一个服务当中,那么就需要mq消息通知删除缓存的服务,可以借助TCC来保证分布式事务的原子性
在这里插入图片描述

缓存穿透

缓存穿透是指客户端请求的数据在缓存中和数据库中都不存在,这样缓存永远不会生效,这些请求都会打到数据库。如果被恶意用户利用,对服务器会造成负载,严重会导致服务不可用
常见的解决方案有两种:

com.sddp.service.impl.ShopServiceImpl#queryById

在这里插入代码片

缓存穿透解决方案调研

在这里插入图片描述

实战解决商铺信息缓存穿透

如果提交的商铺id本身就是瞎写的,查询数据库之后必然没有数据,那此时,redis则将此id存在redis并赋值为null,下次在查询此id时直接走redis返回null即可
在这里插入图片描述

总结

在这里插入图片描述

缓存雪崩

TTL随机数分散降低机率
Redis宕机:利用集群提高服务的可用性
快速失败、拒绝服务

在这里插入图片描述

缓存击穿

缓存击穿问题也叫热点Key问题,就是一个被高并发访问并且缓存重建业务较复杂下图第 2 步比较耗时,导致多线程访问的时候短时间为写入缓存,期间的流量都打到DB上了)的key突然失效了,无数的请求访问会在瞬间给数据库带来巨大的冲击。
在这里插入图片描述

缓存击穿解决方案调研

互斥锁:CP(强一致)
逻辑过期:AP(高可用)
在这里插入图片描述
在这里插入图片描述

实战解决缓存击穿

多个线程同时去查询数据库的这条数据,那么我们可以在第一个查询数据的请求上使用一个 互斥锁 来锁住它。

其他的线程走到这一步拿不到锁就等着,等第一个线程查询到了数据,然后做缓存。

后面的线程进来发现已经有缓存了,就直接走缓存


/*** @auther zzyy* @create 2021-05-01 14:58*/
@Service
@Slf4j
public class UserService {public static final String CACHE_KEY_USER = "user:";@Resourceprivate UserMapper userMapper;@Resourceprivate RedisTemplate redisTemplate;/*** 业务逻辑没有写错,对于小厂中厂(QPS《=1000)可以使用,但是大厂不行* @param id* @return*/public User findUserById(Integer id){User user = null;String key = CACHE_KEY_USER+id;//1 先从redis里面查询,如果有直接返回结果,如果没有再去查询mysqluser = (User) redisTemplate.opsForValue().get(key);if(user == null){//2 redis里面无,继续查询mysqluser = userMapper.selectByPrimaryKey(id);if(user == null){//3.1 redis+mysql 都无数据//你具体细化,防止多次穿透,我们业务规定,记录下导致穿透的这个key回写redisreturn user;}else{//3.2 mysql有,需要将数据写回redis,保证下一次的缓存命中率redisTemplate.opsForValue().set(key,user);}}return user;}/*** 加强补充,避免突然key失效了,打爆mysql,做一下预防,尽量不出现击穿的情况。* @param id* @return*/public User findUserById2(Integer id){User user = null;String key = CACHE_KEY_USER+id;//1 先从redis里面查询,如果有直接返回结果,如果没有再去查询mysql,// 第1次查询redis,加锁前user = (User) redisTemplate.opsForValue().get(key);if(user == null) {//2 大厂用,对于高QPS的优化,进来就先加锁,保证一个请求操作,让外面的redis等待一下,避免击穿mysqlsynchronized (UserService.class){//第2次查询redis,加锁后user = (User) redisTemplate.opsForValue().get(key);//3 二次查redis还是null,可以去查mysql了(mysql默认有数据)if (user == null) {//4 查询mysql拿数据(mysql默认有数据)user = userMapper.selectByPrimaryKey(id);if (user == null) {return null;}else{//5 mysql里面有数据的,需要回写redis,完成数据一致性的同步工作redisTemplate.opsForValue().setIfAbsent(key,user,7L,TimeUnit.DAYS);}}}}return user;}
}

在这里插入图片描述

互斥锁(setnx)
public boolean tryLock(String key){Boolean flag = stringRedisTemplate.opsForValue().setIfAbsent(key, "1", 10, TimeUnit.SECONDS);
http://www.lryc.cn/news/499488.html

相关文章:

  • HCIA-Access V2.5_1_2 PON技术的特点、优势与典型应用
  • css部分
  • 【TCP 网络通信(发送端 + 接收端)实例 —— Python】
  • LSTM+改进的itransformer时间序列预测模型代码
  • Apache-HertzBeat 开源监控默认口令登录
  • Delete Number
  • Linux常用快捷键
  • 针对xpath局限的解决方案
  • 深入解析 HTML Input 元素:构建交互性表单的核心
  • ffmpeg转码与加水印
  • Leetcode 104. 二叉树的最大深度(Java-深度遍历)
  • 阳明心学-传习录学习总结
  • macOS sequoia 15.1中应用程序“程序坞”没有权限打开
  • 使用 MinIO 和 KKFileView 实现在线文件预览功能
  • Conda-Pack打包:高效管理Python环境
  • 云服务器上搭建 WordPress 全流程指南
  • 图像超分辨率技术新进展:混合注意力聚合变换器HAAT
  • 文件IO——01
  • 【opencv入门教程】5. Mat 类用法
  • SSM虾米音乐项目2--分页查询
  • nodejs 获取本地局域网 ip 扫描本地端口
  • 区块链签名种类
  • 【062B】基于51单片机无线病房呼叫系统(+时间)【Keil程序+报告+原理图】
  • 突破空间限制!从2D到3D:北大等开源Lift3D,助力精准具身智能操作!
  • 【pyspark学习从入门到精通24】机器学习库_7
  • Echart折线图属性设置 vue2
  • LabVIEW-简单串口助手
  • Linux下,用ufw实现端口关闭、流量控制(二)
  • C#开发-集合使用和技巧(九)Join的用法
  • Dockerfile容器镜像构建技术