Postgresql在SyncOneBuffer时,为什么可以不加锁判断页面是否为脏(race condition第三篇)
postgresql 判断 为什么 页面 是否 可以 加锁 condition
2023-06-13 09:11:01 时间
1 问题定义
- 在SyncOneBuffer拿到一个脏页时,决定是否需要刷脏需要拿到desc中的标志位来判断。
- 这里取标志位时没有加content lock,那么如果这里刚刚检查完不需要flush,马上并发一个写入把页面标记为脏了怎么办,会不会丢数据?
static int
SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context)
{
BufferDesc *bufHdr = GetBufferDescriptor(buf_id);
int result = 0;
uint32 buf_state;
BufferTag tag;
ReservePrivateRefCountEntry();
/*
* Check whether buffer needs writing.
*
* We can make this check without taking the buffer content lock so long
* as we mark pages dirty in access methods *before* logging changes with
* XLogInsert(): if someone marks the buffer dirty just after our check we
* don't worry because our checkpoint.redo points before log record for
* upcoming changes and so we are not required to write such dirty buffer.
*/
buf_state = LockBufHdr(bufHdr);
if (BUF_STATE_GET_REFCOUNT(buf_state) == 0 &&
BUF_STATE_GET_USAGECOUNT(buf_state) == 0)
{
result |= BUF_REUSABLE;
}
else if (skip_recently_used)
{
/* Caller told us not to write recently-used buffers */
UnlockBufHdr(bufHdr, buf_state);
return result;
}
if (!(buf_state & BM_VALID) || !(buf_state & BM_DIRTY))
{
/* It's clean, so nothing to do */
UnlockBufHdr(bufHdr, buf_state);
return result;
}
/*
* Pin it, share-lock it, write it. (FlushBuffer will do nothing if the
* buffer is clean by the time we've locked it.)
*/
PinBuffer_Locked(bufHdr);
LWLockAcquire(BufferDescriptorGetContentLock(bufHdr), LW_SHARED);
FlushBuffer(bufHdr, NULL);
LWLockRelease(BufferDescriptorGetContentLock(bufHdr));
tag = bufHdr->tag;
UnpinBuffer(bufHdr, true);
ScheduleBufferTagForWriteback(wb_context, &tag);
return result | BUF_WRITTEN;
}
2 场景举例
正常场景:heap_insert中先标记buffer为脏,后写insert的XLOG。
buffer标记脏在写xlog前,那么如果checkpoint在sync时没发现buffer为脏:
- 那么一定可以得出结论:insert的xlog还没写。
- 进一步可以得出结论:checkpoint的redo稳点一定在insert xlog位点之前。
- 进一步:这次检查点的redo位点包含这次插入的xlog。
错误场景:heap_insert中先写insert的XLOG,后标记buffer为脏。
buffer标记脏在写xlog后,那么如果checkpoint在sync时没发现buffer为脏:
- 存在可能性:插入的xlog已经在很早前就写了,但是一直没有标记。checkpoint的刷脏环节漏掉了这个buffer。
- 所以:redo位点在插入的xlog位点后,redo位点无法覆盖这次插入。
- 结论:这个检查点存在问题,如果按这个检查点恢复,redo位点后都做完了,也没有做到刚才insert的xlog;并且数据也没有sync到磁盘上,这个insert的数据彻底丢失了!
相关文章
- [译]PostgreSQL HAVING子句
- Postgresql中不支持事务块中调用plpgsql回滚(多层exception、事务块有检查点)
- Postgresql中使用CAS实现无锁队列
- POSTGRESQL 15 从等待中被驱逐的JSON新功能,只能祈祷 PostgreSQL 16
- Postgresql源码(102)子事务控制语句分析
- PostgreSQL实现按年、月、日、周、时、分、秒的分组统计
- PostgreSQL 教你如何跟踪慢查询SQL详细操作及如何优化
- postgresql中wal_level的三个参数用法说明
- 在postgresql数据库中判断是否是数字和日期时间格式函数操作
- PostgreSQL教程(十五):系统表详解
- PostgreSQL 08007: transaction_resolution_unknown 报错 故障修复 远程处理
- PostgreSQL HV00R: fdw_table_not_found 报错 故障修复 远程处理
- 安装PostgreSQL 极速上手(postgresql安装)
- PostgreSQL中文手册详解数据库操作技巧(postgresql中文手册)
- 初探PostgreSQL:走上NoSQL学习之路(postgresql学习)
- 如何在PostgreSQL中创建新用户(postgresql创建用户)
- 学习PostgreSQL数据库必备!观看高质量视频教程(postgresql视频)
- PostgreSQL工具:提高数据库管理效率的不可或缺的利器(postgresql工具)
- Postgresql安装之路:简单但又重要的指南(postgresql安装教程)
- 走进PostgreSQL:探索官网的宝藏(postgresql官网)