redis 内存管理和持久化机制
文章目录
- 前言
- 一、内存管理
- 1、Redis过期策略
- 1.1、惰性过期
- 1.2、定期过期
- 清理频率配置
- 清理流程
- 2、Redis淘汰策略
- 策略
- 流程
- 算法分析
- 1、LRU
- 2、LFU
- 二、持久化
- 1、RDB
- 2、AOF
前言
redis 内存管理与持久化
一、内存管理
redis我们的数据都是放在内存里面的,但是内存是有大小的,如果一直将数据放入,会导致内存溢出,这时候需要进行数据清理。
内存的大小由一个配置决定
maxmemory 100mb
如果配置是0,那么默认是电脑的内存,如果是32bit 隐式大小为3G。
数据清理,通过两张方式来实现。1、过期策略,2、淘汰策略
1、Redis过期策略
过期策略又分为惰性过期和定期过期
1.1、惰性过期
在Redis里面,就是每次在访问操作Key的时候,判断这个Key是不是过期了,如果过期了就删除。
该策略就可以最大化地节省CPU资源,只有访问的时候才去使用cpu判断一下, 但是这样却对内存非常不友好。如果没有再次访问,本来该key需要过期删除,结果一直堆积在内存里面,造成内存泄露。
是不是可以定期去扫描,然后判断是否过期删除?答案是肯定的,redis也制定了另一种方案,定期过期
1.2、定期过期
清理频率配置
定期就需要去定义这个循环周期,比如五分钟一次,这个参数由redis.conf中的hz配置来决定,它的取值范围是 1 到 500。
hz 参数定义了 Redis 每秒钟执行的事件循环次数,这包括检查和删除过期键的任务。默认情况下,hz 设置为 10,意味着每秒进行 10 次过期键的检查。
要修改这个值,你可以编辑 Redis 配置文件 (redis.conf) 或者在运行时通过 CONFIG SET 命令动态修改:
- 在配置文件中设置
打开你的 redis.conf 文件,并找到 hz 参数,将其值更改为所需的频率。例如,如果你想将频率提高到每秒20次,则设置如下:
hz 20
- 动态配置
如果你不想重启 Redis 服务,也可以使用 CONFIG SET 命令在线更改此参数。例如:
CONFIG SET hz 20
需要注意的是,虽然增加 hz 的值可以让 Redis 更快地处理同时到期的许多键,并且更加精确地处理超时,但这会占用更多的 CPU 资源。
因此,通常不建议将 hz 设置得过高(超过 100),除非是在对请求延时要求非常低的情况下。
从 Redis 6.2 开始,server.hz 的实际生效值可能会根据客户端连接的数量动态调整,以确保即使在高负载下也能保持良好的性能。这意味着配置文件中的 hz 只是一个初始值,而实际运行时的刷新频率是动态变化的。
清理流程
- serverCron方法定时去执行清理,执行频率根据redis.conf中的hz配置的值
- 执行清理的时候,不是去扫描所有的key,而是去扫描所有设置了过期时间的key(redisDb.expires)
- 如果每次去把所有过期的key都拿过来,那么假如过期的key很多,就会很慢,所以也不是一次性拿取所有的key
- 根据hash桶的维度去扫描key,扫到20(可配)个key为止。假如第一个桶是15个key ,没有满足20,继续扫描第二个桶,第二个桶20个key,由于是以hash桶的维度扫描的,所以第二个扫到了就会全扫,总共扫描35个key
- 扫描出来的过期的key,进行删除
- 如果取了400个空桶,或者扫描的删除比例跟扫描的总数超过10%,继续执行4、5步。
- 也不能无限的循环,循环16次后回去检测时间,超过指定时间会跳出。
流程图:
2、Redis淘汰策略
策略
由于Redis内存是有大小的,并且里面的数据都没有过期,这样一直放入数据,使得内存满了的时候,Redis就不能放入新的数据,导致不可用,这是我们不愿意看到的。
所以,我们需要使用一些策略来解决这个问题来保证可用性,
官网提供了8种不同的策略
- noeviction(默认):
不进行任何淘汰。当内存使用超过限制后,所有写入操作将返回错误信息,而读取操作仍然可以正常执行。 - volatile-lru:
使用 LRU (Least Recently Used) 算法从设置了过期时间的键中淘汰最久未使用的键。 - allkeys-lru:
使用 LRU 算法从所有键中淘汰最久未使用的键,包括那些没有设置过期时间的键。 - volatile-lfu:
使用 LFU (Least Frequently Used) 算法从设置了过期时间的键中淘汰最少使用的键。这个策略是在 Redis 4.0 版本引入的。 - allkeys-lfu:
使用 LFU 算法从所有键中淘汰最少使用的键。同样是在 Redis 4.0 中新增的功能。 - volatile-random:
随机地从设置了过期时间的键中挑选一个键进行淘汰。 - allkeys-random:
随机地从所有键中挑选一个键进行淘汰。 - volatile-ttl:
淘汰那些设置了过期时间且剩余生存时间最短的键,即优先删除即将过期的键。
我们可以在config中配置maxmemory-policy来指定相关的淘汰策略
maxmemory-policy noeviction //默认不淘汰数据,能读不能写
流程
- redis有个淘汰池,默认大小是16,并且里面的数据是末尾淘汰制。
- 每次指令操作的时候,会判断当前内存是否满足指令所需要的内存
- 如果当前内存不能满足,会从淘汰池中的尾部拿取一个最适合淘汰的数据
- 会取样(配置 maxmemory-samples)从Redis中获取随机获取到取样的数据,解决一次性读取所有的数据慢的问题
- 在取样的数据中,根据淘汰算法,找到最适合淘汰的数据
- 将最合适的那个数据跟淘汰池中的数据比较,是否比淘汰池中的更适合淘汰,如果更适合,放入淘汰池
- 按照适合的程度进行排序,最适合淘汰的放入尾部
- 将需要淘汰的数据从Redis删除,并且从淘汰池移除。
流程图:
算法分析
1、LRU
LRU,Least Recently Used 翻译过来是最久未使用,根据时间轴来走,仍很久没用的数据。只要最近有用过,我就默认是有效的。
根据使用时间,从近到远,越远的越容易淘汰
1、实现原理
- 首先,LRU是根据这个对象的访问操作时间来进行淘汰的,那我们需要知道这个对象最后的操作访问时间。
- 知道了对象的最后操作访问时间后,我们只需要跟当前的系统时间来进行对比,就能计算出对象已经多久没访问了
2、时间计算
- 它使用一个24位的时钟计数器,每秒递增一次。这个计数器提供的分辨率足以满足大多数应用场景的需求,同时大大减少了存储时间戳所需的内存空间。
- 每当一个键被访问时,就会更新它的 LRU 字段为当前的lruclock值。当时间超过24位(秒,大概是194天),lruclock又会从0开始。redisObject.lru则是记录当前时间。
- 如果redisObject.lru < lruclock,直接通过 lruclock-redisObject.lru 得到这个对象多久没访问
- 如果redisObject.lru > lruclock,通过 lruclock+(24bit的最大值-redisObject.lru)得到这个对象多久没访问
轮询
这里有个轮询的概念,它如果超过24位,又会从0开始。
以时钟为例
昨天10点访问的记录,现在是12点了,请问过了多久?12 - 10 = 2h
昨天10点访问的记录,现在是5点了,请问过了多久?24- 10 + 5 = 19h
这里的现在可能是今天,也可能是明天,就是轮询了多天,那该如何计算
对于redis而言,伪lru算法本来就不是非常精确的。所以redis只考虑1次轮询的情况,对于多次轮询,也只按1次轮询处理。
也就是说现在只当成今天处理,不会当成明天或者后天等。
3、知识点:
系统时间戳记录在全局变量中,每100ms更新一次。当函数查询key调⽤lookupKey中更新数据的Iru热度值时,就不⽤每次调⽤系统函数time,可以提⾼执⾏效率。
4、流程图:
2、LFU
LFU,LeastFrequentlyUsed,最不常⽤,按照使⽤频率删除,4.0版本新增。 它的衡量标准就是次数,次数越少的越容易被淘汰。每次操作访问一次,就+1; 淘汰的时候,直接去比较这个次数,次数越少的越容易淘汰。
1、redisObject.lru的前16bit表示时间,后8bit表示这个对象的访问频率。
前16bit代表的是这个对象最后访问时间的分单位。通过这个值能够得到这个对象多少分钟没访问。结合lfu-decay-time,来控制对象的访问频率。这样能有效控制很久没访问的数据一直保持热点。
lfu-decay-time 1 // 衰减因子,多少分钟没访问就减少一次
2、8bit最大值是255,用后8bit表示访问次数是不够的。redis在这里做了一些处理,让数据达到255很难。方案如下:
- 访问次数最大只能255,如果到了255,不往上加。实际到达255的几率不是很高。可以支撑很大很大的数据量。
- 访问次数属于随机添加,添加的几率根据基数值(LFU_INIT_VAL)、已有的counter值、配置server.lfu_log_factor相关,counter值越大,添加的几率越小,lfu-log-factor配置的值越大,添加的几率越小。
假设基准值是100 - 访问次数 < 100,则每次访问则+1
- 访问次数 100 <= counter <= 255,要通过公式计算,越往上,增长因子越低。会让255很难达到,从而8位就能满足需求了。
3、官方的压测数据如下
二、持久化
Redis 作为一个内存数据库,其主要特性之一是高性能的数据操作,而这种高性能得益于数据存储在内存中。在某些情况下,如服务器重启、操作系统崩溃、电源故障等,内存中的数据会丢失。因此,Redis 需要持久化来保证数据的安全性和可恢复性。
redis持久化方式
- RDB
- AOF
- RDB + AOF
1、RDB
工作原理:
RDB 是通过创建数据集的时间点快照(snapshot)来实现的持久化机制。Redis 会将内存中的数据在特定时间点保存到磁盘上的一个二进制文件(默认名为 dump.rdb)中。
- 文件配置: 磁盘文件的路径和文件名都是可配置的
// 生成的快照文件名
dbfilename dump.rdb
// 快照文件保存的路径
dir ./// 开启数据压缩(默认开启),如果关闭,会导致.rdb文件变得很大
rdbcompression yes
- 自动触发:通过配置文件中的 save 选项来指定自动触发 RDB 快照的条件
save 900 1 # 900秒(15分钟)内有1个键被修改就保存快照
save 300 10 # 300秒(5分钟)内有10个键被修改就保存快照
save 60 10000 # 60秒(1分钟)内有10000个键被修改就保存快照
以上的配置在 redis.conf 完成
- 手动触发:Redis 提供了一些命令可以手动触发 RDB 快照
- BGSAVE 命令:这个命令会在后台进行保存操作,Redis 会继续处理来自客户端的请求。
- SAVE 命令:与 BGSAVE 不同,SAVE 命令在执行期间会阻塞 Redis 服务器,直到快照完成。
优点:
- 生成的rdb文件是一个非常紧凑的文件,所以很适合远程传输、备份和灾难恢复。
- 数据恢复速度快,因为文件格式非常紧凑。
- 对读取性能没有影响,适合用于读取密集型场景。
缺点:
- 安全性很低,可能会有数据丢失。假如每5分钟备份一次,断电宕机后会有5分钟的数据丢失。
- 经常fork子进程,所以比较耗CPU,对CPU不是很友好(对CPU要求高)。
2、AOF
工作原理:
AOF是通过将每一个写命令记录到日志文件(默认名为appendonly.aof)中实现的。Redis 在处理写命令时,会将这些命令追加到日志文件的末尾。在恢复数据时,Redis 会通过重新执行这些命令来重建数据集。
配置:
通过配置文件中的 appendonly 和 appendfsync 选项来启用并配置 AOF ,例如:
appendonly yes # 启用AOF持久化
appendfsync everysec # 每秒持久化一次
# 其他选项可以是:
# appendfsync always # 每次写操作后持久化(最安全但对性能影响最大)
# appendfsync no # 由操作系统决定何时持久化(性能最好,但最不安全)
AOF有个问题:随着时间的推移,写入的命令越来越多,相应的AOF的日志文件越来越大,文件中冗余内容会越来越多。如果redis再重启或做数据恢复,加载数据会很慢。
处理这个问题,则需要重写AOF
AOF 重写机制的工作原理
- Fork 子进程:
- Redis 会 fork 一个子进程,这个子进程会创建一个新的 AOF 文件。
- 子进程将根据当前内存中的数据来生成这个新的 AOF 文件,而不是直接复制现有的 AOF 文件。
- 命令压缩:
- 新的 AOF 文件会包含相同的数据,但是会优化和压缩命令。例如,多个对同一个 key 的写操作可能会被压缩为一个 SET 或者其他合并操作。
- 追加命令:
- 在子进程创建新 AOF 文件的过程中,主进程仍然在接受新写操作。将这些新的写操作记录到一个内存缓冲区中。
- 当子进程完成新 AOF 文件的创建后,主进程将内存缓冲区中的新写操作追加到新的 AOF 文件。
- 文件替换:
- 最后, Redis 会将旧的 AOF 文件替换成新的 AOF 文件。
触发条件和策略
AOF 重写可以通过自动触发和手动触发两种方式进行:
- 自动触发
Redis 可以根据配置的条件自动触发 AOF 重写。以下是与自动触发相关的配置选项:
# 设置当前 AOF 文件大小相对于上一次重写后的大小增加的百分比,当超过这个百分比时触发AOF重写。
# 比如设置为 100,表示当前 AOF 文件大小是上次重写后的两倍时触发重写。
auto-aof-rewrite-percentage 100# 设置触发 AOF 重写的最小 AOF 文件大小。
# 比如设置为 64mb,表示 AOF 文件至少达到 64MB 时才允许触发重写。
auto-aof-rewrite-min-size 64mb
- 手动触发
管理员可以通过 Redis 命令手动触发 AOF 重写:
BGREWRITEAOF 命令:
BGREWRITEAOF
该命令在后台重写 AOF 文件,不会阻塞 Redis 的正常操作
AOF优缺点
优点:
- 提供更高的数据安全性,因为写命令几乎实时持久化,数据丢失风险小。
- AOF文件是可读的,可以通过文本编辑器阅读和编辑日志文件中的命令。
- 可以配置不同的频率来记录命令,使得持久化策略更灵活。
缺点:
- AOF日志文件通常比RDB快照文件大。
- 随着时间推移,日志文件会不断增长,需要定期重写(rewrite)日志文件,以缩减其大小。
- 数据恢复速度较慢,因为需要一条一条重放写操作。
3、AOF + RDB:
Redis 还支持同时开启两种持久化机制,以结合两者的优点。通常会选择 RDB 作为定期的全量备份方案,而 AOF 作为增量日志记录,确保在最后一次 RDB 快照之后的数据变更也能够持久化。
同时开启配置
# 启用 AOF 持久化
appendonly yes
appendfsync everysec# RDB 持久化配置
save 900 1
save 300 10
save 60 10000
这种混合配置可以在确保较好性能的同时,最大限度减少数据丢失的风险。
相关文章:

redis 内存管理和持久化机制
文章目录 前言一、内存管理1、Redis过期策略1.1、惰性过期1.2、定期过期清理频率配置清理流程 2、Redis淘汰策略策略流程算法分析1、LRU2、LFU 二、持久化1、RDB2、AOF 前言 redis 内存管理与持久化 一、内存管理 redis我们的数据都是放在内存里面的,但是内存是有…...

python-42-使用selenium-wire爬取微信公众号下的所有文章列表
文章目录 1 seleniumwire1.1 selenium-wire简介1.2 获取请求和响应信息2 操作2.1 自动获取token和cookie和agent2.3 获取所有清单3 异常解决3.1 请求url失败的问题3.2 访问链接不安全的问题4 参考附录1 seleniumwire Selenium WebDriver本身并不直接提供获取HTTP请求头(header…...

机器人碳钢去毛刺,用大扭去毛刺主轴可轻松去除
在碳钢精密加工的最后阶段,去除毛刺是确保产品质量的关键步骤。面对碳钢这种硬度较高的材料,采用大扭矩的SycoTec去毛刺主轴,成为了行业内的高效解决方案。SycoTec作为精密加工领域的领军品牌,其生产的高速电主轴以其卓越的性能&a…...

day05_Spark SQL
文章目录 day05_Spark SQL课程笔记一、今日课程内容二、Spark SQL 基本介绍(了解)1、什么是Spark SQL**为什么 Spark SQL 是“SQL与大数据之间的桥梁”?****实际意义**为什么要学习Spark SQL呢?**为什么 Spark SQL 像“瑞士军刀”࿱…...
Java线程的异常处理:确保线程安全运行
哈喽,各位小伙伴们,你们好呀,我是喵手。运营社区:C站/掘金/腾讯云/阿里云/华为云/51CTO;欢迎大家常来逛逛 今天我要给大家分享一些自己日常学习到的一些知识点,并以文字的形式跟大家一起交流,互…...

nvim 打造成可用的IDE(2)
上一个 文章写的太长了, 后来再写东西 就一卡一卡的,所以新开一个。 主要是关于 bufferline的。 之前我的界面是这样的。 这个图标很不舒服有。 后来发现是在这里进行配置。 我也不知道,这个配置 我是从哪 抄过来的。 测试结果࿱…...
如何当前正在运行的 Elasticsearch 集群信息
要查看当前正在运行的 Elasticsearch 集群信息,可以通过以下几种方法: 1. 使用 _cluster/health API _cluster/health API 返回集群的健康状态、节点数量、分片状态等信息。可以用 curl 命令直接访问: curl -X GET "http://localhost…...
PHP Filesystem:深入解析与实战应用
PHP Filesystem:深入解析与实战应用 引言 PHP作为一种流行的服务器端编程语言,提供了强大的文件系统操作功能。本文将深入探讨PHP的Filesystem函数,这些函数允许开发者访问和操作服务器上的文件系统。无论是进行基本的文件操作,还是实现复杂的文件管理系统,PHP的Filesys…...

pdf提取文本,表格以及转图片:spire.pdf
文章目录 🐒个人主页:信计2102罗铠威🏅JavaEE系列专栏📖前言:🎀 1. pdfbox1.1导入pdfbox 的maven依赖1.1 提取文本1.2 提取文本表格(可自行加入逻辑处理)1.3 pdf转换成图片代码&…...
jQuery UI 主题
关于“jQuery UI 主题”,我找到了一些有用的信息。 首先,jQuery UI 主题允许开发人员无缝集成UI小部件到他们网站或应用程序的外观和感观。每个插件通过CSS定义样式,包含两层样式信息:标准的jQuery UI CSS框架样式和具体的插件样…...

C# GDI+的DrawString无法绘制Tab键的现象
【啰嗦2句】 现在用C#的人很少了吧?GDI更少了吧?所以这个问题估计也冷门。没关系,分享给特定需要的人也不错。 【问题现象】 工作中开发了一个报告编辑器,实现图文排版等功能,用着没什么问题,直到有一天…...

C# GID+绘制不透明和半透明的线条
绘制线条时,必须将 Pen 对象传递给 DrawLine 类的 Graphics 方法。 Pen 构造函数的参数之一是 Color 对象。 若要绘制不透明的线条,请将颜色的 alpha 分量设置为 255。 若要绘制半透明的线条,请将 alpha 分量设置为从 1 到 254 的任何值。 在…...

L4-Prompt-Delta
Paper List PromptPapers:https://github.com/thunlp/PromptPapersDeltaPapers: https://github.com/thunlp/DeltaPapers Programming Toolkit OpemPrompt: https://github.com/thunlp/OpenPromptOpenDelta: https://github.com/thunlp/OpenDelta 一、传统微调方法࿱…...
Qt 自定义控件(Qt绘图)
一、QPaintEvent绘图事件1、QPaintEvent是Qt框架中一个重要的事件类,专门用于处理绘图事件。 2、当Qt视图组件需要重绘自己的一部分时,就会产生QPaintEvent事件。 3、Qt视图组件重绘自己,通常发生在以下情况: (1)、窗口第一次显示时: 当窗…...

electron 上怎么用node 调用 c++ 提供的方法
背景 在 Electron 上调用 C 代码的场景主要出现在需要执行高性能、低延迟的任务,或者需要与现有的本地 C 库集成时。这些场景往往涉及底层系统交互、性能优化或跨平台兼容性需求。 我们都知道c 的性能和安全性都比JavaScript 要高,但我认为在 Electron …...

Chromium 132 编译指南 Windows 篇 - Git 初始化设置 (四)
1. 引言 在 Chromium 编译指南系列的前几篇文章中,我们已经完成了编译环境的基础设置和关键环境变量的配置。本篇将重点介绍 Git 的安装与初始化配置,这是获取和管理 Chromium 源代码的重要前提。 2. 安装 Git 在 Windows 环境下,Git 并不…...

day03-前端Web-Vue3.0基础
目录 前言1. Vue概述2. 快速入门2.1 需求2.2 步骤2.3 实现 3. Vue指令3.1 介绍3.2 v-for3.2.1 介绍3.2.2 演示3.2.3 v-for的key3.2.4 案例-列表渲染 3.3 v-bind3.3.1 介绍3.3.2 演示3.3.3 案例-图片展示 3.4 v-if & v-show3.4.1 介绍3.4.2 案例-性别职位展示 3.6 v-model3.…...

Windows 下Mamba2 / Vim / Vmamba 环境安装问题记录及解决方法终极版(无需绕过triton)
导航 安装教程导航 Mamba 及 Vim 安装问题参看本人博客:Mamba 环境安装踩坑问题汇总及解决方法(初版)Linux 下Mamba 及 Vim 安装问题参看本人博客:Mamba 环境安装踩坑问题汇总及解决方法(重置版)Windows …...

GitLab本地服务器配置ssh和克隆项目
1. 本地安装好git git链接:https://git-scm.com/downloads/win 无脑点击下一步安装即可,打开Git Bash命令终端如下: 2. 配置本地用户名和邮箱 git config --global user.name "你的名字" git config --global user.email "你的邮箱&quo…...
Win10和11 git/Android Studio遇到filename too long问题的解决
1、打开windows长文件、长路径支持: 可以参考这篇文章: 修改注册表方法: 使用Admin登陆machine,在run中输入regedit并回车; 找到路径 ’Computer -> HKEY_LOCAL_MACHINE -> SYSTEM -> CurrentControlSet -&g…...

wordpress后台更新后 前端没变化的解决方法
使用siteground主机的wordpress网站,会出现更新了网站内容和修改了php模板文件、js文件、css文件、图片文件后,网站没有变化的情况。 不熟悉siteground主机的新手,遇到这个问题,就很抓狂,明明是哪都没操作错误&#x…...
Cesium1.95中高性能加载1500个点
一、基本方式: 图标使用.png比.svg性能要好 <template><div id"cesiumContainer"></div><div class"toolbar"><button id"resetButton">重新生成点</button><span id"countDisplay&qu…...
鸿蒙中用HarmonyOS SDK应用服务 HarmonyOS5开发一个医院挂号小程序
一、开发准备 环境搭建: 安装DevEco Studio 3.0或更高版本配置HarmonyOS SDK申请开发者账号 项目创建: File > New > Create Project > Application (选择"Empty Ability") 二、核心功能实现 1. 医院科室展示 /…...
vue3 定时器-定义全局方法 vue+ts
1.创建ts文件 路径:src/utils/timer.ts 完整代码: import { onUnmounted } from vuetype TimerCallback (...args: any[]) > voidexport function useGlobalTimer() {const timers: Map<number, NodeJS.Timeout> new Map()// 创建定时器con…...

优选算法第十二讲:队列 + 宽搜 优先级队列
优选算法第十二讲:队列 宽搜 && 优先级队列 1.N叉树的层序遍历2.二叉树的锯齿型层序遍历3.二叉树最大宽度4.在每个树行中找最大值5.优先级队列 -- 最后一块石头的重量6.数据流中的第K大元素7.前K个高频单词8.数据流的中位数 1.N叉树的层序遍历 2.二叉树的锯…...

有限自动机到正规文法转换器v1.0
1 项目简介 这是一个功能强大的有限自动机(Finite Automaton, FA)到正规文法(Regular Grammar)转换器,它配备了一个直观且完整的图形用户界面,使用户能够轻松地进行操作和观察。该程序基于编译原理中的经典…...
return this;返回的是谁
一个审批系统的示例来演示责任链模式的实现。假设公司需要处理不同金额的采购申请,不同级别的经理有不同的审批权限: // 抽象处理者:审批者 abstract class Approver {protected Approver successor; // 下一个处理者// 设置下一个处理者pub…...
Caliper 负载(Workload)详细解析
Caliper 负载(Workload)详细解析 负载(Workload)是 Caliper 性能测试的核心部分,它定义了测试期间要执行的具体合约调用行为和交易模式。下面我将全面深入地讲解负载的各个方面。 一、负载模块基本结构 一个典型的负载模块(如 workload.js)包含以下基本结构: use strict;/…...
关于uniapp展示PDF的解决方案
在 UniApp 的 H5 环境中使用 pdf-vue3 组件可以实现完整的 PDF 预览功能。以下是详细实现步骤和注意事项: 一、安装依赖 安装 pdf-vue3 和 PDF.js 核心库: npm install pdf-vue3 pdfjs-dist二、基本使用示例 <template><view class"con…...
深入理解Optional:处理空指针异常
1. 使用Optional处理可能为空的集合 在Java开发中,集合判空是一个常见但容易出错的场景。传统方式虽然可行,但存在一些潜在问题: // 传统判空方式 if (!CollectionUtils.isEmpty(userInfoList)) {for (UserInfo userInfo : userInfoList) {…...