之前介绍过MRP技术概览,其中有一个过程便是Loader Worker(装入程序工作流程),该过程实际上是通过Sql*Loader从操作系统数据文件中将数据加载到数据库中。而在主计划设置选项中,其中一项是设置Sql*Loader参数:

那么这些参数究竟是什么含义?我们又如何通过调整这些参数来优化Loader Worker的性能呢?

先来观察Loader Worker,会发现其执行时有两个或三个参数,如:

  1. CTRL_FILE=/u08/test/prodcomn/admin/out/TEST/M1922497MRLD_ITEMS.ctl
  2. DATA_FILE=/u08/test/prodcomn/admin/out/TEST/M1922497MRLD_ITEMS.dat
  3. DISCARD_FILE=/u08/test/prodcomn/admin/out/TEST/M1922497MRLD_ITEMS.dis

其中CTRL_FILE和DATA_FILE是每个并发请求都会有的,DISCARD_FILE会在部分并发请求中存在,比如载入MRP_SYSTEM_ITEMS表数据时就有该参数。想要理解这些参数的含义,便要先了解Sql*Loader参数的含义。

Sql*Loader 参数有数十个,相关的有如下几个:

  • CONTROL
    该参数指定了一个决定Sql*Loader行为的配置文件,它决定了需要从哪个数据文件读取数据,载入到哪张表里,分别有哪些字段等等。对应于Loader Worker的参数CTRL_FILE。
  • DATA
    该参数指定了数据来源,也就是从哪个数据文件中读取记录。指定的数据文件每行的数据往往有特定的格式,有特定的分隔符区分每个字段的值。对应于Loader Worker的参数DATA_FILE。
  • DISCARD
    该参数指定了一个文件用于记录那些未被正常导入到数据库中的记录。对应于Load Worker的参数DISCARD_FILE。
  • BINDSIZE
    Sql*Loader分批从数据文件中读取记录并提交到数据库中,每批的大小是有限制。该参数决定了Sql*Loader从数据文件读取记录大小的上限,除了每次读取的记录数必须小于ROWS指定的数目外,大小上不得超过BINDSIZE所指定的数值。该参数计量单位是Byte。
  • ROWS (每次Commit的记录数)
    在Conventional Path模式时,它限定了bind array最大记录数。在Direct Path模式时,它限定了保存之前从数据文件中读取的最大记录数。它的作用和BINDSIZE类似,只是一个限制了记录数,一个限制了记录大小。
  • READSIZE (读缓存大小)
    该参数仅针对从数据文件载入数据的方式时有效,默认值为64k,最大值因系统平台各有不同。在Conventional Path模式时,bind array 受限于读缓存,也就是说,在系统内存和bind array足够大的前提下,如果读缓存越大,则可以有更多的记录在commit前被读取,这也就意味着载入性能越好。当READSIZE小于BINDSIZE时,则READSIZE会被自动加大。

上述参数中,BINDSIZE和ROWS对应于Sql*Loader参数设置界面的相应字段,其他参数则对应于并发请求的相应参数。

在执行时,Sql*Loader会先将数据读取到bind array,然后一次性insert到表中并commit,关于Sql*Loader的具体工作原理,可以另行写一篇文章叙述,简单了讲,就是说多数情况下(可用的连续内存空间够大),两个参数越大,意味着更少的Commit次数,更高的效率(当然,这也不是绝对的,只是简单这么一说),所以从性能优化角度看,要确定调整这两个参数以在相应的环境中达到最优。对于数据行数量和大小的关系,可以用以下公式来计算:

bind array 大小= ROWS * (固定宽度字段长度之和 + 可变长度字段最大长度之和 + ( 可变长度字段数 * 字段分割符长度) )

虽然公式看着简单,但是针对具体情况计算起来却也繁琐。有一个简单的方式可以大致获取所需信息:
1. 观察MRP运行过程,在Loader Worker这一步时根据参数备份对应的CONTROL文件和数据文件。
2. 根据CONTROL文件里面所指定表,创建一个相同结构的表。
3. 修改CONTROL文件,将表名和数据文件修改为之前备份的文件名。例:

OPTIONS (BINDSIZE=1000000, ROWS=1000, SILENT=(FEEDBACK,DISCARDS))
LOAD DATA
INFILE 'test.dat'
APPEND
INTO TABLE APPS.TEST_TBL

FIELDS TERMINATED BY '|' # 是个特殊字段,会导致feedproxy无法显示
(
inventory_item_id,
 organization_id,
description nullif( description ='-23453'),
 category_id nullif( category_id ='-23453'),
……
LAST_UPDATE_DATE SYSDATE,
CREATION_DATE SYSDATE
)

4. 运行Sql*Loader,增加参数LOG,将过程日志记录下来。

bash-3.00$ sqlldr userid=apps/apps_pwd control=test.ctl log=test.log

5. 查看日志文件,可看到以下关键信息:

SQL*Loader: Release 8.0.6.3.0 - Production on Thu Mar 25 08:28:17 2010

(c) Copyright 1999 Oracle Corporation.  All rights reserved.

Control File:   test.ctl
Data File:      test.dat
  Bad File:     test.bad
  Discard File:  none specified

 (Allow all discards)

Number to load: ALL
Number to skip: 0
Errors allowed: 50
Bind array:     1000 rows, maximum of 1000000 bytes #这里是Sql*Loader参数设置的
Continuation:    none specified
Path used:      Conventional
Silent options: FEEDBACK and DISCARDS

……

Table APPS.TEST_TBL:
  406082 Rows successfully loaded.
  0 Rows not loaded due to data errors.
  0 Rows not loaded because all WHEN clauses were failed.
  0 Rows not loaded because all fields were null.

Space allocated for bind array:                 992880 bytes(60 rows) #达到了BINDSIZE上限
Space allocated for memory besides bind array:        0 bytes

Total logical records skipped:          0
Total logical records read:        406082
Total logical records rejected:         0
Total logical records discarded:        0

Run began on Thu Mar 25 08:28:17 2010
Run ended on Thu Mar 25 08:30:27 2010

Elapsed time was:     00:02:10.51
CPU time was:         00:01:21.52

平均每一行需要空间 992880/60 ~= 16548 Bytes ~= 16.16 K,再增加一行就超出了BINDSIZE所限1000000,所以这里每次最多只允许读取60行记录。

调整这两个参数,将BINDSIZE设置为165480000,ROWS设置为10000,重新运行Sql*Loader:

Space allocated for bind array:               165480000 bytes(10000 rows)
Space allocated for memory besides bind array:        0 bytes

Total logical records skipped:          0
Total logical records read:        406082
Total logical records rejected:         0
Total logical records discarded:        0

Run began on Thu Mar 25 08:56:22 2010
Run ended on Thu Mar 25 08:58:12 2010

Elapsed time was:     00:01:50.49
CPU time was:         00:01:26.52

这里,根据每行记录的大小,结合实际系统情况,就可以调整BINDSIZE和ROWS到一个更合适的数值了。当然,多数情况下,保持默认值即可,并没有多少调整的必要。

关于Sql*Loader的载入方式,有Conventional Path和Direct Path两种模式,由配置文件MRP:Use Direct Load Option控制,修改为Direct Path模式或许会发现执行速度快了不少,但是强烈不建议采用这种模式。