大云海山数据库(He3DB)源码详解:He3DB-SimpleLruReadPage

大云海山数据库(He3DB)源码详解:He3DB-SimpleLruReadPage

背景

大云He3DB 采用了先进的存储引擎和查询优化技术,能够快速处理大量数据和复杂查询。无论是 OLTP(在线事务处理)还是 OLAP(在线分析处理)场景,都能提供出色的性能表现。He3DB 具备完善的数据备份和恢复机制,能够在系统故障或数据损坏时快速恢复数据,确保业务的连续性。He3DB 支持水平扩展和垂直扩展,可以轻松应对不断增长的数据需求。He3DB 提供了严格的访问控制和数据加密功能,确保数据的安全性和隐私性。

本文基于大云He3DB,针对SimpleLruReadPage进行源码解读分享

SimpleLruReadPage

源码解读

c 复制代码
/*
ctl:指向 SLRU 控制结构的指针,包含共享状态和其他控制信息。
pageno:要读取的页面编号。
write_ok:是否允许对页面进行写操作。
xid:当前事务 ID
*/
int
SimpleLruReadPage(SlruCtl ctl, int pageno, bool write_ok,
				  TransactionId xid)
{
	LOG_FUNCTION_ENTRY();
	SlruShared	shared = ctl->shared;// 初始化共享状态

	/*  外层循环:处理 I/O 等待
    如果页面正在被其他进程读取或写入,当前进程需要等待
     */
	for (;;)
	{
		int slotno = 0;
		bool ok = false;//用于标记页面读取是否成功

		/* 尝试找到页面是否已在内存中。如果页面不在内存中,则选择一个可以替换的槽位*/
		slotno = SlruSelectLRUPage(ctl, pageno);

		/*检查选定槽位的页面编号是否与请求的页面编号匹配,并且页面状态不是空闲 */
		if (shared->page_number[slotno] == pageno &&
			shared->page_status[slotno] != SLRU_PAGE_EMPTY)
		{
			/*
			 检查页面是否正在被读取(SLRU_PAGE_READ_IN_PROGRESS)或正在被写入且不允许写操作
			 */
			if (shared->page_status[slotno] == SLRU_PAGE_READ_IN_PROGRESS ||
				(shared->page_status[slotno] == SLRU_PAGE_WRITE_IN_PROGRESS &&
				 !write_ok))
			{
				SimpleLruWaitIO(ctl, slotno);//如果页面正在被读取或写入,调用 SimpleLruWaitIO 等待 I/O 完成,然后重新检查页面状态
				
				continue;
			}

            //  页面已准备好
			SlruRecentlyUsed(shared, slotno);//标记页面为最近使用,更新 LRU 状态
			pgstat_count_slru_page_hit(shared->slru_stats_idx);//更新统计信息,记录页面命中次数
			LOG_FUNCTION_EXIT();
			return slotno;
		}

		/* We found no match; assert we selected a freeable slot */
		Assert(shared->page_status[slotno] == SLRU_PAGE_EMPTY ||
			   (shared->page_status[slotno] == SLRU_PAGE_VALID &&
				!shared->page_dirty[slotno]));

		/* 将槽位标记为正在读取(SLRU_PAGE_READ_IN_PROGRESS),并清除脏页标志 */
		shared->page_number[slotno] = pageno;
		shared->page_status[slotno] = SLRU_PAGE_READ_IN_PROGRESS;
		shared->page_dirty[slotno] = false;

		/* Acquire per-buffer lock (cannot deadlock, see notes at top) */
		LWLockAcquire(&shared->buffer_locks[slotno].lock, LW_EXCLUSIVE);

		/* Release control lock while doing I/O */
		LWLockRelease(shared->ControlLock);

		/* 从磁盘读取页面数据到槽位 */
		ok = SlruPhysicalReadPage(ctl, pageno, slotno);

		/* 将新读取的页面的 LSN(Log Sequence Number)设置为零*/
		SimpleLruZeroLSNs(ctl, slotno);

	
		LWLockAcquire(shared->ControlLock, LW_EXCLUSIVE);

//断言页面状态为正在读取,并根据读取结果更新页面状态
		Assert(shared->page_number[slotno] == pageno &&
			   shared->page_status[slotno] == SLRU_PAGE_READ_IN_PROGRESS &&
			   !shared->page_dirty[slotno]);

		shared->page_status[slotno] = ok ? SLRU_PAGE_VALID : SLRU_PAGE_EMPTY;

		LWLockRelease(&shared->buffer_locks[slotno].lock);

		/* 如果页面读取失败,调用 SlruReportIOError 报告错误 */
		if (!ok) {
			SlruReportIOError(ctl, pageno, xid);
		}
		else {
			HE3_LOG_PRINT("he3db file %s func %s line %d if statement no else",
				      __FILE__, __FUNCTION__, __LINE__);
		}

		SlruRecentlyUsed(shared, slotno);//标记页面为最近使用,更新 LRU 状态

		/* 更新统计信息,记录页面未命中次数 */
		pgstat_count_slru_page_read(shared->slru_stats_idx);

		LOG_FUNCTION_EXIT();
		return slotno;
	}
	LOG_FUNCTION_EXIT();
	return;
}

函数流程图

该函数流程图如下所示:

流程图解释

  1. 开始

    • 函数开始执行。
  2. 调用 SlruSelectLRUPage 获取槽位

    • 尝试找到页面是否已在内存中,或选择一个可以替换的槽位。
  3. 槽位页面是否匹配

    • 检查槽位的页面编号是否与请求的页面编号匹配。
  4. 页面状态是否为 READ_IN_PROGRESSWRITE_IN_PROGRESS

    • 如果页面正在被读取或写入,需要等待 I/O 完成。
  5. 调用 SimpleLruWaitIO 等待 I/O 完成

    • 等待页面的 I/O 操作完成。
  6. 重新检查页面状态

    • I/O 完成后,重新检查页面状态。
  7. 检查槽位是否为空或未脏

    • 确保槽位可以被使用。
  8. 标记槽位为 READ_IN_PROGRESS

    • 标记槽位为正在读取状态。
  9. 获取槽位锁

    • 获取槽位的独占锁,确保线程安全。
  10. 释放控制锁

    • 释放控制锁,允许其他进程访问 SLRU 缓冲区。
  11. 调用 SlruPhysicalReadPage 读取页面

    • 从磁盘读取页面数据到槽位。
  12. 设置 LSN 为零

    • 初始化页面的 LSN。
  13. 重新获取控制锁

    • 重新获取控制锁,更新页面状态。
  14. 读取是否成功

    • 检查页面读取是否成功。
  15. 更新页面状态为 VALIDEMPTY

    • 根据读取结果更新页面状态。
  16. 释放槽位锁

    • 释放槽位的独占锁。
  17. 标记页面为最近使用

    • 更新 LRU 状态。
  18. 更新统计信息

    • 记录页面命中或未命中的统计信息。
  19. 返回槽号

    • 返回槽号,函数结束。

函数调用栈

通过source insight可以查看函数的调用栈

source insight安装流程可以参考这篇文章:Source insight 工具安装及使用方法

总结

本文基于大云He3DB,针对SimpleLruReadPage进行源码解读分享。

函数名 作用
SubTransSetParent SimpleLruReadPage 函数的作用是从 SLRU 缓冲区中读取一个页面。它通过以下步骤实现:检查页面是否已在内存中。如果页面正在被读取或写入,等待 I/O 完成。如果页面不在内存中,选择一个空闲槽位。从磁盘读取页面数据到槽位。更新页面状态并返回槽号。

其余文章参考链接

大云海山数据库(He3DB)源码详解:He3DB-CLOG日志管理器函数之TransactionIdSetTreeStatus

大云海山数据库(He3DB)+AI(五):一种基于强化学习的数据库旋钮调优方法

大云海山数据库(He3DB)+AI(四):一种基于迁移学习的启发式数据库旋钮调优方法

大云海山数据库(He3DB)源码解读:海山PG 词法、语法分析

大云海山数据库(He3DB)源码详解:海山PG 空闲空间映射表FSM

大云海山数据库(He3DB)源码详解:主备复制SyncRepWaitForLSN

作者信息

公司 职位
中移苏研 助理软件研发工程师
相关推荐
Dxy123931021622 分钟前
MySQL的UPPER函数介绍
数据库·mysql
倔强的石头_24 分钟前
KingbaseES:从兼容到超越,详解超越MySQL的权限隔离与安全增强
数据库
yuezhilangniao1 小时前
mysql mogoDB pg redis-四大数据库选型-数据库对比大白话指南
数据库·redis·mysql
一 乐1 小时前
医疗保健|医疗养老|基于Java+vue的医疗保健系统(源码+数据库+文档)
java·前端·数据库·vue.js·毕设
m0_748248022 小时前
Redis 简介与安装指南
数据库·redis·缓存
Elastic 中国社区官方博客7 小时前
在 Elasticsearch 中使用 Mistral Chat completions 进行上下文工程
大数据·数据库·人工智能·elasticsearch·搜索引擎·ai·全文检索
编程爱好者熊浪9 小时前
两次连接池泄露的BUG
java·数据库
TDengine (老段)11 小时前
TDengine 字符串函数 CHAR 用户手册
java·大数据·数据库·物联网·时序数据库·tdengine·涛思数据
qq74223498411 小时前
Python操作数据库之pyodbc
开发语言·数据库·python
姚远Oracle ACE11 小时前
Oracle 如何计算 AWR 报告中的 Sessions 数量
数据库·oracle