丢失日志文件的风险与对策

实验背景:

  在备份与恢复数据库时,偶尔使用分离/附加的方法。如果在附加时丢失了或者删除了日志文件(LDF),可能会有哪些风险呢?下面通过实验来验证。


一、搭建环境

1. 创建数据库

CREATE DATABASE [db01] ON  PRIMARY

( NAME = N'db01', FILENAME = N'C:\SQLDATA\db01.mdf' , SIZE = 3072KB , FILEGROWTH = 1024KB )

LOG ON

( NAME = N'db01_log', FILENAME = N'C:\SQLDATA\db01_log.ldf' , SIZE = 1024KB , FILEGROWTH = 10%)


2. 创建表

USE db01

CREATE TABLE [dbo].[Table01](

[IntID] [int] NULL,

[CharFill] [varchar](50) NULL  )



二、使用nowait选项停止SQL Server实例(服务)造成数据丢失?

1. 添加2条记录

USE db01

insert Table01 values(1,'abcd')


CHECKPOINT


insert Table01 values(2,'hijk')


select * from Table01

  查询添加的结果,确认上述2条记录已经添加到数据库。区别是:第1条记录后面有一个检查点,此时这条记录已经被回写到MDF文件,而第2条记录还在data cache pool,等待下一个检查点才会写入MDF文件。


2. 使用nowait选项停止SQL Server实例(服务)

SHUTDOWN WITH NOWAIT


3. 转移文件后启动SQL Server服务

  删除LDF文件,再将MDF文件(这个文件我们称之为A文件”)移动到另一个文件夹

  再启动SQL Server服务,然后删除db01数据库。


4. 附加时删除LDF的链接信息

  附加时,由于找不到LDF文件,会显示“找不到”的信息。删除它,让系统重新创建一个LDF文件。

204641971.png


5. 附加数据库时报错

  继续附加数据库,出现报错信息。

204052979.png


6. 修复数据库

(1) 新建db01数据库

CREATE DATABASE [db01] ON  PRIMARY

( NAME = N'db01', FILENAME = N'C:\SQLDATA\db01.mdf' , SIZE = 3072KB , FILEGROWTH = 1024KB )

LOG ON

( NAME = N'db01_log', FILENAME = N'C:\SQLDATA\db01_log.ldf' , SIZE = 1024KB , FILEGROWTH = 10%)


(2) 替换MDF文件

  停止SQL Server服务,把上一步新建的MDF文件删除。再把最初的MDF文件(即前面所称的“A文件”)转移回来。(即用“A文件”替换上一步新建的MDF文件)。


(3)重启SQL Server服务

  重启之后,db01数据库为“可疑”状态,如果直接访问这个数据库则会报错“无法访问数据库db01。”

  执行以下命令,修复数据库。

alter database db01 set emergency


alter database db01 set single_user


dbcc checkdb('db01',REPAIR_ALLOW_DATA_LOSS)

dbcc checkdb('db01',REPAIR_REBUILD)


alter database db01 set multi_user


(4)检查数据

use db01

select * from Table01

  执行上述检查,发现只有第1条记录,丢失了第2条记录。假设这是一家银行的取款操作数据库,由于数据库shutdown with nowait并且LDF文件损坏,你的提款记录就不见了。多爽啊!


结论:

  SQL Server为了加快关机的速度,允许使用NOWAIT选项。此选项将跳过检查点操作,导致部分数据未回写到MDF文件(仅记录在LDF中)。在这种情况下,如果丢失了LDF文件,尽管可以修复数据库,却会有数据丢失。



三、未提交的事务导致不能回滚?

1. 创建事务

  使用上一步的数据库,添加一个事务。

BEGIN TRAN T1

insert Table01 values(3,'lmn')


2. 停止SQL Server

  使用“SQL Server配置管理器”停止SQL Server。


3. 转移MDF文件

  参考前面的实验,把MDF文件转转移到另一个文件夹,并删除LDF文件。


4. 修复数据库

  参考前面的实验,修复数据库


5. 检查数据

  参考前面的实验,查看修复后的数据。你将发现第3条记录已经提交(尽管它属于一个未提交的事务)!假如这是一家银行的存款操作数据库,重启数据库以后发现LDF坏了,即使你的存款操作最后撤销了,可是数据库里显示你的存款操作已经提交(存款成功)。多爽啊!


结论:

  本实验是正常shutdown,所以第3条记录遇到检查点操作而被回写到磁盘的MDF文件,然后事务日志中记录了这条insert操作需要回滚(因为这个事务未提交)。由于LDF文件已丢失,导致数据库启动时不能回滚所有未提交的事务。



四、结论

  丢失了数据库的事务日志文件,最多只能恢复到最后一个检查点。但是:

1. 在最后一个检查点之后,data cache pool中修改过的数据,将全部丢失。

2. 事务日志中未提交的事务,将无法撤销。


### 关于C语言中打开文件时出现错误的原因及解决方案 #### 文件打开失败常见原因分析 当尝试通过`fopen()`函数打开文件时,可能会遇到多种情况导致操作失败。主要因素包括但不限于: - **路径问题**:指定的文件路径不正确或不存在。 - **权限不足**:当前进程缺乏足够的权限来访问目标位置中的文件。 - **模式参数不当**:传递给`fopen()`的第二个参数(即模式字符串)不符合预期需求。 对于上述提到的现象——即使取消了Bash重定向而改用`fprintf()`仍然无法成功向文件写入数据的情况来看[^1],这可能是因为程序在执行过程中被意外中断(如按下Ctrl+C),从而未能及时刷新缓冲区所致[^2]。 #### 缓冲机制的影响 标准I/O库为了提高效率,默认情况下会对输入输出流实施一定的缓存策略。这意味着实际的数据传输并非即时发生,而是累积一定量后再一次性完成。因此,在正常流程未走完之前就强制终止应用程序的话,则有可能造成部分甚至全部待处理的数据丢失现象。 具体来说,如果使用的是行缓冲方式(适用于终端设备上的标准输出),那么只要每条记录以换行符结尾就能立即生效;但对于全缓冲情形下的磁盘文件而言,除非显式调用了诸如`fflush()`之类的API来进行手动同步,否则直到程序结束前都难以确保所有变更能够持久化保存下来。 #### 解决方案建议 针对此类问题的有效对策如下所示: 1. 在适当的位置加入`fflush(stdout)`语句以主动触发缓冲区清空动作; ```c fprintf(fp, "Some message\n"); fflush(fp); // Ensure the data is written out immediately. ``` 2. 修改源码逻辑使得即便遭遇异常状况也能妥善释放资源,比如设置信号处理器捕获SIGINT信号并优雅退出; 3. 调整编译选项改变默认行为,例如GCC可以通过定义宏 `_GNU_SOURCE` 来启用无缓冲的标准IO接口 `setvbuf()`; 4. 使用更底层的操作系统API代替高级别的封装版本,像POSIX提供的`write()`系统调用就不会涉及额外层次带来的不确定性。 综上所述,面对因突然停止而导致的日志缺失难题,除了优化编码习惯外还应考虑增强健壮性的措施,以便更好地应对不可预见的风险事件。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值