简介
最近在进行 postgresql 性能排查的时候,发现 PG 在某一个时间并行执行的 SQL 变得特别慢。最后通过监控监观察到并行发起得时间 buffers_alloc 就急速上升,且低水位伴随在整个慢 SQL,一直是 buferIO 的等待事件,此时也没有其他会话的争抢。SQL 虽然不是高效 SQL ,但是其同样入参在不同时间执行时间却相差甚远。进行 SQL 优化并不能对其根除,查看了一下 shared_buffers 才 12GB。SQL 查询得表 size 就有 16GB 且没有所有和分区剪裁,也没有索引。这里就涉及得内存得环形缓冲机制了
一、什么是环形缓冲区?
环形缓冲区(ring buffer 又称循环缓冲区,Circular Buffer)是一种逻辑上首尾相接的固定大小数组结构,主要用于数据的连续读写操作。它具备高效、无碎片、空间可复用等特点
在结构上,它依赖两个核心指针:
写指针(write pointer):指向即将写入数据的位置。
读指针(read pointer):指向即将读取数据的位置。
由于缓冲区是固定大小的,写入末尾之后指针会回绕至起点,形成 “环形”行为。
二、运行机制
环形缓冲区采用一段固定长度的数组作为底层结构。其核心思想在于逻辑上将数组首尾相连,实现空间的循环复用。
例如,一个长度为 8 的缓冲区:
物理空间: [0] [1] [2] [3] [4] [5] [6] [7]
逻辑行为: ↑ ↑
read_ptr write_ptr
当 write_ptr == 7 写入后,下一步会跳转回 0 位置继续写入,实现回绕(wrap around)。

- 写入逻辑
当有数据需要写入时:
如果 (write_ptr + 1) % size != read_ptr:
将数据写入 write_ptr 所在槽位
write_ptr 前进一格(可回绕)
否则:
缓冲区已满(可能阻塞、丢弃或覆盖旧数据) - 读取逻辑
当有消费者读取时:
如果 write_ptr != read_ptr:
读取 read_ptr 所在槽位的数据
read_ptr 前进一格(可回绕)
否则:
缓冲区为空(可能阻塞或返回空) - 状态判断条件
状态 条件表达式
空 read_ptr == write_ptr
满 (write_ptr + 1) % size == read_ptr
三、触发机制说明
| 触发事件 | 条件 | 后果 |
|---|---|---|
| 缓冲区满 | 写指针追上读指针 | 阻塞 / 丢弃 / 覆盖旧数据三选一 |
| 缓冲区空 | 读指针追上写指针 | 返回空数据或阻塞等待 |
| 回绕 (wraparound) | 指针到达数组尾部 | 指针回到头部,形成循环 |
四、优缺点分析
✅ 优势
| 优点 | 说明 |
|---|---|
| 高效空间复用 | 写满后继续回绕,无需频繁申请 / 释放内存 |
| 无内存碎片 | 顺序访问性能好,适合嵌入式与内核编程 |
| 天然的 FIFO 行为 | 写入顺序即为读取顺序,适合日志、流式数据 |
| 可并发使用 (配合锁或无锁结构) | 在生产者-消费者模型中高效、安全 |
❌ 劣势
| 缺点 | 说明 |
|---|---|
| 容量固定 | 超出容量需设计溢出策略 (覆盖 / 丢弃 / 阻塞) |
| 不适合随机访问 | 只支持顺序读写,随机定位困难 |
| 指针管理复杂 | 需谨慎维护 read_ptr 和 write_ptr 的关系 |
「喜欢这篇文章,您的关注和赞赏是给作者最好的鼓励」
关注作者
【版权声明】本文为墨天轮用户原创内容,转载时必须标注文章的来源(墨天轮),文章链接,文章作者等基本信息,否则作者和墨天轮有权追究责任。如果您发现墨天轮中有涉嫌抄袭或者侵权的内容,欢迎发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。




