0%

postgresql支持在表和文件之间拷贝数据,可以使用PostgreSQL扩展的SQL函数COPY或者psql内部命令\copy来做这件事。

导出数据的格式:

1
COPY table_name (query) TO 'filename' PROGRAM 'command' STDOUT \[ WITH OPTIONS \];

导入数据的格式:

1
COPY table_name (query) FROM 'filename' PROGRAM 'command' STDIN \[ WITH OPTIONS \];

更详细的格式描述见[1]

COPY函数

COPY函数在服务器上执行,如果输出到文件,要注意postgresql服务器进程有没有对指定目录的执行权限,比如:

1
=# COPY (SELECT * FROM base.tb_dictionary WHERE ...) TO '/tmp/designusage.csv' WITH CSV DELIMITER ',';

将查询结果输出到临时目录下的指定文件。debian系统postgresql系统进程用户postgres的主目录是/var/lib/postgresql,也可以将文件输出到此目录。
如无写权限,会提示:

1
ERROR: could not open file "..." for writing: Permission denied

\copy命令

psql命令\copy在客户端执行,如果输出到文件是输出到客户端的文件系统,要注意当前执行psql的用户权限。比如:

1
=# \\copy (SELECT * FROM base.tb_dictionary WHERE ...) TO '/tmp/designusage.csv' WITH CSV DELIMITER ','

如果要输出列名,可以指定HEADER选项,HEADER只用于CSV格式。
可以写到临时目录,也可以写到当前用户的主目录。

其实\copy命令实际上是使用了COPY FROM STDIN或者COPY TO STDOUT,然会通过STDIN或STDOUT与文件交互。

所以可以这样:

1
$ psql -c "COPY (SELECT * FROM base.tb_dictionary WHERE ...) TO STDOUT WITH CSV DELIMITER ',' " -U role -h host dbname > export.csv

使用COPY函数输出到STDOUT,然后重定向到文件。

References:
[1]copy data between a file and a table
[2]Outputting to CSV in Postgresql

===
[erq]

一个小坑,windows计划任务执行的命令或脚本,以及嵌套调用的命令或脚本,其路径名中不能包含括号,不能包含括号,不能包含括号。不然只会告诉你执行失败,退出码0x1,别无其他可用信息。

pandoc是一款强悍的文档的转换工具,支持绝大多数文档格式之间的相互转换,而且具有强大的控制能力。

安装

1
# apt install texlive-xetex pandoc

转换markdown

默认latex引擎pdflatex不支持中文,需要使用xelatex引擎,并且要指定中文字体

可以这样查看系统中文字体:

1
$ fc-list :lang=zh

然后mkd转换到pdf:

1
$ pandoc foo.mkd --latex-engine=xelatex -V CJKmainfont="Noto Sans CJK SC" -o foo.pdf

然而对中文的支持还是有些问题,这样会把较长的文本行截断,需要定制template来解决此问题。

先导出pandoc默认模板:

1
$ pandoc -D latex > mytemplate.tex

在生成的模板文件mytemplate.tex文件的第一行后面添加如下行:

1
2
\\XeTeXlinebreaklocale "zh"
\\XeTeXlinebreakskip = 0pt plus 1pt minus 0.1pt

将模板文件mytemplate.lex置于~/.pandoc/templates/路径下,或者置于转换命令当前目录下。

然后这样转换:

1
$ pandoc manual.mkd --latex-engine=xelatex -V CJKmainfont="Noto Sans CJK SC" -o manual.pdf --template=mytemplate.tex

还有一个小问题,默认生成的pdf边距比较大,可以通过geometry参数来控制边距。

所以最后的命令行大约是这个样子的:

1
$ pandoc manual.mkd --latex-engine=xelatex -V CJKmainfont="Noto Sans CJK SC" -o manual.pdf -V geometry:"top=2cm, bottom=1.5cm, left=1cm, right=1cm" --template=mytemplate.tex

可以用模板文件指定使用的字体,模板文件可以很复杂,嗯,latex太复杂了。

pandoc转换markdown时,对两个tab缩进支持不好,还没找到好的解决办法。

Mac OS X

安装pandoc和mactex

1
2
$ brew install pandoc
$ brew cask install mactex

中文字体可以使用苹方简体中文 “PingFang SC”

References:
[1]Pandoc With Chinese (简体中文)
[2]pandoc faqs
[3]Pandoc: Markdown to PDF, without cutting off code block lines that are too long
[4]纯文本做笔记 — 使用 Pandoc 与 Markdown 生成 PDF 文件
[5]使用xelatex生成中文pdf
[6]使用pandoc转换md为PDF并添加中文支持

===
[erq]

tomcat自身支持会话复制的集群。

此post主要讲使用memcached存储会话,使用msm(memcached-session-manager)来管理会话复制。

通过配置共享会话的tomcat集群,可以提高服务的高可用性,并可以做到不停机连续更新应用程序。

示例配置采用两台机器,ip分别为10.100.0.20和10.100.0.21。每台机器分别部署nginx,tomcat和memcached。两个tomcat实例和两个memcached实例通过msm组成一个会话共享集群,前端由nginx做负载均衡。

还可以在nginx之前做DNS负载均衡。

nginx配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

upstream servers {
server 10.100.0.20:8080;
server 10.100.0.21:8080;
}

location / {
proxy_pass http://servers;
proxy_set_header Accept-Encoding "gzip";

# proxy websocket reverse
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}

nginx会将客户请求分发到后端服务器组

memcached配置
安装

1
# apt install memcached

memcached默认安装只监听本地回环地址,更改/etc/memcached.conf,注释掉下面的行:

1
#-l 127.0.0.1

重启memcached服务会监听所有本地接口。

msm和tomcat配置

kiro序列化性能较高,因此这里使用kiro序列化器。

将msm基础包memcached-session-manager-${version}.jar,memcached-session-manager-tc8-${version}.jar和spymemcached-2.11.1.jar,以及kryo序列化支持jar包拷贝到$CATALINA_HOME/lib/目录。

debian系统中tomcat8的lib目录位于/usr/share/tomcat8/lib/

还有一个包Objenesis也需要下载安装到此目录中。

sticky sessions + kryo配置

/etc/tomcat8/context.xml文件中context一节最后添加:

1
2
3
4
5
6
<Manager className="de.javakaffee.web.msm.MemcachedBackupSessionManager"
memcachedNodes="n1:10.100.0.20:11211,n2:10.100.0.21:11211"
failoverNodes="n1"
requestUriIgnorePattern=".*\\.(icopnggifjpgcssjs)$"
transcoderFactoryClass="de.javakaffee.web.msm.serializer.kryo.KryoTranscoderFactory"
/>

msm默认处于sticky模式,因此不用显式指定sticky参数。failoverNodes参数指定本机memcached节点名称,这样正常情况下msm会存储会话到其他memcached节点,当没有其他节点可用时才会使用failoverNodes。

non-sticky sessions + kryo配置

/etc/tomcat8/context.xml文件中context一节最后添加:

1
2
3
4
5
6
7
<Manager className="de.javakaffee.web.msm.MemcachedBackupSessionManager"
memcachedNodes="n1:10.100.0.20:11211,n2:10.100.0.21:11211"
sticky="false"
sessionBackupAsync="false"
lockingMode="auto"
transcoderFactoryClass="de.javakaffee.web.msm.serializer.kryo.KryoTranscoderFactory"
/>

这里明确指定sticky参数为false,注意不要设置requestUriIgnorePattern参数,否则当前配置下会出现问题,无法完成session共享。

sticky会话模式就是将用户“粘”在某一个服务器节点上,即同一个会话中的请求必须被转发到同一个节点上,除非该节点宕机才转发到故障转移节点。

non-sticky会话模式则是每一次请求都可能转发到不同节点。

sticky会话模式性能更好。

References:
[1]SetupAndConfiguration
[2]分布式session

===
[erq]

众所周知的原因,访问国外的网络不是那么顺畅。gradle下载插件时,几乎无法下载,而使用proxychains也不凑效。
可以这样为gradle设置全局socks5代理:

$HOME/.gradle/gradle.properties文件中添加如下行,如没有该文件请自行建立:

1
org.gradle.jvmargs=-DsocksProxyHost=127.0.0.1 -DsocksProxyPort=1080

如果不设置代理,也可以使用maven镜像仓库,或者maven本地仓库。

最后,祝狗日的Great Fucking Wall和XXX早日死掉!

===
[erq]

设置NAT内部服务器,并且设置了端口回流,内网可以正常通过公网ip访问内部服务。

但是当通过外网访问该服务时,从服务器上观察,TCP连接的源地址也被替换成了防火墙的内网接口地址,不过访问一切正常。

经排查,是因为设置端口回流时,在内网接口的ACL条目中未指定source,类似如下:

1
rule 216 permit ip destination 10.100.0.31 0

导致防火墙将所有访问NAT服务的源地址全部替换成了防火墙的内网接口地址(内网接口优先级高,且不区分内外网网络地址???)。

通过指定哪些内网段访问NAT服务器时可以通过内网接口,可以只替换这些网段的源地址。

1
2
rule 226 permit ip source 192.168.1.0 0.0.0.255 destination 10.100.0.31 0 
rule 227 permit ip source 192.168.2.0 0.0.0.255 destination 10.100.0.31 0

这样以来,外网访问时,源地址没有被替换,但acl rule未指定的其他内网地址段将不能通过公网ip访问NAT服务。

===
[erq]

rman备份脚本出现错误提示:

1
2
3
4
5
6
7
RMAN-06207: WARNING: 380 objects could not be deleted for DISK channel(s) due
RMAN-06208: to mismatched status. Use CROSSCHECK command to fix status
RMAN-06210: List of Mismatched objects
RMAN-06211: ==========================
RMAN-06212: Object Type Filename/Handle
RMAN-06213: --------------- ---------------------------------------------------
RMAN-06214: Archivelog D:\\ARCHIVED_LOG\\ARC24290_0749146507.001

然后手动执行crosscheck并重新删除:

1
2
RMAN> crosscheck archivelog all;
RMAN> delete obsolete;

提示由于恢复目录中没有归档日志的信息,无法删除。列出的归档日志是早已经物理删除掉、无用的日志。

1
RMAN> delete expired archivelog all;

也无法删除

最后,强制删除废弃的归档日志,force关键字会忽略掉错误将其干掉。

1
RAMN> delete force obsolete;

删除成功。

===
[erq]

感谢Let’s Encrypt让互联网更安全。

Let’s Encrypt的客户端刚刚更名为certbot,以前叫letsencrypt。certbot可以自动化的申请,安装和更新证书,让生活更美好。
不过当前nginx插件尚不成熟,而且certbot自身尚处于beta阶段,当前版本0.7.0,debian源里的版本也比较陈旧。

因此还是需要一些简单的手工配置。

安装

从官方仓库克隆certbot

1
$ sudo git clone https://github.com/certbot/certbot /opt/certbot

执行certbot-auto会自动安装发行版依赖和python依赖:

1
2
$ cd /opt/certbot
$ ./certbot-auto

配置nginx

申请证书时,let’s encrypt需要访问域名的特定目录来确认域名的所有权,由certbot配合来完成验证。

需要访问的目录为${webroot-path}/.well-known/acme-challenge/,申请证书时,certbot会写入认证所需信息,由let’s encrypt来验证。

将此目录映射到/var/www/letsencrypt,先建立目录:

1
2
3
$ cd /var/www
# mkdir letsencrypt
# chgroup www-data letsencrypt

然后修改站点配置文件:

1
2
3
4
5
6
7
8
9
server {
listen 80 default_server;
server_name my-domain;

location /.well-known/acme-challenge {
root /var/www/letsencrypt;
}
...
}

最后重新加载nginx配置文件:

1
# nginx -t && sudo nginx -s reload

配置certbot

新建一个配置文件/etc/letsencrypt/configs/my-domain.conf来指定要申请证书的域名等相关信息:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# This is an example of the kind of things you can do in a configuration file.
# All flags used by the client can be configured here. Run Certbot with
# "--help" to learn more about the available options.

# Use a 4096 bit RSA key instead of 2048
rsa-key-size = 2048 # or 4096

# Uncomment and update to register with the specified e-mail address
email = xxx@gmail.com

# Uncomment and update to generate certificates for the specified
# domains.
domains = my-domain, www.my-domain

# Uncomment to use a text interface instead of ncurses
text = True

# Uncomment to use the standalone authenticator on port 443
# authenticator = standalone
# standalone-supported-challenges = tls-sni-01

# Uncomment to use the webroot authenticator. Replace webroot-path with the
# path to the public_html / webroot folder being served by your web server.
authenticator = webroot
webroot-path = /var/www/letsencrypt/

可以使用webroot插件来减轻验证配置,自动修改web server来完整验证。

申请证书

因为let’s encrypt限制每个站点在一定时间内申请的证书数量,所以可以用--test-cert选项进行测试,此时申请的证书是无效的,
但操作步骤是完全一样的,测试通过后去掉此选项就是申请正式证书了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
$ ./certbot-auto --test-cert --config /etc/letsencrypt/configs/mydomain.conf certonly
Checking for new version...
Requesting root privileges to run certbot...
sudo CERTBOT_AUTO=./certbot-auto /home/xxx/.local/share/letsencrypt/bin/letsencrypt
--test-cert --config /etc/letsencrypt/configs/mydomain certonly

-------------------------------------------------------------------------------
Please read the Terms of Service at https://letsencrypt.org/documents/LE-
SA-v1.0.1-July-27-2015.pdf. You must agree in order to register with the ACME
server at https://acme-staging.api.letsencrypt.org/directory
-------------------------------------------------------------------------------
(A)gree/(C)ancel: A

IMPORTANT NOTES:
- Congratulations! Your certificate and chain have been saved at
/etc/letsencrypt/live/mydomain/fullchain.pem. Your cert
will expire on 2016-08-12. To obtain a new version of the
certificate in the future, simply run Certbot again.
- If you lose your account credentials, you can recover through
e-mails sent to xxx@gmail.com.
- Your account credentials have been saved in your Certbot
configuration directory at /etc/letsencrypt. You should make a
secure backup of this folder now. This configuration directory will
also contain certificates and private keys obtained by Certbot so
making regular backups of this folder is ideal.

申请的证书位于/etc/letsencrypt/live/mydomain/目录下

安装证书

1
2
3
4
5
6
7
8
9
server {
listen 443 ssl default_server;
server_name my-domain;

ssl_certificate /etc/letsencrypt/live/my-domain/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/my-domain/privkey.pem;

...
}

更多ssl优化设置参见[4]

更新证书

let’s encrypt发行的证书只有90天的有效期,到期需要更新证书。如果参数没有变化,更新证书只需简单的执行:

1
$ ./certbot-auto renew

certbot-auto会使用上次申请证书时使用的参数来更新证书。
如果要测试证书更新,添加选项--dry-run,此时不会更改系统当前设置。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$ ./certbot-auto renew --dry-run
Checking for new version...
Requesting root privileges to run certbot...
sudo CERTBOT_AUTO=./certbot-auto /home/guoqiang/.local/share/letsencrypt/bin/letsencrypt renew --dry-run

-------------------------------------------------------------------------------
Processing /etc/letsencrypt/renewal/cucc.tazzfdc.com.conf
-------------------------------------------------------------------------------
** DRY RUN: simulating 'certbot renew' close to cert expiry
** (The test certificates below have not been saved.)

Congratulations, all renewals succeeded. The following certs have been renewed:
/etc/letsencrypt/live/cucc.tazzfdc.com/fullchain.pem (success)
** DRY RUN: simulating 'certbot renew' close to cert expiry
** (The test certificates above have not been saved.)

自动更新证书

可以添加cron脚本来自动更新证书,当证书无需更新时,renew命令并不会去更新证书,所以crontab设置的时间间隔并无强制要求,但一般无需设置太过频繁的调度。
let’s encrypt证书大约还剩一个月有效期时,可以进行更新。

自动更新脚本renew‑letsencrypt.sh:

1
2
3
4
5
6
7
8
9
10
11
12
13
#!/bin/bash

cd /opt/certbot/
./certbot-auto renew
if \[ $? -ne 0 \]; then
ERRORLOG=\`tail /var/log/letsencrypt/letsencrypt.log\`
echo -e "The Let's Encrypt cert has not been renewed! \\n \\n" \\
$ERRORLOG
else
nginx -s reload
fi

exit 0

将脚本添加到crontab自动运行即可。比如,每月1号执行此脚本:

1
0 0 1 * * /usr/local/bin/renew-letsencrypt.sh

未来

以上安装配置仍然十分繁琐,nginx插件成熟并且进入官方源后,只要几条指令就可以安装和更新证书了:

安装certbot:

1
# apt install certbot certbot-nginx

安装证书:

1
$ certbot --nginx

更新证书:

1
$ certbot renew

References:
[1]Getting Started
[2]User Guide
[3]Using Free SSL/TLS Certificates from Let’s Encrypt with NGINX
[4]Configuring HTTPS servers

===
[erq]

Dataguard配置中因为网络故障出现了日志gap,修复gap后,恢复自动同步。

备库看一切正常:

1
2
3
4
SQL> SELECT max(sequence#), applied FROM v$archived_log GROUP BY applied;
MAX(SEQUENCE#) APPLIED
-------------- ---------
20114 YES

但主库确显示日志并未应用:

1
2
3
4
5
SQL> SELECT max(sequence#), applied FROM v$archived_log GROUP BY applied;

MAX(SEQUENCE#) APPLIED
-------------- ---------
20114 NO

其实日志已经正确的同步并应用了,只是primary没有更新applied列的值而已,这是个bug, 编号1369630.1

主库上有一个指定的ARCn进程与远程的RFS进程通讯来更新applied列的值,如果此进程挂起,无法与远程通讯也就无法更新v$archived_log的applied列值。

解决方案

主库ALERT_sid.LOG文件查找最新的,包含以下类似信息的行:

1
ARCn: Becoming the heartbeat ARCH

说明ARCn进程/线程出问题挂起了,n是数字,比如ARC0

然后查找ARC0的进程/线程号,*nix平台是进程,windows平台是线程:

windows平台输出:

1
2
3
4
SQL> SELECT spid, osuser, s.program FROM v$process p, v$session s WHERE p.addr=s.paddr and p.program like '%ARC0%'
SPID OSUSER PROGRAM
---------- ----------------- -----------------------
2616 SYSTEM ORACLE.EXE (ARC0)

将对应的进程/线程kill掉就可以了,oracle会重新启动相应的进程/线程。

*nix平台:

1
# kill -9 processid 

windows平台:

1
cmd> orakill sid threadid

sid是oracle实例名

或者更简单粗暴的重新启动oracle实例

alert_sid.log文件中可能会出现错误提示:

1
ORA-16401: archivelog rejected by RFS

sid_arc0_xxxx.trc文件中也会有:

1
2
Error 16401 creating standby archive log file at host 'xxx'
ORA-16401: archivelog rejected by RFS

这是出现重复归档的错误提示,因为ARC0想重新归档已经应用到备库的日志,可以安全的忽略掉这些信息,ARC0会更新已经传输到备库的日志的APPLIED列。

ORA-16401也可能出现在多个主库和/或备库向同一个备库归档的情形下,这种情况一般是因为LOG_ARCHIVE_DEST_n参数配置错误。

References:
[1]Oracle Data-guard Issues - ‘APPLIED’-Column not updated in v$archived_log table
[2]Bug Note: 1369630.1
[3]ORACLE在windows上使用orakill结束oracle会话的线程

===
[erq]

因为网络故障或资源紧张,会使standby出现日志同步间隙(archive gap)。虽然设置好了FAL_CLIENT和FAL_SERVER参数,有时候仍然无法自动解决日志间隙问题。

比如由于CONTROL_FILE_RECORD_KEEP_TIME设置导致,较旧的主库归档日志记录被循环覆盖,standby无法自动获取相应的日志文件:

1
2
3
4
5
6
7
8
9
10
11
12
Fetching gap sequence in thread 1, gap sequence 19767-19866
Thu May 12 09:45:47 2016
FAL\[client\]: Failed to request gap sequence
GAP - thread 1 sequence 19767-19866
DBID 1276927241 branch 874659493
FAL\[client\]: All defined FAL servers have been attempted.
-------------------------------------------------------------
Check that the CONTROL_FILE_RECORD_KEEP_TIME initialization
parameter is defined to a value that is sufficiently large
enough to maintain adequate log switch information to resolve
archivelog gaps.
-------------------------------------------------------------

如果standby缺少的归档日志尚未删除,可以拷贝缺少的日志到备库,然后在备库上注册,备库会apply这些归档日志。
如果归档日志已经被删除,则需要从主库做增量备份,然后在备库进行恢复来修复日志间隙。

手工拷贝归档日志

首先查看日志间隙:

1
2
3
4
sql> select * from v$archive_gap;
THREAD# LOW_SEQUENCE# HIGH_SEQUENCE#
-------- ---------------- ----------------------
1 19767 19866

将对应的归档日志拷贝到备库端

最后在备库端注册这些归档日志:

1
sql> alter database register physical logfile '\\path\\to\\archive_log'

备库会立即应用这些归档日志。如果日志数量过多,也可以使用以下语句来自动恢复数据库:

1
SQL> ALTER DATABASE RECOVER AUTOMATIC STANDBY DATABASE;

创建备库导致的日志不同步问题

最近遇到的一个案例是这样的,rman dupicate创建完备库后,因为没有使用dorecovery恢复增量备份,主库上的部分归档日志又被清除掉了,导致主备库无法进行同步,备库有类似如下错误提示:

1
2
3
4
5
6
7
8
9
10
11
12
13
Media Recovery Waiting for thread 1 sequence 23943
Fetching gap sequence in thread 1, gap sequence 23943-24042
Sun Jul 24 13:02:27 2016
FAL\[client\]: Failed to request gap sequence
GAP - thread 1 sequence 23943-24042
DBID 1276927241 branch 874659493
FAL\[client\]: All defined FAL servers have been attempted.
-------------------------------------------------------------
Check that the CONTROL_FILE_RECORD_KEEP_TIME initialization
parameter is defined to a value that is sufficiently large
enough to maintain adequate log switch information to resolve
archivelog gaps.
-------------------------------------------------------------

主库有类似如下错误提示:

1
2
3
4
FAL Redo Shipping Client Established Network Login
Failed to queue the whole gap
GAP - thread 1 sequence 23943-24042
DBID 1276927241 branch 874659493

此时备库查询v$archive_gap表是没有任何记录的,幸好还有一个备库有完整的归档日志,将相应的归档日志完整拷贝到新建的物理备库上,然后执行以下语句自动恢复数据库:

1
SQL> ALTER DATABASE RECOVER AUTOMATIC STANDBY DATABASE;

所有可用日志全部恢复完毕后,会有如下提示:

1
2
3
4
5
6
7
8
9
10
11
ALTER DATABASE RECOVER AUTOMATIC STANDBY DATABASE
*
ERROR at line 1:
ORA-00279: change 7255663631 generated at 07/25/2016 08:51:20 needed for thread 1
ORA-00289: suggestion : D:\\ARCHIVED_LOG\\ARC24380_0874659493.001
ORA-00280: change 7255663631 for thread 1 is in sequence #24380
ORA-00278: log file 'D:\\ARCHIVED_LOG\\ARC24380_0874659493.001' no longer needed for this recovery
ORA-00308: cannot open archived log 'D:\\ARCHIVED_LOG\\ARC24380_0874659493.001'
ORA-27041: unable to open file
OSD-04002: unable to open file
O/S-Error: (OS 2) 系统找不到指定的文件。

这是因为已经没有日志可用于恢复了,此时重新打开实时日志应用就可以开始自动同步了:

1
SQL> alter database recover managed standby database disconnect from session;

如果归档日志已经找不到了,则可以采用以下增量备份方式修复备库。

增量备份修复备库

  1. 查找恢复点SCN
    备库端:
    首先查看日志间隙:
    1
    2
    3
    4
    sql> select * from v$archive_gap;
    THREAD# LOW_SEQUENCE# HIGH_SEQUENCE#
    -------- ---------------- ----------------------
    1 19767 19766

主库端:
然后主库上查找日志间隙中低端日志序号的上一个日志的first_change#

1
2
3
4
sql> select SEQUENCE#, FIRST_CHANGE# from v$archived_log where SEQUENCE#=19766;
SEQUENCE# FIRST_CHANGE#
--------- -------------
19766 7008518015
  1. 备库停止apply log
    备库端:
    1
    sql> alter database recover managed standby database cancel;
  2. 主库增量SCN备份
    主库端:
    1
    2
    $ rman target /
    RMAN> backup incremental from scn 7008518015 database format '/path/to/stdby_%U.bak';
  3. 生成备库控制文件
    主库端:
    1
    RMAN> backup current controlfile for standby format '/path/to/stdby_%U.ctl';
  4. 拷贝备份和控制文件到备库
  5. 备库使用新生成的控制文件启动
    备库端:
    1
    2
    3
    4
    rman> shutdown immediate;
    rman> startup nomount;
    rman> restore standby controlfile from '/path/to/stdby_xxx.ctl';
    rman> alter database mount
  6. 备库进行数据恢复
    备库端:
    1
    2
    RMAN> catalog start with '/path/to_bak';
    RMAN> recover database noredo;
  7. 备库恢复日志应用
    备库端:
    1
    SQL> alter database recover managed standby database disconnect from session;

References:
[1]DataGuard 中处理archive gap的方法
[2]Roll Forward Physical Standby Database using RMAN incremental backup
[3]Oracle Physical DataGuard使用RMAN增量备份修复GAP
[4]Using RMAN Incremental Backups to Refresh a Standby Database

===
[erq]