PolarDB 增量检查点带来的"副作用"优化
PolarDB 开启增量检查点, 要求FlushList的机制将脏页按oldest LSN排序, 确实可能对buffer修改性能产生影响,但通过多种优化机制有效缓解了这些问题 。
性能影响分析
1. 排序开销
FlushList使用双向链表维护排序,每次buffer变脏时需要插入到正确位置:
static void
append_one_buffer(BufferDesc *buf)
{
if (unlikely(polar_flush_list_is_empty()))
{
// 空列表直接插入
buf->flush_next = POLAR_FLUSHNEXT_END_OF_LIST;
buf->flush_prev = POLAR_FLUSHNEXT_END_OF_LIST;
polar_flush_ctl->first_flush_buffer = buf->buf_id;
polar_flush_ctl->last_flush_buffer = buf->buf_id;
}
else
{
// 插入到链表尾部,保持oldest LSN递增顺序
BufferDesc *tail = GetBufferDescriptor(polar_flush_ctl->last_flush_buffer);
Assert(tail->oldest_lsn <= buf->oldest_lsn);
buf->flush_prev = tail->buf_id;
buf->flush_next = tail->flush_next;
tail->flush_next = buf->buf_id;
polar_flush_ctl->last_flush_buffer = buf->buf_id;
}
}
2. 锁竞争
FlushList操作需要获取自旋锁,在高并发场景下可能成为瓶颈:
void
polar_put_buffer_to_flush_list(BufferDesc *buf, XLogRecPtr lsn)
{
SpinLockAcquire(&polar_flush_ctl->flushlist_lock);
// 设置oldest lsn并插入链表
polar_buffer_set_oldest_lsn(buf, lsn);
append_one_buffer(buf);
SpinLockRelease(&polar_flush_ctl->flushlist_lock);
}
性能优化机制
1. Fake Oldest LSN机制
为减少排序开销,PolarDB使用fake oldest LSN避免实时排序:
void
polar_set_buffer_fake_oldest_lsn(BufferDesc *buf_hdr)
{
if (XLogRecPtrIsInvalid(oldest_lsn))
{
// 使用当前插入LSN作为fake oldest LSN
polar_put_buffer_to_flush_list(buf_hdr, InvalidXLogRecPtr);
buf_hdr->polar_flags |= POLAR_BUF_OLDEST_LSN_IS_FAKE;
}
}
2. 并行刷脏优化
通过多个BGWRITER进程并行刷脏,减少锁竞争:
int
polar_get_batch_buffer(int *batch_buf, int bgwriter_flush_batch_size)
{
// 批量获取buffer,减少锁操作频率
for (num = 0; num < bgwriter_flush_batch_size; num++)
{
batch_buf[num] = buffer_id;
buffer_id = GetBufferDescriptor(buffer_id)->flush_next;
}
return num;
}
3. Copy Buffer机制
对于热点页,使用Copy Buffer避免频繁的FlushList操作:
// Copy Buffer控制结构
typedef struct BufferDesc
{
// ... 其他字段
CopyBufferDesc *copy_buffer; // 指向拷贝的buffer
uint8 polar_flags;
uint16 recently_modified_count;
} BufferDesc;
4. 智能任务调度
根据系统负载动态调整刷脏策略:
static bool
polar_flush_generate_task(XLogRecPtr consistent_lag, int lru_ahead_lap, int *flush_task)
{
// 根据延迟和LRU情况分配任务
if (flushlist_behind && !lru_behind)
{
// flushlist延迟大,优先刷脏
flush_task[FLUSHLIST_TASK] = idle_workers;
flush_task[FLUSHLRU_TASK] = 1;
}
// ... 其他调度策略
}
性能监控与调优
1. 统计信息
系统提供详细的FlushList统计信息用于性能监控:
// FlushList统计计数器
pg_atomic_uint64 insert; // 插入次数
pg_atomic_uint64 remove; // 删除次数
pg_atomic_uint64 batch_read; // 批量读取次数
pg_atomic_uint64 cbuf; // copy buffer使用次数
2. 配置参数
关键性能调优参数:
polar_bgwriter_flush_batch_size
: 控制批量刷脏大小polar_parallel_new_bgwriter_threshold_lag
: 控制并行刷脏阈值polar_lru_works_threshold
: 控制LRU和FlushList任务分配比例
Notes
FlushList的O(1)复杂度获取一致性位点的优势远大于排序开销 Copy Buffer机制有效解决了热点页的性能问题 并行刷脏和批量操作显著减少了锁竞争对性能的影响
文章转载自PolarDB,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。




