暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

PolarDB 增量检查点"副作用"优化

PolarDB 2026-02-24
50

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进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论