41 | 案例分析(四):高性能数据库连接池HiKariCP
王宝令
该思维导图由 AI 生成,仅供参考
实际工作中,我们总会难免和数据库打交道;只要和数据库打交道,就免不了使用数据库连接池。业界知名的数据库连接池有不少,例如 c3p0、DBCP、Tomcat JDBC Connection Pool、Druid 等,不过最近最火的是 HiKariCP。
HiKariCP 号称是业界跑得最快的数据库连接池,这两年发展得顺风顺水,尤其是 Springboot 2.0 将其作为默认数据库连接池后,江湖一哥的地位已是毋庸置疑了。那它为什么那么快呢?今天咱们就重点聊聊这个话题。
什么是数据库连接池
在详细分析 HiKariCP 高性能之前,我们有必要先简单介绍一下什么是数据库连接池。本质上,数据库连接池和线程池一样,都属于池化资源,作用都是避免重量级资源的频繁创建和销毁,对于数据库连接池来说,也就是避免数据库连接频繁创建和销毁。如下图所示,服务端会在运行期持有一定数量的数据库连接,当需要执行 SQL 时,并不是直接创建一个数据库连接,而是从连接池中获取一个;当 SQL 执行完,也并不是将数据库连接真的关掉,而是将其归还到连接池中。
数据库连接池示意图
在实际工作中,我们都是使用各种持久化框架来完成数据库的增删改查,基本上不会直接和数据库连接池打交道,为了能让你更好地理解数据库连接池的工作原理,下面的示例代码并没有使用任何框架,而是原生地使用 HiKariCP。执行数据库操作基本上是一系列规范化的步骤:
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
- 深入了解
- 翻译
- 解释
- 总结
HiKariCP是备受关注的高性能数据库连接池之一,其性能出众得益于编译出的字节码执行效率更高以及采用的两个数据结构——FastList和ConcurrentBag。FastList通过逆序删除和无需越界检查等优化,提升了对Statement的管理效率;而ConcurrentBag则为连接的借用和归还提供了高效支持。这些优化使得HiKariCP在高并发场景下表现突出。文章还介绍了ConcurrentBag的设计原理,使用ThreadLocal避免部分并发问题,以及其在获取和释放连接时的逻辑。总之,HiKariCP以其高性能和稳定性,成为了众多开发者的首选数据库连接池。在实际工作中,选择合适的并发数据结构对于解决特定并发问题至关重要,需要深入了解特定场景的并发特性,才能对症下药。
仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《Java 并发编程实战》,新⼈⾸单¥59
《Java 并发编程实战》,新⼈⾸单¥59
立即购买
© 版权归极客邦科技所有,未经许可不得传播售卖。 页面已增加防盗追踪,如有侵权极客邦将依法追究其法律责任。
登录 后留言
全部留言(34)
- 最新
- 精选
- 空知线程本地的连接会被窃取 这个我觉得是因为 如果 Tl里面没有空闲的 会去 sharedList查找处于 Not_In_Use的连接 这个连接可能已经在其他TL里面存在了 所以就会出现线程T2从sharedList获取到了 T1存在TL里面存放的没有使用的连接这种情况
作者回复: 厉害
2019-06-02366 - 拯救地球好累支持高性能并发的软件通常首先会关注整体的并发设计模式,并发设计模式将影响整个软件的设计架构,比如RateLimiter并非采用较为复杂的生产者消费者模式,而是用细粒度的互斥锁来实现令牌桶算法;Netty采用了Reactor模式而非阻塞的等待-通知机制的一些实现。对设计模式的考量应当根据实际需求先考虑线程分工,再从避免共享的模式考虑到一些无锁的模式,再到细粒度的锁控制,再到复杂的同步和互斥模式。 从高性能队列和高性能数据连接池中,可以看到,性能的提高通常会从几方面着手(实际场景中应当测试优于猜测,再根据阿姆达尔定律从性能瓶颈处先着手):并发设计模式;内存分配算法;缓存利用率;GC情况(有GC的语言);数据结构与算法的效率等。
作者回复: 👍
2019-08-1062 - 晓杰同问为什么线程本地的会被其他线程窃取,麻烦老师解释一下
作者回复: sharedlist和其他线程的threadlocal里有可能都有同一个连接,从前者取到连接,就相当于窃取了后者
2019-06-0218 - Simple life看了第二遍,有个疑问,在一半WEB项目中,每次请求SPRING都新建一个线程服务,所以ThreadLocal中的线程并不能重用,这块性能提升就无效了,都去COW中CAS获取可用线程了,CAS在高并发环境中表现并不好
作者回复: 多次请求之间是不共享任何数据都没有,性能提高只是一次请求范围之内
2020-08-182 - yang老师, 我看文中提到的是调用requite()释放链接的时候将这个链接添加到本地存储中。 那我想问,如果不是调用requite()方法释放连接的情况下,这个连接第一次被放入threadlocal是什么时候啊? 是第一次获取连接的时候吗?
作者回复: 只有requite的时候会放到threatlocal里
2019-06-022 - Monday又来打一次卡,配合代码和debug
作者回复: 👍🏻
2021-05-221 - poordickey这里讲的是连接池 但是很想知道一个数据库连接从拿到到归还的整个过程细节,从一个连接池拿到一个连接,connect之后,执行了SQL,并close了,归还到线程池之后又是怎么一直和数据库保持连接的呢
作者回复: 并没有真的close,你调用了一个被hack的方法
2021-01-101 - 张德强烈建议老师再讲一期
作者回复: 呵呵😄
2019-06-021 - yellow老师你好,请问释放的连接,如果没有,仅仅被保存到线程本地存储中,为什么不需要同时被重新保存到sharedList中呢? 不重新保存到sharedList中,别的线程还怎么有机会拿得到这个连接呢?
作者回复: 所有的连接,不论是否被使用,都在sharedList中
2022-05-19 - DFighting注意到了requite()的一个细节优化,自己使用完了线程后并不是直接交还给线程池,而是先问下有没有其他线程等待,如果有,那么直接分配就好,这里就减少了一个线程上下文切换带来的损失。不过这里的threadList应该只是会使用线程池的连接,不可以在这个连接上做一些自己数据的存储,因为如果这样就会给每次连接的归还时执行一次清洗工作,想来也会是一次性能的浪费吧。老师,关于连接池源码怎么看啊,像实践下今天课堂上学到的内容,但是不只如何下手
作者回复: 先了解实现原理,然后写一个最简单的程序,调试跟代码。
2019-10-16
收起评论