一线大厂Redis高并发缓存架构实战与性能优化
文章目录
- 一、redis主从架构锁失效问题分析
- 二、从CAP角度剖析redis与zookeeper分布式锁区别
- 三、redlock分布式锁原理与存在的问题分析
- 四、大促场景如何将分布式锁性能提升100倍
- 五、高并发redis架构代码实战
一、redis主从架构锁失效问题分析
我们都知道,一般的互联网公司redis部署都是主从结构的,那么复制基本都是异步执行的,那就存在一个问题,当我们设置分布式锁的时候,还没来得及将key复制到从节点,主节点挂了,那么从节点会成为主节点,但是主节点的分布式锁key就会丢失掉,如果新线程进来执行同步代码同样会导致超卖问题

那么这个问题想解决,其实并没有那么容易
二、从CAP角度剖析redis与zookeeper分布式锁区别
我们知道zk也能实现分布式锁,他是怎么实现的呢?
首先zk会有一个leader节点,还会有多个flow节点(类似于redis的master和slave),当我们在leader节点设置一把分布所锁的时候,leader节点不会立即将设置的结果返回客户端,leader会从其flow节点去复制key,当flow复制成功key返回信息给leader节点的时候,leadfer会统计一个同步的数量,当这个数量超过半数的时候,才会返回给客户端表示这把分布式锁设置成功了。
那么zk就不会存在因为主从节点切换导致的分布式锁生效的问题
从CAP角度看,redis更多满足的是AP(可用性和容错性),zk是CP的(一致性和容错性)
但是redis的性能会比zk好,zk从语义角度更适合作为分布式锁的工具
三、redlock分布式锁原理与存在的问题分析
我相信很多同学都听过网上的很多人说利用红锁去解决redis的主从结构带来的分布式锁失效的问题,其实并没有完全解决!
红锁的实现原理是什么呢?
红锁是基于不是主从节点的redis实现,假设又奇数个redis节点,都是平等的,不存在主从,其实也是跟zk的底层实现机制是一样的,也是基于半数的加锁的原理。
红锁牺牲了一些可用性,因为需要往不同的节点去写key,需要半数以上的节点返回,那么客户端是需要等待一下的。但是在C可用性上更加友好一点

但是红锁并没有真正解决分布式锁失效问题
如果每个主节点都拖一个从节点(为了高可用),这样还是会有之前说的问题,redis1同步成功,redis2同步失败,从节点变为主节点;那么redis的从节点中依然没有key,其他线程进来依然可以超过半数去设置分布式锁

那如果不搞从节点,那就可能reids挂了超过一半的节点,那么分布式锁就没法使用了;
可能有人会说我们多搞几个节点,总不会那么多节点都挂掉吧,那我们想想,搞那么多节点,redis写key是不是也得消耗很多性能,我们使用redis的初衷就变了,那还不如用zk
然后会存在一个问题,redis持久化(AOF)的时候,我们一般都会设置为1s去持久化,而不是每条写 命令都去持久化。但是这1s的数据有可能会丢失,所以如果加锁redis1,redis2都成功了的时候,刚好在持久化的这1s中,redis2宕机了,那么redis2 的key就会丢失,依然存在问题
所以说红锁并不能100%解决分布式锁问题
四、大促场景如何将分布式锁性能提升100倍
首选考虑锁的粒度,控制锁住的代码块越小越好。
然后可以设置分段锁,比如某个商品1000个,分布式锁会基于这1000的库存去实现;那么利用分段锁,可以将商品分为100一段的十段,利用10个锁去针对这一个商品实现分布式锁,这10把锁相互之间不会存在并发问题。但是每把锁都是基于100的库存,性能会显著提升。(类似于1.7版本的concruuenthashmap底层原理)
五、高并发redis架构代码实战
public class ProductService {@Autowiredprivate ProductDao productDao;@Autowiredprivate RedisUtil redisUtil;@Autowiredprivate Redisson redisson;public static final Integer PRODUCT_CACHE_TIMEOUT = 60 * 60 * 24;public static final String EMPTY_CACHE = "{}";public static final String LOCK_PRODUCT_HOT_CACHE_PREFIX = "lock:product:hot_cache:";public static final String LOCK_PRODUCT_UPDATE_PREFIX = "lock:product:update:";public static Map<String, Product> productMap = new ConcurrentHashMap<>();@Transactionalpublic Product create(Product product) {Product productResult = productDao.create(product);redisUtil.set(RedisKeyPrefixConst.PRODUCT_CACHE + productResult.getId(), JSON.toJSONString(productResult),genProductCacheTimeout(), TimeUnit.SECONDS);//写入数据库之后,redis写缓存,并设置超时时间// (超时时间设置为1天+随机5h以内的时间,目的是为了了防止那些批量上架的商品同时过期,避免缓存失效(击穿)导致同时有大量请求打到数据库)return productResult;}@Transactionalpublic Product update(Product product) {Product productResult = null;//RLock updateProductLock = redisson.getLock(LOCK_PRODUCT_UPDATE_PREFIX + product.getId());RReadWriteLock readWriteLock = redisson.getReadWriteLock(LOCK_PRODUCT_UPDATE_PREFIX + product.getId());//针对更新方法设置分布式锁(分布式写锁)RLock writeLock = readWriteLock.writeLock();writeLock.lock();//保证了在更新数据库和更新缓存之间不会有其他线程过来更新操作,保证双写一致try {productResult = productDao.update(product);redisUtil.set(RedisKeyPrefixConst.PRODUCT_CACHE + productResult.getId(), JSON.toJSONString(productResult),genProductCacheTimeout(), TimeUnit.SECONDS);productMap.put(RedisKeyPrefixConst.PRODUCT_CACHE + productResult.getId(), product);//往jvm本地缓或者ehcache存放一份数据(为了应对百万并发场景,redis最多支持10w并发//如果redis挂了,会导致雪崩 )} finally {writeLock.unlock();}return productResult;}public Product get(Long productId) throws InterruptedException {Product product = null;String productCacheKey = RedisKeyPrefixConst.PRODUCT_CACHE + productId;product = getProductFromCache(productCacheKey);//先从缓存拿数据if (product != null) {return product;//拿到了就直接返回,需要跟前端沟通,如果是空的商品就 友好提示}//DCL 针对冷门数据突然变热的场景RLock hotCacheLock = redisson.getLock(LOCK_PRODUCT_HOT_CACHE_PREFIX + productId);//为了针对热点商品设置的分布式锁锁//因为大量请求过来,第一次缓存肯定没数据,都会去请求DB,那就不合理;加锁只让一个线程去访问数据库,将数据写入缓存,其他线程在锁释放之后会直接去访问缓存hotCacheLock.lock();//boolean result = hotCacheLock.tryLock(3, TimeUnit.SECONDS);try {product = getProductFromCache(productCacheKey);//其余线程进来从缓存拿到数据if (product != null) {return product;}//RLock updateProductLock = redisson.getLock(LOCK_PRODUCT_UPDATE_PREFIX + productId);RReadWriteLock readWriteLock = redisson.getReadWriteLock(LOCK_PRODUCT_UPDATE_PREFIX + productId);//读写锁是为了 如果都是读请求的话能保证并行执行,只有写操作才会阻塞RLock rLock = readWriteLock.readLock();//同样是为了查询数据库和更新缓存保证不被其他线程影响rLock.lock();//读锁的原理是 利用的锁重入的方法,每次都+1try {product = productDao.get(productId);if (product != null) {redisUtil.set(productCacheKey, JSON.toJSONString(product),genProductCacheTimeout(), TimeUnit.SECONDS);productMap.put(productCacheKey, product);} else {redisUtil.set(productCacheKey, EMPTY_CACHE, genEmptyCacheTimeout(), TimeUnit.SECONDS);//设置空缓存,防止黑客}} finally {rLock.unlock();}} finally {hotCacheLock.unlock();}return product;}private Integer genProductCacheTimeout() {return PRODUCT_CACHE_TIMEOUT + new Random().nextInt(5) * 60 * 60;}private Integer genEmptyCacheTimeout() {return 60 + new Random().nextInt(30);}private Product getProductFromCache(String productCacheKey) {Product product = productMap.get(productCacheKey);//从缓存拿数据之前 先从jvm内存呢拿数据,针对百万并发场景if (product != null) {return product;}String productStr = redisUtil.get(productCacheKey);if (!StringUtils.isEmpty(productStr)) {if (EMPTY_CACHE.equals(productStr)) {//如果拿到的是空的数据,说明是为了防止恶意请求导致缓存穿透而设置的redisUtil.expire(productCacheKey, genEmptyCacheTimeout(), TimeUnit.SECONDS);//那就刷新过期时间return new Product();//返回空的商品信息}product = JSON.parseObject(productStr, Product.class);redisUtil.expire(productCacheKey, genProductCacheTimeout(), TimeUnit.SECONDS); //读延期,热门的数据会一直在缓存中,冷门的数据到时间就过期了,实现了简单了数据冷热分离}return product;}}
相关文章:
一线大厂Redis高并发缓存架构实战与性能优化
文章目录 一、redis主从架构锁失效问题分析二、从CAP角度剖析redis与zookeeper分布式锁区别三、redlock分布式锁原理与存在的问题分析四、大促场景如何将分布式锁性能提升100倍五、高并发redis架构代码实战 一、redis主从架构锁失效问题分析 我们都知道,一般的互联…...
PHP 行事准则:allow_url_fopen 与 allow_url_include
文章目录 参考环境allow_url_fopenallow_url_fopen 配置项操作远程文件file 协议 allow_url_includeallow_url_include 配置项 allow_url_include 与 allow_url_fopen区别联系默认配置配置项关闭所导致异常运行时配置ini_set()限制 参考 项目描述搜索引擎Bing、GoogleAI 大模型…...
Replicate + ngrok云端大模型API实现教程
ChatGPT 的诞生预示着人工智能和机器学习领域的新时代。 日新月异,Hugging Face 不断推出突破性的语言模型,重新定义人机交互的界限。欢迎来到未来! 当然,有很多选项可以对它们进行推断。在本文中,我将告诉大家如何使…...
蓝桥等考Python组别十四级005
蓝桥等考Python组别十四级 第一部分:选择题 1、Python L14 (15分) 运行下面程序,输出的结果是( )。 d = {1 : one, 2 : two, 3 : three, 4 : four} print(d[2]) onetwothreefour正确答案:B...
Linux 本地 Docker Registry本地镜像仓库远程连接
Linux 本地 Docker Registry本地镜像仓库远程连接 Docker Registry 本地镜像仓库,简单几步结合cpolar内网穿透工具实现远程pull or push (拉取和推送)镜像,不受本地局域网限制! 1. 部署Docker Registry 使用官网安装方式,docker命令一键启动,该命令启动一个regis…...
二十九、高级IO与多路转接之epollreactor(收官!)
文章目录 一、Poll(一)定义(二)实现原理(三)优点(四)缺点 二、I/O多路转接之epoll(一)从网卡接收数据说起(二)如何知道接收了数据&…...
vite dev开发模式下支持外部模块引用
web工程中经常需要使用外部的cdn资源,比如lodash、three.js等: <script type"importmap">{"imports": {"lodash": "https://unpkg.com/lodash-es4.17.21/lodash.js"}} </script> vite build通过r…...
Chrome出现STATUS_STACK_BUFFER_OVERRUN解决方法之一
Chrome出现STATUS_STACK_BUFFER_OVERRUN错误代码,setting都无法打开 解决方法1:兼容性设置为win7 解决方法2: 1,开始菜单搜索Exploit Protection 2,添加程序进行自定义,点号,按程序名称添加 …...
【JavaEE】JavaScript
JavaScript 文章目录 JavaScript组成书写方式行内式内嵌式外部式(推荐写法) 输入输出变量创建动态类型基本数据类型数字类型特殊数字值 String转义字符求长度字符串拼接布尔类型undefined未定义数据类型null 运算符条件语句if语句三元表达式switch 循环语…...
剑指offer——JZ7 重建二叉树 解题思路与具体代码【C++】
一、题目描述与要求 重建二叉树_牛客题霸_牛客网 (nowcoder.com) 题目描述 给定节点数为 n 的二叉树的前序遍历和中序遍历结果,请重建出该二叉树并返回它的头结点。 例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建出…...
图片批量编辑器,轻松拼接多张图片,创意无限!
你是否曾经遇到这样的问题:需要将多张图片拼接成一张完整的画面,却缺乏专业的图片编辑技能?现在,我们为你带来一款强大的图片批量编辑器——让你轻松实现多张图片拼接,创意无限! 这款图片批量编辑器可以帮助…...
蓝桥等考Python组别十四级008
第一部分:选择题 1、Python L14 (15分) 运行下面程序,输出的结果是( )。 d = {1: "red", 2: "yellow", 3: "blue", 4: "green"} print(d[2]) redyellowbluegreen正确答案:B 2、Python L14 (...
【linux进程(二)】如何创建子进程?--fork函数深度剖析
💓博主CSDN主页:杭电码农-NEO💓 ⏩专栏分类:Linux从入门到精通⏪ 🚚代码仓库:NEO的学习日记🚚 🌹关注我🫵带你学更多操作系统知识 🔝🔝 进程状态管理 1. 前言2. 查看…...
数字IC前端学习笔记:数字乘法器的优化设计(华莱士树乘法器)
相关阅读 数字IC前端https://blog.csdn.net/weixin_45791458/category_12173698.html?spm1001.2014.3001.5482 进位保留乘法器依旧保留着阵列的排列规则,只是进位是沿斜下角,如果能使用树形结构来规划这些进位保留加法器,就能获得更短的关键…...
CountDownLatch 批量更改使用,
代码 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.first.pet.platform.entity.PlatformAddress; import com.first.pet.platform.mapper.PlatformAddressMapper; …...
910数据结构(2019年真题)
算法设计题 问题1 有一种排序算法叫做计数排序。这种排序算法对一个待排序的表(采用顺序存储)进行排序,并将排序结果存放到另一个新的表中。必须注意的是,表中所有待排序的关键字互不相同,计数排序算法针对表中的每个元素,扫描待排序的表一趟,统计表中有多少个元素的关…...
推荐系统实践 笔记
诸神缄默不语-个人CSDN博文目录 这是我2020年写的笔记,我从印象笔记搬过来公开。 如果那年还在读本科的同学也许有印象,那年美赛出了道根据电商评论给商户提建议的题。其实这件事跟推荐系统关系不大,但我们当时病急乱投医,我打开…...
【JavaEE】JUC(Java.util.concurrent)常见类
文章目录 前言ReentrantLock原子类线程池信号量CountDownLatch相关面试题 前言 经过前面文章的学习我们大致了解了如何实现多线程编程和解决多线程编程中遇到的线程不安全问题,java.util.concurrent 是我们多线程编程的一个常用包,那么今天我将为大家分…...
清除浮动的方法
为什么需要清除浮动? 父级的盒子不能把height定死这样,浮动子类就没有了(行内块元素的特点),父类高度为零。故引用清除浮动 1、父级没有高度 2、子盒子浮动了 3、影响下面的布局了,我们就应该清除浮动了…...
LangChain 摘要 和问答示例
在Azure上的OpenAI端点 注意 OpenAI key 可以用微软 用例【1. 嵌入 ,2. 问答】 1. import os import openai from langchain.embeddings import OpenAIEmbeddings os.environ["OPENAI_API_KEY"] "****" # Azure 的密钥 os.environ["OP…...
第19节 Node.js Express 框架
Express 是一个为Node.js设计的web开发框架,它基于nodejs平台。 Express 简介 Express是一个简洁而灵活的node.js Web应用框架, 提供了一系列强大特性帮助你创建各种Web应用,和丰富的HTTP工具。 使用Express可以快速地搭建一个完整功能的网站。 Expre…...
深入浅出Asp.Net Core MVC应用开发系列-AspNetCore中的日志记录
ASP.NET Core 是一个跨平台的开源框架,用于在 Windows、macOS 或 Linux 上生成基于云的新式 Web 应用。 ASP.NET Core 中的日志记录 .NET 通过 ILogger API 支持高性能结构化日志记录,以帮助监视应用程序行为和诊断问题。 可以通过配置不同的记录提供程…...
【杂谈】-递归进化:人工智能的自我改进与监管挑战
递归进化:人工智能的自我改进与监管挑战 文章目录 递归进化:人工智能的自我改进与监管挑战1、自我改进型人工智能的崛起2、人工智能如何挑战人类监管?3、确保人工智能受控的策略4、人类在人工智能发展中的角色5、平衡自主性与控制力6、总结与…...
shell脚本--常见案例
1、自动备份文件或目录 2、批量重命名文件 3、查找并删除指定名称的文件: 4、批量删除文件 5、查找并替换文件内容 6、批量创建文件 7、创建文件夹并移动文件 8、在文件夹中查找文件...
Qt Http Server模块功能及架构
Qt Http Server 是 Qt 6.0 中引入的一个新模块,它提供了一个轻量级的 HTTP 服务器实现,主要用于构建基于 HTTP 的应用程序和服务。 功能介绍: 主要功能 HTTP服务器功能: 支持 HTTP/1.1 协议 简单的请求/响应处理模型 支持 GET…...
WEB3全栈开发——面试专业技能点P2智能合约开发(Solidity)
一、Solidity合约开发 下面是 Solidity 合约开发 的概念、代码示例及讲解,适合用作学习或写简历项目背景说明。 🧠 一、概念简介:Solidity 合约开发 Solidity 是一种专门为 以太坊(Ethereum)平台编写智能合约的高级编…...
多模态大语言模型arxiv论文略读(108)
CROME: Cross-Modal Adapters for Efficient Multimodal LLM ➡️ 论文标题:CROME: Cross-Modal Adapters for Efficient Multimodal LLM ➡️ 论文作者:Sayna Ebrahimi, Sercan O. Arik, Tejas Nama, Tomas Pfister ➡️ 研究机构: Google Cloud AI Re…...
MySQL 知识小结(一)
一、my.cnf配置详解 我们知道安装MySQL有两种方式来安装咱们的MySQL数据库,分别是二进制安装编译数据库或者使用三方yum来进行安装,第三方yum的安装相对于二进制压缩包的安装更快捷,但是文件存放起来数据比较冗余,用二进制能够更好管理咱们M…...
比较数据迁移后MySQL数据库和OceanBase数据仓库中的表
设计一个MySQL数据库和OceanBase数据仓库的表数据比较的详细程序流程,两张表是相同的结构,都有整型主键id字段,需要每次从数据库分批取得2000条数据,用于比较,比较操作的同时可以再取2000条数据,等上一次比较完成之后,开始比较,直到比较完所有的数据。比较操作需要比较…...
LLaMA-Factory 微调 Qwen2-VL 进行人脸情感识别(二)
在上一篇文章中,我们详细介绍了如何使用LLaMA-Factory框架对Qwen2-VL大模型进行微调,以实现人脸情感识别的功能。本篇文章将聚焦于微调完成后,如何调用这个模型进行人脸情感识别的具体代码实现,包括详细的步骤和注释。 模型调用步骤 环境准备:确保安装了必要的Python库。…...
