本文从数据库基本原理的角度入手,通过对Exchange Server Store模块的分析,来揭示Exchange Server邮件存储系统的工作原理和维护技巧。文章适合有一定Exchange Server管理经验的专业IT人员阅读,目的是使读者在维护Exchange Server邮件系统时,能够做到知其然,更知其所以然。
Information Store和Extensible Storage Engine的层次关系
众所周知,在Exchange Server中,Information Store (简称IS)Service是至关重要的。这个服务控制了对邮箱和公共文件夹数据库的操作请求。
更进一步的来看,事实上Exchange Server的数据库系统是由名为Extensible Storage Engine(简称ESE)的数据库引擎来管理的。这个ESE引擎是微软专门为保存非关系型数据而开发的,在微软的很多系统中都有应用:例如,AD的数据库(ntds.dit文件)、Windows DHCP、Windows WINS、SRS等,后台都是由ESE数据库来提供支持的。
我们知道,Exchange Server的数据库由edb文件、stm文件和众多的log文件组成。在这些文件内部,微软使用了名为“B+树”的内部数据结构,ESE引擎的任务之一,就是当Information Store服务请求访问数据库的时候,把这些请求转化成对内部数据结构的读写访问。B+树的特点是能够对存储在磁盘上的数据提供快速的访问能力。微软选用 B+树作为ESE后台结构的一个原因,就是尽可能提高访问数据时的I/O性能。这些B+树的结构对于Exchange Server Store服务来说是透明的,Store只需要把请求发给ESE即可,ESE会对这些数据结构进行操作。
另外,作为一个数据库系统,ESE有责任提供事务(Transaction)级别操作的支持,并维护整个数据库的完整性和一致性。对于现代数据库系统,当我们提到事务时,一般用ACID这样的缩写来描述事务的特点:
我们会在后面的篇幅中详细的讨论Exchange Server和ESE是怎样实现上述的要求的。
对于Information Store Service来说,ESE封装了对数据库操作的所有细节,IS只要根据ESE提供的接口进行调用既可。在Exchange Server 2000中,IS服务对应的进程是store.exe,每一个Storage Group会在store.exe进程中产生一个ESE引擎的实例。
Exchange Server 2000/2003 存储系统的新特点
在微软发布Exchange Server 2000时,Exchange Server的存储系统得到了很大的更新和改进。
从ESE引擎的角度来看,ESE的版本由5.5中的ESE97升级为ESE98,并且在如下方面得到了改进:
1.I/O性能得到进一步的优化和提高
2.对日志文件增加了计算校验和的操作,进一步降低了数据库出错的可能性
3.提高了ESEUtil等维护工具的速度
相比幕后的ESE引擎,Information Store方面的更新更加引人注意,例如:
1.在每台Server上提供多个Storage Group和Store的支持,这是区别于5.5的最大特征之一
2.数据库中stm流文件格式的引入,提高了操作Internet邮件的性能
3.Web Storage System的引入,用户可以使用多种协议访问数据库
EDB文件和STM文件的关系
在Exchange Server 5.5中,数据库只有扩展名为edb的文件。在Exchange Server 5.5发布的时候,微软的重点还是企业内部的邮件传输系统,当时主推的协议是MAPI协议,这是微软的私有邮件协议,edb格式的数据库为此协议作了专门的优化。因此,Exchange Server 5.5为了支持Internet标准的SMTP邮件格式,必须在每次处理Internet邮件时将其转化为edb可以识别的格式,这样做带来的巨大的性能损失。
在Exchange Server 2000中,微软加大了对Internet标准协议SMTP的支持力度。因此,适用于Internet格式邮件的存储就应运而生:这就是stm文件。
MAPI格式的邮件是基于微软的RPC和二进制标准的,而Internet格式的邮件是由纯文本的邮件头和经过MIME编码的字符流组成的。这两者的特性就决定他们无法共存在一种数据库结构的文件中。
因此,在Exchange Server 2000中,微软分别使用edb文件和stm文件保存这两种格式的邮件,并在edb和stm文件之间建立了关联和引用。对于用户来说,他的邮箱内容实际上是由跨越了edb和stm文件中的内容共同组成的。值得一提的是,edb文件中除了实际的信件信息以外,还保存了每个用户的邮箱结构、每一个文件夹的内容列表和视图等信息。这是区别于stm中只保存字符流的地方。
我们分下面几种情况讨论edb和stm文件的使用:
1.用户使用Outlook 以MAPI协议的方式和发送和访问邮件
2.用户使用 SMTP/POP3等Internet协议访问Exchange Server。
情景一:
当邮件从MAPI协议的客户端(通常是Microsoft Office中的Outlook)提交到数据库后,邮件内容被保存在edb文件中。
当用户通过MAPI协议的客户端对邮箱中的邮件进行读取访问时,如果请求的邮件是保存在edb文件中的,那么信件被直接打开后返回给用户。如果被请求的信件保存在stm文件中(此信件是SMTP格式的),那么,Exchange Server数据库引擎首先会做一个转换,把stm文件中的数据格式转换成MAPI可以识别的格式,然后再发送给客户端。这个过程称之为“On- demand Conversion”。
情景二:
用户使用SMTP/POP3客户端(如Outlook Express, FoxMail等)跟邮箱连接。当SMTP协议向Exchange Server提交邮件时,邮件的内容被保存在stm文件中。前面提到过,edb文件中包含了用户邮箱的文件夹和信件内容列表,因此,当邮件被保存到stm 文件后,数据库引擎把这封邮件的一些重要信息(通常是邮件头中的内容和信件在stm文件中的位置)提取出来,保存到edb文件,这个过程称之为 “Property Promotion”。正是有了这个过程,用户才可以得到信箱内容的完整列表,MAPI客户端需要访问位于stm文件中的邮件时,由此能够得到stm文件中信件的正确保存位置。当用户使用POP3协议来读取邮件时,如果被访问的邮件位于edb文件中,同样,一个从MAPI到Internet格式的转化(“ On-demand Conversion”)也会在后台悄悄的发生。
通过上面的描述,我们知道在实际的Exchange Server环境中,这两个文件是紧密关联的。在任何时候都不要单独的操作这两个文件,要始终把他们视为一个整体。edb文件中包含了每一个邮箱的内容列表(store tables),当客户端需要得到文件夹的内容时,都必须向edb文件发出请求。两种格式的文件,对两种类型的协议分别提供了支持,有效的减少了不必要的格式转换的发生。
Log文件的作用
我们讨论Exchange Server的邮件存储,就不得不谈谈它的日志文件。我不止一次的听到Exchange Server的管理员抱怨:日至文件每天都在疯长,太消耗硬盘空间了。
我们来看看这些日志文件到底有些什么作用。对于每一个Storage Group,Exchange Server会产生一系列与之对应的日志文件。这些日志文件的大小为5M,扩展名为log,他们的前缀为E0x,其中x是日志文件所对应的Storage Group的编号[脚注:虽然在Storage Group的属性中有“Log File Prefix”这一个文本框,但实际上这是不能更改的。]。因此第一个Storage Group的日志文件前缀为E00,第二个的为E01,依次类推。这样做的目的是当存在多个Storage Group时,可以避免管理员在维护的时候把日志文件”张冠李戴”。另外,除了连续的Log文件,我们还能看到E0x.chk、Res1.log、 Res2.log等文件。
很多管理员都对日志文件非常的头疼,那么,微软在Exchange Server的数据库系统中引入Log文件的目的是什么呢?我们从以下几个方面来看:
1.作为一个企业级的邮件数据库系统,必须做到数据安全和完整性的万无一失。必须能够面对随时可能发生的崩溃和宕机,What happens if we crash? 要能够把数据的损失减少到最新程度。
2.必须提供高性能的邮件吞吐能力,对数据库中的邮件的事务操做在完成后必须马上被记录到存储介质上(事务的持久性)。
3.当灾难发生时,使用数据库的备份恢复必须要返回到灾难发生前一刻的数据库状态。
现在我们更进一步的来看一下,当我要修改邮箱中的内容时,被修改的内容首先被读取出来放到内存中。实际的修改发生在内存中,当修改完成后,这些内容必须被写回存储介质,才能表示一个修改成功地完成了。
对于这样的修改过程,在数据库级别上,我们叫做一个“事务”。我们知道,为了确保数据库的完整性和一致性,事务的操作是“原子级别”的。如果一个事务成功,那么标志着他所作的改变被永久的保存下来了;如果一个事务失败,系统必须回到事务开始之前的状态。
当系统在内存中完成修改时,事务并没有完成。如果这个时候宕机,数据库中保存的仍然是没有更改的内容。那么,怎么样确保在内存中完成的修改能够在第一时间写入到数据库呢(以达到数据库事务持久性的要求)?注意,这里的要是第一时间,也就是越快越好。如果我们直接向edb文件写入,无法做到最快,因为,edb 文件通常都很大,I/O系统在对大的文件进行随机写入操作时,会花费大量的时间在等待磁盘查找到合适的磁道和扇区,当系统繁忙时,这将会是一个瓶颈。因此,数据库系统使用日志文件,当内存中的更改完成后,首先写入到日志文件中。日志文件的尺寸很小,写入性能要远远优于庞大的edb文件。在写入完成后,事务也随之成功的保存在存储介质上了。Exchange Server 的数据库引擎会在后台把Log文件中的内容写入到数据库中,因为此时事务操作已经完成,即使此时掉电或者宕机,也不会使完成的事务遗失。这是日志文件的第一个作用:确保事务能够在第一时间保存到非易失存储介质上。(提供持久性Durable支持)
根据上面的描述,我们知道在运行中的Exchange Server数据库,是由三部分组成的
--内存中已经完成修改但是还没有写入日志文件的内容(Dirt Page)。
--还没有写入到数据库文件的日志文件内容。
--Edb和stm文件。
对于内存中的数据(Dirt Page),这些数据会在系统掉电或者崩溃时遗失。
Exchange Server使用了一个名为E0x.chk(Check Point)的文件记录了那些Log文件已经写入到了数据库文件。这是一个类似指针的记录。
我们可以使用命令 ESEUTIL /MK来查看这个文件chk的内容
C:\...\Exchsrvr\BIN> ESEUtil /mk “C:\...\Exchsrvr\mdbdata\e00.chk”
Microsoft(R) Exchange Server(TM) Database Utilities
Version 6.0 Copyright (C) Microsoft Corporation 1991-2000. All Rights Reserved.
Initiating FILE DUMP mode...
Checkpoint file: C:\program files\exchsrvr\mdbdata\e00.chk
LastFullBackupCheckpoint: (0x0,0,0)
Checkpoint: (0x8,26DA,30)
FullBackup: (0x0,0,0)
FullBackup time: 00/00/1900 00:00:00
IncBackup: (0x0,0,0)
IncBackup time: 00/00/1900 00:00:00
Signature: Create time:03/28/2004 20:26:10 Rand:6519986 Computer:
Env (CircLog,Session,Opentbl,VerPage,Cursors,LogBufs,LogFile,Buffers)
( off, 202, 10100, 1365, 10100, 128, 10240, 40828)
Operation completed successfully in 1.47 seconds.
在命令的输出中, Checkpoint: <0x8,26DA,30>表示了当前提交到数据库文件的Log完全位置。其中,0x8是Log文件的序号,一般对应于E0x00008.log,剩下的两个参数是Log文件内部页面(page)的编号。
下面我们再看一下日志文件对系统备份和恢复的作用。
前面提到过,Exchange Server要求在灾难发生后能够恢复到灾难发生前一刻的状态。对于一般的系统,我们总是每周或者每天进行备份,那么,在备份之后和灾难发生之前这段时间的数据如何保护?答案是日志文件。我们知道,对于数据库的任何更改,都会先被写入到日志文件,然后再由日志文件更新到数据库中。我们现在假设有这样一套系统,在每天的3:00 AM进行备份,备份完成后,系统正常运转。如果在中午12:00的时候系统出现故障,管理员用3:00AM的磁带恢复了系统,那么,从3:00AM到 12:00AM这段时间的数据,将由log文件来填补的。具体的情况是,当3:00AM的备份恢复完成后,Exchange Server会自动扫描到跟这个store相关联的日志文件夹,如果发现有比当前数据库还新的日志存在,Exchange Server会自动把这些日志按照顺序写入到数据库中。因此,从3:00AM到12:00AM这段时间对数据库所作的更改,可以被恢复回来。这是日志文件第二个重要的作用。(前提是没有开启循环日志功能)
有人可能会问,如果数据库文件和日志文件同时损坏怎么办?答案是这样的:避免这种情况发生。首先,数据库文件损坏的概率要远远大于日志文件,另外,微软推荐的做法是把数据库文件和日志文件分别放置在不同的磁盘上。我们会在下一期的文章中着重讨论这个问题。
管理员针对日志文件的抱怨是,这些文件会每天不断的增长,大量消耗硬盘空间。对于这个问题,唯一合理的解决办法是:定期的做针对Storage Group的全备份或增量备份。因为Exchange Server会在全备份或增量备份完成后把这次备份之前产生的Log文件全部删除。很多管理员手动的删除日志文件,或者启动“循环日志”来减少对硬盘空间的消耗,这都是不正确的做法。残缺不全的日志文件会使系统在进行备份恢复的时候无法还原到最近的状态。如果你的系统是一周做一次全备份,而你碰巧又在备份后删除了一些日志文件,那么你就有可能在需要恢复的时候丢失备份以后的数据。记住,数据总是比磁盘空间更宝贵。
ESE数据库引擎以及Information Store服务的启动和关闭
在ESE 引擎加载数据库文件时,它会检查数据库文件的一个特殊标志位。这个标志位保存了数据库文件上次是否被正常关闭。这个状态由“ Consistent”或“ Inconsistent”来表示。对于一个正常关闭的数据库文件,所有在Log文件和内存中的内容都应该已经提交到数据库文件中,只有在这个时候,数据库才会被标记为“ Consistent”。有一点需要注意,在运行中的数据库,它的状态一定是“ Inconsistent”,因为在Log文件中肯定还有没提交到数据库文件内容。对于一个已经关闭并且状态被标示为“ Inconsistent”的数据库,并不意味着这个数据库库文件损坏了,“ Inconsistent”只是表示,还有未曾写入到数据库文件的内容保存在Log文件中。
使用命令 ESEUTIL /MH可以查常看数据库的关闭状态。
C:\...\Exchsrvr\BIN> ESEUtil /mh “C:\...\Exchsrvr\mdbdata\priv1.edb”
Microsoft(R) Exchange Server(TM) Database Utilities Version 6.0
Copyright (C) Microsoft Corporation 1991-2000. All Rights Reserved.
Initiating FILE DUMP mode...
Database: C:\program files\exchsrvr\mdbdata\priv1.edb
File Type: Database
Format ulMagic: 0x89abcdef
Engine ulMagic: 0x89abcdef
Format ulVersion: 0x620,9
Engine ulVersion: 0x620,9
Created ulVersion: 0x620,9
DB Signature: Create time:03/28/2004 20:26:24 Rand:6536656 Computer:
cbDbPage: 4096
dbtime: 63139 (0-63139)
State: Clean Shutdown <-----表示数据库关闭时的状态
Log Required: 0-0
Streaming File: Yes
Shadowed: Yes
Last Objid: 574
…<略> …
Operation completed successfully in 1.391 seconds.
State字段的“Clean Shutdown”表示数据库处在Consistent状态。
当ESE 加载数据库文件时,对于“Consistent”的数据库文件,它直接Mount其中的Store;对于“In consistent”的数据库文件,ESE将执行称之为“Soft Recovery”的过程,在这个过程中,未及时提交进数据库文件的日志内容将被写入数据库。当所有的日志都写入完毕,数据库才会被标记为“ Consistent”状态,然后正常加载。
Soft Recovery开始的时候,ESE会根据check point文件所指向的位置来进行Log文件的写入(如果check point文件也损坏或者不存在,那么数据库就从最旧的Log文件开始)。当ESE从Log文件向Store写入数据时,它会根据dbTime这个时间戳来决定是否需要把Log文件写入到数据库。
在这个过程中,Event Log中会有如下的记录
Event Type: Information
Event Source: ESE98
Event Category: Logging and Recovery
Event ID: 301
Date: 10/17/2001
Time: 5:52:11 AM
User: N/A
Computer:
Description: Information Store (XXXX) The database engine has begun replaying logfile ..\..\E0014553.log.
我们也可以针对已经“Dis-mount”的并且是处在“Inconsistent”的数据库手工地进行“Soft Recovery”。
具体的命令是“eseutil /r”,后跟数据库文件的路径。(推荐在掉电重启以后执行此命令,可以先运行eseutil /mh确定数据库状态,如果是“Inconsistent”,再执行此命令)
由此我们可以发现,Exchange Server有能力对未正常关闭的数据库进行“自我修复”。因此,ESE确保了即使在突然掉电的情况下,数据库仍然能够处在一个可恢复的状态,并且会在重启服务以后自动完成状态检测和恢复。
M盘的来龙去脉
在Exchange Server 2000发布时,微软提出了“Web Storage System”的概念,其核心就是提供多种途径来访问Exchange Server的数据库。这些途径包括
文件系统/IFS
--Http WebDAV
--ExOLEDB/ ADO
--CDO
其中,提供文件系统服务的IFS技术是引起争议比较多的一个模块。在安装Exchange Server 2000后,系统会出现一个M盘。这个M盘,就是由微软通过IFS(Installable File System)技术实现的一个数据库到文件系统的映射。开发人员可以通过标准的文件操作API(如CreateFile, OpenFile等)来访问Exchange Server的邮箱和邮件。
打开M盘,你可以看到一个以你当前域名命名的文件夹。在这个文件加下面,你会看到一个包含了所有邮箱的文件夹,名为MBX。MBX下面,是以用户的姓名来命名的邮箱文件夹,在每个文件夹下面,都可以看到Inbox、 Outbox等邮箱的内容。每一封信件,都是以扩展名为EML的文件来表示的。
ExIFS使用了一个名为\\.\BackOfficeStorage的特殊共享名称来指向数据库文件。你可以在命令行中运行“Dir \\.\BackOfficeStorage\domain.con\MBX”,这个命令的实行结果跟直接使用M盘作为盘符是一样的。
我们可以通过修改注册表的方式所来改变Exchange Server所映射的盘符。
HLKM\System\CurrentControlSet\Services\ExIFS\Parameters
Name: DriveLetter
Data Type: REG_SZ
Value: Drive letter for IFS (盘符,不需要跟冒号)
在更改注册表以后,需要重启Information Store Service使更改生效。
我们也可以使用如下的命令行工具来改变M盘的映射:
Subst X: \\.\BackOfficeStorage 注释:把Exchange Store映射到X盘
Subst /d M: 注释:删除对M盘的映射
如果我们移除了M盘,我们还是可以通过\\.\BackOfficeStorage这个共享名字来访问Exchange Server的数据库。
ExIFS在Windows中是作为一个隐藏的服务来运行的。下面的注册表键值定义了这个服务的参数:
HLKM\System\CurrentControlSet\Services\ExIFS\Parameters
由于这是一个隐藏的服务,因此我们没有办法通过Service控制面板来对这个服务进行控制。但是我们可以通过命令行来做到:
NET Start ExIFS 注释:启动服务
NET Stop ExIFS 注释:停止服务
下面这张图表示了ExIFS的架构。
ExIFS是使用运行在Windows内核模式的ExIFS.sys驱动程序来实现的。
我们知道,文件系统和Exchange Server的store是两个完全不同的体系结构。文件系统中的文件只包含比较少的属性,而保存在Store中的邮件,有其特定的属性,并且,在 Store中,邮件之间还有非常复杂的关联关系(跟邮箱的关系,邮箱文件夹的视图等)。因此,M中以EML形式存在的文件(邮件),只是反映了邮件所有属性和关系的一个子集。一些对于M盘的不适当操作,往往会破坏数据库内部的关系,造成数据库损坏。比较典型的例子是,防病毒软件扫描M盘,发现“嫌疑病毒” 并予以清除。根据微软技术支持部门的统计,这是造成Exchange Server Store数据库损坏的主要原因之一。因为防病毒软件在清除病毒文件(EML文件)时,采取“野蛮施工”手段,往往会破坏数据库内部的关联和邮件结构,进而造成数据库文件内部结构的损坏。
另一个针对ExIFS的错误观点是:管理员认为对M进行备份即可保存Exchange Server的状态和所有数据。这是完全不正确的。M盘只是数据库内容在文件系统上的一个映射,M中所保存的“文件”,归根结底还是数据库中保存的邮件。由于映射到M盘,数据库中的邮件关联和关系都被去掉了,备份M盘,是没有办法恢复数据库的所有信息。