GaussDB(DWS)通信库libpq重构介绍(二)
1. Poller锁优化详细设计
1.1外部接口介绍
CleanConnection、pg_pooler_status、pv_total_memory_detail、pgxc_pool_reload、pg_pool_validate等pooler相关函数视图在内部实现上均有修改,但未修改接口对外表征及具体语义。
1.2 空闲连接的无锁存取
数据结构设计
图 1‑1 空闲连接存储数据结构(优化前)
图 1‑2 空闲连接数组的存入(优化前,需加锁)
图 1‑3 空闲连接数组的取出(优化前,需加锁)
如上方图片所示,优化前空闲连接的总数据结构是,不同的DB属性DBPool组成的全局DBPool List,不同DB间的连接不能复用。
每个DBPool拥有一个存储DN节点个数NodePool的Hash表,用来存储连往不同DN的空闲连接。
每个NodePool拥有一个slot指针数组,前面的freeSize是有效的连接。slot连接的取还都是加锁后操作最后一个非空指针实现的。
优化前需要加锁的位置:
- DBPool List的查找、遍历、新增(代码未实现删除操作)。
- NodePool Hash的查找、遍历、新增、删除(LW_EXCLUSIVE)。
- Slots数组的新增、删除、遍历。
图 1‑4 空闲连接存储数据结构(优化后)
无锁ringbuffer介绍,从read到max_read为可读区域,从max_read到write为正在写入但还未完全写好的区域,从write到read为可写区域,三个index数组下标的操作需为CAS操作,支持并发读写。
如上方图片所示,优化后的修改主要是将存储空闲连接的slot指针数组修改为无锁ringbuffer。
优化后去除加锁的位置:
- DBPool List的查找,遍历不加锁,新增保留加锁,不实现删除。(在不free list item的前提下可实现新增不加锁。不删除drop的DB如果影响性能,可以考虑挪到List尾的方式保留内存,不影响查找效率。)
- NodePool Hash的查找、遍历加LW_SHARED锁,新增加LW_EXCLUSIVE锁,为了保证无锁查找不做删除操作。新增仅在增删节点时使用,遍历只在视图函数中使用,不影响普通场景下查询性能。
- Slots的新增、删除、遍历(使用对无锁队列先pop再push的方法)。
2. Agent index的无锁存取
数据结构设计
图 4‑6 Agent连接存储数据结构(优化前)
如上方图片所示,优化前使用中的连接(以下称Agent连接)存储在每个线程自己的poolAgent数据结构中,因为遍历所有poolAgent的需求,每个线程又会把自己的poolAgent指针挂到全局的poolAgents指针数组。
优化前需要加锁的位置:
- 工作线程将agent指针在全局数组的存入和取出。
- 工作线程将某个节连接存入Conn*指针数组。
- 视图函数对全局poolAgents数组的遍历及对某个agent的读取。
图 4‑7 Agent连接存储数据结构(优化后)
如上方图片所示,优化后的修改点如下:
- 使用无锁ringbuffer保存可用的agent index,并发存取index不再需要加锁。
- poolAgents全局数组中每个元素除了原有的agent指针外,增加status标志,状态为RUN时,保证agent指针内存是可读的,此时其他视图函数线程可CAS修改为HOLD状态,agent此时将无法修改为idle状态,内存不会被释放,实现无锁并发读。
- Conn*指针会随时free,无法实现无锁并行读。agent中Conn*连接指针中保存的socket和remotePid信息,复制一份存入ConnDef。ConnDef与agent整体一起malloc/free,在RUN状态时,此部分内存是实时可读的。
优化后去除加锁的位置:
- agent index的并发存取。
- Conn*指针数组不再会被其他线程并发读,可以视为thread local变量,无需加锁。
- agent数组的遍历及单个agent中部分元素的并发读(需HOLD状态读保护)。
想了解GuassDB(DWS)更多信息,欢迎微信搜索“GaussDB DWS”关注微信公众号,和您分享最新最全的PB级数仓黑科技,后台还可获取众多学习资料~
- 点赞
- 收藏
- 关注作者
评论(0)