了解一下 HikariCP 连接池的逻辑

0.

最近使用 commons-pool2 遇到了各种问题,所以了解了一下其它连接池方案:

  1. 用得比较多的通用连接池(对象池)也就是 commons-pool2 了,其它都比较小众。
  2. JDBC 连接池真是百花齐放。
  3. Netty RPC 连接池看起来没什么比较通用的库,异步连接池基本上是空白。(Netty 自己的 FixedChannelPool 性能上有点儿缺陷

其中看到比较有启发的也就是 HikariCP 了,代码实现很精巧, About Pool Sizing 这篇文章值得一读。

1. 连接池的大小如何设置

About Pool Sizing 一文中提到了一个 Oracle 数据库压测的例子,降低了连接数,平均时延反而降低了,吞吐量也上去了。

这个例子让我想起了之前在 redis 代理上遇到的问题,事情大致是这样,

  • 有个 redis 代理集群实例比较多(一百多个),在代理中使用 JedisPool 连接 redis 实例(最大连接数设置比较大,数百个),
  • 出问题的时候,首先是某个 redis 实例变慢了,业务方的请求量没有变化,
  • 此时 redis 代理已建立的连接用完了,根据 JedisPool 的配置会创建更多的连接,
  • 由于创建了更多连接 redis 实例变得更慢了(建立连接这个操作也挺消耗资源),基本上是不可用了,redis 代理由于阻塞了太多请求,也濒临 OOM。

总结一下,对于访问数据库的连接池来说:

  1. 连接数不能太多;
  2. 连接池不能太动态,突然建连接很可能会造成恶性循环。

现在的 RPC 框架都是异步长连接,所以不会遇到上述问题,对于比较就旧的同步阻塞框架应该也会遇到上述问题。

这篇文章还提到一个计算数据库连接数的公式,看起来只对 PostgreSQL 这类数据库借鉴意义比较大,但是接下来的“公理”值得好好理解一下:

Axiom: You want a small pool, saturated with threads waiting for connections.

HikariCP 的核心逻辑就是基于这个公理实现的,连接池的大小是固定的(也是可变的),重点是如何快速为等待的任务分配连接

在文章末尾作者也附加了一个 Caveat Lector:

Pool sizing is ultimately very specific to deployments.

如何设置连接数没有一个简单的公式,但是这篇文章的思路值得借鉴。

2. ConcurrentBag

Down the Rabbit Hole 一文介绍了 HikariCP 做的各种优化,其中比较核心的就是 ConcurrentBag,看一下代码。

使用的入口是 HikariPool 的 getConnection 方法:

代码写得很简洁。

在此之前先看一下 ConcurrentBag 主要的数据结构:

  1. sharedList 是全局的 entry 列表,在 HikariPool 的使用场景下就是全局的连接列表。
  2. threadList 顾名思义就是一个线程本地的 entry 列表,可想而知使用方式就是先查线程本地列表,再查全局列表。
  3. waiters 记录正在等待获取连接的线程数。
  4. handoffQueue 是一个等待队列,从上面两个列表获取不到 entry 时,就通过这个零队列等待其它线程归还的 entry。

再回过头来看一下 borrow 方法,很清晰的三个层次:

首先,从线程本地列表获取,一开始这个列表当然是空的,添加到本地队列的位置在归还 entry 的 requite 方法中(稍后再看)。通过 CAS 操作标志位 state 的方式判断是否可以借到该 entry。

Q: 既然是线程本地的列表,为什么还要通过 CAS 判断?不是直接拿就可以了么?
A:因为其它线程可能会来偷 entry,也就是下面的逻辑。

然后,从全局列表获取,遍历 sharedList,也是通过 CAS 操作标志位的方式判断是否可以借到该 entry,在这里获取到的 entry 有可能是别的线程本地持有的,这种策略比较形象的说法就是 Queue-stealing

(从这也能看出 HikariCP 在实现时就假设了一个前提:最大连接数不能太大,否则遍历列表的成本就很显著了。)

另外需要提一下,这种只改标识位不实际从列表中移除的方式是 A lock-free design 的关键。(可以对比一下 commons-pool2 中的 idleObjects

如果还是获取不到 entry,那么调了一下 listener.addBagItem,判断是否应该再建新的 entry。

最后,如果还是获取不到可用的 entry,那通过 handoffQueue 等待其它线程归还的或者新创建的。

用完之后归还 entry,调用的是 requite 方法:

首先,通过设置标志位归还 entry。

然后,如果有其它线程在等待 entry,那么尝试将该 entry 交给等待线程。

最后,如果没有等待的线程,那么将该 entry 放到线程本地的列表,下次就能直接获取到。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,013评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,205评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,370评论 0 342
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,168评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,153评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,954评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,271评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,916评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,382评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,877评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,989评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,624评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,209评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,199评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,418评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,401评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,700评论 2 345

推荐阅读更多精彩内容