给数据泵加压
作者:Jonathan Gennick
Oracle数据库10g中的新的实用程序使其性能和多功能性达到了新的水平。
Oracle数据库10g中增加的叫做Oracle Data Pump(数据泵)的新的导入和导出特性,彻底改变了数据库用户已经习惯的过去几代Oracle数据库的客户/服务器工作方式。现在服务器可以运行导出和导入任务。你可以通过并行方式快速装入或卸载大量数据,而且你可以在运行过程中调整并行的程度。导出和导入任务现在可以重新启动,所以发生故障不一定意味着要从头开始。API是公诸于众的,并且易于使用;用PL/SQL建立一个导入和导出任务非常简单。一旦启动,这些任务就在后台运行,但你可以通过客户端实用程序从任何地方检查任务的状态和进行修改。
体系结构
在Oracle数据库10g之前(从Oracle7到Oracle9I),导入和导出实用程序都作为客户端程序运行,并且完成大量工作。导出的数据由数据库实例读出,通过连接传输到导出客户程序,然后写到磁盘上。所有数据在整个导出进程下通过单线程操作。今天的数据量比这个体系结构最初采用的时候要大得多,使得单一导出进程成了一个瓶颈,因为导出任务的性能受限于导出实用程序所能支持的吞吐量。
在Oracle数据库10g和全新的数据泵(Data Pump)体系结构下,如今所有的工作都由数据库实例来完成。数据库实例可以用两种方法来并行处理这些工作:通过建立多个数据泵工作进程来读/写正在被导出/导入的数据,以及建立并行I/O服务器进程以更快地选取(SELECT)或插入(INSERT)这些数据。这样,单进程瓶颈再也就不存在了。
数据泵任务用新的DBMS_DATAPUMP PL/SQL API来建立、监测和调整。新的导入和导出实用程序(分别为impdp和expdp)对于这个API来说只是命令行接口。你可以使用数据泵导出实用程序初始化一个任务,例如一个导出任务。然后你就可以关闭你的客户端,回家过夜和享用晚餐,而你的任务会一直运行。到了深夜,你可以重新连接到那个任务,检查其状态,甚至可以提高并行程度,以便在深夜系统没有用户在用的情况下多完成一些工作。第二天早上,你可以降低并行度甚至挂起该任务,为白天在线的用户释放资源。
重新启动任务的功能是数据泵体系结构的一个重要特性。你可以随时停止和重启动一个数据泵任务,比如为在线用户释放资源。你还可以从文件系统的空间问题中轻松地恢复。如果一个12小时的导出任务在进行了11小时后因磁盘空间不够而失败,那么你再也不用从头开始重新启动该任务,重复前面11小时的工作。而是你可以连接到这个失败的任务,增加一个或多个新的转储(dump)文件,从失败的地方重新启动,这样只需一个小时你就可以完成任务了。这在你处理很大数据量时非常有用。
对文件系统的访问
由服务器处理所有的文件I/O对于远程执行导出和导入任务的数据库管理员来说非常有利。如今,用户可以很轻松地在类似UNIX的系统(如Linux)上telnet或ssh到一个服务器,在命令行方式下初始化一个运行在服务器上的导出或导入任务。然而,在其他操作系统上就不那么容易,Windows是最明显的例子。在推出数据泵之前,要从一个Windows系统下的Oracle数据库中导出大量数据,你很可能必须坐在服务器控制台前发出命令。通过TCP/IP连接导出数据只对小数据量是可行的。数据泵改变了这一切,因为即使你通过在你的客户端上运行该导出和导入实用程序来初始化一个导出或导入任务,该任务其实也运行在服务器上,所有的I/O也都发生在该服务器上。
出于安全性考虑,数据泵要求你通过Oracle的目录对象来指定其中存放着你要建立或读取的转储文件的目标目录。例如:
CREATE DIRECTORY export_dumps AS 'c:/a'; GRANT read, write ON DIRECTORY export_dumps TO gennick;
我以SYSTEM身份登录到我的实验室数据库上,并执行以上语句来建立一个目录对象,这个目录对象指向了我磁盘上的一个临时目录,以用来存放导出的转储文件。GRANT语句为用户gennick-就是我-分配了访问该目录的权限。我给自己分配读/写权限,因为我将导出和导入数据。你可以为一个用户分配读权限,限制他只能导入数据。
启动一个导出任务
你可以使用新的expdp实用程序来启动一个导出任务。因为参数与老的exp实用程序不同,所以你得熟悉这些新的参数。你可以在命令行中指定参数,但在本文中我使用了参数文件。我想导出我的整个模式(schema),使用了以下参数:
DUMPFILE=gnis%U.dmp DIRECTORY=export_dumps LOGFILE=gnis_export.log JOB_NAME=gnis_export
DUMPFILE指定我将向其中写入被导出数据的文件。%U语法给出了一个增量计数器,得到文件名gnis01.dmp、gnis02.dmp等。DIRECTORY指定了我的目标目录。
我的LOGFILE参数指定了日志文件的名字,这个文件是为每个导出任务默认创建的。JOB_NAME给任务指定了一个名字。我选择了一个易于记忆(和输入)的名字,因为我可能需要在后面才连接这个任务。要注意在指定任务名称时不要与你登录模式(schema)中的模式对象名称冲突。数据泵在你的登录模式中建立一个被称为任务主表的数据表,该表的名字与任务的名字相匹配。这个数据表跟踪该任务的状态,并最终被写入转储文件中,作为该文件所含内容的一个记录。
清单1显示了一个导出任务已被启动。该任务所做的第一件事是估计所需的磁盘空间大小。当估计值显示出来后,我按ctrl-C进入一个交互式的导出提示窗口,然后使用EXIT_CLIENT命令回到我操作系统的命令窗口。该导出任务仍然运行在服务器上。
注意,如果我要做并行导出并且将我的I/O分布在两个磁盘上,那么我可以对DUMPFILE参数值做出修改,并如下添加PARALLEL参数和值,如下所示:
DUMPFILE=export_dumps01:gnis%U.dmp, export_dumps02:gnis%U.dmp PARALLEL=2
注意,在这个并行导出任务中,目录名作为文件名的一部分来被指定。
检查状态
你可以随时连接到一个运行中的任务来检查其状态。要连接到一个导出任务,必须执行一条expdp命令,使用ATTACH参数来指定任务名称。清单2显示了到GNIS_EXPORT任务的连接。当你连接到一个任务,expdp显示该任务的相关信息和当前状态,并为你提供一个EXPORT>提示符。
当你连接到了一个任务后,你可以随时执行STATUS命令查看当前状态,如清单3所示。你还可以执行CONTINUE_CLIENT命令返回到显示任务进度的日志输出状态,该命令可以被缩写成如清单4所示的CONTINUE。
你可以通过查询DBA_DATAPUMP_JOBS视图快速查看所有数据泵任务的状态。你不能获得STATUS命令所给出的详细信息,但你可以快速查看到哪些任务在执行、哪些处于空闲状态等。另一个需要了解的视图是DBA_DATAPUMP_SESSIONS,它列出了所有活跃的数据泵工作进程。
从故障中恢复
重启动任务的能力使你可以从某些类型的故障中恢复过来。例如,清单5显示了一个用完了转储文件空间的导出任务的日志文件的结尾部分。然而,什么也没有丢失。该任务只是进入了一个空闲状态,当你连接到该任务并查看状态输出时就可以看到这一点。这个状态不显示任务空闲的原因。要确定这是因为转储文件的空间不够了,则你需要查看日志文件。
连接到因转储文件空间不够用了而停止的任务后,你可以在两个操作中选择其一:你可以使用KILL_JOB命令来中止该任务,或者增加一个和多个转储文件来继续该任务的运行。如果空间不够的问题是因为磁盘空间不足,则当然你要确保你增加的文件是在另一个有可用空间的磁盘上。你也许需要创建一个新的Oracle目录对象来指向这一新位置。
清单6使用ADD_FILES命令为我的空闲任务增加两个文件。这两个文件位于不同的目录中,它们都不同于为该任务的第一个转储文件所指定的目录。我使用START_JOB命令来重新启动该任务,然后使用CONTINUE查看屏幕上滚动的其余日志输出。
导入任务不会受到卸载(dump)文件空间不足的影响。但是,它们可能会受到数据表空间不足或无法扩展表空间的影响。导入的恢复过程和导出任务的基本上相同。首先,通过向表空间增加一个数据文件、扩展一个数据文件或其他方法来提供可用空间。然后连接到该任务,执行START_JOB命令。导入任务将从它中断的地方继续执行。
导入选定的数据
本文中的例子到目前为止显示的是对用户GENNICK拥有的所有对象进行模式(schema)数据库级别的导出。为了展示数据泵的一些新的功能,我要导入那些数据,而且为了使问题更有意思,我列出了以下要求:
- 仅导入GNIS数据表
- 将该数据表导入到MICHIGAN模式中
- 仅导入那些与密歇根州相关的数据行
- 不导入原始的存储参数
一开始,我可以在我的导入参数文件中写出以下四行:
DUMPFILE=gnis%U.dmp DIRECTORY=export_dumps LOGFILE=gnis_import.log JOB_NAME=gnis_import
这四行没有什么新意。他们指定了转储文件、目录、日志文件和该任务的名称。根据我们的四个要求,我可以使用INCLUDE参数将导入操作限制在我们感兴趣的一个数据表上:
INCLUDE=TABLE:"= 'GNIS'"
INCLUDE是个很有意思的参数。当你需要导入一个转储文件的部分内容时,你可以有两个方法:
- 你可以使用一个或多个INCLUDE参数列出你要导入的那些对象。
- 你可以使用EXCLUDE参数列出那些你不需要的内容,然后导入其余的内容。
因为我只需要一个对象,明确包含该对象比起明确不包括其它对象要容易得多。我的INCLUDE参数值的第一部分是关键字TABLE,表明我要导入的对象是一个数据表(其它的可能是一个函数或一个过程)。 接下来是一个冒号,然后是一个WHERE子句的谓词。我明确希望数据表名为GNIS,所以这个谓词是"= 'GNIS'"。如果必要,则你可以写出多个详细的谓词。通过INCLUDE和EXCLUDE参数,你可以确切地指出以什么样的粒度导入或导出。我建议你仔细地阅读关于这两个参数的文档。它们的功能之强大和多功能性是我在本文中所无法描述的。
我可以很轻松地完成该模式的改变,将来自GNIS模式的数据表重新映射到MICHIGAN模式:
REMAP_SCHEMA=gennick:michigan
我只需要关于密歇根州的数据行。为此,我可以使用QUERY参数来指定一个WHERE子句:
QUERY="WHERE gnis_state_abbr='MI'"
QUERY在老的实用程序中也有,但只能用于导出操作。数据泵使QUERY也能用于导入操作,因为数据泵利用了Oracle较新的外部数据表功能。只要可能,数据泵会选择直接路径来导出或导入数据,包括从数据库数据文件中读取数据然后直接写到一个导出转储文件中,或读取转储文件然后直接写入数据库数据文件中。但是,当你指定了QUERY参数时,数据泵将使用一个外部数据表。对于一个导入任务,数据泵将使用ORACLE_DATAPUMP存取驱动程序建立一个外部数据表,并执行一条INSERT...SELECT...FROM语句。
我的最后一个要求是避免导入与已被导出的数据表相关的存储参数。我希望MICHIGAN模式中的新GNIS表沿用该模式的默认表空间的默认存储参数。原因是MICHIGAN的默认表空间不足以容纳该数据表的本来大小,但是是以仅仅容纳与密歇根有关的数据行。通过TRANSFORM参数,我可以告诉导入任务不要包含与原始表相关的任何数据段属性:
TRANSFORM=SEGMENT_ATTRIBUTES:N
这看起来是件小事,但以前有很多次我都希望老的导入实用程序的TRANSFORM参数有这样的功能。我在试图将少量生产数据导入到测试系统中时经常失败,因为即使存储生产数据的各个区段当中许多是空的,其数据量也比我测试系统所能支持的大得多。对于只导入一张数据表的情况,预先建立数据表是解决这个问题的一个办法。然而,随着数据表的增多,预先建表会很麻烦。而TRANSFORM这样的简单开关可以轻松地将转储文件中所有数据段的属性全体忽略掉。
将我上面描述的所有选项放到一个参数文件中后,我可以调用导入实用程序,如下所示:
impdp michigan/password parfile=gnis_import.par
当作为一个没被授权的用户进行导入时,你需要连接到目标模式。如果你拥有IMP_FULL_DATABASE角色,那么你可以用自己的身份登录,然后导入到任何目标模式。
性能和多功能性
Oracle数据泵比起以前的导出和导入实用程序在性能上有很大的提高。这种性能提高大部分来自于读写转储文件的并行操作。你可以指定并行程度来达到你所要求的速度与资源消耗的折中。
|
数据泵还很好地利用了Oracle数据库其他最新开发的创新特性。Flashback(回闪)用于确保导出数据的一致性,而 FLASHBACK_SCN和FLASHBACK_TIME参数使你能够完全控制这一功能。直接路径(direct-path)API用于在任何可能的时候提高性能,当直接路径API不能使用时,用外部数据表和新的ORACLE_DATAPUMP外部数据表存取驱动程序来传输数据。
数据泵除了提供全新的性能外还为你提供灵活性。这表现在INCLUDE和EXCLUDE参数、QUERY参数、TRANSFORM参数和其他参数的实现中,这些参数使你能够精细地控制被加载和卸载的数据和对象。
人们一直在不断地对"大数据?quot;的含意进行重新定义,这种数据库容量之大在十年前还只能是梦想。在这样的世界里,数据泵对于你的数据库管理员所用的工具库是个不错的补充,使你能够以前所未有的速度对数据库进行数据导入和导出。
在Oracle 10g中, exp 和 imp 被重新设计为Oracle Data Pump(虽然Oracle 仍然装载了exp 和imp,并完全地支持它们)。如果你以前使用过exp 和 imp,那么Data Pump 的命令行程序的语法对你来说就不陌生了。
Data Pump 是运行在数据库内部的, 而不是像一个独立的客户端应用程序一样存在。这就意味着这部分的工作在一定程度上独立于发起执行导入或者导出任务的进程 。 在一台机器上 (例如一个定期任务) 可以开始执行导出的任务,而另一台机器上 (例如 DBA 的 手提电脑) 可以对任务的运行状态进行监控。 也正因为任务是运行在数据库内部的,所以如果你要将数据导出到一个文件中,那么你首先要做的事情就是为输出路径建立一个数据库的DIRECTORY 对象,然后给将要进行数据导入和导出的用户授权访问,命令如下:
create or replace directory dumpdir as 'c:/';
grant read,write on directory dumpdir to scott;
一旦该路经被授权后,就可以通过以下的命令参数导出用户的对象,这些命令与 exp 和 imp 中的命令非常相似:
expdp scott/tiger directory=dumpdir dumpfile=scott.dmp
当导出工作开始执行以后,可以通过按下[Ctrl]C (或者是客户端中具有相同功能的按键) 来“中止”导出任务。这样就不会再有数据发送到你的客户端了,但是该任务在数据库中仍然还在运行。你的客户端会进入交互模式(出现Export>提示符)。 在提示符后输入status就可以查看到当前有哪些任务正在运行。如果在客户端输入expdp attach=<任务名>,你就可以连接到一个正在运行的任务上。
Data Pump 并不是一定要写入到文件中。现在可以通过选项设置就可以将数据库对象通过SQL*Net直接导到一个远程数据库中。你所要做的就仅仅是指定remote 选项,然后加上与远程数据库连接的连接字符串。 这就有点类似于对数据库的一次性复制过程。
Data Pump 执行起来要比原来的exp 和 imp 客户端命令快得多。Data Pump 运行得更快是因为它有一个新特性——“parallel”选项。选定这个选项后,Data Pump 将会以四个不同的线程同时压送数据。下面举个例子,我先执行下面的任务,然后按 [Ctrl]C,接着察看后台任务的状态:
expdp scott/tiger directory=dumpdir dumpfile=scott2.dmp parallel=4
job_name=scott2
Export: Release 10.1.0.2.0 - Production on Friday, 31 December, 2004 14:54
Copyright (c) 2003, Oracle. All rights reserved.
Connected to: Oracle Database 10g Enterprise Edition Release 10.1.0.2.0 -
Production
With the Partitioning, OLAP and Data Mining options
FLASHBACK automatically enabled to preserve database integrity.
Starting "SCOTT"."SCOTT2": scott/******** directory=dumpdir
dumpfile=scott2.dmp parallel=4 job_name=scott2
Estimate in progress using BLOCKS method...
Export> status
Job: SCOTT2
Operation: EXPORT
Mode: SCHEMA
State: EXECUTING
Bytes Processed: 0
Current Parallelism: 4
Job Error Count: 0
Dump File: C:/SCOTT2.DMP
bytes written: 4,096
Worker 1 Status:
State: EXECUTING
Worker 2 Status:
State: WORK WAITING
Worker 3 Status:
State: WORK WAITING
Worker 4 Status:
State: WORK WAITING
其实不仅仅只有Data Pump 是在数据库内部运行的,事实上大部分的命令行性质的命令都是在数据库内部运行的,只不过是通过一个PL/SQL API—— DBMS_DATAPUMP显示出来。例如,可以通过以下的PL/SQL代码来实现通过PL/SQL 包启动导出任务:
declare
handle number;
begin
handle := dbms_datapump.open('EXPORT','SCHEMA');
dbms_datapump.add_file(handle,'SCOTT3.DMP','DUMPDIR');
dbms_datapump.metadata_filter(handle,'SCHEMA_EXPR','= ''SCOTT''');
dbms_datapump.set_parallel(handle,4);
dbms_datapump.start_job(handle);
dbms_datapump.detach(handle);
end; /
仔细研究 Data Pump ,你可以了解到 Data Pump 其他更多的新特性。例如,Data Pump 可以对数据文件重命名,可以将对象移动到不同的表空间中,还可以通过使用通配符结构或者是语句来查找图表对象或是图表。Data Pump 还可以用作外部表的接口 (例如,可以将一个表与存储在一个数据泵导出文件中的数据关联起来,这就像Oracle 9i 以及更高版本中的Oracle Loader 接口一样)。