级别: 初级
David Kline, DB2® 供应商支持,PartnerWorld® for Developers, IBM 开发人员技术支持(DTS)中心 — 达拉斯
Nanda Pilaka, DB2® 供应商支持,PartnerWorld® for Developers, IBM 开发人员技术支持(DTS)中心 — 达拉斯
2003 年 2 月 01 日
DB2 确保数据一致性和可恢复性所使用的主要机制之一是事务性日志记录。本文概述了主要概念,帮助您了解如何能够控制事务性日志记录以最优化可恢复性,并且向您介绍了 DB2 UDB V8.1 中新增的日志特性。
简介
以下文章适用于 IBM® DB2® Universal Database™ for UNIX®、Linux 和 Windows®
任何数据库管理系统都必须拥有确保数据一致性和可恢复性的机制。关系数据库系统为确保那些非常重要的特性所使用的众多机制之一是事务性日志记录。在本文中,我们将定义和讨论事务性日志记录的类型。我们将详细讨论如何分配日志文件、如何存储它们,以及您可能会遇到什么样的错误。最后,我们将讨论 V8 中可用的新特性,这些特性使事务性日志记录变得比以往更加可伸缩并且更通用。
事务性日志记录是什么?
数据库存储了供应用程序访问和处理的数据。那些应用程序会插入、读取、更新或删除数据。每一个这样的活动都是在一个事务中执行的,该事务被 定义成“应用程序过程中一个可恢复的操作序列”。除非已经提交了事务(也称作“工作单元”),否则它不会影响数据库。
将数据库操作组合到事务中只是确保数据一致性解决方案的一半。另一半是称作 预写式日志记录(write-ahead logging)的数据库管理器实现。不管事务是否被提交,只要它们发生,就会记录这些事务。在将任何数据从缓冲池写到数据库结构之前,事务会从 日志缓冲区(log buffer)写到 日志文件(事务性日志记录)。用于记录事务的文件叫作 事务日志 。
事务性日志记录的类型不止一种吗?
DB2 UDB 有两种可用的日志记录类型 — 循环(circular)日志记录和 归档(archive)日志记录。
循环日志记录
循环日志记录是数据库使用的缺省日志记录策略。在此策略中,一旦日志目录中最后一个主日志文件被写满了,就会将新的事务写到第一个日志文件中,从而覆盖现有的日志数据。这些新事务会继续依次覆盖每个旧日志文件。这种日志记录方法确保了所有已提交事务的数据一致性,这样就可以执行应急恢复。
循环日志记录通常在数据仓库环境中使用,在该环境中,恢复数据库需要的只是恢复数据库映象的问题。该策略不应该用在线事务处理(on-line transaction processing,OLTP)环境,因为它不可能进行前滚恢复。下面的图 1 说明了循环日志记录:
图 1. 循环日志记录
归档日志记录
与循环日志记录相比,当最后一个日志文件写满时,归档日志记录过程会创建一个新的日志文件,这样将来的事务就不会覆盖现有的日志文件。当初始化数据库时,系统会在活动日志目录中分配一定数量、指定大小的主日志文件。这个数量由数据库配置参数(在 下一节中讨论)控制。当主日志文件都写满时,就会“根据需要”创建辅助日志文件,直到创建了最大数量的辅助日志文件为止。一旦达到了这个数量,如果需要附加的日志空间,就会发出一个错误,指出没有更多的可用日志文件,所有数据库活动停止。
利用归档日志记录,就可能采取联机数据库备份,在执行这一操作期间,会继续记录数据库活动。如果数据库崩溃或发生故障,就会使用全备份映象,然后执行使用归档日志的前滚操作,通过前滚到日志结尾,将数据库恢复到时间点状态或最近的一致状态,从而恢复数据库。
有两种归档日志:
- 联机归档日志:它们是驻留在数据库日志目录(“online”)中、普通数据库活动不再需要的日志文件。
- 脱机归档日志:它们是已经从数据库日志目录移到脱机存储位置(如备份服务器)、普通数据库活动不需要的日志文件。
图 2 说明了归档日志记录:
图 2. 归档日志记录
有什么参数可以控制日志?
通过数据库配置参数在数据库级别上控制事务性日志记录。以下是影响事务性日志记录的参数:
LOGRETAIN
该参数使归档日志保留在数据库日志路径目录中。通过将它设置成“RECOVERY”来启用它,从而允许数据库管理器使用前滚恢复方法。当启用了 logretain配置参数时,就不需要启用 userexit 。这两个参数中的任何一个都足以允许前滚恢复方法。
使用该参数表示覆盖了循环日志记录(缺省值)。以下是 logretain的有效值:
- No(缺省值)— 表示不保留日志。
- Recovery— 表示保留日志,且可以用于前滚恢复。此外,如果您使用数据复制,CAPTURE 程序可以将日志中所记录的更新写到更改表。
- Capture— 表示只保留日志,这样 Capture 程序可以将更新写到更改表。如果没有裁剪这些日志,那么它们可以用于正向恢复。注:通常仅当为了数据复制而设置数据库时,才使用 Capture 设置。
如果 logretain设置成“Recovery”或者 userexit设置成“Yes”(请参阅 下一节),将保留活动日志文件,而且这些文件将变成联机归档日志文件,以便在前滚恢复中使用。这称为日志保留记录。
在将 logretain设置成“Recovery”和/或将 userexit设置成“Yes”之后,必须对数据库进行完全备份。这一状态由 backup_pending标志参数表示。
如果 logretain设置成“No”并且 userexit也设置成“No”,就不能对数据库执行前滚恢复,而且可恢复性仅限于最新的数据库备份。
当 logretain设置成“Capture”时,在 Capture 程序完成时,它会调用 PRUNE LOGFILE 命令来删除日志文件。虽然如果不裁剪日志,这些日志就可以用于正向恢复,但如果您想要确保可以对数据库执行前滚恢复,就不应该将 logretain设置成“Capture”。
如果 logretain设置成“No”并且 userexit也设置成“No”,就不会保留日志。在这种情况下,数据库管理器会删除 logpath目录中的所有日志文件(包括联机归档日志文件),分配新的活动日志文件,并且回复到循环日志记录。
当 logretain配置参数设置成“RECOVERY”时,日志文件将保留在活动日志路径中。活动日志路径由数据库配置文件中的“日志文件路径(Path to Log Files)( logpath)”或“更改的日志文件路径(Changed Path to Log Files)( newlogpath)”值确定。
USEREXIT
该参数使数据库管理器调用用户出口程序来归档和检索日志。启用了用户出口之后,就允许前滚恢复。当启用了 userexit配置参数时,就不需要启用 logretain。这两个参数中的任何一个都足以允许前滚恢复方法。
使用该参数表示覆盖了循环日志记录(缺省值)。 userexit包含有 logretain的功能,反之却不成立。
当使用 userexit 配置参数或 logretain配置参数以允许前滚恢复时,活动日志路径非常重要。当启用了 userexit配置参数时,会调用用户出口来归档日志文件,并将它们移到活动日志路径以外的位置。
以下是该参数的有效值:
如果启用了该参数,无论 logretain参数如何设置,都会执行日志保留记录。该参数还表示用户出口程序应该用于归档和检索日志文件。当数据库管理器关闭日志文件时,会归档日志文件。当 ROLLFORWARD 实用程序需要使用日志文件来恢复数据库时,就会检索它们。
在启用了参数 logretain和/或 userexit时,必须对数据库进行完全备份。这一状态由 backup_pending标志参数表示。
如果取消选择这两个参数,就不能对数据库进行前滚恢复,因为将不再保留日志。在这种情况下,数据库管理器会删除 logpath目录中的所有日志文件(包括联机归档日志文件),分配新的活动日志文件,并且回复到循环日志记录。
LOGPRIMARY
该参数指定要创建的主日志的数量。
无论主日志是空的还是满的,所需的磁盘空间量都是相同的。因此,如果您配置的日志数量比需要的多,那么您就不必要地多使用了磁盘空间。如果您配置的日志太少了,就会遇到“日志满”情况。当选择要配置的日志数量时,必须考虑您生成的每个日志的大小,以及您的应用程序是否能处理“日志满”情况。
如果您对现有数据库启用前滚恢复,将主日志的数量更改成当前正在使用的主日志和辅助日志的数量总和,再加 1。对于为了前滚恢复而在数据库中启用 long varchar和 LOB字段这样的附加信息,也会记录在日志中。
对于 V7.2,总的日志文件大小限制是 32 GB,对于 V8.1,这个限制是 256 GB。即,日志文件的数量(LOGPRIMARY + LOGSECOND)乘以以字节为单位的每个日志文件的大小(LOGFILSIZ * 4096)必须分别小于 32 GB 或 256 GB。
LOGSECOND
该参数指定为恢复日志文件(仅当需要时)而创建和使用的辅助日志文件的数量。请注意,日志文件的总数由以下等式限制:
logprimary+ logsecond<= 128(DB2 UDB V7.2),256(DB2 UDB V8.1)
当主日志文件满了时,就会按需要每次分配一个辅助日志文件(大小为 logfilsiz),最多达到由该参数控制的最大数量。如果所需的辅助日志文件的数量比该参数允许的数量大,就会将一个错误代码返回到应用程序,并且会停止对数据库的操作。
LOGFILSZ
该参数确定了每个已配置日志的页数量。一页的大小是 4 KB。每个主日志的大小(页数量)对数据库性能有直接影响。当将数据库配置成保留日志,每当写满一个日志时,就会发出一个分配和初始化一个新日志的请求。增加日志大小会减少为分配和初始化新日志所需的请求数量。但是,请注意,日志大小越大,格式化每个新日志所花费的时间就越多。格式化新日志对于连接到数据库的应用程序是透明的,而且也不会影响数据库性能。
LOGBUFSZ
该参数允许您指定数据库共享内存的数量,在将日志记录写到磁盘之前,用该共享内存作为这些记录的缓冲区。当发生以下情况之一时,会将日志记录写到磁盘:
- 事务提交。
- 日志缓冲区满了。
- 引起写操作的其它一些内部数据库管理器事件。
缓冲日志记录将导致使日志文件 I/O 更有效,因为将日志记录写到磁盘的频率将更低,而每次写入磁盘的日志记录则更多。
MINCOMMIT
该参数允许您延迟将日志记录写到磁盘,直到已经执行了所规定的最小数量的提交。当您有多个针对数据库的应用程序正在运行,并且应用程序在非常短的时间段里请求了许多提交,那么该延迟可以帮助减少与写日志记录相关的数据库管理器开销,从而提高性能。
这种提交分组只有在该参数的值大于 1 且连接到数据库的应用程序数量大于该参数的值时才会发生。当执行提交分组时,应用程序提交请求将被挂起,直到以下两种情况有一种先发生:时间过去一秒或者提交请求的数量等于该参数的值。
NEWLOGPATH
数据库日志最初创建在名为 SQLOGDIR 的目录中,该目录是数据库目录的子目录。可以通过将该配置参数的值更改成指向另一个目录或设备来更改放置活动日志和将来归档日志的位置。如果将数据库配置成进行前滚恢复,那么就不会将当前存储在数据库日志路径目录中的归档日志移到新的位置。
因为您可以更改日志路径位置,所以前滚恢复所需的日志可能会在不同的目录中或在不同的设备上存在。在前滚过程中可以更改此配置参数以允许您访问多个位置中的日志。
在数据库处于一致状态之前,将不会更改对 newlogpath的值。信息性数据库配置参数 database_consistent表示数据库的状态。
注:数据库管理器每次写一个事务日志。可以是活动状态的事务的总大小受数据库配置参数限制:
日志空间
>= LOGFILSIZ * LOGPRIMARY * 4096 字节
<= LOGFILSIZ * (LOGPRIMARY + LOGSECOND) * 4096 字节 <= 32 GB(对于 V7.2)或 <= 256 GB(对于 V8.1)
如何分配日志文件,将它们存储在哪里?
如何分配日志文件?
一旦更新了在前一节讨论的所有数据库配置参数,就需要确保所有应用程序从数据库断开连接。当应用程序重新连接时,新的设置将生效,日志文件将预先分配到您的磁盘。所分配的日志文件数量基于 logprimary参数值和活动日志目录中日志文件的总数。
使用 归档日志记录 时,如果您没有把旧的日志文件从活动日志目录中移走,它们将累积起来,并与活动日志文件共享该目录。例如,让我们假设在活动日志目录中有五个日志文件。其中两个日志文件是完全满的,而且已被提交,因此还有三个日志文件是空闲的。在将 logprimary参数从 五更新成 八之后第一次与数据库连接时,会多分配五个日志文件到活动日志目录中,从而确保总共有八个空闲的日志文件(3 个空闲文件 + 5 个新文件 = 8 个空闲文件)。
使用 循环日志记录时,如果 logretain或 userexit参数被设置成 no,就会重用日志文件。这表示在第一次连接到数据库时,主日志的总数等于驻留在活动日志目录中的日志文件的总数。如果我们采用前一个示例并将 logprimary参数从 五更新成 八,那么只会多分配三个日志文件到活动日志目录中,即使某些日志文件完全是满的(5 个空闲的 + 3 个新的 = 8)。
正如我们在前一节中讨论的,根据 logfilsiz参数为所创建的每个日志文件分配相同的空间。如果更改了 logfilsiz参数值,具有事务性数据的现有日志文件并不受影响,而将创建新的日志文件并将空的日志文件的大小增加到新值。
请注意,反映每个日志文件的大小主要是预先分配的空间,一些日志头控制字符除外。通过在日志文件中预先分配这个空间,数据库管理器就不必在它需要该日志文件时进行空间分配。将空间分配到硬盘驱动器将耗费大量时间和资源,因此最好在需要空间时已经有了可用的空间。
logsecond参数是 V7.2 和 V8 之间唯一有区别的参数。在 V7.2 中,该参数只在所有应用程序进行了断开连接和重新连接之后才生效。而 V8 允许该参数在更新之后立即生效。这并不表示您会在重新连接到数据库之后立即在日志文件目录中看到新的日志文件。该参数只会当超过 logprimary值时才会分配日志文件,这种情况最有可能在一系列长时间未提交的事务期间发生。
以下是说明 logsecond 如何影响日志分配的示例。您只是将 logprimary 更新成五,将 logsecond 更新成二(预先分配五个,当需要时再分配两个)。事务正在运行,并用完所有五个主日志文件,但仍将记录事务。当需要第六个日志文件时,数据库管理器将检查 logsecond 值并将另一个日志文件分配到日志目录。现在需要第七个日志文件,则分配该文件。由于 logsecond 参数只指定了两个日志文件,因此在第七个日志文件写满之前必须提交事务,否则会返回一个错误并且事务将回滚。
那么提交之后,日志文件会发生什么情况?为了保持 logprimary值,数据库管理器将根据前一个事务中使用了多少日志文件来分配新的日志文件。例如,如果写满了三个日志文件,就将创建另三个日志文件以确保空闲日志文件的总数等于 logprimary值。
当所有应用程序从数据库断开连接时,会发生什么?当重新连接到数据库时,前一个连接中的最后一个要放置数据的日志文件将被截断,使它的大小等于文件中数据的大小。这会除去可用空间,并允许根据上面说明的数据库配置参数精确地分配空间。
日志文件存储在哪里?
缺省情况下,日志文件存储在以下目录中:
在 Windows 上:c:\<instance name>\NODE0000\SQL00001\SQLOGDIR
在 UNIX 上:<instance home directory>/<instance name>/NODE0000/SQL00001/SQLOGDIR
在上述目录路径中,对于所创建的每个数据库,会有一个 SQLxxxxx(“xxxxx”是以 0 开头的数字)目录。如果在 DB2 实例中有多个物理数据库,就很难知道哪个 SQLxxxxx 目录属于哪个数据库。要解决这个问题,只要输入以下 DB2 命令:
db2 "list database directory on c:"
|
其中 c: 是数据库驻留的盘符。
在 UNIX 上,您要指定:
db2 "list database directory on /<instance home directory>"
|
以下是上述命令返回的输出样本:
清单 1. DB2 数据库目录
Database 1 entry:
Database alias = DWCTRLDB
Database name = DWCTRLDB
Database directory = SQL00001
Database release level = 9.00
Comment =
Directory entry type = Home
Catalog node number = 0
Node number = 0
Database 2 entry:
Database alias = SAMPLE
Database name = SAMPLE
Database directory = SQL00002
Database release level = 9.00
Comment =
Directory entry type = Home
Catalog node number = 0
Node number = 0
Database 3 entry:
Database alias = UTFDB
Database name = UTFDB
Database directory = SQL00003
Database release level = 9.00
Comment =
Directory entry type = Home
Catalog node number = 0
Node number = 0
|
正如以上所示,每个 SQLxxxxx 目录与一个数据库名称相关联。当然,如果您的数据库没有创建在缺省目录中或者您更改了日志文件路径,您可以检查数据库配置文件中的“日志文件路径”或 newlogpath参数。您也许还要确保正在寻找的数据库驻留在正在搜索的实例中。
以下是在 Windows 上存储日志文件的位置的直观表示(缺省情况下):
图 3. 日志文件的 Windows Explorer 视图
在上图中,您可以看到一个名为 SQL0003 的数据库目录。根据我们从前面的“list database...”命令的输出判定,该目录与 UTFDB 数据库相关联。
事务性日志记录会引起错误的常见情形
我们已经讨论了影响日志记录的数据库配置参数以及如何分配日志,但现在应该讨论当没有正确设置参数时您可能遇到的问题。不正确地设置日志参数不仅对发出长时间运行事务的用户造成严重损害,而且还可能损害共享系统的其它用户。以下是您可能遇到的一些问题:
- 在 IMPORT 操作期间,突然弹出一条警告,它看起来如下:
清单 2. import 操作的警告示例
C:\data>db2 "import from temp2.ixf of ixf create into temp2"
SQL3150N The H record in the PC/IXF file has product "DB2 02.00", date
"20010910", and time "171430".
SQL3153N The T record in the PC/IXF file has name "temp2.ixf", qualifier "",
and source " ".
SQL3109N The utility is beginning to load data from file "temp2.ixf".
SQL3186W Data was not loaded into the database, because the log was full.
SQLCODE "-964" was returned. A commit will be attempted and the operation
will continue if the commit is successful.
SQL0964C The transaction log for the database is full. SQLSTATE=57011
SQL3221W ...Begin COMMIT WORK. Input Record Count = "78".
SQL3222W ...COMMIT of any database changes was successful.
|
由于 IMPORT 操作实质上是使用 SQL 插入进行行移动并关闭了自动提交,所以您会看到上面的警告,说明日志文件已满并将试图提交事务。如果将数据库配置参数 logretain设置成打开(ON),会启用数据库的日志恢复,并且在 IMPORT 操作可以继续之前需要创建新的日志文件。知道您是否需要大日志文件和/或众多日志文件的组合非常重要,因为要花时间来分配这些日志文件。如果将 logretain设置成 OFF,那么将进行循环日志记录,从而消除了创建新日志文件的必要性。
如果您正将非常多的行导入数据库,在每天系统活动较少的时候更新数据库配置日志记录参数也许比较好,这样分配日志文件不会给系统带来过多压力。DB2 UDB V7.2 允许分配总共 32 GB 的日志空间,而 V8 允许 256 GB 的海量空间。
另一个考虑事项是使用 LOAD 操作,它允许您对某个表使用 NOT LOGGED INITIALLY 选项。顾名思义,当执行 LOAD 时,该选项不会引起日志记录操作。但它也阻止了表成为可恢复的,因此在执行 LOAD 之后立即备份数据库非常重要。请注意,NOT LOGGED INITIALLY 选项只适合于当前命令的执行,数据库管理器会继续记录在 LOAD 操作完成之后发生的事务。
-
当某个应用程序在一个工作单元中执行许多 INSERT、DELETE 或 UPDATE 语句时,会发生另一种常见情况。自动提交被禁用,只有在工作单元完成之后,才会显式地执行提交。与可以识别日志文件写满情况并执行提交的 IMPORT 实用程序不同,应用程序中的逻辑也许还没有考虑这个问题,那么它将由于以下错误而失败:
SQL0964C The transaction log for the database is full. SQLSTATE=57011
|
因此,如果您的应用程序调用具有罕见长事务的文件,根据您的日志设置包含频繁的提交操作,或者更新数据库日志记录参数,也许是好办法。
-
当对表执行 RUNSTATS 或 REORG 时,可能出现第三个问题。当到处移动表数据时,RUNSTATS 和 REORG 实用程序都使用日志文件。如果遇到 SQL0964C 错误,就需要增加数据库日志文件参数的值。
|
|
V8 的新特性
由于 V8 提供了一些很棒的新特性,所以 DB2 日志记录也向前迈了一大步。以下是其中的一些新特性:
日志记录可伸缩性— 对于那些喜欢使用非常长的事务的人,DB2 已经将最大活动日志空间从 32 GB(V7.2)增加到 256 GB。这是 logprimary和 logsecond参数的总和可能达到的字节总数。
飘浮事务现在可以超过数据库日志记录参数指定的限制,或者如果将 userexit数据库配置参数设置成打开,那么它甚至可以超过活动日志路径中的可用磁盘空间。通过将 logsecond更新成 -1(无穷大)来设置该新特性。其想法是由于在长事务期间,日志文件将经常由 userexit 程序归档,在活动日志目录中始终要有新的空间来存放额外的日志文件。在数据库上运行的飘浮事务的大小或数量没有限制。但是其缺点是:如果发生回滚,就可能有性能影响,因为 userexit 程序将需要从归档日志路径中检索未提交的日志文件。此外,如果将 logsecond设置成 -1,新的辅助日志分配将不受( logprimary+ logsecond)<= 256 等式的限制。
DB2_BLOCK_ON_DISK_FULL注册表变量将被 BLK_LOG_DSK_FUL 替换。当启用该选项时,在活动日志路径上发生磁写盘满情况时正在运行的应用程序将不会断开连接。那样 DBA 就有时间删除文件或扩大文件系统,从而允许事务完成。当遇到日志写满情况,仍允许对数据库的读访问。
日志文件镜像— DB2 UDB V7 中的 newlogpath2注册表变量被 DB2 UDB V8 中的 mirrorlogpath数据库配置参数替换。这一变化将允许数据库级别上的颗粒度,而不是实例级别上的颗粒度。 newlogpath2变量和 mirrorlogpath参数具有相同的作用 — 它们用于实现双重或镜像日志记录。 mirrorlogpath必须指向全限定路径名(即,绝对路径名)。
日志记录速度更快— 通过异步日志写操作提高日志记录器的性能。这在 SMP 环境中特别有益,因为它们能够利用异步处理。