java中时钟回拨,【追光者系列】HikariCP源码分析之evict、时钟回拨、连接创建生命周期...

本文详细分析了HikariCP连接池中关于时钟回拨的处理,以及连接的创建、生命周期管理和驱逐策略。通过源码解读,阐述了`evict`如何标记和处理不可用连接,以及`getConnection`时的检查机制。同时,文章还探讨了`connectionTimeout`、`maxLifetime`参数的作用,并介绍了`softEvictConnection`的软驱逐机制。此外,还提及了HikariCP在时钟回拨情况下的应对策略。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

摘自【工匠小猪猪的技术世界】 1.这是一个系列,有兴趣的朋友可以持续关注 2.如果你有HikariCP使用上的问题,可以给我留言,我们一起沟通讨论 3.希望大家可以提供我一些案例,我也希望可以支持你们做一些调优

概念

evict定义在com.zaxxer.hikari.pool.PoolEntry中,evict的汉语意思是驱逐、逐出,用来标记连接池中的连接不可用。

getConnection

在每次getConnection的时候,borrow连接(PoolEntry)的时候,如果是标记evict的,则会关闭连接,更新timeout的值,重新循环继续获取连接

如下我们聚焦一下源码,hardTimeout默认值是30000,这个值实际上就是connectionTimeout,构造器默认值是SECONDS.toMillis(30) = 30000,默认配置validate之后的值是30000,validate重置以后是如果小于250毫秒,则被重置回30秒。

connectionTimeout  This property controls the maximum number of milliseconds that a client (that's you) will wait for a connection from the pool. If this time is exceeded without a connection becoming available, a SQLException will be thrown. Lowest acceptable connection timeout is 250 ms. Default: 30000 (30 seconds)

关闭连接这块的源码如下,从注释可以看到(阅读hikari源码强烈建议看注释),这是永久关闭真实(底层)连接(吃掉任何异常):

吃掉体现在quietlyCloseConnection,这是吃掉Throwable的

<think>嗯,用户问的是“不定时过期的LRU算法”,这我得先理清楚LRU本身的基础,再考虑不定时过期的部分。LRU是Least Recently Used的缩写,也就是最近最少使用算法,常用于缓存淘汰策略。它的核心思想是,当缓存空间不足时,优先淘汰最久未被访问的数据。 那不定时过期是什么意思呢?可能是指缓存中的每个数据项都有一个独立的过期时间,而这个时间可能不是固定的,或者每个项的过期时间可以不同。这时候,传统的LRU需要结合这样的过期机制,可能就形成了某种变种算法,比如结合了TTL(Time To Live)的LRU。 我需要先回顾一下标准LRU的实现方式。通常有两种方法:一种是使用链表和哈希表结合的方式,当访问某个数据时,将其移到链表头部,这样链表尾部就是最近最少使用的元素;另一种是记录每个元素的最后访问时间戳,淘汰时选择时间戳最早的那个。 当引入不定时过期后,每个缓存项可能有一个不同的过期时间。这时候,除了考虑访问时间,还要检查是否过期。比如,一个即使最近被访问过,但如果已经过期的项,也应该被淘汰。这时候,可能需要将LRU的淘汰策略和过期时间结合起来处理。 接下来需要考虑的是,如何在这种混合策略下高效管理缓存。可能的实现方式包括: 1. 每个节点除了记录访问时间,还要记录过期时间。当需要淘汰时,先检查哪些项已经过期,优先淘汰过期的项;如果没有过期项,再按LRU策略淘汰最久未使用的项。 2. 或者在每次访问缓存项时,不仅更新访问时间,还要检查是否过期,如果过期则直接移除,不再提供服务,并触发加载新数据。 但这样可能会增加每次访问的开销,因为需要检查过期时间。这时候需要平衡效率和准确性。例如,是否需要在每次访问时都严格检查过期时间,还是可以定期批量清理过期项。 另外,不定时过期的LRU可能在实际应用中需要考虑的问题包括: - 如何高效地管理具有不同过期时间的项。例如,使用优先队列(比如最小堆)来跟踪最近的过期时间,这样可以快速找到即将过期的项,但这可能与LRU的链表结构结合使用,增加复杂度。 - 当系统时间发生变化时(比如服务器时间被调整),如何处理过期时间的计算,这可能带来一些问题,需要确保时间计算的正确性。 举个例子,假设有一个缓存项A在10:00被访问,过期时间是30分钟后,即10:30。而另一个缓存项B在10:15被访问,过期时间是20分钟后,即10:35。此时如果缓存满了,当需要淘汰时,如果当前时间是10:25,那么A已经接近过期,而B还有10分钟。这时候应该优先淘汰A还是按照LRU的策略? 可能需要根据具体的业务需求来决定。比如,如果过期时间更重要,那么即使A是更久未被访问,但因为它即将过期,可以先淘汰它。而如果访问频率更重要,可能还是按LRU处理。 不过,用户的问题中的“不定时过期”可能指的是每个项的过期时间可能不同,而不仅仅是固定时间间隔。这时候,可能需要将过期时间和LRU结合起来,设计一种混合的淘汰策略。 可能的实现步骤: 1. 每个缓存项保存最后访问时间和过期时间(可以是绝对时间或相对时间)。 2. 当访问一个项时,更新其最后访问时间,并可能重置过期时间(根据业务需求,比如每次访问后延长过期时间)。 3. 淘汰策略分为两步: a. 首先扫描所有项,移除所有已过期的项。 b. 如果仍有空间不足,再按照LRU策略淘汰最久未使用的项。 这样的方式结合了过期淘汰和LRU,但可能需要较高的维护成本,尤其是在频繁扫描过期项时,或者需要高效的数据结构来管理。 另一种方法是,将过期时间和LRU的顺序结合起来,比如维护两个优先级队列:一个按过期时间排序,另一个按最后访问时间排序。当需要淘汰时,先看是否有过期的项,如果有则淘汰;否则淘汰最久未使用的。 此外,可能还需要处理并发访问的问题,比如多线程环境下的锁机制,确保数据一致性。 可能的应用场景包括网页缓存,其中不同页面的缓存时间由服务器动态指定;或者API响应缓存,根据接口的不同设置不同的缓存过期时间。 总结来说,不定时过期的LRU算法是在标准LRU的基础上,为每个缓存项引入独立的过期时间,使得淘汰策略同时考虑访问时间和过期状态,从而更灵活地管理缓存。实现时需要结合两种机制,可能采用分阶段淘汰(先过期后LRU)或混合排序的方式,并选择合适的数据结构来优化性能。</think>### 不定时过期的 LRU 算法介绍 #### 1. **基础概念回顾** - **LRU(Least Recently Used)**:核心思想是淘汰**最久未被使用**的数据。例如,缓存满时,优先移除长时间未被访问的项。 - **不定时过期**:每个缓存项有**独立的过期时间**(例如:项 A 在 5 分钟后过期,项 B 在 10 分钟后过期)。 #### 2. **算法核心思想** 不定时过期的 LRU 是 **LRU + TTL(Time To Live)** 的结合体,淘汰策略需同时满足: 1. **过期优先**:已过期的项无论访问时间如何,必须淘汰。 2. **LRU 兜底**:若缓存未满且无过期项,则按 LRU 规则淘汰最久未使用的项。 --- #### 3. **实现步骤** **步骤 1:数据结构设计** - 每个缓存项需记录: - **最后访问时间**(用于 LRU 排序)。 - **过期时间**(绝对时间戳或相对时长)。 - 使用**双向链表 + 哈希表**管理 LRU 顺序,同时维护**过期时间的最小堆**(快速找到最早过期的项)。 **步骤 2:访问缓存项** - 若命中缓存: 1. 检查是否已过期: - **是**:直接移除,重新加载。 - **否**:更新最后访问时间,移动至链表头部。 - 若未命中:加载新数据并插入链表头部,记录过期时间,更新最小堆。 **步骤 3:淘汰策略** 1. **主动清理**:定期扫描最小堆,移除所有过期项。 2. **被动触发**: - 当缓存满且需要插入新数据时: - 先清理所有过期项。 - 若空间仍不足,按 LRU 规则淘汰链表尾部的项。 --- #### 4. **关键问题与优化** - **问题 1:如何高效管理过期时间?** - **优化方案**:使用**最小堆**按过期时间排序,每次淘汰时弹出堆顶元素(最早过期的项)。 - **代价**:插入/删除堆的时间复杂度为 $O(\log n)$。 - **问题 2:如何平衡过期淘汰与 LRU 顺序?** - **混合策略**:优先清理过期项,再使用 LRU 兜底。例如: ```python def evict(): while not cache.is_full(): expired_item = heap.pop() if expired_item is None: break cache.remove(expired_item) if cache.is_full(): lru_item = linked_list.tail cache.remove(lru_item) ``` - **问题 3:时间同步与精度** - 若系统时间被修改(例如服务器时钟回拨),需使用**相对时间**计算过期时间(例如:记录“存活时长”而非绝对时间戳)。 --- #### 5. **应用场景** - **动态缓存策略**:例如 CDN 中不同资源的缓存时间由业务动态指定。 - **会话管理**:用户会话的过期时间各异,需结合活跃度和过期时间淘汰。 - **数据库查询缓存**:不同查询结果的缓存有效期根据数据更新频率设置。 --- #### 6. **示例说明** 假设缓存容量为 3,按以下操作序列演示: 1. 插入项 A(过期时间:10:30),最后访问时间 10:00。 2. 插入项 B(过期时间:10:35),最后访问时间 10:05。 3. 插入项 C(过期时间:10:25),最后访问时间 10:10。 4. 在 10:20 访问项 B,更新其最后访问时间为 10:20。 5. 插入项 D(需触发淘汰): - **步骤 1**:清理过期项(C 已过期)。 - **步骤 2**:剩余空间仍不足,按 LRU 淘汰最久未使用的项 A。 --- #### 7. **总结** 不定时过期的 LRU 算法通过**混合淘汰策略**(过期优先 + LRU 兜底),在传统 LRU 基础上增加了时间维度,适用于需要动态调整缓存生命周期的场景。其实现需权衡数据结构复杂度与性能,通常结合最小堆和双向链表来优化操作效率。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值