Sql Server数据库备份和恢复:原理篇

时间:2022-02-03 00:43:21

本文与您探讨为什么Sql Server有完整备份、差异备份和事务日志备份三种备份方式,以及为什么数据库又有简单模式、完整模式和大容量日志模式这三种恢复模式。本文内容适用于2005以上所有版本的Sql Server数据库 。

单就操作过程而言,Sql Server中数据库备份和恢复过程是相当简单的,可以通过Management Studio的图形界面进行操作,也可以使用几句T-SQL语句完成。但要明白备份恢复的整个过程,定制符合系统需求数据库备份方案,却需要知晓数据库的实现原理。备份和恢复是数据库的核心功能。

1 数据库原理

本节简要讨论了Sql Server数据库的存储的物理和逻辑结构,这是备份恢复的理论基础。

1.1 为什么使用数据库

为什么要使用数据库,而不是使用一个文件,然后自己写一些方法来保存数据,那是因为使用数据库我们就不用自己去实现下面这些算法上非常复杂的功能:

  • a.高效一致的结构化数据存取方法,进行复杂的任意条件组合嵌套的数据查询(关系数据库管理系统甚至为实现这一功能专门设计了一种语言:SQL)
  • b.保障数据操作的原子性和完整性
  • c.确保在服务器断电、网络中断、服务器崩溃、客户端崩溃的情况下,故障恢复后数据仍然是完整的
  • d.在服务器被完全物理摧毁的情况下,快速在新的服务器上使用远程设备上的备份将数据恢复到灾难发生之前的状态
  • e.随时将数据恢复到任意时刻或任意一个操作之前
  • ...

a 主要是功能和性能

bcde 四条都可以归结为完整性。

数据库系统的核心目标就是:在实现功能需求的基础上,保证数据完整性和数据存取性能。

1.2 Sql Server存储的物理结构和逻辑结构

1.2.1 如何实现完整性

经过很多理论研究和实践,目前公认的最有效的实现完整性的方法是:将数据的存储分为数据事务日志,在存取过程中使用来控制并发访问。

数据是实时状态。

事务日志记录了数据发生变化的过程,对于数据库数据的任何一个改变,都被作为一条记录写在了事务日志文件中。

事务是指一组操作要么全部成功要么全部不执行。

任何数据更改操作在写入到数据文件中之前,必须先将更前后的数据写入事务日志文件中,这样当事务由于用户取消、数据逻辑错误或软硬件故障中断时能够正确的回滚或前滚到正确的状态。

通过控制多用户对同一数据的并发访问。

这是事务系统最基本的原理,大多数关系数据库系统和面向对象NoSQL数据库系统都采用这种方法。

Sql Server也不例外,每个Sql Server数据库由数据文件(*.mdf)和事务日志文件(*.ldf)构成。Sql Server包括了从行到表,从页面到文件的各种粒度的锁。

1.2.2 如何实现高性能

在计算机数据存取过程中,保障性能的算法最终都可以归结为:索引和缓存。从实现层面到应用层面,Sql Server都在建立管理各种索引和各种缓存。

对于需要随机存取的数据,分页是最自然的索引方法,同时能够方便的进行缓存,实现高效率的随机存取。我们所熟悉的操作系统内存管理系统、文件管理系统都采用了分页的方法。

在Sql Server中,对于数据文件mdf,主要是随机存取,因此mdf以分页的形式进行组织管理,每个页面8KB。还进一步将8个相邻的页组成一个扩展,方便管理,类似Windows中的簇。

mdf中保存着多种类型的数据,包括表数据、索引数据和大块数据等等。每个页面只保存一种数据。

表中的每一条记录都保存在一个表数据页中,记录不能跨页,因此任何表中记录的最大长度是略小于8KB(ntext之类的大块列只计算引用的大小)的一个数值,略小是因为每个页面需要一些空间来保存页面自身的信息(页头)和记录在页面中的存储位置。

在Sql Server中,对于事务日志文件ldf,主要是顺序存取。在正常运行时,数据库管理系统定期将日志顺序写入到ldf文件;在恢复时,顺序读取ldf文件。因此,日志文件不需要分页,只要由一条条顺序存储的事务日志记录构成即可。日志记录也有缓存,定期写入到ldf文件。

尽管数据库系统是一个允许并发访问的系统,但是对事务日志的写入是串行化的,不可以并发,任何一个原子操作对应的事务日志记录在事务日志文件中都有其唯一的位置,也就是唯一的事务日志记录编号,这个编号是一直增大的,不会重复,越新的记录编号越大,存储位置也越靠后。

以上内容总结自Sql Server的产品文档Sql Server Books Online,如有兴趣可以在里面了解更详细的内容。这些就是备份和恢复的基础原理。

2 数据库备份的实现

如前所述,数据文件mdf保存了数据的实时状态,事务日志文件ldf记录了数据库中数据变化的过程。这个时候,我们要对数据库进行备份,自然而然就有两个选择:

  1. 因为mdf文件中保存了数据库中数据的实时状态,那么我们只要把mdf文件拷贝一份就实现了当前时刻的数据库备份。
  2. 既然ldf文件中保存了数据库中数据变化的整个过程,那么我们就可以把这个ldf文件备份起来,然后用这些备份的事务日志记录从头重建整个数据库,而且比至第一种方法,通过事务日志记录恢复的方法可以将数据库恢复到任一时刻。

实际上,Sql Server同时使用了这两种方法。

2.1 拷贝数据文件:数据备份

笼统的说,数据备份很简单,就是拷贝mdf文件。

但是如果数据库特别大,比如几十个G,将整个mdf文件拷贝一遍会占用大量的IO资源和磁盘空间。考虑到mdf文件内容是分页保存的,而且数据库中的数据在一定时间内不会全部发生变化,那么我们不必每次备份都把整个mdf文件都拷贝。只需要在每个页的页头中设一个bit位记录一下自从上次备份以来,这一页是否有被修改过,那么,有了上次的备份,我们只需要把那些发生了变化的页备份一下就可以。

这就是Sql Server中的完整备份差异备份

出于简化设计和简化备份恢复过程的考虑,虽然Sql Server在每个完整备份后面可以进行任意次的差异备份,但是所有的差异备份都是相对于最近一次完整备份的,而不是可能存在的上一次差异备份。

很自然的,任何差异备份之前必须有一次完整备份。

2.2 切割不断变大的事务日志文件:事务日志备份

事务日志文件ldf记录了数据库中数据变化的过程。想象一下,数据库中表的每条记录不停的发生变化,而事务日志要记录变化前和变化后的数据,如果一个数据库的mdf文件有1G,那么它的ldf文件呢,非常有可能是几个G。

对于频繁发生数据修改的数据库,事务日志文件ldf的增长是非常快的,到最后,比如若干年后,ldf文件很有可能是mdf文件的几百倍大。

问题就出来了,记录数据变化过程的文件将占居整个系统的绝大多数存储空间,但是,却用不到,因为我们真正频繁访问的是存储当前数据的mdf文件。

这个时候,就要想办法定期的将ldf中暂时用不到的很久以前的日志记录转移到其它存储设备上。

这里要插一点,尽管现在的计算和存储资源相对很廉价了,但是,在数据库服务器上,计算和存储都是相当昂贵的资源,原因有以下几点:

  1. 现有关系数据系统多数不是分布式的,也就是单台服务器必须足够强大,一台4路的服务器成本远高于两台2路的服务器,更多路的就属于中大机了,成本更高。
  2. 用于存放数据库文件的存储往往采用的是SLC SSD之类昂贵的高性能服务器硬盘,而备份存储就可以使用廉价的近线硬盘甚至磁带机。
  3. 数据库系统的软件授权都是按CPU甚至按核来的,因此数据库服务器上每个CPU、每个IO,每个存储的成本不只是其硬件自身,还附带了相对硬件更加昂贵的软件授权。

因此,将数据库服务器上大量存储用在存放基本不用的ldf文件在成本上是不能被接受的。

到这里,你会明白,对于ldf文件,备份已经不只是为了防止服务器上ldf文件意外损坏才备份了,也是为了减小ldf的大小。

这样,Sql Server中的事务日志备份就应运而生,而且一箭双雕。

在Sql server中,事务日志备份将ldf文件中所有已经提交的事务的日志记录备份出来,将其占用的ldf文件空间置为可用。

ldf本身分成很多逻辑段,事务日志记录在逻辑段间循环存放,并可以根据需要扩大或收缩ldf文件。关于ldf的格式的详细描述都可以在Books Online中找到。

2.3 恢复模式

或许你会说,过一定时间的事务日志我不要了,覆盖就好,确实有这种需求的存在。但是同时,对于更多的生产用户,系统完整的历史日志是必须的,比如金融、国防以及大多数中大企业。

作为一个通用的产品,SqlServer考虑到了这两个方向的需求。Sql Server提供了一种设置,称为恢复模式来满足这些不同的应用需求,包括三种:

简单模式:就是不需要历史事务日志记录的模式,所有成功提交的事务的日志记录都被标记为可覆盖,该模式下的数据库不能进行事务日志备份,只能进行完整备份和差异备份。那么,这个时候可以不要ldf了吗?不可以,尽管不需要历史的事务日志记录,但是事务操作的完整性还是要保证的,这个时候ldf的作用主要就是实现事务。

很明显的,除非数据库执行过的某一个事务中包含了数量巨大的操作,否则,在简单模式下,ldf文件会一直保持很小,因为它只要能存放得下一个事务中的所有操作的事务日志记录就可以了。

完整模式:(请不要和完整备份中的完整混淆,两者描述的是完全不同的东西)这一模式就是前面一直在描述的要记录所有已提交的历史事务日志记录的模式,这也是大多数系统应用的模式。在这种模式下,ldf会随着数据库的运行不断增大。在该模式下,可以进行完整备份、差异备份和事务日志备份三种备份。在故障发生时,可能还需要进行日志尾部备份,就是将最近一次事务日志备份之后到发生故障时刻的日志备份出来,以进行恢复。

大容量日志模式:这是完整模式下的一种可切换的模式。就是在完整模式下,如果要进行大量数据的批量操作,比如插入,可以切换到大容量日志模式,这时ldf只记录整个批量操作的结果,而不记录每个操作,可以减少不必要的ldf文件占用。当然恢复的时候,只能恢复整个批量操作,不能恢复到其中任意一个操作。应该只在需要批量操作的时候短暂的使用这一模式。

2.4 混合使用数据备份和事务日志备份

概括来说:数据备份有完整备份和差异备份两种,事务日志备份就本身一种。

对于恢复模式为简单模式的数据库,只能进行数据备份,不能进行事务日志备份,数据库发生故障时只能恢复到备份时刻,备份时刻到发生故障间的数据将丢失。

这里要讲的是完整模式,这也是数据库主要使用的模式。

理论上,有了事务日志,不需要数据备份也可以,因为我们可以从头恢复嘛。但是有一个问题,那就是性能问题,从事务日志恢复数据相当于把数据库创建以来所有的操作重新执行一遍,这将会消耗大量时间,对于运行很久的数据库,这个时间可能会以天甚至月计,这明显是不能接受的。银行数据库崩溃了,花几天去恢复,若不是天朝的估计得倒闭。

这个时候,数据备份就有用了,对于完整备份,数据恢复就是简单的文件拷贝,分分钟钟完成;对于差异备份,先进行最近一次完整备份的恢复,再执行差异恢复,只是页的拷贝覆盖,也是很快的。

这样我们可以定期进行完整备份,比如每天凌晨0点执行一次完整备份,因为完整备份毕竟拷贝了整个数据库,因此最好在业务不繁忙时进行,然后每6个小时进行一次差异备份,每隔20分钟进行一次日志备份,日志备份是很快的,因为频繁备份使得需要备份的日志一般都不大,而且日志文件本身是顺序访问文件,已提交的事务日志记录都是只读的,不会有写操作,不涉及同步的问题,适合于频繁备份操作。

这样,当故障发生时,如果可能,先将日志的结尾进行备份。我们就可以先恢复最近一次完整备份,再恢复最近一次差异备份,然后再将跨越差异备份以及差异备份之后的所有事务日志备份连同结尾备份依次进行恢复,将数据库在可接受的时间内完美恢复到发生故障的时刻。

通过以上分析我们也可以得知:

数据备份中,差异备份依赖于前一次完整备份,而各个完整备份之间没有任何依赖关系。

对于事务日志备份,除了在事务日志备份之前必须有一次完整备份之外(这主要是为从简单模式转到完整模式的数据库设计的),事务日志备份与数据备份完全独立,两者各进行各的。

每个数据备份中记录了最后提交的事务日志记录的编号,假设为N,在数据备份恢复完成后,接下来的事务日志备份恢复时,第一个事务日志备份只需要选择包含了编号为N+1的事务日志记录的备份文件就可以,然后依次选择下一个事务日志备份文件,直到恢复到需要的状态。Sql Server允许通过事务日志恢复操作将数据库恢复到任何一个事务日志记录编号或任何时刻。

3 总结

我们用一张表简单总结一下:

  简单模式 完整模式 大容量日志模式 备份的文件 可将数据库恢复到
完整备份 可以 可以 可以 mdf 执行备份的时刻
差异备份 可以 可以 可以 mdf 执行备份的时刻
事务日志备份   可以 可以 ldf 备份涵盖范围内的任意时刻

本文描述了数据库备份和恢复的基本原理,为进行实际的备份和恢复操作做好了理论准备。

在下一篇文章中将详细讨论Sql Server数据库的备份和恢复操作,以及如何制定合理的备份方案。

另外,本文虽然是针对Sql Server编写,但其原理适用于大多数关系数据库管理系统。