当前位置: 首页 > news >正文

Redis是如何进行内存管理的?缓存中有哪些常见问题?如何实现分布式锁?

Redis内存管理

Redis的内存用完了会怎样?

如果达到设置的上限,Redis的写命令会返回错误信息(但是读命令还可以正常返回)。

也可以配置内存淘汰机制,当Redis达到内存上限时会冲刷掉旧的内容。

Redis如何做内存优化?

可以好好利用Hash,list,sorted set,set等集合类型数据,因为通常情况下很多小的Key-Value可以用更紧凑的方式存放到一起。尽可能使用散列表(hashes),散列表(是说散列表里面存储的数少)使用的内存非常小,所以你应该尽可能的将你的数据模型抽象到一个散列表里面。比如你的web系统中有一个用户对象,不要为这个用户的名称,姓氏,邮箱,密码设置单独的key,而是应该把这个用户的所有信息存储到一张散列表里面。

过期key的删除策略?

详情请查看:过期删除策略

  1. 惰性删除。在访问key时,如果发现key已经过期,那么会将key删除。
  2. 定时删除。定时清理key,每次清理会依次遍历所有DB,从db随机取出20个key,如果过期就删除,如果其中有5个key过期,那么就继续对这个db进行清理,否则开始清理下一个db。

定期删除策略原理?

Redis内部维护一个定时任务,默认每秒进行10次(也就是每隔100毫秒一次)过期扫描,过期扫描不会遍历过期字典中所有的 key,而是采用了一种简单的贪心策略。

  1. 从过期字典中随机取出20个key
  2. 删除这 20 个 key 中已经过期的 key;
  3. 如果这20个key中过期key的比例超过了25%,则重复步骤1

为了保证过期扫描不会出现循环过度,导致线程卡死现象,算法还增加了扫描时间的上限,默认不会超过 25ms。

为什么定期删除不是把所有过期 key 都删除呢?

这样会对性能造成太大的影响。如果我们 key 数量非常庞大的话,挨个遍历检查是非常耗时的,会严重影响性能。Redis 设计这种策略的目的是为了平衡内存和性能。

过期key对持久化的影响

RDB:

  • 生成rdb文件:生成时,程序会对key进行检查,过期key不放入rdb文件。
  • 载入rdb文件:载入时,如果以主服务器模式运行,程序会对文件中保存的key进行检查,未过期的key会被载入到数据库中,而过期key则会忽略;如果以从服务器模式运行,无论键过期与否,均会载入数据库中,过期key会通过与主服务器同步而删除。

AOF:

  • 当服务器以AOF持久化模式运行时,如果数据库中的某个key已经过期,但它还没有被惰性删除或者定期删除,那么AOF文件不会因为这个过期key而产生任何影响
  • 当过期key被惰性删除或者定期删除之后,程序会向AOF文件追加一条DEL命令,来显示的记录被该key已经发被删除
  • 在执行AOF重写的过程中,程序会对数据库中的key进行检查,已过期的key不会被保存到重写后的AOF文件中

主从复制:当服务器运行在复制模式下时,从服务器的过期删除动作由主服务器控制:

  • 主服务器在删除一个过期key后,会显式地向所有从服务器发送一个del命令,告知从服务器删除这个过期key;
  • 从服务器在执行客户端发送的读命令时,即使碰到过期key也不会将过期key删除,而是继续像处理未过期的key一样来处理过期key;
  • 从服务器只有在接到主服务器发来的del命令后,才会删除过期key。

内存淘汰策略有哪些?

当Redis的内存超过最大允许的内存之后,Redis 会触发内存淘汰策略,删除一些不常用的数据,以保证Redis服务器正常运行。

  • volatile-lru:针对设置了过期时间的key,使用lru算法进行淘汰。
  • allkeys-lru:针对所有key使用lru算法进行淘汰。
  • volatile-lfu:针对设置了过期时间的key,使用lfu算法进行淘汰。
  • allkeys-lfu:针对所有key使用lfu算法进行淘汰。
  • volatile-random:针对设置了过期时间的key中使用随机淘汰的方式进行淘汰。
  • allkeys-random:针对所有key使用随机淘汰机制进行淘汰。
  • volatile-ttl:针对设置了过期时间的key,越早过期的越先被淘汰。

Redis对随机淘汰和LRU策略进行的更精细化的实现,支持将淘汰目标范围细分为全部数据和设有过期时间的数据,这种策略相对更为合理一些。因为一般设置了过期时间的数据,本身就具备可删除性,将其直接淘汰对业务不会有逻辑上的影响;而没有设置过期时间的数据,通常是要求常驻内存的,往往是一些配置数据或者是一些需要当做白名单含义使用的数据(比如用户信息,如果用户信息不在缓存里,则说明用户不存在),这种如果强行将其删除,可能会造成业务层面的一些逻辑异常。

内存淘汰策略可以通过配置文件来修改,相应的配置项是maxmemory-policy,默认配置是noeviction

缓存常见问题

Redis 中如何保证缓存与数据库的数据一致性?

  1. 先更新缓存,再更新数据库
  2. 先更新数据库存,再更新缓存
  3. 先删除缓存,再更新数据库,后续等查询把数据库的数据回种到缓存中
  4. 先更新数据库,再删除缓存,后续等查询把数据库的数据回种到缓存中
  5. 缓存双删策略。更新数据库之前,删除一次缓存;更新完数据库后,再进行一次延迟删除
  6. 使用 Binlog 异步更新缓存,监听数据库的 Binlog 变化,通过异步方式更新 Redis 缓存

以上就是实现数据库与缓存一致性的六种方式,这里前面三种都不太推荐使用,后面三种需要根据实际场景选择:

  • 如果是要考虑实时一致性的话,先写 MySQL,再删除 Redis 应该是较为优的方案,虽然短期内数据可能不一致,不过其能尽量保证数据的一致性。
  • 如果考虑最终一致性的话,推荐的是使用 binlog + 消息队列的方式,这个方案其有重试和顺序消费,能够最大限度地保证缓存与数据库的最终一致性:。

详情可以看这篇文章:缓存和数据库一致性问题

缓存雪崩

在使用缓存时,通常会对缓存设置过期时间,一方面目的是保持缓存与数据库数据的一致性,另一方面是减少冷缓存占用过多的内存空间。

那么,当大量缓存数据在短时间集体失效或者 Redis 故障宕机时,请求全部转发到数据库,从而导致数据库压力骤增,甚至宕机,从而形成一系列连锁反应,造成整个系统崩溃,这就是缓存雪崩的问题。

所以,发生缓存雪崩的场景通常有两个:

  • 大量热点key同时过期;
  • 缓存服务故障;

大量热点key同时过期

针对大量数据同时过期而引发的缓存雪崩问题,常见的应对方法有下面这几种:

  1. 均匀失效(建议):将key的过期时间后面加上一个随机数(比如随机1-5分钟)
    • 如果要给缓存数据设置过期时间,应该避免将大量的数据设置成同一个过期时间。就可以给这些数据的过期时间加上一个随机数,这样就保证数据不会在同一时间过期。
  2. 考虑用队列或者互斥锁的方式,保证缓存单线程写,但这种方案可能会影响并发量,不建议
    • 当业务线程在处理用户请求时,如果发现访问的数据不在 Redis 里,就加个互斥锁,保证同一时间内只有一个请求来构建缓存(从数据库读取数据,再将数据更新到 Redis 里。也就不会有大量的热点数据需要从数据库读取数据了),即保证缓存单线程写。
    • 实现互斥锁的时候,最好设置超时时间,防止线程出现意外一直阻塞导致其它请求也无法获取锁。
  3. 可以让热点数据永久有效(不推荐,一般都会要求必须设置过期时间),后台异步更新缓存,适用于不严格要求缓存一致性的场景。
    • 事实上即使数据永久有效,数据也不一定会一直在内存中,这是因为 Redis的内存淘汰策略 ,当系统内存紧张的时候,有些缓存数据会被“淘汰”。如果此时用户读取的是淘汰数据,那就有可能会返回空值,误以为数据丢失了。解决方式:
    • 后台频繁地检测缓存是否有效,如果检测到缓存失效了,那就从数据库读取数据,并更新到缓存。但这个频繁 的时间不好掌握,总会有时间间隔,间隔时间内就有可能导致空值的返回;
    • 用户读取数据时,发现数据不在Redis中,则通知后台线程更新缓存。后台线程收到消息后,发现数据不存在就读取数据库数据,并将数据加载到缓存。
  4. 双key策略,主key设置过期时间,备key不设置过期时间,当主key失效时,直接返回备key值。
    • 这个只是 key 不一样,但是 value 值是一样的,相当于给缓存数据做了个副本。但是在更新缓存时,需要同时更新「主 key 」和「备 key 」的数据。
    • 双 key 策略的好处是,当主 key 过期了,有大量请求获取缓存数据的时候,直接返回备 key 的数据,这样可以快速响应请求。而不用因为 key 失效而导致大量请求被锁阻塞住(采用了互斥锁,仅一个请求来构建缓存),后续再通知后台线程,重新构建主 key 的数据。
    • 但是需要同时存储两份数据,增大了内存开销

缓存服务故障

  1. 构建缓存高可用集群(针对缓存服务故障情况)。
    • 如果 Redis 缓存的主节点故障宕机,从节点可以切换成为主节点,继续提供缓存服务,避免了由于 Redis 故障宕机而导致的缓存雪崩问题
  2. 当缓存雪崩发生时,可以实行服务熔断、限流、降级等措施进行保障。
    • 服务熔断机制,就是暂停业务应用对缓存服务的访问,直接返回错误,也就不用再继续访问数据库,从而降低对数据库的访问压力,保证数据库系统的正常运行,然后等到 Redis 恢复正常后,再允许业务应用访问缓存服务。这种方式就是暂停了业务访问
    • 请求限流机制,就是只将少部分请求发送到数据库进行处理,再多的请求就在入口直接拒绝服务,等到 Redis 恢复正常并把缓存预热完后,再解除请求限流的机制。

缓存击穿

缓存雪崩是指大量热点key同时失效的情况,如果是单个热点key,一直都有着大并发访问,那么在这个key失效的瞬间,这个大并发请求就会击破缓存,直接请求到数据库,好像蛮力击穿一样。这种情况就是缓存击穿(Cache Breakdown)。

缓存击穿和前面提到的缓存雪崩产生的原因其实很相似。区别点在于:

  • 缓存雪崩是大面积的缓存失效导致大量请求涌入数据库。
  • 缓存击穿是少量缓存失效的时候恰好失效的数据遭遇大并发量的请求,导致这些请求全部涌入数据库中。

因此可以将缓存击穿视为缓存雪崩的子集,应对方案也是缓存雪崩说到的方案。

解决方案:

  1. 过期时间续期:比如每次请求的时候自动将过期时间续期一下

  2. 使用互斥锁(Mutex Key):当缓存不可用时,仅持锁的线程负责从数据库中查询数据并写入缓存中,其余请求重试时先尝试从缓存中获取数据,避免所有的并发请求全部同时打到数据库上。步骤如下:

    1. 没有命中缓存的时候,先请求获取分布式锁,获取到分布式锁的线程,执行DB查询操作,然后将查询结果写入到缓存中;

    2. 没有抢到分布式锁的请求,原地自旋等待一定时间后进行再次重试;

    3. 未抢到锁的线程,再次重试的时候,先尝试去缓存中获取下是否能获取到数据,如果可以获取到数据,则直接取缓存已有的数据并返回;否则重复上述123步骤。

  3. 逻辑过期:热点数据不设置过期时间,后台异步更新缓存,适用于不严格要求缓存一致性的场景。

对于业务中最常使用的旁路型缓存而言,通常会先读取缓存,如果不存在则去数据库查询,并将查询到的数据添加到缓存中,这样就可以使得后面的请求继续命中缓存。

但是这种常规操作存在个“漏洞”,因为大部分缓存容量有限制,且很多场景会基于LRU策略进行内存中热点数据的淘汰,假如有个恶意程序(比如爬虫)一直在刷历史数据,容易将内存中的热点数据变为历史数据,导致真正的用户请求被打到数据库层。

针对这种场景,在缓存的设计时,需要考虑到对这种冷数据的加热机制进行一些额外处理,如设定一个门槛,如果指定时间段内对一个冷数据的访问次数达到阈值,则将冷数据加热,添加到热点数据缓存中,并设定一个独立的过期时间,来解决此类问题。

比如可以约定同一秒内对某条冷数据的请求超过10次,则将此条冷数据加热作为临时热点数据存入缓存,设定缓存过期时间为30天。通过这样的机制,来解决冷数据的突然窜热对系统带来的不稳定影响。

缓存穿透

缓存穿透(cache penetration)是用户访问的数据既不在缓存当中,也不在数据库中。出于容错的考虑,如果从底层数据库查询不到数据,则不写入缓存。这就导致每次请求都会到底层数据库进行查询,缓存也失去了意义。当高并发或有人利用不存在的Key频繁攻击时,数据库的压力骤增,甚至崩溃,这就是缓存穿透问题。

缓存穿透与缓存击穿同样非常相似,区别点在于缓存穿透的实际请求数据在数据库中也没有,而缓存击穿是仅仅在缓存中没命中,但是在数据库中其实是存在对应数据的。

发生场景:

  • 原来数据是存在的,但由于某些原因(误删除、主动清理等)在缓存和数据库层面被删除了,但前端或前置的应用程序依旧保有这些数据;
  • 黑客恶意攻击,外部爬虫,故意大量访问某些读取不存在数据的业务;

缓存穿透解决方案:

  1. 缓存空值(null)或默认值
    • 分析业务请求,如果是正常业务请求时发生缓存穿透现象,可针对相应的业务数据,在数据库查询不存在时,将其缓存为空值(null)或默认值,但是需要注意的是,针对空值的缓存失效时间不宜过长,一般设置为5分钟之内。当数据库被写入或更新该key的新数据时,缓存必须同时被刷新,避免数据不一致。
  2. 业务逻辑前置校验
    • 在业务请求的入口处进行数据合法性校验,检查请求参数是否合理、是否包含非法值、是否恶意请求等,提前有效阻断非法请求。比如,根据年龄查询时,请求的年龄为-10岁,这显然是不合法的请求参数,直接在参数校验时进行判断返回。
  3. 使用 布隆过滤器快速判断数据不存在(推荐)
    • 在写入数据时,使用布隆过滤器进行标记(相当于设置白名单),业务请求发现缓存中无对应数据时,可先通过查询布隆过滤器判断数据是否在白名单内(布隆过滤器可以判断数据一定不存在),如果不在白名单内,则直接返回空或失败。
  4. 用户黑名单限制:当发生异常情况时,实时监控访问的对象和数据,分析用户行为,针对故意请求、爬虫或攻击者,进行特定用户的限制;
  5. 添加反爬策略:比如添加请求签名校验机制、比如添加IP访问限制策略等等

缓存预热

缓存预热就是系统上线后,将相关的缓存数据直接加载到缓存系统。这样就可以避免在用户请求的时候,先查询数据库,然后再将数据缓存的问题!用户直接查询事先被预热的缓存数据!

解决方案:

  1. 直接写个缓存刷新页面,上线时手工操作一下;
  2. 数据量不大,可以在项目启动的时候自动进行加载;
  3. 定时刷新缓存;

缓存降级

当访问量剧增、服务出现问题(如响应时间慢或不响应)或非核心服务影响到核心流程的性能时,仍然需要保证服务还是可用的,即使是有损服务。系统可以根据一些关键数据进行自动降级,也可以配置开关实现人工降级。

缓存降级的最终目的是保证核心服务可用,即使是有损的。而且有些服务是无法降级的(如加入购物车、结算)。

在进行降级之前要对系统进行梳理,看看系统是不是可以丢卒保帅;从而梳理出哪些必须誓死保护,哪些可降级;比如可以参考日志级别设置预案:

  1. 一般:比如有些服务偶尔因为网络抖动或者服务正在上线而超时,可以自动降级;
  2. 警告:有些服务在一段时间内成功率有波动(如在95~100%之间),可以自动降级或人工降级,并发送告警;
  3. 错误:比如可用率低于90%,或者数据库连接池被打爆了,或者访问量突然猛增到系统能承受的最大阀值,此时可以根据情况自动降级或者人工降级;
  4. 严重错误:比如因为特殊原因数据错误了,此时需要紧急人工降级。

服务降级的目的,是为了防止Redis服务故障,导致数据库跟着一起发生雪崩问题。因此,对于不重要的缓存数据,可以采取服务降级策略,例如一个比较常见的做法就是,Redis出现问题,不去数据库查询,而是直接返回默认值给用户。

分布式锁

详情请查看:Redis实现分布式锁

Redis 中如何实现分布式锁?

在 Redis 中实现分布式锁的常见方法是通过 setnx 命令 + lua 脚本组合使用。确保多个客户端不会获得同一个资源锁的同时,也保证了安全解锁和意外情况下锁的自动释放,

LUA脚本

Redis 通过 LUA 脚本创建具有原子性的命令: 当lua脚本命令正在运行的时候,不会有其他脚本或 Redis 命令被执行,实现组合命令的原子操作。

在Redis中执行Lua脚本有两种方法:evalevalshaeval命令使用内置的 Lua 解释器,对 Lua 脚本进行求值。

//第一个参数是lua脚本,第二个参数是键名参数个数,剩下的是键名参数和附加参数
> eval "return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}" 2 key1 key2 first second
1) "key1"
2) "key2"
3) "first"
4) "second"

lua脚本作用

  1. Lua脚本在Redis中是原子执行的,执行过程中间不会插入其他命令。
  2. Lua脚本可以将多条命令一次性打包,有效地减少网络开销。

使用Redis + lua方式可能存在的问题?

  1. 不可重入性。同一个线程无法多次获取同一把锁
  2. 不可重试。获取锁只尝试一次就返回false,没有重试机制
  3. 超时释放。锁超时释放虽然可以避免死锁,但如果是业务执行耗时较长,也会导致锁的释放,存在安全隐患
  4. 主从一致性。如果Redis是主从集群,主从同步存在延迟,当主机宕机时,从成为了主,但可能存在从此时还未完成同步,因此从上就没有锁标识,此时会出现线程安全问题。

分布式锁在未完成逻辑前过期怎么办?

若锁在未完成逻辑前就过期,此时可能会产生数据不一致的问题,因为锁过期了,此时如果再出现一个客户端争抢锁,即可拿到锁然后同时进行业务操作,这等于锁失效了。

此时可以在逻辑执行过程中定期续期锁,确保锁在处理过程中不会过期。

业界出了一个看门狗机制来防止这种情况的产生。理论很简单,在抢到锁之后,后台会有一个任务,定时向redis进行锁的续期、比如锁的过期时间是 305,可以每过三分之一时长(30/3)10s后就去redis 重新设置过期时间为 30s

在锁被释放的时候,就移除这个定时任务。

什么是Reddsion?

RLock是Redisson分布式锁的最核心接口,继承了concurrent包的Lock接口和自己的RLockAsync接口,RLockAsync的返回值都是RFuture,是Redisson执行异步实现的核心逻辑,也是Netty发挥的主要阵地。

redisson 是一个类库,封装了很多 redis 操作,便于我们使用。其实现的分布式锁就引入了看门狗机制,具体原理和上面所述的一致,并且 redisson 支持可重入锁,即同一个线程可以多次获取同一个分布式锁,而不会导致死锁。

实现方法如下:在获取锁时,检查当前锁的唯一标识是否已经属于当前线程。

  • 如果是,则增加一个重入计数器。
  • 释放锁时,减少重入计数器,只有当计数器为0时才真正释放锁。

说说 Redisson 分布式锁的原理?

Redison 是基于 Redis实现的分布式锁,实际上是使用 Redis 的原子操作来确保多线程、多进程或多节点系统中,只有一个线程能获得锁,避免并发操作导致的数据不一致问题

  1. 锁的获取:Redisson 使用Lua 脚本,利用 exists+ hexists+ hincrby 命令来保证只有一个线程能成功设置键(表示获得锁)。同时,Redisson 会通过 pexpire命令为锁设置过期时间,防止因宕机等原因导致锁无法释放(即死锁问题)。
  2. 锁的续期:为了防止锁在持有过程中过期导致其他线程抢占锁,Redisson 实现了锁自动续期的功能(看门狗机制)。持有锁的线程会定期续期,即更新锁的过期时间,确保任务没有完成时锁不会失效。
  3. 锁的释放:锁释放时,Redisson也是通过 Lua 脚本保证释放操作的原子性。利用 hexists+del 确保只有持有锁的线程才能释放锁,防止误释放锁的情况。Lua 脚本同时利用 publish 命令,广播唤醒其它等待的线程。
  4. 可重入锁:Redison 支持可重入锁,持有锁的线程可以多次获取同一把锁而不会被阻塞。具体是利用 Redis 中的哈希结构,哈希中的 key 为线程ID,如果重入则 value + 1,如果释放则 value - 1 ,减到0说明锁被释放了,则 del 锁。

Redisson 看门狗(watch dog)机制了解吗?

Redisson 的看门狗(watchdog)主要用来避免 Redis 中的锁在超时后业务逻辑还未执行完毕,锁却被自动释放的情况。它通过定期刷新锁的过期时间来实现自动续期

主要原理:

  1. 定时刷新:如果当前分布式锁未设置过期时间,Redisson基于 Netty 时间轮启动一个定时任务,定期向 Redis 发送命令更新锁的过期时间,默认每 10s发送一次请求,每次续期 30s.
  2. 释放锁:当客户端主动释放锁时,Redison 会取消看门狗刷新操作。如果客户端宕机了,定时任务自然也就无法执行了,此时等超时时间到了,锁也会自动释放。

什么是RedLock?

Red Lock,又称为红锁,是一种分布式锁的实现方案,旨在解决在分布式环境中使用 Redis 实现分布式锁时的安全性问题。一般情况下,我们在生产环境会使用主从+哨兵方式来部署 Redis。如果我们正在使用 redis 分布式锁,此时发生了主从切换,但从节点上不一定已经同步了主节点的锁信息,所以新的主节点上可能没有锁的信息。此时另一个业务去加锁,一看锁还没被占,于是抢到了锁,开始执行业务逻辑。此时就发生了两个竞争者同时进入临界区操作临界资源的情况,可能就会发生数据不一致的问题。所以 Redis 官方推出了红锁,避免这种状况产生,它主要解决的问题就是当部分节点发生故障也不会影响锁的使用和数据问题的产生,。

相关文章:

Redis是如何进行内存管理的?缓存中有哪些常见问题?如何实现分布式锁?

Redis内存管理 Redis的内存用完了会怎样? 如果达到设置的上限,Redis的写命令会返回错误信息(但是读命令还可以正常返回)。 也可以配置内存淘汰机制,当Redis达到内存上限时会冲刷掉旧的内容。 Redis如何做内存优化? 可以好好利用Hash,list,sorted set,set等集合类型数据,…...

5 遥感与机器学习第三方库安装 - 详解

5 遥感与机器学习第三方库安装 - 详解pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", monospace !important…...

移远OPENCPU笔记

OPENCPU 支持的操作系统:Linux、ThreadX 芯片平台:国产ASRqq:505645074...

2025.9.16——1绿

普及+/提高 P3155 [CQOI2009] 叶子的染色 昨天用贪心写只拿了部分分,wpmx告诉我要用DP,但当时因为要赶作业没时间写。 今天补上了。...

Unity游戏开发:互动小游戏的技术实现与运营盈利之道

在数字化娱乐飞速发展的当下,互动小游戏凭借其独特的趣味性与强交互性,深受广大用户喜爱。从热闹非凡的线下活动,到流量庞大的线上平台,互动小游戏都展现出了强大的影响力与商业价值。在众多游戏开发引擎中,Unity以其卓越的性能和广泛的适用性,成为了互动小游戏开发的首选…...

如何实现主线程捕获子线程异常

一、基础概念...

LGP5688 [CSP-S-JX 2019] 散步 学习笔记

LGP5688 [CSP-S-JX 2019] 散步 学习笔记 Luogu Link 前言 一题多解这一块。 题意简述 \(n\) 个人在公园内散步。公园可以看作一个环形,上有 \(m\) 个出口,按逆时针顺序记作 \(1\) 号口到 \(m\) 号口。 环总长 \(V\) 米。记 \(a_i\) 为 \(i\) 号出口从 \(1\) 号口按逆时针走到…...

少儿练字控笔字帖

握笔和控笔是写好字的基础 准备了一些基本的控笔练习字帖少儿练字控笔字帖下载...

架构师必备:缓存更新模式总结

大家好,我是Java烘焙师。如何更新缓存和DB、做到性能和一致性的取舍,是一个很常见的话题。下面结合笔者的经验和思考,系统性地总结一下缓存更新模式,讲透讲明白。 1、旁路缓存(cache-aside) 实现方案查询:先查缓存,查不到缓存时再查DB,并把DB内容写入缓存、设置合适的…...

为什么不能在try-catch中捕获子线程的异常 ?

一、基础概念...

sensitive-word 敏感词性能提升14倍优化全过程 v0.28.0 - 实践

sensitive-word 敏感词性能提升14倍优化全过程 v0.28.0 - 实践pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New"…...

2025 PHP 开发者必看得 25 个容易犯的常见错误 90% 的开发者都踩过

2025 PHP 开发者必看得 25 个容易犯的常见错误 90% 的开发者都踩过 前言 PHP 发展到今天,新特性层出不穷,最佳实践也在不断更新。写出干净、高效、好维护的代码,对每个 PHP 开发者来说都很重要。 这篇文章总结了 PHP 开发中最容易踩的坑,以及对应的解决方案。 不管你是刚入…...

一款带有AI功能的markdown工具

大家好,我是晓凡。 写在前面 关注晓凡的同学都知道,晓凡是一名程序员。 平常有空的时候会写写博客,将一些技术干货或者踩过的坑分享给大家。 和我一样写技术博客的小伙伴都知道,平常素材来源太杂:脑子里突然而来的灵感 群里的一张梗图 随手的一张截图 读书看到的一段文字 …...

45万亿!中国智驾的新风口来了

微信视频号:sph0RgSyDYV47z6快手号:4874645212抖音号:dy0so323fq2w小红书号:95619019828B站1:UID:3546863642871878B站2:UID: 3546955410049087 添加图片注释,不超过 140 字(可选)2025年L4智驾场景化应用加速发展,亿欧汽车研究院认为2035年中国L4级别及以上智能驾驶…...

apache poi 导出繁琐的excel表格

apache poi 导出繁琐的excel表格pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", monospace !important; fon…...

Ubuntu Server SSH 连接

使用 SSH 连接(最推荐的方式) 这是最专业、最稳定的方式,特别适合服务器环境:一、在 Ubuntu Server 中确保 SSH 服务已安装并运行: sudo apt update sudo apt install -y openssh-server sudo systemctl enable ssh sudo systemctl start ssh 二、获取虚拟机的 IP 地址: …...

利用竞态条件轻松上传Web Shell

本文详细介绍了如何通过竞态条件漏洞上传PHP Web Shell,使用普通Burp Intruder工具而非Turbo Intruder,包含完整的攻击步骤、PHP代码分析和实战配置,适合初学者理解文件上传漏洞的利用方式。通过竞态条件上传Web Shell(简易方法) 方法特点 本攻击使用普通Burp Intruder而非…...

我亲眼目睹我上海的家长朋友陷进去了

微信视频号:sph0RgSyDYV47z6快手号:4874645212抖音号:dy0so323fq2w小红书号:95619019828B站1:UID:3546863642871878B站2:UID: 354695541004908701我发现在网上很多人都在说不要内卷了,不要卷了,因为我们快被自己人卷死了。我这人天生迟钝,后知后觉的,所以总是赶不上时…...

蔚小理的辅助驾驶,谁最拉跨?

微信视频号:sph0RgSyDYV47z6快手号:4874645212抖音号:dy0so323fq2w小红书号:95619019828B站1:UID:3546863642871878B站2:UID: 3546955410049087汽车诞生以来,从来没试过像现在这样,一台车可以在买回来之后,不花一分钱,表现就能获得提升。而且还不只是那些贵的车型,就…...

C 语言的 printf() 函数

概述 printf() 的作用: Print formatted output to the standard output stream. printf() 的函数原型: int printf( const char *format, argument1, argument2, ... );printf() 函数的基本格式: printf(格式字符串, 待打印项列表);或者写为: printf(格式字符串, 待打印项 1,…...

【GitHub每日速递 250915】3 个宝藏开源项目:超长语音合成、算法学习库、自托管软件导航,开发者速收

原文:【GitHub每日速递 250915】3 个宝藏开源项目:超长语音合成、算法学习库、自托管软件导航,开发者速收 VibeVoice:挑战传统TTS,最长90分钟多角色语音合成神器来袭! VibeVoice 是一个长对话文本转语音模型的开源项目。简单讲,它能将连续的文本内容自然地转换成语音,支…...

C 语言头文件

两种写法: #include <stdio.h> // 编译系统在系统头文件所在目录搜索 #include "stdlib.h" // 编译系统首先在当前的源文件目录中查找 stdlib.h,找不到的话,再转向系统头文件所在目录搜索引用系统头文件时,使用两种形式都可以,但是 #include <> 的…...

AFL++环境搭建

第 1 步:更新软件源并安装基本工具 首先更新软件包列表 sudo apt update 升级现有软件包 sudo apt upgrade -y 安装基本编译工具(包括gcc) sudo apt install -y build-essential 安装Python3和相关工具 sudo apt install -y python3 python3-dev python3-pip 创建python符号…...

晚安

cai~一定是做梦,不然还能是什么,对不对? 总之就是梦到一年前的自己对我说话: 第一句:变菜了 第二句:本来就挺菜 然后开始大笑,前车窗上晃动着一双眼睛,小,但是不瞎。...

读人形机器人12体育领域

读人形机器人12体育领域1. 体育领域 1.1. 在体育竞技这个不断挑战人类极限的领域,人形机器人正以教练和训练助手的身份进入 1.2. 由金属材料、代码和AI技术打造的机器人,正在革新运动员的训练方式、恢复过程和竞技表现 2. 个性化训练计划 2.1. 追求卓越的运动表现既是一门科学…...

【QT】C++基础

前言 目前打算一周内学习QT的基本使用,从此篇"C++基础"文章开始记录分享学习经验。 C++及其各种定义C++是面向对象的,什么是面向对象,什么是面向过程? 举例 计算a+b的值: C语言,直接计算a+b;C++,先将a+b封装,封装到类里面形成一个方法,然后再通过这个类去…...

安全研究者的MCP服务器宝典:BugBounty工具集锦

本资源库汇集了多个专为漏洞赏金、渗透测试和信息安全研究设计的MCP服务器,提供一站式安全工具整合,帮助安全专业人员高效开展授权安全测试和研究工作。BugbountiesMCP 一个专为漏洞赏金、渗透测试和信息安全研究设计的Model Context Protocol (MCP) 服务器综合列表资源库。 …...

Unity的VisualStudio工程链接不同步、显示异常处理方法

前段时间,在某次更新项目工程后,原本好好的VS工程,变得不认Unity_Editor的宏、Using显示不正确了,一般来说,这个在Unity使用中,很常见,通用解决办法是:在Unity的Extrenal Tool里有一个Regenerate的按钮,直接重新生成解决方案。 关了Unity工程,到路径里把sln和proj这些…...

Java 高性能与可维护性实战:从语言特性到工程化全链路

一、设计目标:为什么“快”和“稳”必须同时追 在真实业务里,“快”(吞吐、延迟)与“稳”(可维护、可演进)往往拉扯。高性能带来的复杂度不应成为维护负担,而可维护的抽象也不应牺牲关键路径。可落地的目标是:可预测性能:接口在 SLA 下延迟稳定,P95/P99 可控。渐进式…...

二叉树的递归遍历

前中后序的递归遍历 递归算法的三要素确定递归函数的参数和返回值:要确定哪些参数是递归过程中需要处理的,需要处理的就在递归函数里面加上这个参数;然后确定每次返回的递归值是什么; 确定终止条件:必须写终止条件;如果不写终止条件就会栈溢出; 确定单层递归的逻辑: 确…...

我的大学成长与规划

大家好,我是李思慧,一名计算机科学与技术专业的大二学生。生活里,我是个爱折腾的人,喜欢在文字的世界里遨游,也热衷于用视频和图片记录生活、表达创意。 在信工分团委宣传部当干事到现在的部长,是我成长很快的一段时光。从最开始跟着学长学姐学习写各种活动和通知的推文,…...

【笔记】拉格朗日插值

拉格朗日插值的推导对于一个 \(n\) 次多项式 \(f(x) = \sum_{k = 0}^n a_kx^k\),我们只要知道它在 \(n+1\) 个不同点处的取值,就可以进一步解出它的系数 但使用高斯消元法的时间复杂度是 \({\cal O}(n^3)\) 的,如果我们只是想知道这个多项式在某一点 \(x\) 处的值,希望有复…...

自定义渲染管线(Unity Cocos)

参考链接: 团结引擎 - 手册: 在自定义渲染管线中创建简单渲染循环 可定制渲染管线(Deprecated) | Cocos Creator Custom SRP - Custom Render Pipeline | 三叔的数字花园 自定义渲染管线_Unity SRP从零搭建一套图形渲染管线_UWA学堂(翻译的Catlik,还收费) Unity Custom S…...

这是一个测试

这是一个测试...

文献阅读 | Survey of Hallucination in Natural Language Generation

问题描述 本文主要讲了NLG中的幻觉现象 幻觉定义:模型生成不忠实于源内容或无意义的文本 幻觉分类:内在幻觉(矛盾、完全错误的)、外在幻觉(无法被验证) 幻觉危害:隐私泄露 成因:评估指标:统计 metric:基于 n-gram 重叠,如 PARENT(结合源和目标)、Knowledge F1(对…...

技术 | LLaMA Factory微调记录重修版

之前投的那篇教程我自己回看一遍都不太搞得明白,从新梳理一遍 1. 云服务器准备 恒源云 (gpushare.com) 配置建议:GPU: RTX 3090 (24GB) 或 RTX 4090 (24GB) 系统: Ubuntu 20.04/22.04 存储: 至少 50GB 空间2. 环境检查与初始化 # 检查GPU状态 nvidia-smi# 检查系统信息 df -h…...

支付中心的钱包类业务应该怎么设计

钱包类业务在支付行业里有一些比较固定的模式(无论是支付宝余额宝、微信零钱,还是 Stripe Balance / Paytm Wallet),基本设计目标是:余额和资金安全:必须有严格的账实一致、幂等和防篡改能力。高并发读写:充值/消费/退款频繁,要求快速的扣减和回滚能力。清晰的流水:任…...

MySQL索引浅析

NORMAL:普通索引,仅用于加速查询,允许字段值重复。 UNIQUE:唯一索引,不仅能加速查询,还会强制字段值的唯一性(即该字段下的值不能重复)。 FULLTEXT:全文索引,用于全文搜索场景(如文章内容的关键词检索)。 SPATIAL:空间索引,用于地理空间数据类型(如 GEOMETRY、P…...

WF 2025 游记

第一次出国旅行因为错误在 EC-Final 显神威成功出线,我得以以 RCDS 随队人员的身份作为 ICPC Guest 参与 2025 ICPC World Final Baku. Day -5 ​ 被然叔拉到苏州给软件杯打工。 ​ 出站时落雨大暴,翻书包发现没有雨伞,凌乱中就看到然叔开着辆绿色 SUV 过来接我,到了苏州大…...

17.时间处理

17.时间处理日期和时间是日常编程常用的功能之一。如果没有日期和时间,会导致很多功能无法实现,例如日志记录、定时任务、时间延迟等。Go标准库提供了操作日期和时间的方法。在提到时间,还需要注意不同地区的时间会不一样,所以这里还需要考虑到不同时区、不同历法等带来的影…...

[MCP][02]快速入门MCP开发

快速入门MCP Server和MCP Client 开发,以及Client集成LLM前言 很多文档和博客都只介绍如何开发MCP Server,然后集成到VS Code或者Cursor等程序,很少涉及如何开发MCP Host和MCP Client。如果你想要在自己的服务中集成完整的MCP功能,光看这些是远远不够的。所以本文及后续的M…...

numpy入门

numpy 基本属性 import numpy as np arr = np.arange(15).reshape((3,5)) print(arr) # [[ 0 1 2 3 4],[ 5 6 7 8 9],[10 11 12 13 14]] print(type(arr)) # 类型 <class numpy.ndarray> print(arr.shape) # 形状元组 (3, 5) print(arr.ndim) # 维度 2 print(arr…...

【simpleFOC】一个电机如何模拟不同旋钮的手感反馈?

原文链接:https://mp.weixin.qq.com/s?__biz=MzU1NjEwMTY0Mw==&mid=2247597033&idx=2&sn=e92f8f1dec8b363aa209788354f8fa64&chksm=fad1130bfafd0b9af53b0f110e354d8772f6c5a0d98735690d1c0c75d0c3c75785ea1041ea1a&scene=27概述simpleFOC可以实现对各种…...

第一周作业2

我叫陈俊杰,今年19岁,目前是一名计算机相关专业的学生。很高兴能在博客园与大家分享我的学习与生活经历。 兴趣爱好 我热爱运动,尤其是羽毛球、篮球、游泳和攀岩。这些运动不仅让我保持了健康的体魄,也培养了我的团队协作能力和坚持不懈的精神。此外,我也喜欢探索新技术,…...

第一次课堂作业

大家好!我是一名数据科学与大数据技术专业的大三学生。如果用一句话形容现在的自己,那就是 “正处在专业技能积累的爬坡期,一边为过去的基础不扎实查漏补缺,一边对未来的技术方向满怀期待”。这篇博客想和大家聊聊我的故事、我的技能现状,以及我为接下来的学习和未来发展制…...

[高可用/负载均衡] Ribbon LoadBalancer: 开源的客户端式负载均衡框架

0 序言某项目上,原先为自建的数据库集群提供了负载均衡IP服务器(简称: ELB IP Server),客户端的数据库请求URL都统一走ELB IP。但随着业务量的增长,识别到一个严峻的现实:其一,考虑到未来的业务增长情况,云厂商提供的 ELB IP Server 云服务的入网带宽必将完全无法满足本项…...

梦话周记

忘记是哪天了。 傍晚,暗蓝色的天空,水雾,朦胧的光晕。 此时的天空与以往理解的深邃可谓是一点关系都没有,它的深邃不再来自于天空,而是来自于大海。什么地方是深蓝色的,湿润的,广阔的?海洋。 其实气体与液体有很多相似之处,它们都有浮力,都是流体。我们是不是也生活在…...

【电机控制】无刷电机结构阐述---磁极数、槽数

一、磁极数P与槽数N 1.磁极数P 定义:转子上磁极的数量,既转子上磁钢的数量,磁钢均匀的排列在转子上磁铁必定是NS极成对使用,所以极数必然是偶数。 2.槽数N 定义:定子铁芯的槽数量,既定子上的电磁铁极数量,每一个槽上都饶有一组线圈,如上图有12个槽,所以是12N电机由于无…...

金刚怒目是我哭

金刚怒目是我哭是你们太不善良,还是我太不正常马喽马基米退圈了。我下载的its my cry没了,my mujuca也只有前三集 这个可以说是我的入坑作 确实有点刻意 反正不是日常向 即使现在看来也是无可厚非的 但还是爆了 我应该说戾气很重吗 杂食党,,,理中客,和稀泥 说不出话 雨...

nginx使用默认端口80作为服务端口

背景:http默认端口是80,配置nignx.conf,希望服务url直接输入ip不用输入端口 给server配置80以及加default_server ,老是报错,后面发现是因为 include /etc/nginx/sites-enabled/*; 这个配置的server段占用了80 解决办法:把默认配置/etc/nginx/sites-available/default 里…...