架构思维:缓存层场景实战_读缓存(下)

news2025/4/21 18:32:55

文章目录

  • Pre
  • 业务场景
  • 缓存存储数据的时机与常见问题解决方案
    • 1. 缓存读取与存储逻辑
    • 2. 高并发下的缓存问题及解决方案
    • 3. 缓存预热(减少冷启动问题)
  • 缓存更新策略(双写问题)
    • 1. 先更新缓存,再更新数据库(不推荐)
    • 2. 先删除缓存,再更新数据库(不推荐)
    • 3. 先更新数据库,再更新缓存(不推荐)
    • 4. 先更新数据库,再删除缓存(Cache-Aside模式 推荐⭐)
    • 5. 延迟双删(先删缓存→更新DB→再删缓存)(最佳实践⭐)
    • 总结:如何选择缓存更新策略
    • 最终建议
  • 缓存高可用设计核心要点与监控方案
    • 1、缓存高可用设计的5大核心要点
    • 2、缓存监控关键指标与工具
    • 3、总结

在这里插入图片描述


Pre

Java避坑案例 - 高并发场景下的分布式缓存策略

Redis - 缓存设计深度解析:穿透、并发、雪崩与热点策略

深入理解分布式技术 - 先更新数据库,还是先更新缓存

架构思维:读缓存 - 减少数据库读操作压力


业务场景

某系统商品详情页因叠加推荐、成交记录、优惠活动等功能,导致每次访问需执行数十条SQL,平均响应时间从3.61秒恶化至15.53秒。初期考虑本地缓存(如Guava),但测算发现5万商品数据需750GB内存(30节点×25GB),成本过高。最终采用分布式缓存方案,将数据集中存储(如Redis),所有服务节点共享同一缓存源,避免冗余存储,显著提升访问速度至毫秒级,同时降低硬件成本。优化后,异步加载非核心数据(如成交记录)进一步减轻实时查询压力。

技术选型完成后,开始考虑缓存的一些具体问题,先从缓存何时存储数据入手


缓存存储数据的时机与常见问题解决方案

1. 缓存读取与存储逻辑

  1. 先查缓存:请求到达时,优先从缓存(如Redis)读取数据。
  2. 缓存未命中:若数据不存在或过期,则查询数据库,并将结果写入缓存。
  3. 返回数据:最终返回缓存中的数据给调用方。

2. 高并发下的缓存问题及解决方案

这种逻辑唯一麻烦的地方是,当用户发来大量的并发请求时,它们会发现缓存中没有数据,那么所有请求会同时挤在第2)步,此时如果这些请求全部从数据库读取数据,就会让数据库崩溃。

数据库的崩溃可以分为3种情况。

  • 1)单一数据过期或者不存在,这种情况称为缓存击穿

    解决方案:第一个线程如果发现Key不存在,就先给Key加锁,再从数据库读取数据保存到缓存中,最后释放锁。如果其他线程正在读取同一个Key值,那么必须等到锁释放后才行。关于锁的问题前面已经讲过,此处不再赘述。

  • 2)数据大面积过期或者Redis宕机,这种情况称为缓存雪崩

    解决方案:设置缓存的过期时间为随机分布或设置永不过期即可。

  • 3)一个恶意请求获取的Key不在数据库中,这种情况称为缓存穿透

    比如正常的商品ID是从100000到1000000(10万到100万之间的数值),那么恶意请求就可能会故意请求2000000以上的数据。这种情况如果不做处理,恶意请求每次进来时,肯定会发现缓存中没有值,那么每次都会查询数据库,虽然最终也没在数据库中找到商品,但是无疑给数据库增加了负担。这里给出两种解决办法。
    ①在业务逻辑中直接校验,在数据库不被访问的前提下过滤掉不存在的Key。
    ②针对恶意请求的Key存放一个空值在缓存中,防止恶意请求骚扰数据库。

故,总结如下:

  • 缓存击穿(单一Key失效)

    • 问题:热点Key失效时,大量请求同时穿透到数据库。
    • 解决:加锁(如Redis分布式锁),仅允许一个线程查询DB并回填缓存,其余线程等待。
  • 缓存雪崩(大量Key同时失效或Redis宕机)

    • 问题:缓存大面积失效,导致数据库被压垮。
    • 解决
      • 设置随机过期时间(如30分钟±5分钟)。
      • 缓存永不过期,后台异步更新(如定时任务)。
  • 缓存穿透(恶意查询不存在的数据)

    • 问题:恶意请求查询不存在的Key,导致频繁访问DB。
    • 解决
      1. 业务层校验:如商品ID范围检查(100000~1000000)。
      2. 缓存空值:对不存在的Key存储null或占位符,并设置较短过期时间。

3. 缓存预热(减少冷启动问题)

上面这些逻辑都是在确保查询数据的请求已经过来后如何适当地处理,如果缓存数据找不到,再去数据库查询,最终是要占用服务器额外资源的。那么最理想的就是在用户请求过来之前把数据都缓存到Redis中。这就是缓存预热。

其具体做法就是在深夜无人访问或访问量小的时候,将预热的数据保存到缓存中,这样流量大的时候,用户查询就无须再从数据库读取数据了,将大大减小数据读取压力。

故,总结如下:

  • 时机:在低峰期(如凌晨)提前加载热点数据到缓存。
  • 方式
    • 定时任务扫描DB并写入缓存。
    • 启动时自动加载核心数据。

缓存更新策略(双写问题)

关于缓存何时存数据的问题就讨论完了,接下来开始讨论更新缓存的问题,这部分内容因涉及双写(缓存+数据库),所以会花费一些篇幅。

  • 先更新DB还是缓存?
    • Cache-Aside(旁路缓存):先更新DB,再删除缓存(推荐)。
    • Write-Through(写穿透):先更新缓存,再同步到DB(一致性高,但性能较低)。
    • Write-Behind(写回):先更新缓存,异步刷回DB(高性能,但可能丢数据)。

在缓存更新时,我们需要考虑 数据库与缓存的一致性,同时避免 并发问题性能瓶颈。以下是 5种常见的缓存更新策略,分析它们的优缺点,并给出推荐方案。


1. 先更新缓存,再更新数据库(不推荐)

对于这个组合,会遇到这种情况:假设第二步更新数据库失败了,要求回滚缓存的更新,这时该怎么办呢?Redis不支持事务回滚,除非采用手工回滚的方式,先保存原有数据,然后再将缓存更新回原来的数据,这种解决方案有些缺陷。

这里简单举个例子。
1)原来缓存中的值是a,两个线程同时更新库存。
2)线程A将缓存中的值更新成b,且保存了原来的值a,然后更新数据库。
3)线程B将缓存中的值更新成c,且保存了原来的值b,然后更新数据库。
4)线程A更新数据库时失败了,它必须回滚,那现在缓存中的值更新成什么呢?理论上应该更新成c,因为数据库中的值是c,但是,线程A里面无从获得c这个值。

如果在线程A更新缓存与数据库的整个过程中,先把缓存及数据库都锁上,确保别的线程不能更新,是否可行?当然是可行的。但是其他线程能不能读取?

假设线程A更新数据库失败回滚缓存时,线程C也加入进来,它需要先读取缓存中的值,这时又返回什么值?

看到这个场景,是不是有点儿熟悉?不错,这就是典型的事务隔离级别场景。所以就不推荐这个组合,因为此处只是需要使用一下缓存,而这个组合就要考虑事务隔离级别的一些逻辑,成本太大。接着考虑别的组合。

故, 总结如下:

流程

  1. 更新缓存
  2. 更新数据库
线程A 缓存 数据库 线程B 线程C 初始状态:缓存=a,数据库=a 1. 更新缓存为b(保存旧值a) 2. 更新缓存为c(保存旧值b) 3. 尝试更新数据库为b 4. 更新失败(需回滚) 此时缓存最新值是c\n但线程A只记录旧值a 5. 尝试回滚为a(覆盖当前c值) 6. 读取缓存值(得到a) 数据不一致:数据库值为a\n缓存被错误回滚为a\n实际数据库应为c(线程B已更新成功) 如果使用全局锁: 加锁(阻塞线程B/C) 更新失败后回滚 解锁 读请求被阻塞直到解锁 线程A 缓存 数据库 线程B 线程C

问题

  • 事务回滚困难:如果数据库更新失败,缓存无法自动回滚(Redis不支持事务回滚)。
  • 并发问题
    • 线程A更新缓存为 b,线程B更新缓存为 c,最终缓存可能是 bc,而数据库可能是另一个值。
    • 需要加锁,但会降低性能。

结论:❌ 不推荐,容易导致数据不一致。


2. 先删除缓存,再更新数据库(不推荐)

使用这种方案,即使更新数据库失败了也不需要回滚缓存。这种做法虽然巧妙规避了失败回滚的问题,却引出了两个更大的问题。

1)假设线程A先删除缓存,再更新数据库。在线程A完成更新数据库之前,后执行的线程B反而超前完成了操作,读取Key发现没有数据后,将数据库中的旧值存放到了缓存中。线程A在线程B都完成后再更新数据库,这样就会出现缓存(旧值)与数据库的值(新值)不一致的问题。

2)为了解决一致性问题,可以让线程A给Key加锁,因为写操作特别耗时,这种处理方法会导致大量的读请求卡在锁中。

以上描述的是典型的高可用和一致性难以两全的问题,如果再加上分区容错就是CAP(一致性Consistency、可用性Availability、分区容错性Partition Tolerance)了,这里不展开讨论,接下来继续讨论另外3种组合


故, 总结如下:

流程

  1. 删除缓存
  2. 更新数据库
线程A 缓存 数据库 线程B 初始状态:缓存=a(旧值),数据库=a 1. 删除缓存Key 2. 开始更新数据为b(未提交) 3. 读取Key(未命中) 4. 查询旧值a 5. 写入缓存值为a(旧值回填) 6. 提交更新为b(完成) 数据不一致:缓存=a(旧值)\n数据库=b(新值) 加锁方案导致的问题: 获取Key锁(阻塞线程B) 更新期间占用锁 读请求等待锁释放 释放锁 线程A 缓存 数据库 线程B

问题

  • 缓存与数据库不一致(旧数据问题):
    • 线程A删除缓存 → 线程B查询缓存未命中 → 从DB读取旧数据并写入缓存 → 线程A更新DB,导致缓存是旧数据。
  • 高并发下缓存击穿:大量请求穿透到数据库。

解决方案

  • 加锁(如分布式锁),但会影响性能。

结论:❌ 不推荐,容易导致缓存与数据库不一致。


3. 先更新数据库,再更新缓存(不推荐)

对于组合3,同样需要考虑两个问题。
1)假设第一步(更新数据库)成功,第二步(更新缓存)失败了怎么办?
因为缓存不是主流程,数据库才是,所以不会因为更新缓存失败而回滚第一步对数据库的更新。此时一般采取的做法是重试机制,但重试机制如果存在延时还是会出现数据库与缓存不一致的情况,不好处理。

2)假设两个线程同时更新同一个数据,线程A先完成了第一步,线程B先完成了第二步怎么办?线程A把值更新成a,线程B把值更新成b,此时数据库中的最新值是b,因为线程A先完成了第一步,后完成第二步,所以缓存中的最新值是a,数据库与缓存的值还是不一致,这个逻辑还是有问题的。

因此,也不建议采用这个组合

故, 总结如下:

流程

  1. 更新数据库
  2. 更新缓存
线程A 线程B 数据库 缓存 场景1:缓存更新失败导致不一致 1. 更新数据为a(成功) 2. 尝试更新缓存为a(失败) 3. 读取数据(命中旧值b) 场景2:并发更新导致顺序错乱 1. 更新数据为a(完成) 2. 更新数据为b(完成) 3. 更新缓存为b(先完成) 4. 更新缓存为a(后完成) 最终状态:数据库=b\n缓存=a(不一致) 重试机制问题 更新成功 更新失败 异步重试更新(延迟) 在此期间读取旧值 线程A 线程B 数据库 缓存

问题

  • 缓存更新失败:如果第二步失败,缓存仍是旧数据。
  • 并发问题
    • 线程A更新DB为 a,线程B更新DB为 b → 线程B先更新缓存为 b,线程A后更新缓存为 a,导致缓存是 a,而DB是 b

结论:❌ 不推荐,仍可能不一致。


4. 先更新数据库,再删除缓存(Cache-Aside模式 推荐⭐)

针对组合4,先看看它能不能解决组合3的第二个问题。
假设两个线程同时更新同一个数据,线程A先完成第一步,线程B先完成第二步怎么办?
线程A把值更新成a,线程B把值更新成b,此时数据库中的最新值是b,因为线程A先完成了第一步,所以第二步谁先完成已经不重要了,因为都是直接删除缓存数据。这个问题解决了。

那么,它能解决组合3的第一个问题吗?假设第一步成功,第二步失败了怎么办?
这种情况的出现概率与组合3相比明显低不少,因为删除比更新容易多了。虽然这个组合方案不完美,但出现一致性问题的概率较低。

故, 总结如下:

流程

  1. 更新数据库
  2. 删除缓存
线程A 线程B 数据库 缓存 线程C 场景:组合4(先更新DB再删缓存) 1. 更新数据为a(成功) 2. 删除Key(成功) 3. 更新数据为b(成功) 4. 删除Key(成功) 对比组合3(先更新DB再更新缓存) 更新为a(成功) 更新为b(成功) 更新为b(先完成) 更新为a(后完成) 组合4的核心优势 5. 读Key(未命中) 6. 查询最新值b 7. 回填b(强制重新加载) 线程A 线程B 数据库 缓存 线程C

优点

  • 缓存删除失败概率低(删除比更新更简单)。
  • 并发问题较少
    • 即使线程A更新DB后未及时删除缓存,线程B读取旧数据,但下次查询会重新加载最新数据。

问题

  • 短暂不一致
    • 线程A更新DB后,未删除缓存前,线程B可能读到旧数据。
  • 缓存击穿:删除缓存后,大量请求穿透到DB。

解决方案

  • 延迟双删(组合5)可进一步降低不一致概率。
  • 缓存预热减少击穿影响。

结论:✅ 推荐,相比前3种方案更可靠。


5. 延迟双删(先删缓存→更新DB→再删缓存)(最佳实践⭐)

除了组合3会碰到的问题,组合4还会碰到别的问题吗?

是的。假设线程A要更新数据,先完成第一步更新数据库,在线程A删除缓存之前,线程B要访问缓存,那么取得的就是旧数据。这是一个小小的缺陷。

那么,以上问题有办法解决吗?

还有一个方案,就是先删除缓存,再更新数据库,再删除缓存。这个方案其实和先更新数据库,再删除缓存差不多,因为还是会出现类似的问题:假设线程A要更新数据库,先删除了缓存,这一瞬间线程C要读缓存,先把数据迁移到缓存;然后线程A完成了更新数据库的操作,这一瞬间线程B也要访问缓存,此时它访问到的就是线程C放到缓存里面的旧数据。

不过组合5出现类似问题的概率更低,因为要刚好有3个线程配合才会出现问题(比先更新数据库,再删除缓存的方案多了一个需要配合的线程)。

但是相比于组合4,组合5规避了第二步删除缓存失败的问题——组合5是先删除缓存,再更新数据库,假设它的第三步“再删除缓存”失败了,也没关系,因为缓存已经删除了。

其实没有一个组合是完美的,它们都有读到脏数据(这里指旧数据)的可能性,只不过概率不同。根据以上分析,组合5相对来说是比较好的选择。

不过这个组合也有一些问题要考虑,具体如下。

  • 1)删除缓存数据后变相出现缓存击穿,此时该怎么办?此问题在前面已经给出了方案。
  • 2)删除缓存失败如何重试?这个重试可以做得复杂一点,也可以做得简单一点。简单一点就是使用try…catch…,假设删除缓存失败了,在catch里面重试一次即可;复杂一点就是使用一个异步线程不断重试,甚至用到MQ。不过这里没有必要大动干戈。而且异步重试的延时大,会带来更多的读脏数据的可能性。所以仅仅同步重试一次就可以了。
  • 3)不可避免的脏数据问题。虽然这个问题在组合5中出现的概率已经大大降低了,但是还是有的。关于这一点就需要与业务沟通,毕竟这种情况比较少见,可以根据实际业务情况判断是否需要解决这个瑕疵。

任何一个方案都不是完美的,但如果剩下1%的问题需要花好几倍的代价去解决,从技术上来讲得不偿失,这就要求架构师去说服业务方,去平衡技术的成本和收益。

故, 总结如下:

流程

  1. 删除缓存
  2. 更新数据库
  3. 延迟几百毫秒后,再次删除缓存
线程A 缓存 数据库 线程B 线程C 延迟双删核心流程 1. 第一次删除缓存 2. 更新数据为b 3. 延迟500ms后二次删除缓存 第一次删除后的读穿透 4. 读缓存(未命中) 5. 查询旧值a(未提交) 6. 回填旧值a 二次删除后的强一致性 7. 读缓存(未命中) 8. 查询新值b(已提交) 9. 回填新值b 最终状态:缓存=b,数据库=b(强一致) 线程A 缓存 数据库 线程B 线程C

优点

  • 减少不一致窗口:第二次删除能清理可能的脏数据。
  • 避免缓存更新失败问题:即使第二次删除失败,缓存已被第一次删除,不会长期存储旧数据。

问题

  • 短暂不一致仍存在(但概率更低)。
  • 实现稍复杂:需要引入延迟任务(如MQ、定时任务)。

适用场景

  • 对一致性要求较高的电商、金融等业务。

结论:✅ 最佳实践,比方案4更可靠。 延迟双删通过两次删除操作建立安全窗口,在工程实践上实现了性能与一致性的最佳平衡,是分布式系统缓存更新的首选方案 .


总结:如何选择缓存更新策略

方案描述一致性推荐度
1️⃣ 先更新缓存,再更新DB易回滚问题❌ 差❌ 不推荐
2️⃣ 先删缓存,再更新DB旧数据问题❌ 差❌ 不推荐
3️⃣ 先更新DB,再更新缓存并发问题⚠️ 一般❌ 不推荐
4️⃣ 先更新DB,再删缓存较可靠✅ 较好⭐ 推荐
5️⃣ 延迟双删最可靠✅ 最佳⭐⭐⭐ 最佳

最终建议

  • 普通业务:使用 方案4(先更新DB,再删缓存),简单可靠。
  • 高一致性业务(如支付、库存):使用 方案5(延迟双删),减少不一致窗口。
  • 补充优化
    • 缓存预热减少击穿影响。
    • 异步重试(如MQ)处理删除失败情况。
    • 加锁(如Redis分布式锁)防止并发问题。

没有完美方案,但 方案4和5 在大多数场景下能平衡 性能与一致性


缓存高可用设计核心要点与监控方案


1、缓存高可用设计的5大核心要点

  1. 负载均衡(读扩展)

    • 目标:通过多节点分摊读请求压力。
    • 方案
      • 使用代理层(如Twemproxy、Redis Cluster)自动分配请求。
      • 客户端分片(如一致性哈希)直接路由请求。
  2. 数据分片(写扩展)

    • 目标:分散数据存储与写压力。
    • 方案
      • Redis Cluster:自动分片(16384个槽),支持动态扩缩容。
      • Codis:Proxy层分片,兼容原生Redis协议。
  3. 数据冗余(容灾)

    • 目标:单节点故障时数据不丢失。
    • 方案
      • 主从复制(Replication):主节点写,从节点读+备份。
      • 多副本存储:如Redis Cluster的每个分片包含主从节点。
  4. 故障自动转移(Failover)

    • 目标:节点宕机时自动切换,保障服务可用。
    • 方案
      • 哨兵模式(Sentinel):监控主节点,自动选举新主。
      • Redis Cluster内置Failover:主节点宕机时,从节点自动升级。
  5. 一致性保证

    • 目标:数据分片、故障恢复时避免脏数据。
    • 挑战:Redis默认异步复制,可能丢失少量数据。
    • 解决方案
      • WAIT命令:同步复制(牺牲性能)。
      • 业务层补偿:如定时校对DB与缓存。

推荐架构

  • 中小规模:Redis Sentinel + 主从复制。
  • 大规模:Redis Cluster(内置分片、Failover)。

2、缓存监控关键指标与工具

  1. 核心监控指标

    • 命中率keyspace_hits / (keyspace_hits + keyspace_misses),低于80%需优化缓存策略。
    • 内存使用used_memory,避免超过最大内存(maxmemory)触发淘汰。
    • 慢查询slowlog分析耗时命令(如KEYS *、大Value操作)。
    • 延迟redis-cli --latency,超过1ms需排查网络或阻塞命令。
    • 连接数connected_clients,防止连接泄漏。
  2. 开源监控工具

    • RedisLive:实时仪表盘展示关键指标。
    • Prometheus + Grafana:自定义报警与可视化。
    • Redis-exporter:为Prometheus提供Redis指标。
  3. 自研监控建议

    • 定时巡检脚本:检查INFO命令输出的关键指标。
    • 自动化报警:如内存使用率超90%、命中率低于70%时触发告警。

3、总结

  • 高可用核心:分片扩展写、冗余保障读、自动Failover。
  • 监控关键点:命中率、内存、慢查询、延迟。
  • 工具选型
    • 快速上手:RedisLive。
    • 长期运维:Prometheus+Grafana。

最终建议:根据业务规模选择Redis Sentinel或Cluster,并配套监控告警体系,确保缓存稳定支撑业务高峰。

在这里插入图片描述

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

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

相关文章

uniapp微信小程序实现sse

微信小程序实现sse 注:因为微信小程序不支持sse请求,因为后台给的是分包的流,所以我们就使用接受流的方式,一直接受,然后把接受的数据拿取使用。这里还是使用uniapp的原生请求。 上代码 //注意:一定要下…

新能源汽车能量流测试的传感器融合技术应用指南

第一部分:核心原理模块化拆解 模块1:多源传感器物理层融合 关键技术: 高精度同步采集架构 采用PXIe-8840控制器同步定时模块(NI PXIe-6674T),实现CAN/LIN/模拟量信号的μs级同步光纤电压传感器&#xff0…

人工智能与网络安全:AI如何预防、检测和应对网络攻击?

引言:网络安全新战场,AI成关键角色 在数字化浪潮不断推进的今天,网络安全问题已经成为每一家企业、每一个组织无法回避的“隐形战场”。无论是电商平台、金融机构,还是政府机关、制造企业,都可能面临数据泄露、勒索病毒…

链表知识回顾

类型:单链表,双链表、循环链表 存储:在内存中不是连续存储 删除操作:即让c的指针指向e即可,无需释放d,因为java中又内存回收机制 添加节点: 链表的构造函数 public class ListNode {// 结点…

FPGA学习(五)——DDS信号发生器设计

FPGA学习(五)——DDS信号发生器设计 目录 FPGA学习(五)——DDS信号发生器设计一、FPGA开发中常用IP核——ROM/RAM/FIFO1、ROM简介2、ROM文件的设置(1)直接编辑法(2)用C语言等软件生成初始化文件 3、ROM IP核配置调用 二、DDS信号发…

OpenCv高阶(六)——图像的透视变换

目录 一、透视变换的定义与作用 二、透视变换的过程 三、OpenCV 中的透视变换函数 1. cv2.getPerspectiveTransform(src, dst) 2. cv2.warpPerspective(src, H, dsize, dstNone, flagscv2.INTER_LINEAR, borderModecv2.BORDER_CONSTANT, borderValue0) 四、文档扫描校正&a…

性能比拼: Go vs Bun

本内容是对知名性能评测博主 Anton Putra Go (Golang) vs. Bun: Performance (Latency - Throughput - Saturation - Availability) 内容的翻译与整理, 有适当删减, 相关指标和结论以原作为准 我对 Bun 在之前的基准测试中的出色表现感到惊讶,因此我决定将它与 Go …

定制化 Docsify 文档框架实战分享

🌟 定制化 Docsify 文档框架实战分享 在构建前端文档平台时,我们希望拥有更友好的用户界面、便捷的搜索、清晰的目录导航以及实用的代码复制功能。借助 Docsify,我实现了以下几个方面的定制优化,分享给大家 🙌。 &…

鸿蒙ArkUI之布局实战,线性布局(Column,Row)、弹性布局(Flex)、层叠布局(Stack),详细用法

本文聚焦于ArkUI的布局实战,三种十分重要的布局,线性布局、弹性布局、层叠布局,在实际开发过程中这几种布局方法都十分常见,下面直接上手 线性布局 垂直布局(Column) 官方文档: Column-行列…

测试基础笔记第七天

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 一、cat命令二、ls -al命令三、>重定向符号四、>>追加重定向符号五、less/more命令六、grep命令七、|管道符八、clear命令九、head命令十、tail命令十一、…

[Windows] Adobe Camera Raw 17.2 win/Mac版本

[Windows] Adobe Camera Raw 链接:https://pan.xunlei.com/s/VOOIAXoyaZcKAkf_NdP-qw_6A1?pwdpd5k# Adobe Camera Raw,支持Photoshop,lightroom等Adobe系列软件,对相片无损格式进行编辑调色。 支持PS LR 2022 2023 2024 2025版…

开源模型应用落地-Podcastfy-从文本到声音的智能跃迁-Gradio(一)

一、前言 在当今信息呈现方式越来越多样化的背景下,如何将文字、图片甚至视频高效转化为可听的音频体验,已经成为内容创作者、教育者和研究者们共同关注的重要话题。Podcastfy是一款基于Python的开源工具,它专注于将多种形式的内容智能转换成…

Python 深度学习实战 第11章 自然语言处理(NLP)实例

Python 深度学习实战 第11章 自然语言处理(NLP)实例 内容概要 第11章深入探讨了自然语言处理(NLP)的深度学习应用,涵盖了从文本预处理到序列到序列学习的多种技术。本章通过IMDB电影评论情感分类和英西翻译任务,详细介绍了如何使…

将 DeepSeek 集成到 Spring Boot 项目实现通过 AI 对话方式操作后台数据

文章目录 项目简介本项目分两大模块 GiteeMCP 简介环境要求项目代码核心实现代码MCP 服务端MCP 客户端 DeepSeek APIDockersse 连接ws 连接(推荐)http 连接 vue2-chat-windowCherry Studio配置模型配置 MCP调用 MCP 项目简介 在本项目中,我们…

《前端面试题之 Vue 篇(第三集)》

目录 1、 nvm的常用命令①.Node.js 版本与 npm 版本的对应关系②Vue2 与 Vue3 项目的 Node.js 版本分界线③版本管理实践建议 2、Vue2 项目搭建(基于 vue-cli Webpack)① 环境准备② 安装 Vue CLI(脚手架)③.创建项目&#xff08…

嵌入式C语言位操作的几种常见用法

作为一名老单片机工程师,我承认,当年刚入行的时候,最怕的就是看那些密密麻麻的寄存器定义,以及那些让人眼花缭乱的位操作。 尤其是遇到那种“明明改了寄存器,硬件就是不听话”的情况,简直想把示波器砸了&am…

基于Djiango实现中药材数据分析与可视化系统

中药材数据分析与可视化系统 项目截图 登录 注册 首页 药材Top20 药材价格 产地占比 历史价格 新闻资讯 后台管理 一、项目概述 中药材数据分析与可视化系统是一个基于Django框架开发的专业Web应用,致力于对各类中药材数据进行全面、系统的采集、分析和可视化展示…

stm32(gpio的四种输出)

其实GPIO这个片上外设的功能: 用于控制IO引脚。 CPU就如同大脑,而这些片上外设就如同四肢一样的关系 如图 —————————————————————————————— OK类比了以上 其实GPIO是有 八种工作模式的 这八种工作模式 因为GPIO是面向IO…

Zookeeper 可观测性最佳实践

Zookeeper 介绍 ZooKeeper 是一个开源的分布式协调服务,用于管理和协调分布式系统中的节点。它提供了一种高效、可靠的方式来解决分布式系统中的常见问题,如数据同步、配置管理、命名服务和集群管理等。本文介绍通过 DataKit 采集 Zookeeper 指标&#…

微信小程序三种裁剪动画有效果图

效果图 .wxml <image class"img inset {{status?action1:}}" src"{{src}}" /> <image class"img circle {{status?action2:}}" src"{{src}}" /> <image class"img polygon {{status?action3:}}" src&quo…