ConcurrentBag 听过没?好家伙高并发知识点十分密集!

你好,我是 yes。

今天给大家剖析下一个叫 ConcurrentBag 的并发集合类,对 C# 熟悉的同学应该听过这个名字,不过我今天介绍的是 HikariCP 中的 ConcurrentBag。

我们知道 SpringBoot 默认连接池就是 HikariCP,而 HikariCP 就是以快著称的,而这个快离不开 ConcurrentBag。

如果你看过很多源码你就会发现好多框架都会自定义集合类,因为 JDK 通用的集合需要照顾到很多场景,而定制化肯定优于普适化

像 HikariCP 就没有用 ArrayList 而是定义了一个 FastList,因为 ArrayList 每次 get 都会有范围检查,并且 remove 是从前往后遍历的。

而在 HikariCP 这个场景每次 get 范围检查没有必要,并且 remove 的时候从后往前遍历更好,所以就定制化了。

HikariCP 还有很多优化,这篇文章我们就谈谈其中之一,也就是今天的主角就是 ConcurrentBag 。

不过今天的目的不是为了分析 HikariCP ,而只是介绍这个集合类。

从它身上找点优化的思路,到时候像面试官问你如何设计一个连接池的时候就可以搬出来:“哎呀,我有个优化思路。”

ConcurrentBag

一般而言我们设计一个连接池的初始想法是用锁来保证线程安全,或者用一些线程安全的并发容器来存储连接。

而 HikariCP 不满足于此,它专门设计了 ConcurrentBag 用来存数据库连接,当 HikariPool#getConnection 的时候就是去 ConcurrentBag 拿连接。

ConcurrentBag 整体就是无锁设计,有三个重要的成员变量:

  • ThreadLocal 缓存,加快本地连接获取速度
  • CopyOnWriteArrayList,写时拷贝List
  • SynchronousQueue,无存储的等待队列

获取数据库连接基本流程如下:

  1. 当取连接的时候会先去 ThreadLocal 去找以前用过的连接,如果找到连接状态是可以使用的话拿直接返回。
    (ThreadLocal 是本地资源,每个线程都优先去自己本地去找,所以竞争也更少,需要遍历的连接也更少,所以速度就更快)
  2. 找不到再去 sharedList 这个共享的写时复制列表中查找可用连接。
  3. 如果再找不到,则通过 handoffQueue 等待可用的连接,如果超过一定时间则返回 null。

其实这种思想很简单。

每个线程一开始本地资源肯定是空的,然后每个线程把自己用过的连接存起来,之后优先用存着的链接。

久而久之每个线程都会有自己的本地存储的连接,这样大家都用自己的就少了竞争,那速度不就快了?

我们再来看下取连接的源码,里面还是有一些细节的。

其实应该叫借连接,因为要还的,而且也不是把连接从 ConcurrentBag 移除,只是返回一个引用罢了。

细节已经在代码上标注了,这里强调一下借连接不是移除连接,别的线程还是能通过 sharedList 找到这个连接的,无非这个连接如果被占用则状态是 STATE_IN_USE,这样别的线程就不会用这个连接了。

总体思路就是从本地找,没有的话再去每个线程都能访问的 sharedList 找,再没有就等着。

这里还有个窃取的概念,其实没什么花头,就是充分利用连接。

无非就是本来属于某个线程的本地连接,当它归还连接的时,恰巧有另一个线程从 sharedList 遍历找到这个连接,这时候连接的状态是 STATE_NOT_IN_USE,那么这个连接就会被另一个线程也保存到 ThreadLocal 中了。

这就是窃取,我们再来看下归还连接的代码,连接就是在这里保存到 ThreadLocal 中的。

我在《HikariCP数据库连接池实战》这本书中看到,归还连接的代码在 HikariCP 2.6.0 是长下面这个样子的

先停下来想想看有没有啥问题?

当前归还连接的线程需要等这个连接被其他线程取走时或者没有等待线程时才能摆脱这个循环。

但是会出现一种情况:在设置连接为可用时,这个连接已经被其他线程借走了,然后当前线程还傻傻的执行循环,而恰巧等待线程一直有,但是每次 handoffQueue.offer 就是没线程取,然后 yield ,如此往复。

这就造成明明连接已经归还了,而归还的线程还做无用功的自旋操作,所以就做优化成上面的代码,如果bagEntry.getState() != STATE_NOT_IN_USE 说明已经被别的线程借去用了,所以直接 return。

再提一提 CopyOnWriteArrayList 吧。

连接池是一个典型的读多写少的场景,所以写时复制用在此处再合适不过了。

简单的说:写操作的时候会复制当前的 list 来做修改,等修改完了再替换老的 list。

在替换之前读的线程读取的是老的 list 的数据,这样就能做到读的时候是无锁的。

写时复制的缺点就是内存的占用,因为需要拷贝一份数据,如果数据很大的话那就需要考虑内容的占用量了。

比如操作系统进程的 fork 操作也会用到写时复制,子进程和父进程一开始共享数据,当有修改的时候就会拷贝一份。

在 Redis 的 BGSAVE 命令或者 BGREWRITEAOF 命令的过程中就会 fork 子进程来进行后台操作,而此时 Redis 的哈希表扩容的负载因子就会变大,来避免 fork 期间不必要的内存写入操作 (扩容)。

最后

所以 ConcurrentBag 的优化思路就是本地缓存有的去本地缓存找连接,找不到就去公共的 sharedList 去找,还找不到就等着。

通过将连接本地存储化来减少竞争,又根据连接池读多写少的特性用 CopyOnWriteArrayList 来实现 sharedList 。

当然还有像上面 borrow 和 requite 的一些细节也值得品味,追求极致速度就需要扣细节。

更多文章可看我的文章汇总:https://github.com/yessimida/yes 欢迎 star !


我是 yes,从一点点到亿点点,欢迎在看、转发、留言,我们下篇见。

巨人的肩膀

《HikariCP数据库连接池实战》

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

推荐阅读更多精彩内容