linux的dirty page回写磁盘过程中是否允许并发写入更新page?

Linux内核在回写dirty page到磁盘时,通常允许并发更新pagecache内容,但在特定条件下如stable pages时会限制。文章详细探讨了write系统调用的流程,页面引用计数变化以及writeback过程中的并发行为。

概述

众所周知Linux内核write系统调用采用pagecache机制加速写入过程,避免write系统调用长时间block应用进程,用户态进程执行write调用的时候,内核只是将用户态buffer copy到内核的pagecache当中,write系统调用就返回了,完全不需要等待数据完全写入存储设备,因为存储设备是的写入可能是个耗时很长的过程。Linuxn内核会在恰当的时机回写这个dirty page,随之而来一个问题:内核正在回写page到存储设备的过程中,是否允许应用层同时更新page内容?应用层更新page是否会被block? 本文将从理论和内核源码两个方面彻底分析该问题

内核write写入页面的流程

write
    --->filemap.c:generic_perform_write
        --->1.写入pagecache
            --->1.1 a_ops->write_begin
                --->ext4_write_begin
                    --->1.1.0 lock_page
                    --->1.1.1 wait_for_stable_page
                    --->__block_write_begin
                        --->create_page_buffers
                            --->alloc_page_buffers
                            --->attach_page_private
                                --->1.1.2 get_page
                         
### 数据提交后未持久化到磁盘的常见原因 在数据库系统中,`COMMIT` 操作并不意味着数据本身立即写入磁盘,而是确保事务的重做日志(Redo Log)或事务日志(Transaction Log)被持久化,以便在系统崩溃时能够恢复数据。数据页(Data Page)通常会在后续由数据库后台进程(如 DBWn 在 Oracle 中或 InnoDB 的后台线程)异步写入磁盘。因此,即使事务已经提交,数据可能尚未写入数据文件,以下是几种常见原因: #### 1. 数据库使用异步写入机制 大多数数据库系统为了提高性能,采用异步写入机制,将脏数据页(Dirty Pages)缓存在内存中,并由后台进程定期写入磁盘。这意味着即使事务已经提交,数据页仍可能保留在缓冲池中,而未实际写入磁盘。例如,在 Oracle 中,DBWn(Database Writer)进程负责将脏数据页写入数据文件,而不是在每次 `COMMIT` 时立即执行[^3]。 #### 2. Redo Log 缓冲区的刷新策略 在 MySQL InnoDB 引擎中,参数 `innodb_flush_log_at_trx_commit` 控制 Redo Log 的刷新策略。当设置为 `1` 时,每次事务提交都会调用 `fsync` 将 Redo Log 写入磁盘,但数据页仍然由后台线程异步刷新。这意味着即使事务提交成功,数据页仍可能未写入磁盘。此外,如果其他事务提交时触发了 Redo Log 的刷新,当前事务的日志也可能被一并写入磁盘[^1]。 #### 3. 重做日志缓冲区的共享机制 在并发环境中,多个事务共享同一个 Redo Log 缓冲区。当一个事务提交时,如果其他事务的日志已经积压在缓冲区中,该事务的 Redo 日志可能随着其他事务的刷新操作被一同写入磁盘。这种机制虽然提高了性能,但也意味着事务提交后,数据页仍可能未写入磁盘[^1]。 #### 4. 数据库日志写入与数据写入的分离 事务的 `COMMIT` 操作主要涉及日志的持久化,而不是数据页的持久化。例如,在 Oracle 中,`COMMIT` 操作仅确保 Redo Log 被写入磁盘,而数据页的写入由 DBWn 进程在后台处理。因此,即使事务提交成功,数据页可能仍驻留在内存中,尚未写入数据文件[^2]。 #### 5. 系统崩溃或断电导致数据丢失 如果在 `COMMIT` 成功后,但数据页尚未写入磁盘时发生系统崩溃或断电,数据库在重启后将通过 Redo Log 或事务日志进行恢复,确保事务的持久性。然而,在某些极端情况下(如 Redo Log 文件损坏),可能导致数据无法恢复[^1]。 --- ### 示例代码:模拟异步写入机制 以下是一个简单的伪代码示例,用于模拟数据库后台进程异步写入脏数据页的机制: ```python # 模拟数据库缓冲池 buffer_pool = {} # 模拟脏数据页队列 dirty_pages_queue = [] def modify_data(page_id, new_data): # 修改数据页并标记为脏页 buffer_pool[page_id] = new_data if page_id not in dirty_pages_queue: dirty_pages_queue.append(page_id) def commit_transaction(): # 提交事务时,确保 Redo Log 已写入磁盘 flush_redo_log_to_disk() def flush_redo_log_to_disk(): # 模拟 Redo Log 刷盘操作 print("Redo Log 已写入磁盘") def background_writer(): # 后台进程定期刷新脏数据页 while True: if dirty_pages_queue: page_id = dirty_pages_queue.pop(0) data = buffer_pool[page_id] # 模拟写入磁盘操作 print(f"将数据页 {page_id} 写入磁盘:{data}") else: # 没有脏页时休眠 time.sleep(1) ``` --- ###
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值