如需将 Oracle Direct NFS (dNFS) 与备份/恢复设备搭配使用,必须满足以下要求:
数据库服务器与备份/恢复设备之间的网络带宽充足
使用所有 Oracle 要求或建议的补丁。Oracle 在 Oracle 支持文档中维护着必需或建议的补丁列表。
配置管理控制台,以便通过 dNFS 保护和挂载虚拟 Oracle 数据库
如需执行基于 dNFS 的备份,您必须将备份/恢复设备暂存磁盘格式(磁盘偏好设置)设为 NFS。
按照以下说明将暂存磁盘格式(磁盘偏好设置)设为 NFS:
依次选择管理 > 主机。
右键点击主机,然后选择修改。
在暂存磁盘格式中,选择 NFS,然后点击保存。
要在目标主机上运行 dNFS,需要执行的操作
请执行以下操作,确保 dNFS 配置正确无误:
在 DB Alert.log 下查看以下消息,以确认 dNFS 已启用:
Oracle instance running with ODM: Oracle Direct NFS ODM Library Version 3.01.
如果 dNFS 未启用,请启用它:
NFS 客户端软件包必须存在于执行保护作业的目标数据库主机上,以及您可能使用 dNFS 挂载已捕获的 Oracle 数据库的任何 Oracle 主机上。例如,对于 Linux,
nfs-util
软件包应存在于主机上。请检查以下内容:rpm -qa |grep nfs-util
在 Oracle 主机上启用 dNFS:
cd $ORACLE_HOME/rdbms/lib make -f ins_rdbms.mk dnfs_on
重启在该
ORACLE_HOME
上运行的数据库,然后在 DB Alert.log 下查找以下消息,以确认 dNFS 已启用:使用 ODM 运行的 Oracle 实例:Oracle Direct NFS ODM 库版本 3.0
在备份作业期间,运行以下查询以检查 dNFS 用量:
select * from gv$dnfs_servers;
您可以查看正在进行的 I/O 的 NFS 读写统计信息:
select inst_id, PNUM, NFS_READ, NFS_WRITE, NFS_COMMIT, NFS_MOUNT from gv$dnfs_stats where NFS_READ>0 or NFS_WRITE>0 order by inst_id, PNUM;
我们可以看到 dnfs 渠道处理信息。
select c.inst_id, program, pid,pname, local, path from gv$process p, gv$dnfs_channels c where p.inst_id = c.inst_id and c.pnum = p.pid;
排查 dNFS 问题:数据库问题
其中包括:
提醒日志
进行任何调试操作的第一步是检查提醒日志中是否有与 dNFS 相关的消息。在使用 dNFS 的数据库中,我们发现了一个常见问题,即套接字缓冲区空间受限。Oracle 会尝试调整大小,但可能会受到操作系统的限制。在这种情况下,提醒日志中会出现如下错误:
Direct NFS: Failed to set socket buffer size.wtmax=[1048576]\
rtmax=[1048576], errno=-1
在提醒日志中要查找的其他内容包括,是否使用正确的网卡与文件传送器通信。您可以通过查找类似如下内容的消息来确定这一点:
Direct NFS: channel id [0] path [192.168.56.3] to filer [192.168.56.3] via local [] is UP
数据库跟踪文件
如果发生 I/O 问题,您可以在数据库中设置以下事件,以捕获其他日志记录信息。设置这些事件,等待相应问题发生,然后查看轨迹文件。
ALTER SYSTEM SET MAX_DUMP_FILE_SIZE =UNLIMITED;
ALTER SYSTEM SET EVENTS '10298 trace name context forever, level 1'; # KSFD I/O tracing
ALTER SYSTEM SET EVENTS '19392 trace name context forever, level 8'; # kgnfs tracing
ALTER SYSTEM SET EVENTS '19394 trace name context forever, level 8'; # skgnfs tracing
ALTER SYSTEM SET EVENTS '19396 trace name context forever, level 6'; # kgodm tracing
ALTER SYSTEM SET EVENTS '19398 trace name context forever, level 128'; # mount tracing errors
数据库无响应
如果在 dNFS 上运行的数据库没有响应,请使用 sqlplus 以 SYSDBA 身份登录,然后执行“hanganalyze”或转储:
```oradebug
oradebug setmypid
oradebug unlimited
oradebug hanganalyze 3
oradebug dump systemstate 266
```
如果数据库是 RAC 数据库,请在最后两个 oradebug 命令中添加 -g 选项。
dNFS 观看次数
dNFS 客户端实际上位于数据库内核中。因此,数据库中存在多个 v$ 视图,用于从数据库内监控和检查 dNFS 的运行状况。Oracle 提供了一个软件包,可用于快速监控 dNFS 性能。此软件包位于 Oracle dNFS 监控器软件包中。
部署后,DBA 可以执行以下操作来获取信息(参数:dnfs_monitor[休眠时间]、dnfs_itermonitor[休眠时间、检查次数],休眠时间以秒为单位):
SQL> set serveroutput on
SQL> set lines 200
SQL> exec dnfs_monitor(60);
Started at 01/18/2017 10:09:46 AM
Finished at 01/18/2017 10:10:46 AM
READ IOPS: 2
WRITE IOPS: 3
TOTAL IOPS: 5
READ Throughput: 0 MB/s
WRITE Throughput: 0 MB/s
TOTAL Throughput: 0 MB/s
SQL> exec dnfs_itermonitor(2,10)
Started at 01/18/2017 10:20:18 AM
TIMESTAMP READ IOPS WRITE IOPS TOTAL IOPS READ(MB/s) WRITE (MB/s) TOTAL (MB/s)
01/18/2017 10:20:20 AM 15 7 22 0 0 0
01/18/2017 10:20:22 AM 2 3 5 0 0 0
01/18/2017 10:20:24 AM 0 3 3 0 0 0
01/18/2017 10:20:26 AM 2 2 4 0 0 0
01/18/2017 10:20:28 AM 0 3 3 0 0 0
01/18/2017 10:20:30 AM 2 3 5 0 0 0
01/18/2017 10:20:32 AM 4 3 7 0 0 0
01/18/2017 10:20:34 AM 0 3 3 0 0 0
01/18/2017 10:20:36 AM 2 3 5 0 0 0
01/18/2017 10:20:38 AM 2 3 5 0 0 0
Finished at 01/18/2017 10:20:38 AM
V$ 观看次数的计算方式如下:
V$DNFS_SERVER:显示所有 NFS 服务器连接的信息(每个 NFS 服务器一个)。此视图有助于验证连接性和 TCP 套接字设置。
V$DNFS_CHANNELS:显示指向 NFS 服务器创建的所有网络路径的信息。每个 dNFS 客户端都会为每个进程和每个网络路径创建一个通道。如果存在多个路径(多个 NIC),dNFS 客户端会在所有通道之间进行负载均衡。数据反映的是自上次选择以来的活动。
V$DNFS_FILES:显示使用 dNFS 客户端打开的文件。
V$DNFS_STAT:dNFS 客户端的性能指标。
列 | 说明 |
---|---|
SRVNAME
|
NFS 服务器名称 |
DIRNAME
|
NFS 服务器导出的卷 |
MNTPORT
|
本地挂载端口 |
NFSPORT
|
NFS 服务器端口 |
WTMAX
|
NFS 服务器的最大写入大小 |
RTMAX
|
NFS 服务器的最大读取大小 |
列 | 说明 |
---|---|
PNUM
|
Oracle 进程编号(链接到 v$process 中的 PID) |
SVRNAME
|
NFS 服务器名称 |
PATH
|
到服务器的网络路径 |
CH_ID
|
dNFS 渠道 ID |
SVR_ID
|
dNFS 服务器 ID |
SENDS
|
自上次选择以来通过渠道发送的操作。 |
RECVS
|
接收自上次选择以来通过渠道进行的操作。 |
PINGS
|
自上次选择以来通过渠道执行的 ping 操作。 |
列 | 说明 |
---|---|
FILENAME
|
文件的名称。 |
FILESIZE
|
文件大小。 |
PNUM
|
进程 ID(指向 v$process 中的 PID) |
SRV_ID
|
NFS 服务器 ID |
列 | 说明 |
---|---|
PNUM
|
Oracle 进程编号(指向 v$process 中的 PID)
|
NFS_NULL
|
Null operations
|
NFS_GETATTR
|
获取属性操作 |
NFS_SETATTR
|
设置属性操作 |
NFS_LOOKUP
|
查找运算 |
NFS_ACCESS
|
访问操作 |
NFS_READLINK
|
读取关联操作 |
NFS_READ
|
读取操作 |
NFS_WRITE
|
写入操作 |
NFS_CREATE
|
创建操作 |
NFS_MKDIR
|
执行目录操作 |
NFS_MKNOD
|
执行节点操作 |
NFS_SYMLINK
|
符号链接操作 |
NFS_REMOVE
|
移除操作 |
NFS_RMDIR
|
移除目录操作 |
NFS_RENAME
|
重命名操作 |
NFS_LINK
|
关联操作 |
NFS_READDIR
|
读取目录操作 |
NFS_READDIRPLUS
|
读取目录加操作 |
NFS_FSSTAT
|
文件系统状态操作 |
NFS_FSINFO
|
文件系统信息操作 |
NFS_PATHCONF
|
路径配置操作 |
NFS_COMMIT
|
提交操作 |
NFS_MOUNT
|
挂载操作 |
Oracle dNFS 监控器软件包
CREATE OR REPLACE PROCEDURE dnfs_monitor
(sleepSecs IN NUMBER)
IS
startTime DATE;
startReadIOPS NUMBER;
startWriteIOPS NUMBER;
startReadBytes NUMBER;
startWriteBytes NUMBER;
endTime DATE;
endReadIOPS NUMBER;
endWriteIOPS NUMBER;
endReadBytes NUMBER;
endWriteBytes NUMBER;
readThr NUMBER;
writeThr NUMBER;
readIOPS NUMBER;
writeIOPS NUMBER;
elapsedTime NUMBER;
BEGIN
SELECT sysdate, SUM(stats.nfs_readbytes), SUM(stats.nfs_writebytes),
SUM(stats.nfs_read), SUM(stats.nfs_write)
INTO startTime, startReadBytes, startWriteBytes, startReadIOPS, startWriteIOPS
FROM dual, v$dnfs_stats stats;
DBMS_OUTPUT.PUT_LINE('Started at ' || TO_CHAR(startTime,'MM/DD/YYYY HH:MI:SS AM'));
DBMS_LOCK.SLEEP(sleepSecs);
SELECT sysdate, SUM(stats.nfs_readbytes), SUM(stats.nfs_writebytes), SUM(stats.nfs_read), SUM(stats.nfs_write)
INTO endTime, endReadBytes, endWriteBytes, endReadIOPS, endWriteIOPS
FROM dual, v$dnfs_stats stats;
DBMS_OUTPUT.PUT_LINE('Finished at ' || to_char(endTime,'MM/DD/YYYY HH:MI:SS AM'));
elapsedTime := (endTime - startTime) * 86400;
readThr := (endReadBytes - startReadBytes)/(1024 * 1024 * elapsedTime);
writeThr := (endWriteBytes - startWriteBytes)/(1024 * 1024 * elapsedTime);
readIOPS := (endReadIOPS - startReadIOPS)/elapsedTime;
writeIOPS := (endWriteIOPS - startWriteIOPS)/elapsedTime;
DBMS_OUTPUT.PUT_LINE('READ IOPS: ' || LPAD(TO_CHAR(readIOPS, '999999999'), 10, ' '));
DBMS_OUTPUT.PUT_LINE('WRITE IOPS: ' || LPAD(TO_CHAR(writeIOPS,
'999999999'), 10, ' '));
DBMS_OUTPUT.PUT_LINE('TOTAL IOPS: ' || LPAD(TO_CHAR(readIOPS + writeIOPS, '999999999'), 10, ' '));
DBMS_OUTPUT.PUT_LINE('READ Throughput: ' || LPAD(TO_CHAR(readThr, '999999999'), 10, ' ') || ' MB/s');
DBMS_OUTPUT.PUT_LINE('WRITE Throughput: ' || LPAD(TO_CHAR(writeThr,
'999999999'), 10, ' ') || ' MB/s');
DBMS_OUTPUT.PUT_LINE('TOTAL Throughput: ' || LPAD(TO_CHAR(readThr + writeThr, '999999999'), 10, ' ') || ' MB/s');
END;
/
CREATE OR REPLACE PROCEDURE dnfs_itermonitor
(sleepSecs IN NUMBER,
iter IN NUMBER)
IS
startTime DATE;
startReadIOPS NUMBER;
startWriteIOPS NUMBER;
startReadBytes NUMBER;
startWriteBytes NUMBER;
endTime DATE;
endReadIOPS NUMBER;
endWriteIOPS NUMBER;
endReadBytes NUMBER;
endWriteBytes NUMBER;
readThr NUMBER;
writeThr NUMBER;
readIOPS NUMBER;
writeIOPS NUMBER;
i NUMBER;
elapsedTime NUMBER;
BEGIN
DBMS_OUTPUT.PUT_LINE('Started at ' || TO_CHAR(SYSDATE, 'MM/DD/YYYY HH:MI:SS AM'));
DBMS_OUTPUT.PUT_LINE(
LPAD('TIMESTAMP', 15, ' ')||
LPAD('READ IOPS', 33, ' ')||
LPAD('WRITE IOPS', 15, ' ')||
LPAD('TOTAL IOPS', 15, ' ')||
LPAD('READ (MB/s)', 15, ' ')||
LPAD('WRITE (MB/s)', 15, ' ')||
LPAD('TOTAL (MB/s)', 15, ' '));
FOR i IN 1..iter
LOOP
SELECT sysdate, SUM(stats.nfs_readbytes), SUM(stats.nfs_writebytes), SUM(stats.nfs_read), SUM(stats.nfs_write)
INTO startTime, startReadBytes, startWriteBytes, startReadIOPS, startWriteIOPS
FROM dual, v$dnfs_stats stats;
DBMS_LOCK.SLEEP(sleepSecs);
SELECT sysdate, SUM(stats.nfs_readbytes), SUM(stats.nfs_writebytes), SUM(stats.nfs_read), SUM(stats.nfs_write)
INTO endTime, endReadBytes, endWriteBytes, endReadIOPS, endWriteIOPS
FROM dual, v$dnfs_stats stats;
elapsedTime := (endTime - startTime) * 86400;
readThr := (endReadBytes-startReadBytes)/(1024 * 1024 * elapsedTime);
writeThr := (endWriteBytes-startWriteBytes)/(1024 * 1024 * elapsedTime);
readIOPS := (endReadIOPS - startReadIOPS)/elapsedTime;
writeIOPS := (endWriteIOPS - startWriteIOPS)/elapsedTime;
DBMS_OUTPUT.PUT_LINE(
TO_CHAR(endTime, 'MM/DD/YYYY HH:MI:SS AM')||
LPAD(TO_CHAR(readIOPS, '999999999'), 15, '') ||
LPAD(TO_CHAR(writeIOPS, '999999999'), 15,' ') ||
LPAD(TO_CHAR(readIOPS + writeIOPS, '999999999'),15, ' ') ||
LPAD(TO_CHAR(readThr, '999999999'), 15, '') ||LPAD(TO_CHAR(writeThr, '999999999'), 15, '
') ||
LPAD(TO_CHAR(readThr + writeThr, '999999999'), 15, ' '));
END LOOP;
DBMS_OUTPUT.PUT_LINE('Finished at ' || to_char(endTime, 'MM/DD/YYYY HH:MI:SS AM'));
END;