一线大厂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…...
【网络】每天掌握一个Linux命令 - iftop
在Linux系统中,iftop是网络管理的得力助手,能实时监控网络流量、连接情况等,帮助排查网络异常。接下来从多方面详细介绍它。 目录 【网络】每天掌握一个Linux命令 - iftop工具概述安装方式核心功能基础用法进阶操作实战案例面试题场景生产场景…...

XCTF-web-easyupload
试了试php,php7,pht,phtml等,都没有用 尝试.user.ini 抓包修改将.user.ini修改为jpg图片 在上传一个123.jpg 用蚁剑连接,得到flag...
连锁超市冷库节能解决方案:如何实现超市降本增效
在连锁超市冷库运营中,高能耗、设备损耗快、人工管理低效等问题长期困扰企业。御控冷库节能解决方案通过智能控制化霜、按需化霜、实时监控、故障诊断、自动预警、远程控制开关六大核心技术,实现年省电费15%-60%,且不改动原有装备、安装快捷、…...
聊一聊接口测试的意义有哪些?
目录 一、隔离性 & 早期测试 二、保障系统集成质量 三、验证业务逻辑的核心层 四、提升测试效率与覆盖度 五、系统稳定性的守护者 六、驱动团队协作与契约管理 七、性能与扩展性的前置评估 八、持续交付的核心支撑 接口测试的意义可以从四个维度展开,首…...
是否存在路径(FIFOBB算法)
题目描述 一个具有 n 个顶点e条边的无向图,该图顶点的编号依次为0到n-1且不存在顶点与自身相连的边。请使用FIFOBB算法编写程序,确定是否存在从顶点 source到顶点 destination的路径。 输入 第一行两个整数,分别表示n 和 e 的值(1…...

蓝桥杯3498 01串的熵
问题描述 对于一个长度为 23333333的 01 串, 如果其信息熵为 11625907.5798, 且 0 出现次数比 1 少, 那么这个 01 串中 0 出现了多少次? #include<iostream> #include<cmath> using namespace std;int n 23333333;int main() {//枚举 0 出现的次数//因…...

sipsak:SIP瑞士军刀!全参数详细教程!Kali Linux教程!
简介 sipsak 是一个面向会话初始协议 (SIP) 应用程序开发人员和管理员的小型命令行工具。它可以用于对 SIP 应用程序和设备进行一些简单的测试。 sipsak 是一款 SIP 压力和诊断实用程序。它通过 sip-uri 向服务器发送 SIP 请求,并检查收到的响应。它以以下模式之一…...

嵌入式学习笔记DAY33(网络编程——TCP)
一、网络架构 C/S (client/server 客户端/服务器):由客户端和服务器端两个部分组成。客户端通常是用户使用的应用程序,负责提供用户界面和交互逻辑 ,接收用户输入,向服务器发送请求,并展示服务…...
JavaScript基础-API 和 Web API
在学习JavaScript的过程中,理解API(应用程序接口)和Web API的概念及其应用是非常重要的。这些工具极大地扩展了JavaScript的功能,使得开发者能够创建出功能丰富、交互性强的Web应用程序。本文将深入探讨JavaScript中的API与Web AP…...
【Android】Android 开发 ADB 常用指令
查看当前连接的设备 adb devices 连接设备 adb connect 设备IP 断开已连接的设备 adb disconnect 设备IP 安装应用 adb install 安装包的路径 卸载应用 adb uninstall 应用包名 查看已安装的应用包名 adb shell pm list packages 查看已安装的第三方应用包名 adb shell pm list…...