Redis学习系列之——高并发应用的缓存问题(二)

一、布隆过滤器

        布隆过滤器由一个 BitMap 和若干 Hash 函数组成,可以用来快速判断一个值是否存在后端存储中。它是解决 Redis 缓存穿透问题的一个不错的解决方案。

工作原理

步骤1:当 key-value 键值对存储到 Redis 后,向布隆过滤器添加 key

步骤2:布隆过滤器会对 key 进行多个独立的哈希运算,假设是3个Hash,则得到 hash1、hash2、hash3

步骤3:假设布隆过滤器的 BitMap 长度为 L,那么 BitMap 中的 hash1 % L、hash2 % L、hash3 % L 这三个位会被设置为1

步骤4:若要查询某个 key 是否存在于 Redis 中,可以先查询布隆过滤器。布隆过滤器会检查 hash1 % L、hash2 % L、hash3 % L 这三个位置是否都为1

步骤4.1:若否,则 key 一定不存在于 Redis 中

步骤4.2:若是,由于可能出现 Hash 冲突,因此无法判断 key 是否存在于 Redis 中

特点

1、布隆过滤器无法删除数据,若想要删除只能全量初始化

2、布隆过滤器采用 Bitmap 结构,十分节省空间(假如1亿的key,一般需要10亿长度的Bitmap,这样的 Bitmap 只需要 120MB 左右的空间即可)

3、布隆过滤器适用于数据命中率不高、数据相对固定、数据量大的场景。配合“缓存空值”的策略来对抗缓存穿透时,可以大幅减少 Redis 中空值对应的 key 的数量,从而节约 Redis 空间

示例代码

void init(){// 初始化布隆过滤器, 预计元素10万, 误差率 3%,// 根据这两个参数会计算出底层 bitmap 的大小及 Hash 函数的数量RBloomFilter<String> bloomFilter = redisson.getBloomFilter("bloom1");bloomFilter.tryInit(100000, 0.03);// 将现有 key 全部初始化for(String key : allKeys){bloomFilter.put(key);}
}String get(String key){// 由布隆过滤器判断是否存在if(!bloomFilter.contains(key)){return "";}// 布隆过滤器不能确定, 尝试从缓存中获取String value = cache.get(key);if(value != null){return value;}// 缓存中不存在, 尝试从数据库获取, 并更新缓存value = db.get(key);if(value != null){cache.set(key, value, 600s)} else {// 缓存空值value = "";cache.set(key, value, 30s);}return value;
}void add(String key, String value){// 新增元素时,除了写入数据库,还需要将 key 写入布隆过滤器db.add(key, value);bloomFilter.put(key);
}

二、bigkey 问题

定义

        bigkey 是指单值类型(string)大小很大,二级数据结构的大小很大或元素过多。

        一般地,超过 10KB 的 string,或者超过 5000 个元素的 hash/list/set/zset,可以认定为 bigkey。

危害

1、高并发请求大 key 时,容易导致 Redis 阻塞

2、高并发请求大 key 时,容易导致网络拥塞

3、过期删除大 key 时,存在阻塞 Redis 的可能性。(Redis 4.0 之后默认采用过期异步删除,可以一定程度缓解,但是大 key 删除仍有较大的性能开销)

产生原因

        程序设计不当,或对数据规模预估不足。比如:

1、粉丝列表,若没有精心设计,大 V 的粉丝列表容易成为大 key;

2、为了方便,把关联的数据全部放到一个 key 中存储;

优化方法

1、数据分段存储,比如一个大的 list,分成10个小的 list 存储

2、如果 bigkey 不可避免,每次请求尽量取出少量数据,比如用 hmget 代替 hgetall

3、使用合适的数据结构,比如一个大的 JSON 存储为 string 类型,容易成为大 key,使用 hash 可以将数据分摊到多个 field 中。

4、设置 key 的过期时间,避免 Redis 内存不断膨胀

三、命令使用

1、O(N) 操作注意性能问题

        对于 hgetall、smembers、zrange 等等 O(N) 操作,要关注 N 的值。有遍历元素的需求时,可以使用 hscan、sccan、zscan 等代替,这些 scan 命令使用游标进行遍历,一次只返回有限数量的元素以及下一次 scan 的游标,而不是一次性返回全量数据。

2、通过 rename 禁用 keys、flushall、flushdb 等危险命令

3、合理使用 select

        Redis 多数据库的功能比较弱,使用数字区分。多个业务分别使用同一个 Redis 实例的不同数据库,Redis 实际上还是单线程处理。

4、使用批量操作提高效率

        原生命令:例如用 mget/mset 代替多次 get/set;非原生命令:使用 pipeline 打包发送多个命令。

        但也要注意批量操作规模不能太大。

四、客户端连接池

        应用程序使用 Redis 连接池,可以避免连接频繁建立、释放造成的性能损耗。

        使用连接池后,由连接池维护若干连接,负责这些连接的建立、保活、销毁。业务层需要使用连接的时候,从连接池取出直接使用,使用完毕后归还给连接池。

        连接池的关键参数:

1、maxTotal 最大连接数

        maxTotal 的估算要考虑多方因素,比如:

        (1)一次“借出连接 -> 执行命令 -> 归还连接"大约耗时 1ms,那么单个连接的 QPS 就是1000,如果业务要求的 QPS 是 50000,那么至少需要 50 个连接,此时 maxTotal 可以比 50 大一些

        (2)Redis 服务端的最大连接数是 maxClient,则应用节点数量 * maxTotal 不能超过 maxClient

        (3)从性能最优考虑,一般设置为和 maxIdle 相等,可以避免缓存池伸缩带来性能开销。如果考虑连接占用

        (4)maxTotal 不是越大越好,设置合理的值可以限制应用程序消耗 Redis 的资源,而且由于 Redis 工作线程为单线程,一个大命令阻塞的发生时,即使 maxTotal 设置再大也没用

2、maxIdle 最大空闲连接数

        超出 maxIdle 的连接在被归还后会被缓慢释放。

        从性能最优考虑,一般设置为 maxIdle = maxIdle,可以避免缓存池伸缩带来性能开销。如果考虑连接占用问题,maxIdle 一般设为业务预期的最高并发数,maxTotal 再放大一倍。

3、minIdle 最少空闲连接数

        注意,Redis 连接池是懒加载的,因此应用程序启动时,可以用的连接数不会直接到达 minIdle,如果应用启动后会有很多请求过来,那么我们提前执行代码进行预热,使得初始的可用连接数到达 minIdle 个。

4、blockWhenExhausted  连接池没有空闲连接时,调用方是否等待

5、maxWaitMillis  blockWhenExhausted=true 时,调用方等待的最长时间

6、testOnBorrow  连接池是否在借出连接时,检测连接的有效性

7、testOnReturn  连接池是否在归还连接时,检测连接的有效性

五、数据清除策略

        在 Redis 中,maxmemory 参数规定了能够使用的最大内存。

1、已使用内存 < maxmemory

        此时,Redis 会对过期数据进行清除,方式如下:

(1)被动删除:惰性删除,当一个 key 到期时,Redis 不会立刻删除。而是当客户端读/写一个已经过期的 key 时,Redis 进行删除

(2)主动删除:惰性删除无法确保冷数据被及时删除,所以 Redis 会定期主动删除一批已经过期的 key

2、已使用内存 > maxmemory

        此时,Redis 会触发主动清理策略。主动清理策略有 8 种:

(1)针对设置了有效期的 key 进行删除

        a. volatile-ttl:越早过期的数据越先被删除

        b. volatile-random:随机删除

        c. volatile-lru:使用 LRU 算法删除

        d. volatile-lfu:使用 LFU 算法删除

(2)针对所有的 key 进行删除

        e. allkeys-random:随机删除

        f. allkeys-lru:使用 LRU 算法删除

        g. allkeys-lfu:使用 LFU 算法删除

(3)不删除

        h. noeviction:缺省值。不会删除任何数据,并拒绝客户端的所有写操作。

3、配置建议

(1)Redis 默认不限制 maxmemory。生产环境建议配置,并且不要让 maxmemory 超过物理内存大小,否则会触发磁盘 swap 影响性能。

(2)一般推荐使用 volatile-lru;如果热点数据比较多,则用 volatile-lfu

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/bicheng/89449.shtml
繁体地址,请注明出处:http://hk.pswp.cn/bicheng/89449.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Expression 类的静态方法

public static MethodCallExpression Call(Type type, // 包含目标方法的类型string methodName, // 方法名称Type[]? typeArguments, // 泛型方法的类型参数&#xff08;非泛型方法为 null&#xff09;params Expression[]? arguments // 方…

[Nagios Core] 事件调度 | 检查执行 | 插件与进程

第五章&#xff1a;事件调度 欢迎回到Nagios Core&#xff01; 在上一章第四章&#xff1a;配置加载中&#xff0c;我们了解了Nagios如何读取配置文件以知晓需要监控的对象&#xff0c;比如我们的朋友"Web Server 1"。此时Nagios内存中已构建完整的基础设施拓扑图。…

Web3 常用前端库介绍

一、Web3 前端开发&#xff1a;连接用户与区块链的桥梁 随着 Web3 生态的蓬勃发展&#xff0c;前端开发从传统的页面渲染进化为区块链交互的核心枢纽。Web3 前端库作为连接用户与区块链的桥梁&#xff0c;承担着钱包集成、合约交互、数据可视化等关键功能。本文将系统解析主流 …

cnpm命令报internal/modules/cjs/loader.js:797 throw err; ^ Error: Cannot find

在运行一个项目的时候&#xff0c;需要升级电脑各组件的版本&#xff0c;结果导致cnpm命令无法正常使用&#xff0c;cnpm任何命令都会报如下这个错&#xff1a;找了半天&#xff0c;发现是由于cnpm与npm的版本不一致导致的&#xff0c;所以需要卸载并重新安装cnpm&#xff0c;重…

15、鸿蒙Harmony Next开发:创建自定义组件

目录 自定义组件的基本用法 自定义组件的基本结构 struct Component freezeWhenInactive build()函数 Entry EntryOptions Reusable 成员函数/变量 自定义组件的参数规定 build()函数 自定义组件生命周期 自定义组件的创建和渲染流程 自定义组件重新渲染 自定义…

深入理解Map.Entry.comparingByValue()和Map.Entry.comparingByKey()

文章目录深入理解Map.Entry.comparingByValue()和Map.Entry.comparingByKey()1. 方法定义comparingByKey()comparingByValue()2. 基本用法2.1 使用comparingByKey()2.2 使用comparingByValue()3. 方法重载版本comparingByKey(Comparator)comparingByValue(Comparator)4. 高级用…

Mac下载mysql

安装 brew list --versions | grep mysql查看已安装的mysql版本brew search mysql查看支持的mysql版本brew info mysql查看mysql版本信息brew install mysql进行安装/opt/homebrew/opt/mysql/bin/mysqld --initialize-insecure --user$(whoami) --basedir$(brew --prefix mysql…

PageHelper使用说明文档

文章目录一、简介二、集成步骤三、使用方法四、注意事项五、高级用法一、简介 PageHelper 是一个开源的 MyBatis 分页插件&#xff0c;它可以帮助我们在使用 MyBatis 进行数据库操作时方便地实现分页功能。通过简单的配置和少量的代码修改&#xff0c;就可以在查询数据时实现分…

grpo nl2sql qwen3 模型强化学习训练有效果的成立条件有哪些

在使用GRPO&#xff08;强化学习算法&#xff09;对Qwen3模型在NL2SQL&#xff08;自然语言到SQL转换&#xff09;任务上进行强化学习&#xff08;RL&#xff09;训练时&#xff0c;其效果成立的核心条件可归纳为以下几个关键维度&#xff0c;这些条件相互关联&#xff0c;共同…

面向向量检索的教育QA建模:九段日本文化研究所日本语学院的Prompt策略分析(6 / 500)

面向向量检索的教育QA建模&#xff1a;九段日本文化研究所日本语学院的Prompt策略分析&#xff08;6 / 500&#xff09; 系列说明 500 所日本语言学校结构化建模实战&#xff0c;第 6 篇。每篇拆解 1 所学校在 Prompt-QA 系统中的建模策略&#xff0c;分享工程经验&#xff0c;…

墨刀原型图的原理、与UI设计图的区别及转换方法详解-卓伊凡|贝贝

墨刀原型图的原理、与UI设计图的区别及转换方法详解-卓伊凡|贝贝最近有个设计由于时间比较仓促直接用 原型做的&#xff0c;但是原型做的大家都知道是没法用的&#xff0c;以下讲解原型和ui的区别&#xff0c;其次我们下面有三种方法把墨刀的原型变成UI图。一、墨刀原型图的原理…

前端 nodejs vue2 开发环境和微信开发环境 故障终极处理

现象某个vue2旧项目 引入vue-ls 组件等组件&#xff0c;冲突失败后删除,导致开发环境 vxe-table加载失败&#xff0c;还原后还是不行。前段项目崩溃。报警sass 某个方法 Deprecated &#xff0c;之前不会处理方式_失败回退代码项目代码 删除 node_modules&#xff0c; 删除 …

【后端】.NET Core API框架搭建(9) --配置使用Log4Net日志

目录 1.添加包 2.新建公用类 3.新建配置 4.注册 4.1.类库项目设置 5.使用 在 .NET Core 项目中使用 Log4Net 做日志记录&#xff0c;具有很多优势。尽管 .NET Core 自带了 ILogger 接口&#xff08;如使用内置的 ConsoleLogger、DebugLogger 等&#xff09;&#xff0c;但…

Agent交互细节

本文参考了https://www.bilibili.com/video/BV1v9V5zSEHA/视频及原作者代码实践 本文主要实践在第3节1、MCP MCP官方地址&#xff1a;https://modelcontextprotocol.io/introduction MCP 是一个开放协议&#xff0c;它规范了应用程序向 LLM 提供上下文的方式。 架构&#xff1a…

AI+医疗!VR和MR解剖学和针灸平台,智能时代如何重塑健康未来

在智能时代&#xff0c;“AI医疗”正从精准诊断入手&#xff0c;推动医疗系统变革&#xff0c;通过个性化健康管理、智能诊疗辅助等方式重塑健康未来&#xff01;将人工智能&#xff08;AI&#xff09;与虚拟实境&#xff08;VR&#xff09;应用到中医教学&#xff0c;透过该系…

Sersync和Rsync部署

学习参考连接 以下是我在学习过程中借鉴的经验和下载资源链接&#xff0c;感谢几位大佬的帮助&#xff0c;也供各位参考。 Rsync踩坑&#xff1a; https://blog.csdn.net/XiaoXiaoYunXing/article/details/120160395 Sersync下载源 http://down.whsir.com/downloads/sersy…

Django基础(四)———模板常用过滤器

前言上篇文章给大家介绍了DTL模板的部分知识点这篇文章继续带大家深入理解Django框架中的模板过滤器一、模板常用过滤器1.add将传进来的参数添加到原来的值上面。这个过滤器会尝试将值和 参数转换成整形然后进行相加。如果转换成整形过程中失败了&#xff0c;那么会将值和参数进…

国内MCP服务器搜索引擎有哪些?MCP导航站平台推荐

在人工智能技术蓬勃发展的今天&#xff0c;AI模型与外部工具和服务的交互能力正成为推动技术进步的关键。AIbase&#xff08;<https://mcp.aibase.cn/>&#xff09;作为一个专注于MCP(Model Context Protocol&#xff0c;模型上下文协议)服务器的集合平台&#xff0c;为全…

Python中with的作用和用法

在这里我们来详细解释一下Python中非常重要的 with 语句。 我会从 “为什么需要它” 开始&#xff0c;然后讲解 “它是什么以及如何使用”&#xff0c;最后深入到 “它的工作原理” 和 “如何自定义”。1. 为什么需要 with 语句&#xff1f;(The Problem) 在编程中&#xff0c;…

缓存雪崩、缓存穿透,缓存击穿

Redis是一个完全开源免费的高性能非关系型&#xff08;NOSQL&#xff09;的key-value数据库。 Redis不可能把所有的数据都缓存起来(内存昂贵且有限)&#xff0c;所以Redis需要对数据 设置过期时间&#xff0c;并采用的是惰性删除定期删除两种策略对过期键删除。Redis对过期键的…