How to Analyse Row Lock Contention in Oracle 10gR2 and later

我们有一道面试题,原以为很简单,但是却发现面试者能够完美解出的几乎没有,一部分人有思路,但是可能是因为面试紧张,很难在指定时间内完成解题,而更大一部分人连思路也不清晰。

题目是:请将emp.empno=7369的记录ename字段修改为“ENMOTECH”并提交,你可能会遇到各种故障,请尝试解决。

其实题目的设计非常简单,一个RAC双节点的实例环境,面试人员使用的是实例2,而我们在实例1中使用select for update将EMP表加锁。

SQL> select * from emp for update;

此时在实例2中,如果执行以下SQL语句尝试更新ename字段,必然会被行锁堵塞。

SQL> update emp set ename='ENMOTECH' where empno=7369;

这道面试题中包含的知识点有:
1. 如何在另外一个session中查找被堵塞的session信息;
2. 如何找到产生行锁的blocker;
3. 在杀掉blocker进程之前会不会向面试监考人员询问,我已经找到了产生堵塞的会话,是不是可以kill掉;
4. 在获得可以kill掉进程的确认回复后,正确杀掉另一个实例上的进程。

这道题我们期待可以在5分钟之内获得解决,实际上大部分应试者在15分钟以后都完全没有头绪。

正确的思路和解法应该如下:

检查被阻塞会话的等待事件

更新语句回车以后没有回显,明显是被锁住了,那么现在这个会话经历的是什么等待事件呢?

SQL> select sid,event,username,sql.sql_text 
  2  from v$session s,v$sql sql
  3  where s.sql_id=sql.sql_id
  4  and sql.sql_text like 'update emp set ename%';

       SID EVENT                          USERNAME   SQL_TEXT
---------- ------------------------------ ---------- ----------------------------------------------------------------------
        79 enq: TX - row lock contention  ENMOTECH   update emp set ename='ENMOTECH' where empno=7369

以上使用的是关联v$sql的SQL语句,实际上通过登录用户名等也可以快速定位被锁住的会话。

查找blocker

得知等待事件是enq: TX – row lock contention,行锁,接下来就是要找到谁锁住了这个会话。在10gR2以后,只需要gv$session视图就可以迅速定位blocker,通过BLOCKING_INSTANCE和BLOCKING_SESSION字段即可。

SQL> select SID,INST_ID,BLOCKING_INSTANCE,BLOCKING_SESSION from gv$session where INST_ID=2 and SID=79;

       SID    INST_ID BLOCKING_INSTANCE BLOCKING_SESSION
---------- ---------- ----------------- ----------------
        79          2                 1               73

上述方法是最简单的,如果是使用更传统的方法,实际上也并不难,从gv$lock视图中去查询即可。

SQL> select TYPE,ID1,ID2,LMODE,REQUEST from v$lock where sid=79;

TY        ID1        ID2      LMODE    REQUEST
-- ---------- ---------- ---------- ----------
TX     589854      26267          0          6
AE        100          0          4          0
TM      79621          0          3          0

SQL> select INST_ID,SID,TYPE,LMODE,REQUEST from gv$Lock where ID1=589854 and ID2=26267;

   INST_ID        SID TY      LMODE    REQUEST
---------- ---------- -- ---------- ----------
         2         79 TX          0          6
         1         73 TX          6          0

乙方DBA需谨慎

第三个知识点是考核作为乙方的谨慎,即使你查到了blocker,是不是应该直接kill掉,必须要先征询客户的意见,确认之后才可以杀掉。

清除blocker

已经确认了可以kill掉session之后,需要再找到相应session的serail#,这是kill session时必须输入的参数。

SQL> select SID,SERIAL# from gv$session where INST_ID=1 and SID=73;

       SID    SERIAL#
---------- ----------
        73      15625

如果是11gR2数据库,那么直接在实例2中加入@1参数就可以杀掉实例1中的会话,如果是10g,那么登入实例1再执行kill session的操作。

SQL> alter system kill session '73,15625,@1';

System altered.

再检查之前被阻塞的更新会话,可以看到已经更新成功了。

SQL> update emp set ename='ENMOTECH' where empno=7369;

1 row updated.

对于熟悉整个故障解决过程的人,5分钟之内就可以解决问题。

深入一步

对于TX锁,在v$lock视图中显示的ID1和ID2是什么意思? 解释可以从v$lock_type视图中获取。

SQL> select ID1_TAG,ID2_TAG from V$LOCK_TYPE where type='TX';

ID1_TAG         ID2_TAG
--------------- ----------
usn<<16 | slot  sequence

所以ID1是事务的USN+SLOT,而ID2则是事务的SQN。这些可以从v$transaction视图中获得验证。

SQL> select taddr from v$session where sid=73;

TADDR
----------------
000000008E3B65C0

SQL> select XIDUSN,XIDSLOT,XIDSQN from v$transaction where addr='000000008E3B65C0';

    XIDUSN    XIDSLOT     XIDSQN
---------- ---------- ----------
         9         30      26267

如何和ID1=589854 and ID2=26267对应呢? XIDSQN=26267和ID2=26267直接就对应了,没有问题。 那么ID1=589854是如何对应的?将之转换为16进制,是0x9001E,然后分高位和低位分别再转换为10进制,高位的16进制9就是十进制的9,也就是XIDUSN=9,而低位的16进制1E转换为10进制是30,也就是XIDSLOT=30。

文章写到这里,忽然感觉网上那些一气呵成的故障诊断脚本其实挺误人的,只需要给一个参数,运行一下脚本就列出故障原因。所以很少人愿意再去研究这个脚本为什么这么写,各个视图之间的联系是如何环环相扣的。所以当你不再使用自己的笔记本,不再能迅速找到你赖以生存的那些脚本,你还能一步一步地解决故障吗?

Oracle ASM Filter Driver (ASMFD) – New Features for Oracle ASM 12.1.0.2

什么是Oracle ASM Filter Driver(ASMFD)?

简单地说,这是一个可以取代ASMLIB和udev设置的新功能,并且还增加了I/O Filter功能,这也体现在该功能的命名中。ASMFD目前只在Linux操作系统中有效,并且必须要使用最新版的Oracle ASM 12.1.0.2。在之前,由于Linux操作系统对于块设备的发现顺序不定,所以在系统重启以后,无法保证原来的/dev/sda还是sda,所以不能直接使用这样原始的设备名来做ASM Disk的Path,因此出现了ASMLIB,以Label的方式给予设备一个固定的名字,而ASM则直接使用这个固定的名字来创建ASM磁盘,后来ASMLIB必须要ULN帐号才可以下载了,于是大家全部转到了udev方式,我还因此写了几篇文章来阐述在Linux中如何设置udev rule。比如:

How to use udev for Oracle ASM in Oracle Linux 6

Oracle Datafiles & Block Device & Parted & Udev

现在Oracle推出了ASMFD,可以一举取代ASMLIB和手动设置udev rules文件的繁琐,并且最重要的是I/O Filter功能。

什么是I/O Filter功能?

文档原文如下:

The Oracle ASM Filter Driver rejects any I/O requests that are invalid. This action eliminates accidental overwrites of Oracle ASM disks that would cause corruption in the disks and files within the disk group. For example, the Oracle ASM Filter Driver filters out all non-Oracle I/Os which could cause accidental overwrites.

意思是:该功能可以拒绝所有无效的I/O请求,最主要的作用是防止意外覆写ASM磁盘的底层盘,在后面的测试中可以看到对于root用户的dd全盘清零这样的变态操作也都是可以过滤的。

真是不错,那么该怎么启用这个功能呢?

通常我们原先的ASM中都应该使用的是ASMLIB或者udev绑定的设备,这里就直接描述如何将原先的设备名重新命名为新的AFD设备名。

--确认目前ASMFD模块(以下简称AFD)的状态,未加载。
[grid@dbserver1 ~]$ asmcmd afd_state
ASMCMD-9526: The AFD state is 'NOT INSTALLED' and filtering is 'DEFAULT' on host 'dbserver1.vbox.com'

--获取当前ASM磁盘发现路径,我这里是使用udev绑定的名称
[grid@dbserver1 ~]$ asmcmd dsget
parameter:/dev/asm*
profile:/dev/asm*

--设置ASM磁盘路径,将新的Disk String加入
--可以看到在设置该参数时,ASM会检查现有已经加载的磁盘,如果不在发现路径上,将会报错。
[grid@dbserver1 ~]$ asmcmd dsset AFD:*
ORA-02097: parameter cannot be modified because specified value is invalid
ORA-15014: path '/dev/asm-disk7' is not in the discovery set (DBD ERROR: OCIStmtExecute)

--因此我们必须将新的路径加在原始路径后面,设置成多种路径,该操作会运行一段时间,视ASM磁盘多少而定
[grid@dbserver1 ~]$ asmcmd dsset '/dev/asm*','AFD:*'

[grid@dbserver1 ~]$ asmcmd dsget
parameter:/dev/asm*, AFD:*
profile:/dev/asm*,AFD:*

--检查现在GI环境中的节点。
[grid@dbserver1 ~]$ olsnodes -a
dbserver1       Hub
dbserver2       Hub

--以下命令必须在所有Hub节点上都运行,可以使用Rolling的方式。以下命令有些需要root用户,有些需要grid用户,请注意#或者$不同的提示符表示不同的用户。
--先停止crs
[root@dbserver1 ~]# crsctl stop crs

--作AFD Configure,实际上这是一个解压程序包,安装,并加载Driver的过程,需要消耗一些时间
[root@dbserver1 ~]# asmcmd afd_configure
Connected to an idle instance.
AFD-627: AFD distribution files found.
AFD-636: Installing requested AFD software.
AFD-637: Loading installed AFD drivers.
AFD-9321: Creating udev for AFD.
AFD-9323: Creating module dependencies - this may take some time.
AFD-9154: Loading 'oracleafd.ko' driver.
AFD-649: Verifying AFD devices.
AFD-9156: Detecting control device '/dev/oracleafd/admin'.
AFD-638: AFD installation correctness verified.
Modifying resource dependencies - this may take some time.

--结束以后,可以再次检查AFD状态,显示已加载。
[root@dbserver1 ~]# asmcmd afd_state
Connected to an idle instance.
ASMCMD-9526: The AFD state is 'LOADED' and filtering is 'DEFAULT' on host 'dbserver1.vbox.com'

--接下来需要设置AFD自己的磁盘发现路径了,其实这里很像以前ASMLIB的操作。
--设置AFD磁盘发现路径,必须先启动CRS,否则将会遇到下面的错误。同时也可以看到这个信息是存储在每个节点自己的OLR中,因此必须在所有节点中都设置。
[root@dbserver1 ~]# asmcmd afd_dsget
Connected to an idle instance.
ASMCMD-9511: failed to obtain required AFD disk string from Oracle Local Repository
[root@dbserver1 ~]#
[root@dbserver1 ~]# asmcmd afd_dsset '/dev/sd*'
Connected to an idle instance.
ASMCMD-9512: failed to update AFD disk string in Oracle Local Repository.

--启动CRS
[root@dbserver1 ~]# crsctl start crs
CRS-4123: Oracle High Availability Services has been started.

--此时查看后台的ASM告警日志,可以看到加载的磁盘仍然使用的是原始路径。但是也可以看到libafd已经成功加载。
2014-11-20 17:01:04.545000 +08:00
NOTE: Loaded library: /opt/oracle/extapi/64/asm/orcl/1/libafd12.so
ORACLE_BASE from environment = /u03/app/grid
SQL> ALTER DISKGROUP ALL MOUNT /* asm agent call crs *//* {0:9:3} */
NOTE: Diskgroup used for Voting files is:
  CRSDG
Diskgroup with spfile:CRSDG
NOTE: Diskgroup used for OCR is:CRSDG
NOTE: Diskgroups listed in ASM_DISKGROUP are
  DATADG
NOTE: cache registered group CRSDG 1/0xB8E8EA0B
NOTE: cache began mount (first) of group CRSDG 1/0xB8E8EA0B
NOTE: cache registered group DATADG 2/0xB8F8EA0C
NOTE: cache began mount (first) of group DATADG 2/0xB8F8EA0C
NOTE: Assigning number (1,2) to disk (/dev/asm-disk3)
NOTE: Assigning number (1,1) to disk (/dev/asm-disk2)
NOTE: Assigning number (1,0) to disk (/dev/asm-disk1)
NOTE: Assigning number (1,5) to disk (/dev/asm-disk10)
NOTE: Assigning number (1,3) to disk (/dev/asm-disk8)
NOTE: Assigning number (1,4) to disk (/dev/asm-disk9)
NOTE: Assigning number (2,3) to disk (/dev/asm-disk7)
NOTE: Assigning number (2,2) to disk (/dev/asm-disk6)
NOTE: Assigning number (2,1) to disk (/dev/asm-disk5)
NOTE: Assigning number (2,5) to disk (/dev/asm-disk12)
NOTE: Assigning number (2,0) to disk (/dev/asm-disk4)
NOTE: Assigning number (2,6) to disk (/dev/asm-disk13)
NOTE: Assigning number (2,4) to disk (/dev/asm-disk11)

--将afd_ds设置为ASM磁盘的底层磁盘设备名,这样以后就不再需要手工配置udev rules了。
[grid@dbserver1 ~]$ asmcmd afd_dsset '/dev/sd*'

[grid@dbserver1 ~]$ asmcmd afd_dsget
AFD discovery string: /dev/sd*

--我在测试的时候,上面犯了一个错误,将路径设置为了“dev/sd*”,缺少了最开始的根目录。因此此处没有发现任何磁盘,如果你的测试中,这一步已经可以发现磁盘,请告诉我。
[grid@dbserver1 ~]$ asmcmd afd_lsdsk
There are no labelled devices.

--再次提醒,到此为止的所有命令,都必须要在集群环境的所有节点中都执行。
--接下来就是将原先的ASM磁盘路径从udev转到AFD
--先检查现在的磁盘路径
[root@dbserver1 ~]# ocrcheck -config
Oracle Cluster Registry configuration is :
         Device/File Name         :     +CRSDG

[root@dbserver1 ~]# crsctl query css votedisk
##  STATE    File Universal Id                File Name Disk group
--  -----    -----------------                --------- ---------
 1. ONLINE   4838a0ee7bfa4fbebf8ff9f58642c965 (/dev/asm-disk1) [CRSDG]
 2. ONLINE   72057097a36e4f02bfc7b5e23672e4cc (/dev/asm-disk2) [CRSDG]
 3. ONLINE   7906e2fb24d24faebf9b82bba6564be3 (/dev/asm-disk3) [CRSDG]
Located 3 voting disk(s).

[root@dbserver1 ~]# su - grid
[grid@dbserver1 ~]$ asmcmd lsdsk -G CRSDG
Path
/dev/asm-disk1
/dev/asm-disk10
/dev/asm-disk2
/dev/asm-disk3
/dev/asm-disk8
/dev/asm-disk9

--由于要修改OCR所在的磁盘,因此修改之前需要停止Cluster。
[root@dbserver1 ~]# crsctl stop cluster -all

--直接修改会报错,因为/dev/asm-disk1已经存在在ASM中了。
[grid@dbserver1 ~]$ asmcmd afd_label asmdisk01 /dev/asm-disk1
Connected to an idle instance.
ASMCMD-9513: ASM disk label set operation failed.
disk /dev/asm-disk1 is already provisioned for ASM

--必须要增加migrate关键字,才可以成功。
[grid@dbserver1 ~]$ asmcmd afd_label asmdisk01 /dev/asm-disk1 --migrate
Connected to an idle instance.
[grid@dbserver1 ~]$ asmcmd afd_lsdsk
Connected to an idle instance.
--------------------------------------------------------------------------------
Label                     Filtering   Path
================================================================================
ASMDISK01                   ENABLED   /dev/asm-disk1

--在我的测试ASM中,一共有13块磁盘,因此依次修改。
asmcmd afd_label asmdisk01 /dev/asm-disk1 --migrate
asmcmd afd_label asmdisk02 /dev/asm-disk2 --migrate
asmcmd afd_label asmdisk03 /dev/asm-disk3 --migrate
asmcmd afd_label asmdisk04 /dev/asm-disk4 --migrate
asmcmd afd_label asmdisk05 /dev/asm-disk5 --migrate
asmcmd afd_label asmdisk06 /dev/asm-disk6 --migrate
asmcmd afd_label asmdisk07 /dev/asm-disk7 --migrate
asmcmd afd_label asmdisk08 /dev/asm-disk8 --migrate
asmcmd afd_label asmdisk09 /dev/asm-disk9 --migrate
asmcmd afd_label asmdisk10 /dev/asm-disk10 --migrate
asmcmd afd_label asmdisk11 /dev/asm-disk11 --migrate
asmcmd afd_label asmdisk12 /dev/asm-disk12 --migrate
asmcmd afd_label asmdisk13 /dev/asm-disk13 --migrate

[grid@dbserver1 ~]$ asmcmd afd_lsdsk
Connected to an idle instance.
--------------------------------------------------------------------------------
Label                     Filtering   Path
================================================================================
ASMDISK01                   ENABLED   /dev/asm-disk1
ASMDISK02                   ENABLED   /dev/asm-disk2
ASMDISK03                   ENABLED   /dev/asm-disk3
ASMDISK04                   ENABLED   /dev/asm-disk4
ASMDISK05                   ENABLED   /dev/asm-disk5
ASMDISK06                   ENABLED   /dev/asm-disk6
ASMDISK07                   ENABLED   /dev/asm-disk7
ASMDISK08                   ENABLED   /dev/asm-disk8
ASMDISK09                   ENABLED   /dev/asm-disk9
ASMDISK10                   ENABLED   /dev/asm-disk10
ASMDISK11                   ENABLED   /dev/asm-disk11
ASMDISK12                   ENABLED   /dev/asm-disk12
ASMDISK13                   ENABLED   /dev/asm-disk13

--在另外的节点中,不再需要作label,而是直接scan即可,这跟使用ASMLIB的操作非常相像。
[grid@dbserver2 ~]$ asmcmd afd_scan
Connected to an idle instance.
[grid@dbserver2 ~]$ asmcmd afd_lsdsk
Connected to an idle instance.
--------------------------------------------------------------------------------
Label                     Filtering   Path
================================================================================
ASMDISK12                   ENABLED   /dev/asm-disk12
ASMDISK09                   ENABLED   /dev/asm-disk9
ASMDISK08                   ENABLED   /dev/asm-disk8
ASMDISK11                   ENABLED   /dev/asm-disk11
ASMDISK10                   ENABLED   /dev/asm-disk10
ASMDISK13                   ENABLED   /dev/asm-disk13
ASMDISK01                   ENABLED   /dev/asm-disk1
ASMDISK04                   ENABLED   /dev/asm-disk4
ASMDISK06                   ENABLED   /dev/asm-disk6
ASMDISK07                   ENABLED   /dev/asm-disk7
ASMDISK05                   ENABLED   /dev/asm-disk5
ASMDISK03                   ENABLED   /dev/asm-disk3
ASMDISK02                   ENABLED   /dev/asm-disk2

--重新启动Cluster
[root@dbserver1 ~]# crsctl start cluster -all

--可以看到ASM告警日志中已经显示开始使用新的名称。关于其中WARNING的含义表示目前AFD还不支持Advanced Format格式的磁盘,普通磁盘格式一个扇区是512字节,而高级格式则为4K字节。
2014-11-20 17:46:16.695000 +08:00
* allocate domain 1, invalid = TRUE
* instance 2 validates domain 1
NOTE: cache began mount (not first) of group CRSDG 1/0x508D0B98
NOTE: cache registered group DATADG 2/0x509D0B99
* allocate domain 2, invalid = TRUE
* instance 2 validates domain 2
NOTE: cache began mount (not first) of group DATADG 2/0x509D0B99
WARNING: Library 'AFD Library - Generic , version 3 (KABI_V3)' does not support advanced format disks
NOTE: Assigning number (1,0) to disk (AFD:ASMDISK01)
NOTE: Assigning number (1,1) to disk (AFD:ASMDISK02)
NOTE: Assigning number (1,2) to disk (AFD:ASMDISK03)
NOTE: Assigning number (1,3) to disk (AFD:ASMDISK08)
NOTE: Assigning number (1,4) to disk (AFD:ASMDISK09)
NOTE: Assigning number (1,5) to disk (AFD:ASMDISK10)
NOTE: Assigning number (2,0) to disk (AFD:ASMDISK04)
NOTE: Assigning number (2,1) to disk (AFD:ASMDISK05)
NOTE: Assigning number (2,2) to disk (AFD:ASMDISK06)
NOTE: Assigning number (2,3) to disk (AFD:ASMDISK07)
NOTE: Assigning number (2,4) to disk (AFD:ASMDISK11)
NOTE: Assigning number (2,5) to disk (AFD:ASMDISK12)
NOTE: Assigning number (2,6) to disk (AFD:ASMDISK13)

--检查磁盘加载路径,以及功能全部是AFD样式了。
[grid@dbserver1 ~]$ asmcmd lsdsk
Path
AFD:ASMDISK01
AFD:ASMDISK02
AFD:ASMDISK03
AFD:ASMDISK04
AFD:ASMDISK05
AFD:ASMDISK06
AFD:ASMDISK07
AFD:ASMDISK08
AFD:ASMDISK09
AFD:ASMDISK10
AFD:ASMDISK11
AFD:ASMDISK12
AFD:ASMDISK13

--但是我们可以看到在数据字典中仍然存在之前的磁盘路径。
SQL> select NAME,LABEL,PATH from V$ASM_DISK;

NAME                 LABEL                           PATH
-------------------- ------------------------------- --------------------------------------------------
                                                     /dev/asm-disk7
                                                     /dev/asm-disk6
                                                     /dev/asm-disk13
                                                     /dev/asm-disk12
                                                     /dev/asm-disk11
                                                     /dev/asm-disk4
                                                     /dev/asm-disk2
                                                     /dev/asm-disk9
                                                     /dev/asm-disk3
                                                     /dev/asm-disk5
                                                     /dev/asm-disk10
                                                     /dev/asm-disk8
                                                     /dev/asm-disk1
CRSDG_0000           ASMDISK01                       AFD:ASMDISK01
CRSDG_0001           ASMDISK02                       AFD:ASMDISK02
CRSDG_0002           ASMDISK03                       AFD:ASMDISK03
DATADG_0000          ASMDISK04                       AFD:ASMDISK04
DATADG_0001          ASMDISK05                       AFD:ASMDISK05
DATADG_0002          ASMDISK06                       AFD:ASMDISK06
DATADG_0003          ASMDISK07                       AFD:ASMDISK07
CRSDG_0003           ASMDISK08                       AFD:ASMDISK08
CRSDG_0004           ASMDISK09                       AFD:ASMDISK09
CRSDG_0005           ASMDISK10                       AFD:ASMDISK10
DATADG_0004          ASMDISK11                       AFD:ASMDISK11
DATADG_0005          ASMDISK12                       AFD:ASMDISK12
DATADG_0006          ASMDISK13                       AFD:ASMDISK13

26 rows selected.

--需要将ASM磁盘发现路径(注意,这跟设置AFD磁盘发现路径不是一个命令)中原先的路径去除,只保留AFD路径。
[grid@dbserver1 ~]$ asmcmd dsset 'AFD:*'
[grid@dbserver1 ~]$ asmcmd dsget
parameter:AFD:*
profile:AFD:*

--再次重启ASM,一切正常了。
SQL> select NAME,LABEL,PATH from V$ASM_DISK;

NAME                 LABEL                           PATH
-------------------- ------------------------------- ------------------------------------------------------------
CRSDG_0000           ASMDISK01                       AFD:ASMDISK01
CRSDG_0001           ASMDISK02                       AFD:ASMDISK02
CRSDG_0002           ASMDISK03                       AFD:ASMDISK03
DATADG_0000          ASMDISK04                       AFD:ASMDISK04
DATADG_0001          ASMDISK05                       AFD:ASMDISK05
DATADG_0002          ASMDISK06                       AFD:ASMDISK06
DATADG_0003          ASMDISK07                       AFD:ASMDISK07
CRSDG_0003           ASMDISK08                       AFD:ASMDISK08
CRSDG_0004           ASMDISK09                       AFD:ASMDISK09
CRSDG_0005           ASMDISK10                       AFD:ASMDISK10
DATADG_0004          ASMDISK11                       AFD:ASMDISK11
DATADG_0005          ASMDISK12                       AFD:ASMDISK12
DATADG_0006          ASMDISK13                       AFD:ASMDISK13

13 rows selected.

--收尾工作,将原先的udev rules文件移除。当然,这要在所有节点中都运行。以后如果服务器再次重启,AFD就会完全接管了。
[root@dbserver1 ~]# mv /etc/udev/rules.d/99-oracle-asmdevices.rules ~oracle/

还有什么发现?

其实,AFD也在使用udev。囧。

[grid@dbserver1 ~]$ cat /etc/udev/rules.d/53-afd.rules
#
# AFD devices
KERNEL=="oracleafd/.*", OWNER="grid", GROUP="asmdba", MODE="0770"
KERNEL=="oracleafd/*", OWNER="grid", GROUP="asmdba", MODE="0770"
KERNEL=="oracleafd/disks/*", OWNER="grid", GROUP="asmdba", MODE="0660"

Label过后的磁盘在/dev/oracleafd/disks目录中可以找到。

[root@dbserver2 disks]# ls -l /dev/oracleafd/disks
total 52
-rw-r--r-- 1 root root 9 Nov 20 18:52 ASMDISK01
-rw-r--r-- 1 root root 9 Nov 20 18:52 ASMDISK02
-rw-r--r-- 1 root root 9 Nov 20 18:52 ASMDISK03
-rw-r--r-- 1 root root 9 Nov 20 18:52 ASMDISK04
-rw-r--r-- 1 root root 9 Nov 20 18:52 ASMDISK05
-rw-r--r-- 1 root root 9 Nov 20 18:52 ASMDISK06
-rw-r--r-- 1 root root 9 Nov 20 18:52 ASMDISK07
-rw-r--r-- 1 root root 9 Nov 20 18:52 ASMDISK08
-rw-r--r-- 1 root root 9 Nov 20 18:52 ASMDISK09
-rw-r--r-- 1 root root 9 Nov 20 18:52 ASMDISK10
-rw-r--r-- 1 root root 9 Nov 20 18:52 ASMDISK11
-rw-r--r-- 1 root root 9 Nov 20 18:52 ASMDISK12
-rw-r--r-- 1 root root 9 Nov 20 18:52 ASMDISK13

这里有一个很大不同,所有磁盘的属主变成了root,并且只有root才有写入的权限。很多文章认为,这就是AFD的filter功能体现了,因为现在用oracle或者grid用户都没有办法直接对ASM磁盘进行写入操作,自然就获得了一层保护。比如以下命令会直接报权限不足。

[oracle@dbserver1 disks]$ echo "do some evil" > ASMDISK99
-bash: ASMDISK99: Permission denied

但是如果你认为这就是AFD的保护功能,那也太小看Oracle了,仅仅是这样也对不起名字中Filter字样。且看后面分解。

操作系统中也可以看到AFD磁盘和底层磁盘的对应关系。

[grid@dbserver1 /]$ ls -l /dev/disk/by-label/
total 0
lrwxrwxrwx 1 root root 9 Nov 20 19:17 ASMDISK01 -> ../../sdc
lrwxrwxrwx 1 root root 9 Nov 20 19:17 ASMDISK02 -> ../../sdd
lrwxrwxrwx 1 root root 9 Nov 20 19:17 ASMDISK03 -> ../../sde
lrwxrwxrwx 1 root root 9 Nov 20 19:17 ASMDISK04 -> ../../sdf
lrwxrwxrwx 1 root root 9 Nov 20 19:17 ASMDISK05 -> ../../sdg
lrwxrwxrwx 1 root root 9 Nov 20 19:17 ASMDISK06 -> ../../sdh
lrwxrwxrwx 1 root root 9 Nov 20 19:17 ASMDISK07 -> ../../sdi
lrwxrwxrwx 1 root root 9 Nov 20 19:17 ASMDISK08 -> ../../sdj
lrwxrwxrwx 1 root root 9 Nov 20 19:17 ASMDISK09 -> ../../sdk
lrwxrwxrwx 1 root root 9 Nov 20 19:17 ASMDISK10 -> ../../sdl
lrwxrwxrwx 1 root root 9 Nov 20 19:17 ASMDISK11 -> ../../sdm
lrwxrwxrwx 1 root root 9 Nov 20 19:17 ASMDISK12 -> ../../sdn
lrwxrwxrwx 1 root root 9 Nov 20 19:17 ASMDISK13 -> ../../sdo

再次重启服务器以后,afd_lsdsk的结果中显示的路径都已经变为底层磁盘,但是Filtering却变成了DISABLED。不要在意这里的Label和Path的对应和上面的不一样,因为有些是在节点1中执行的结果,有些是在节点2中执行的结果,而这也是AFD功能的展示,不管两边机器发现块设备的顺序是不是一样,只要绑定了AFD的Label,就没问题了。

ASMCMD> afd_lsdsk
--------------------------------------------------------------------------------
Label                     Filtering   Path
================================================================================
ASMDISK01                  DISABLED   /dev/sdd
ASMDISK02                  DISABLED   /dev/sde
ASMDISK03                  DISABLED   /dev/sdf
ASMDISK04                  DISABLED   /dev/sdg
ASMDISK05                  DISABLED   /dev/sdh
ASMDISK06                  DISABLED   /dev/sdi
ASMDISK07                  DISABLED   /dev/sdj
ASMDISK08                  DISABLED   /dev/sdk
ASMDISK09                  DISABLED   /dev/sdl
ASMDISK10                  DISABLED   /dev/sdm
ASMDISK11                  DISABLED   /dev/sdn
ASMDISK12                  DISABLED   /dev/sdo
ASMDISK13                  DISABLED   /dev/sdp

最后,该来测试一下I/O Filter功能了吧,等好久了!

对,这才是重点。

先看一下如何启用或者禁用Filter功能。在我的测试中,单独设置某块盘启用还是禁用是不生效的,只能全局启用或者禁用。

[grid@dbserver1 ~]$ asmcmd help afd_filter
afd_filter
        Sets the AFD filtering mode on a given disk path.
        If the command is executed without specifying a disk path then
        filtering is set at node level.

Synopsis
        afd_filter {-e | -d } [<disk-path>]

Description
        The options for afd_filter are described below

        -e      -  enable  AFD filtering mode
        -d      -  disable AFD filtering mode

Examples
        The following example uses afd_filter to enable AFD filtering
        on a given diskpath.

        ASMCMD [+] >afd_filter -e /dev/sdq

See Also
       afd_lsdsk afd_state

启用Filter功能。

[grid@dbserver1 ~]$ asmcmd afd_filter -e
[grid@dbserver1 ~]$ asmcmd afd_lsdsk
Connected to an idle instance.
--------------------------------------------------------------------------------
Label                     Filtering   Path
================================================================================
ASMDISK01                   ENABLED   /dev/sdb
ASMDISK02                   ENABLED   /dev/sdc
ASMDISK03                   ENABLED   /dev/sdd
ASMDISK04                   ENABLED   /dev/sde
ASMDISK05                   ENABLED   /dev/sdf
ASMDISK06                   ENABLED   /dev/sdg
ASMDISK07                   ENABLED   /dev/sdh
ASMDISK08                   ENABLED   /dev/sdi
ASMDISK09                   ENABLED   /dev/sdj
ASMDISK10                   ENABLED   /dev/sdk
ASMDISK11                   ENABLED   /dev/sdl
ASMDISK12                   ENABLED   /dev/sdm
ASMDISK13                   ENABLED   /dev/sdn

为了以防万一,不破坏我自己的实验环境,增加了一块磁盘来作测试。

[root@dbserver1 ~]# asmcmd afd_label asmdisk99 /dev/sdo
Connected to an idle instance.
[root@dbserver1 ~]# asmcmd afd_lsdsk
Connected to an idle instance.
--------------------------------------------------------------------------------
Label                     Filtering   Path
================================================================================
ASMDISK01                   ENABLED   /dev/sdb
ASMDISK02                   ENABLED   /dev/sdc
ASMDISK03                   ENABLED   /dev/sdd
ASMDISK04                   ENABLED   /dev/sde
ASMDISK05                   ENABLED   /dev/sdf
ASMDISK06                   ENABLED   /dev/sdg
ASMDISK07                   ENABLED   /dev/sdh
ASMDISK08                   ENABLED   /dev/sdi
ASMDISK09                   ENABLED   /dev/sdj
ASMDISK10                   ENABLED   /dev/sdk
ASMDISK11                   ENABLED   /dev/sdl
ASMDISK12                   ENABLED   /dev/sdm
ASMDISK13                   ENABLED   /dev/sdn
ASMDISK99                   ENABLED   /dev/sdo

创建一个新的磁盘组。

[grid@dbserver1 ~]$ sqlplus / as sysasm
SQL> create diskgroup DGTEST external redundancy disk 'AFD:ASMDISK99';

Diskgroup created.

先用KFED读取一下磁盘头,验证一下确实无误。

[grid@dbserver1 ~]$ kfed read AFD:ASMDISK99
kfbh.endian:                          1 ; 0x000: 0x01
kfbh.hard:                          130 ; 0x001: 0x82
kfbh.type:                            1 ; 0x002: KFBTYP_DISKHEAD
kfbh.datfmt:                          1 ; 0x003: 0x01
kfbh.block.blk:                       0 ; 0x004: blk=0
kfbh.block.obj:              2147483648 ; 0x008: disk=0
kfbh.check:                  1854585587 ; 0x00c: 0x6e8abaf3
kfbh.fcn.base:                        0 ; 0x010: 0x00000000
kfbh.fcn.wrap:                        0 ; 0x014: 0x00000000
kfbh.spare1:                          0 ; 0x018: 0x00000000
kfbh.spare2:                          0 ; 0x01c: 0x00000000
kfdhdb.driver.provstr:ORCLDISKASMDISK99 ; 0x000: length=17
kfdhdb.driver.reserved[0]:   1145918273 ; 0x008: 0x444d5341
kfdhdb.driver.reserved[1]:    961237833 ; 0x00c: 0x394b5349
kfdhdb.driver.reserved[2]:           57 ; 0x010: 0x00000039
kfdhdb.driver.reserved[3]:            0 ; 0x014: 0x00000000
kfdhdb.driver.reserved[4]:            0 ; 0x018: 0x00000000
kfdhdb.driver.reserved[5]:            0 ; 0x01c: 0x00000000
kfdhdb.compat:                168820736 ; 0x020: 0x0a100000
kfdhdb.dsknum:                        0 ; 0x024: 0x0000
kfdhdb.grptyp:                        1 ; 0x026: KFDGTP_EXTERNAL
kfdhdb.hdrsts:                        3 ; 0x027: KFDHDR_MEMBER
kfdhdb.dskname:               ASMDISK99 ; 0x028: length=9
kfdhdb.grpname:                  DGTEST ; 0x048: length=6
kfdhdb.fgname:                ASMDISK99 ; 0x068: length=9

直接使用dd尝试将整个磁盘清零。dd命令本身没有任何错误返回。

[root@dbserver1 ~]# dd if=/dev/zero of=/dev/sdo
dd: writing to `/dev/sdo': No space left on device
409601+0 records in
409600+0 records out
209715200 bytes (210 MB) copied, 19.9602 s, 10.5 MB/s

之后重新mount磁盘组,如果磁盘被清零,在重新mount的时候一定会出现错误,而现在正常挂载。

SQL>  alter diskgroup DGTEST dismount;

Diskgroup altered.

SQL>  alter diskgroup DGTEST mount;

Diskgroup altered.

觉得不过瘾?那再创建一个表空间,插入一些数据,做一次checkpoint,仍然一切正常。

SQL> create tablespace test datafile '+DGTEST' size 100M;

Tablespace created.

SQL> create table t_afd (n number) tablespace test;

Table created.

SQL> insert into t_afd values(1);

1 row created.

SQL> commit;

Commit complete.

SQL> alter system checkpoint;

System altered.

SQL> select count(*) from t_afd;

  COUNT(*)
----------
         1

但是诡异的是,这时候在操作系统级别直接去读取/dev/sdo的内容,会显示全部都已经被清空为0了。

[root@dbserver1 ~]# od -c -N 256 /dev/sdo
0000000  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0
*
0000400

使用strings命令也完全看不到任何有意义的字符。

[root@dbserver1 disks]# strings /dev/sdo
[root@dbserver1 disks]# 

但是,千万不要被这样的假象迷惑,以为磁盘真的被清空了,在dd的时候,/var/log/message会产生大量日志,明确表示这些在ASM管理的设备上的IO操作都是不被支持,这才是Filter真正起作用的场合。

afd_mkrequest_fn: write IO on ASM managed device (major=8/minor=224)  not supported

使用kfed,仍然可以读取到正常的信息。

[grid@dbserver1 ~]$ kfed read AFD:ASMDISK99
kfbh.endian:                          1 ; 0x000: 0x01
kfbh.hard:                          130 ; 0x001: 0x82
kfbh.type:                            1 ; 0x002: KFBTYP_DISKHEAD
kfbh.datfmt:                          1 ; 0x003: 0x01
kfbh.block.blk:                       0 ; 0x004: blk=0
kfbh.block.obj:              2147483648 ; 0x008: disk=0
kfbh.check:                  1854585587 ; 0x00c: 0x6e8abaf3
kfbh.fcn.base:                        0 ; 0x010: 0x00000000
kfbh.fcn.wrap:                        0 ; 0x014: 0x00000000
kfbh.spare1:                          0 ; 0x018: 0x00000000
kfbh.spare2:                          0 ; 0x01c: 0x00000000
kfdhdb.driver.provstr:ORCLDISKASMDISK99 ; 0x000: length=17
......

直到重新启动服务器(重新启动ASM,重新启动Cluster,在操作系统仍然看到的是清零后的数据),所有的数据又回来了。目前还不确认Oracle是使用了怎样的重定向技术实现了这样的神奇效果。

[root@dbserver1 ~]# od -c -N 256 /dev/sdo
0000000 001 202 001 001  \0  \0  \0  \0  \0  \0  \0 200   u 177   D   I
0000020  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0
0000040   O   R   C   L   D   I   S   K   A   S   M   D   I   S   K   9
0000060   9  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0
0000100  \0  \0 020  \n  \0  \0 001 003   A   S   M   D   I   S   K   9
0000120   9  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0
0000140  \0  \0  \0  \0  \0  \0  \0  \0   D   G   T   E   S   T  \0  \0
0000160  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0
0000200  \0  \0  \0  \0  \0  \0  \0  \0   A   S   M   D   I   S   K   9
0000220   9  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0
0000240  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0
*
0000300  \0  \0  \0  \0  \0  \0  \0  \0 022 257 367 001  \0   X  \0 247
0000320 022 257 367 001  \0   h 036 344  \0 002  \0 020  \0  \0 020  \0
0000340 200 274 001  \0 310  \0  \0  \0 002  \0  \0  \0 001  \0  \0  \0
0000360 002  \0  \0  \0 002  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0
0000400
[root@dbserver1 ~]# 
[root@dbserver1 ~]# strings /dev/sdo | grep ASM
ORCLDISKASMDISK99
ASMDISK99
ASMDISK99
ORCLDISKASMDISK99
ASMDISK99
ASMDISK99
ASMDISK99
ASMDISK99
ASMPARAMETERFILE
ASMPARAMETERBAKFILE
ASM_STALE

最后将Filter禁用之后再测试。

[root@dbserver1 ~]# asmcmd afd_filter -d
Connected to an idle instance.
[root@dbserver1 ~]# asmcmd afd_lsdsk
Connected to an idle instance.
--------------------------------------------------------------------------------
Label                     Filtering   Path
================================================================================
ASMDISK01                  DISABLED   /dev/sdb
ASMDISK02                  DISABLED   /dev/sdc
ASMDISK03                  DISABLED   /dev/sdd
ASMDISK04                  DISABLED   /dev/sde
ASMDISK05                  DISABLED   /dev/sdf
ASMDISK06                  DISABLED   /dev/sdg
ASMDISK07                  DISABLED   /dev/sdh
ASMDISK08                  DISABLED   /dev/sdi
ASMDISK09                  DISABLED   /dev/sdj
ASMDISK10                  DISABLED   /dev/sdk
ASMDISK11                  DISABLED   /dev/sdl
ASMDISK12                  DISABLED   /dev/sdm
ASMDISK13                  DISABLED   /dev/sdn
ASMDISK99                  DISABLED   /dev/sdo

同样使用dd命令清零整个磁盘。

[root@dbserver1 ~]# dd if=/dev/zero of=/dev/sdo
dd: writing to `/dev/sdo': No space left on device
409601+0 records in
409600+0 records out
209715200 bytes (210 MB) copied, 4.46444 s, 47.0 MB/s

重新mount磁盘组,如期报错,磁盘组无法加载。

SQL> alter diskgroup DGTEST dismount;

Diskgroup altered.

SQL> alter diskgroup DGTEST mount;
alter diskgroup DGTEST mount
*
ERROR at line 1:
ORA-15032: not all alterations performed
ORA-15017: diskgroup "DGTEST" cannot be mounted
ORA-15040: diskgroup is incomplete

重新启动数据库,也会发现由于表空间中数据库不存在而导致数据库无法正常Open。

SQL> startup
ORACLE instance started.

Total System Global Area  838860800 bytes
Fixed Size                  2929936 bytes
Variable Size             385878768 bytes
Database Buffers          226492416 bytes
Redo Buffers                5455872 bytes
In-Memory Area            218103808 bytes
Database mounted.
ORA-01157: cannot identify/lock data file 15 - see DBWR trace file
ORA-01110: data file 15: '+DGTEST/CDB12C/DATAFILE/test.256.864163075'

有结论吗?

以上还不够吗?就酱!

【Oracle Database 12c New Feature】Aggregate Data Across Many PDBs by CONTAINERS Clause

在最新版本的Oracle Database 12.1.0.2中,新特性提供了PDB Containers子句,用以从CDB$ROOT层面直接聚合查询多个PDB中同一张表的数据。在新特性文档中该段如下描述: Screen Shot 2014-10-16 at 8.54.26 AM

但是实现起来并非看上去如此简单。

现有测试环境如下: 当前CDB中有2个PDB,分别是PDB1和PDB2;每个PDB中都有一个相同名字的Local User,为KAMUS;每个KAMUS用户下都有一个TT表,表结构相同,数据不同。

  • 首先按照想象,在CDB$ROOT中直接使用SYS用户查询,会报ORA-00942错误。
SQL> show user
USER is "SYS"
SQL> show con_name

CON_NAME
------------------------------
CDB$ROOT

SQL> select count(*) FROM CONTAINERS(KAMUS.TT) WHERE CON_ID IN (3);
select count(*) FROM CONTAINERS(KAMUS.TT) WHERE CON_ID IN (3)
                                      *
ERROR at line 1:
ORA-00942: table or view does not exist
  • 这要求我们首先创建一个Common User。并赋予其足够的权限。赋予select any table权限是为了方便测试,在真实环境中你可能需要更精细地规划权限。
SQL> CREATE USER C##KAMUS IDENTIFIED BY oracle DEFAULT tablespace users;

User created.

SQL> GRANT dba TO C##KAMUS CONTAINER=ALL;

Grant succeeded.

SQL> grant select any table to C##KAMUS CONTAINER=ALL;

Grant succeeded.
  • 其次要求用Common User分别连接所有需要聚合查询的PDB,在其中创建一个与表名字相同的视图。
sqlplus "C##KAMUS/oracle@db-cluster-scan:1521/pdb1"
CREATE OR REPLACE VIEW tt AS SELECT * FROM kamus.tt;
alter session set container=pdb2;
CREATE OR REPLACE VIEW tt AS SELECT * FROM kamus.tt;
  • 然后还需要在Common User中创建一个相同名字的空表,否则查询仍然会报ORA-00942错误。
SQL> show user
USER is "C##KAMUS"
SQL> show con_name

CON_NAME
------------------------------
CDB$ROOT
SQL> create table TT (dummy char(1));

Table created.

SQL> select count(*) FROM CONTAINERS(TT);

  COUNT(*)
----------
    117362
  • 只需要创建一个名字相同的表,已经可以聚合查询count(*)了。但是如果在SQL语句中涉及到特定列仍会有问题。从报错中透露的P000进程,可知Oracle在实现此过程中使用了并行查询,不同的并行子进程在不同的PDB中查询相关表,最后在CDB级别中的汇总显示。
SQL> select OBJECT_NAME FROM CONTAINERS(TT) where rownum<11;
select OBJECT_NAME FROM CONTAINERS(TT) where rownum<11
*
ERROR at line 1:
ORA-12801: error signaled in parallel query server P000, instance dbserver1.vbox.com:cdb12c_1 (1)
ORA-00904: "OBJECT_NAME": invalid identifier


SQL> select count(*) from CONTAINERS(TT) where OBJECT_NAME='XX';
select count(*) from CONTAINERS(TT) where OBJECT_NAME='XX'
*
ERROR at line 1:
ORA-12801: error signaled in parallel query server P000, instance dbserver1.vbox.com:cdb12c_1 (1)
ORA-00904: "OBJECT_NAME": invalid identifier
  • 因此可以将所有期望聚合查询的列都加入到C##KAMUS用户的TT表中,此处增加了OBJECT_NAME字段,可以看到特意在测试中增加了number类型的OBJECT_NAME字段,而PDB中的OBJECT_NAME字段均为varchar2类型,因此可见只需列名称相同即可,无需类型相同。
SQL> alter table TT add OBJECT_NAME number(10);

Table altered.

SQL> select OBJECT_NAME from CONTAINERS(TT) where rownum<11;

OBJECT_NAME
------------------------------------
ICOL$
I_CDEF3
TS$
CDEF$
I_FILE2
I_OBJ5
I_OBJ1
I_OBJ4
I_USER2
I_COL2

10 rows selected.

SQL> select count(*) from CONTAINERS(TT) where OBJECT_NAME like 'ICOL%';

  COUNT(*)
----------
        12
  • 从以上已经看出,如果更简单,那么在C##KAMUS中创建一个与PDB中KAMUS.TT表完全相同结构的空表即可。这里用impdp来实现。
impdp C##KAMUS/oracle@db-cluster-scan:1521/cdb12c DIRECTORY=dpump DUMPFILE=expdat.dmp EXCLUDE=TABLE_DATA TABLES=KAMUS.TT REMAP_SCHEMA=KAMUS:C##KAMUS

SQL> show con_name

CON_NAME
------------------------------
CDB$ROOT
SQL> show user
USER is "C##KAMUS"
SQL> select table_name from tabs;

TABLE_NAME
------------------------------
TT

SQL> select count(*) from TT;

  COUNT(*)
----------
         0

SQL>
SQL> SELECT count(*) FROM CONTAINERS(TT);

  COUNT(*)
----------
    117386

SQL> SELECT count(*) FROM CONTAINERS(TT) WHERE CON_ID IN (3);

  COUNT(*)
----------
     58693
  • 查看执行计划,在执行计划中已经完全没有显示最终表的名称,而是出现X$CDBVW$这样的FIXED TABLE名称,在CDB中的执行计划将很难判断真实的执行路径。
SQL> set autot on
SQL> SELECT count(*) FROM CONTAINERS(TT);


  COUNT(*)
----------
    117386


Execution Plan
----------------------------------------------------------
Plan hash value: 3954817379

---------------------------------------------------------------------------------------------------------------------------
| Id  | Operation                 | Name     | Rows  | Cost (%CPU)| Time     | Pstart| Pstop |    TQ  |IN-OUT| PQ Distrib |
---------------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT          |          |     1 |     1 (100)| 00:00:01 |       |       |        |      |            |
|   1 |  SORT AGGREGATE           |          |     1 |            |          |       |       |        |      |            |
|   2 |   PX COORDINATOR          |          |       |            |          |       |       |        |      |            |
|   3 |    PX SEND QC (RANDOM)    | :TQ10000 |     1 |            |          |       |       |  Q1,00 | P->S | QC (RAND)  |
|   4 |     SORT AGGREGATE        |          |     1 |            |          |       |       |  Q1,00 | PCWP |            |
|   5 |      PX PARTITION LIST ALL|          | 58693 |     1 (100)| 00:00:01 |     1 |   254 |  Q1,00 | PCWC |            |
|   6 |       FIXED TABLE FULL    | X$CDBVW$ | 58693 |     1 (100)| 00:00:01 |       |       |  Q1,00 | PCWP |            |
---------------------------------------------------------------------------------------------------------------------------


Statistics
----------------------------------------------------------
     117574  recursive calls
          0  db block gets
      58796  consistent gets
          0  physical reads
        124  redo size
        544  bytes sent via SQL*Net to client
        551  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
         13  sorts (memory)
          0  sorts (disk)
          1  rows processed

结论: 操作起来稍显复杂,功能正常。

【Oracle Database 12c New Feature】In-Memory Column Store Initialization Parameters

Oracle发布的Database 12c最新版本12.1.0.2中正式引入了IMCS(列式内存)功能,正是在刚发布的时候被称为In-Memory Database Option的选件。

到底什么是IMCS?跟SAP HANA不一样吗?

IMCS是SGA中的一块新的独立区域,就跟众所周知的Buffer Cache或者Shared Pool一样,IMCS更接近于Buffer Cache,就是缓存表或者表分区或者物化视图中的数据的,但是不同的地方是在IMCS中缓存的数据格式并不是按照传统的行方式,而是按照列方式来缓存的。更简单地说,现在Oracle数据库在内存中既可以缓存行式数据也可以缓存列式数据了,但是在磁盘中存储的始终只有一份行式数据,这是Oracle IMCS与SAP HANA或者Sybase IQ等列式内存数据库截然不同的地方。 Screen Shot 2014-09-16 at 8.45.35 AM

Oracle在磁盘中始终只有一份行式数据,只是在缓存到内存中的时候,可以选择附加一份列式缓存。请注意,这里提及的“附加”一词,意味着,Oracle数据库以前的所有行为都没有发生变化,应该缓存到Buffer Cache(传统的行式缓存)中的还是会缓存到Buffer Cache中,只是如果设置了某张表也需要一份列式缓存,那么Oracle会额外维护一份该表的列式数据,这份数据就是存在SGA的新区域In-Memory Area中。这份数据只会保存在内存中,一旦数据库实例重启,这部分内存会重新加载(按照不同的设置,可以是一启动就立刻加载,也可以使用到再加载)。

Oracle需要保证效率的地方是当大量的DML产生时,额外的这部分列式缓存如何高效地保持更新和同步,特别是在RAC环境中更会是一个挑战,Oracle并没有透露更多关于这方面的信息,所有一切唯有等待真正应用的测试。

本文主要通过解释跟IMCS相关的数据库初始化参数,来介绍其中的知识。包括隐含参数,总共有115个参数跟inmemory相关(wow!)。

SQL> @p "inmemory"

NAME                                                         VALUE
------------------------------------------------------------ ----------------------------------------
_inmemory_check_prot_meta                                    FALSE
_inmemory_private_journal_quota                              100
_inmemory_private_journal_sharedpool_quota                   20
_inmemory_private_journal_numbkts                            512
_inmemory_private_journal_numgran                            128
_inmemory_jscan                                              0
_inmemory_pin_hist_mode                                      16
_inmemory_txn_checksum                                       0
_inmemory_buffer_waittime                                    100
_inmemory_cu_timeout                                         100
_inmemory_cudrop_timeout                                     1000
_inmemory_exclto_timeout                                     1000
_inmemory_num_hash_latches                                   256
_inmemory_strdlxid_timeout                                   0
_inmemory_incremental_repopulation                           FALSE
_inmemory_lock_for_smucreate                                 FALSE
_inmemory_auto_distribute                                    TRUE
_inmemory_autodist_2safe                                     FALSE
_inmemory_distribute_timeout                                 300
_inmemory_distribute_ondemand_timeout                        300
inmemory_size                                                218103808
_inmemory_64k_percent                                        30
_inmemory_min_ima_defersize                                  0
_inmemory_memprot                                            TRUE
_inmemory_analyzer_optimize_for                              0
_inmemory_default_flags                                      8459
_inmemory_default_new                                        FALSE
inmemory_clause_default
inmemory_force                                               DEFAULT
inmemory_query                                               ENABLE
_inmemory_query_scan                                         TRUE
_inmemory_scan_override                                      FALSE
_inmemory_scan_threshold_percent_noscan                      50
_inmemory_small_segment_threshold                            65536
_inmemory_query_fetch_by_rowid                               FALSE
_inmemory_pruning                                            ON
_inmemory_enable_sys                                         FALSE
_inmemory_populate_fg                                        FALSE
_inmemory_pga_per_server                                     536870912
inmemory_max_populate_servers                                1
_inmemory_servers_throttle_pgalim_percent                    55
inmemory_trickle_repopulate_servers_percent                  1
_inmemory_populate_wait                                      FALSE
_inmemory_populate_wait_max                                  600
_inmemory_imco_cycle                                         120
_inmemory_enable_population_verify                           1
_inmemory_log_level                                          1
_inmemory_fs_verify                                          FALSE
_inmemory_force_fs                                           FALSE
_inmemory_force_fs_tbs                                       SYSAUX
_inmemory_force_fs_tbs_size                                  1073741824
_inmemory_fs_raise_error                                     FALSE
_inmemory_fs_nodml                                           FALSE
_inmemory_fs_enable                                          FALSE
_inmemory_fs_enable_blk_lvl_inv                              TRUE
_inmemory_fs_blk_inv_blkcnt
_inmemory_fs_blk_inv_blk_percent                             20
_inmemory_enable_stat_alert                                  FALSE
_inmemory_imcu_align                                         TRUE
_inmemory_max_populate_retry                                 3
_inmemory_imcu_target_rows                                   1048576
_inmemory_imcu_target_bytes                                  0
_inmemory_imcu_source_extents                                0
_inmemory_imcu_source_blocks                                 0
_inmemory_imcu_source_minbytes                               1048576
_inmemory_imcu_populate_minbytes                             5242880
_inmemory_imcu_source_analyze_bytes                          134217728
_inmemory_imcu_target_maxrows                                8388608
_inmemory_imcu_source_maxbytes                               536870912
_inmemory_max_queued_tasks                                   208
_inmemory_repopulate_threshold_rows
_inmemory_repopulate_threshold_blocks
_inmemory_pct_inv_rows_invalidate_imcu                       50
_inmemory_pct_inv_blocks_invalidate_imcu                     100
_inmemory_repopulate_threshold_mintime_factor                5
_inmemory_repopulate_threshold_mintime                       0
_inmemory_repopulate_threshold_scans                         0
_inmemory_repopulate_priority_scale_factor                   100
_inmemory_repopulate_invalidate_rate_percent                 100
_inmemory_repopulate_priority_threshold_row                  20
_inmemory_repopulate_priority_threshold_block                40
_inmemory_repopulate_threshold_rows_percent                  5
_inmemory_repopulate_threshold_blocks_percent                10
_inmemory_repopulate_disable                                 FALSE
_inmemory_check_protect                                      FALSE
_inmemory_checksum                                           FALSE
_inmemory_validate_fetch                                     FALSE
_inmemory_journal_row_logging                                FALSE
_inmemory_journal_check                                      0
_inmemory_rows_check_interrupt                               1000
_inmemory_dbg_scan                                           0
_inmemory_segment_populate_verify                            0
_inmemory_query_check                                        0
_inmemory_test_verification                                  0
_inmemory_invalidate_cursors                                 TRUE
_inmemory_prepopulate_fg                                     0
_inmemory_prepopulate                                        TRUE
_inmemory_trickle_repopulate                                 TRUE
_inmemory_trickle_repopulate_threshold_dirty_ratio           0
_inmemory_trickle_repopulate_min_interval                    300
_inmemory_trickle_repopulate_fg                              0
_inmemory_force_non_engineered                               FALSE
_inmemory_suppress_vsga_ima                                  FALSE
optimizer_inmemory_aware                                     TRUE
_optimizer_inmemory_table_expansion                          TRUE
_optimizer_inmemory_gen_pushable_preds                       TRUE
_optimizer_inmemory_autodop                                  TRUE
_optimizer_inmemory_access_path                              TRUE
_optimizer_inmemory_quotient                                 0
_optimizer_inmemory_pruning_ratio_rows                       100
_parallel_inmemory_min_time_threshold                        AUTO
_parallel_inmemory_time_unit                                 1
_optimizer_inmemory_bloom_filter                             TRUE
_optimizer_inmemory_cluster_aware_dop                        TRUE
_optimizer_inmemory_minmax_pruning                           TRUE

115 rows selected.

先将注意力放在非隐含参数上,总共是7个参数(Phew!)。

SQL> show parameter inmemory

NAME                                               TYPE        VALUE
-------------------------------------------------- ----------- --------------------------------------------------
inmemory_clause_default                            string
inmemory_force                                     string      DEFAULT
inmemory_max_populate_servers                      integer     1
inmemory_query                                     string      ENABLE
inmemory_size                                      big integer 208M
inmemory_trickle_repopulate_servers_percent        integer     1
optimizer_inmemory_aware                           boolean     TRUE

inmemory_clause_default

该参数主要设置IMCS的一些默认参数值,这些默认值在今后为新的表或者分区设置IMCS时会自动参考。该参数默认值为空,表示除非在建表时明确指定要使用IMCS,否则不会将表缓存到列式内存中。该参数的设置可选项包括:

NO INMEMORY:与默认值效果一样,除非在建表时明确指定要使用IMCS,否则不会将表缓存到列式内存中。

INMEMORY:表示默认建表时就会使用IMCS,除非建表时明确指定“NO INMEMORY”。如果设置为该值,就可以继续设置如下这些其它的IMCS参数值。

NO MEMCOMPRESS | MEMCOMPRESS FOR { DML | QUERY [ LOW | HIGH ] | CAPACITY [LOW | HIGH] }: 表示在IMCS中缓存的列式数据是否需要压缩,如果压缩,采取何种压缩模式。

PRIORITY { NONE | LOW | MEDIUM | HIGH | CRITICAL }:优先级表示Oracle将表数据加载到列存中的积极性,Oracle会最优先考虑CRITICAL级别的加载,最后才进行NONE级别的加载。要知道内存毕竟是有限的,因此将那些必须要缓存到列存中的表设置为CRITICAL是更好的选择。

DISTRIBUTE [ AUTO | BY ROWID RANGE | BY PARTITION | BY SUBPARTITION ]:该参数只对RAC环境有效,指定IMCS中的数据在RAC环境多个节点的内存中如何分布。分别表示自动|按照ROWID范围|按照分区|按照子分区等方式在多个节点内存中缓存数据。

NO DUPLICATE | DUPLICATE [ ALL ]:该参数只对RAC环境有效,指定IMCS中的数据在RAC环境多个节点中是否要镜像。默认为NO DUPLICATE,也就是一份数据只会在多个节点中维护一份缓存,DUPLICATE表示维护两份相同的缓存,而DUPLICATE ALL则表示在所有节点内存中都维护一份相同的缓存。注意:在非Oracle工程系统(比如Exadata)中,只能是NO DUPLICATE效果,即使设置了DUPLICATE也不生效。

示例命令:

alter system set
INMEMORY_CLAUSE_DEFAULT='INMEMORY MEMCOMPRESS FOR CAPACITY HIGH'
scope=both;

inmemory_force

INMEMORY_FORCE = { DEFAULT | OFF } 当设置为默认值DEFAULT时,明确指定了INMEMORY的表和物化视图会缓存到IMCS中;如果设置为OFF,则表示全局禁用IMCS,意味着即使在表和物化视图上指定了INMEMORY,也不会缓存到In-Memory Area中。可以看到,这个参数没有提供全局打开IMCS的选项。

inmemory_max_populate_servers

该参数定义后台启动的用于将表数据加载至In-Memory Area区域中的进程个数,根据文档描述,该参数默认值是min(一般的CPU核数,PGA_AGGREGATE_TARGET/512),当inmemory_size=0的时候(也就是没有设置In-Memory Area大小的时候),该参数值为0,一旦设置了inmemory_size,该参数会根据上述算法自动设置。 如果该参数为0,则没有后台进程加载数据,表现为V$INMEMORY_AREA视图中的POPULATE_STATUS列始终为POPULATING。 Oracle建议该参数值不能设置过高,否则将导致CPU资源消耗严重。

inmemory_query

该参数用于在SYSTEM级别或者SESSIOn级别指定是否要启动inmemory查询,当设置了inmemory_size之后,该值默认会设置未ENABLE。 该参数可以在会话级别设置,临时启用或者禁用inmemory查询,用以比较两者之间的性能差距。一旦禁用,则即使相应表已经被缓存到In-Memory Area中,Oracle也不会选择扫描该区域。

inmemory_size

该参数用于指定In-Memory Area的大小,默认值为0,意味着默认没有启用IMCS。如同其他的内存相关参数,修改了此参数以后需要重新启动数据库实例才可以生效。该参数最小需要设置为100MB。在多租户环境中,如果是在PDB级别设置了该参数,则表明该PDB可以使用到的最大IMCS,但是注意的是多个PDB的该参数值的总和未必一定要等于在CDB级别的设置,甚至可以超越CDB级别的值。如果没有在PDB级别设置,那么所有PDB都会继承CDB上的该参数值,意味着所有PDB都可以用到所有可用的IMCS。 由于IMCS是列式存储,并且具有强大的压缩功能,因此即使准备将整个数据库的表都缓存到IMCS中,也无需特别大的内存空间。万一内存空间不够,表数据无法缓存到IMCS中,在V$INMEMORY_AREA.POPULATE_STATUS字段中可以发现“OUT OF MEMORY”字样。 实际上在绝大多数情况下,启用IMCS只需要设置这一个初始化参数即可,设置足够大的内存空间,然后尽情地Cache那些想缓存的表吧。

inmemory_trickle_repopulate_servers_percent

该参数定义后台用于重新加载IMCS中数据的进程负载,该参数值是一个相对于inmemory_max_populate_servers参数值的百分比。举例说,如果inmemory_max_populate_servers=10,而inmemory_trickle_repopulate_servers_percent=5,则意味着有10*0.05=0.5个进程会负责重新加载。什么是0.5个进程,实际上就是重新加载数据的进程负载最多占据一颗CPU的50%。 该参数默认值为1,在大多数场合都是适用的。

optimizer_inmemory_aware

该参数用于限制Oracle优化器对于inmemory功能的感知,默认值为TRUE,如果将该参数修改为FALSE,则Oracle优化器不会考虑使用IMCS,等同于设置OPTIMIZER_FEATURES_ENABLE小于12.1.0.2。

US Travel Tips (OOW, ACE Directors) – Cont. II

第一篇任意门:OOW US Travel Tips for ACE Director – Visa, Hotel, Car Rental, etc.

第二篇任意门:US Travel Tips (OOW, ACE Directors) – Cont.

为什么还会有第三篇呢,因为有人说,你这太不务正业了,介绍完签证,就是租车购物是吧。我深感惭愧,知错就改,最重要的要放在最后,所以就有第三篇。

在OOW前几天专门为ACE Directors举办的Product Briefing是干嘛的?

其实最开始的那篇文章提过了,在这个会议上会有Oracle各个产品线的总监级人物,预先透露一些会在OOW中宣布的产品新特性,回答作为听众的大家的一些问题,这实际上是给ACED与Oracle官方和研发Team更近距离的交流机会。小会议的现场,果粉免费宣传单元。 IMG_3361

参加Product Briefing的话住在哪里?

不出意外的话,所有人都会住在从SFO机场下高速很近的Sofitel,这个酒店不用像Hilton那样在网上预定,Oracle会直接处理好,只要拎包到酒店前台报名字就可以入住了。从Sofitel走到Oracle总部大约需要15分钟时间,在秋意爽朗的加州清晨走走路是很好的享受。 这就是酒店。 IMG_3379

从SFO机场怎么到酒店?

每个酒店都会有从机场接客人到酒店的班车,不过Sofitel比较挫,貌似客流量不大,所以必须要到了机场以后给他们前台打电话要车,再等大概二十分钟才会有车过来。本来想找到打电话的那个候车厅(在机场到达出口外面)照片,未果。打车也可以,到酒店大约是40美金,只能自己掏。

参加OOW住在哪里?

每年都是Hilton Union Square,就是联合广场旁边的希尔顿,楼下马路对面就是一堆租车公司,所以如果选择OOW结束以后租车,那么Pickup的地点选择Union Square就没错。

从Hilton怎么去OOW的Moscone Center?

Google Maps实在刷不出来,只好将就用Yahoo,每天是有很多趟Shuttle Bus往返酒店和会场,但是最方便的方法还是沿着路线走路过去,A是希尔顿,B是Moscone Center,大约10-15分钟的路程。中间标出来的是Apple Store,每年OOW的前后都是iPhone发布新品的时候,想买新品要一大早去排队,去年抢购土豪金的火爆场景还记忆犹新,今年可能可以直接带个iPhone 6回来。 Screen_Shot_2014-08-25_at_5_51_23_PM

听Larry演讲的Keynote有什么值得说的?

包含Larry演讲的Keynote一般都是在OOW第二天的下午3点开始,一开始通常是最大的钻石赞助商上台,大约半个小时后,三点半左右,Larry会上台,第一次参加的兄弟们会觉得很激动,因为Oracle选用的出场音乐通常都很震撼,如果又有大型新品发布(比如2008年Exadata的发布),各种长枪短炮,场面极其火爆。 IMG_0161

在Keynote快结束的前3分钟就要赶紧出场,否则你就会面临最终退场时候人山人海的挪动,特别如果你又很靠前,没有十几分钟出不来。所以,你是愿意在楼梯上做个照相的人,还是愿意在下面的人海中做个被照的人呢? IMG_0192

该如何选择感兴趣的Session?

每年的OOW有数千session,勤劳如老杨这样的也不过每天能够听4-5场,4天的话也就最多20场,所以从浩如烟海的讲座中预先筛选出感兴趣的话题,并提前做好计划是很重要的。Moscone Center并不是只有一个地方,有West,South和North三栋楼,大部分跟Oracle Database相关的话题都是South,还有一部分在West。OOW有专门的网页可以用于规划Session,并添加到自己的日程表中。

US Travel Tips (OOW, ACE Directors) – Cont.

之前写过一篇:OOW US Travel Tips for ACE Director – Visa, Hotel, Car Rental, etc.,还是颇受好评的,为了纪念今年美国签证系统崩溃了好多天,现在补一个更新的问答版。

什么资格适合递签(非面签)?

之前的美国签证过期不到4年,或者未满14周岁或80周岁以上。所以我去年的签证是今年8月份到期,我夫人的签证去年8月份就到期了,我儿子4岁从来没去过美国,这样的三个人可以全部都递签,而无需面试。

如果我符合递签条件,但是为了陪必须要免签的其它人(比如父母、妻子)一起,也可以选择面签吗?

这是很多人纠结的地方,因为在大使馆签证页面中有一句话“所有符合中信代传递续签条件的申请人通常必须使用中信代传递服务”,看上去似乎这是强制的,并且中信银行的职员也通常会告知,如果你满足递签条件却执意要去面签,是有可能会被拒绝进入美国大使馆的,理由是本来人就那么多了,你符合递签条件,就别来裹乱了。 但是实际上,还是有很多人通过的,参看穷游的经验,并且根据我的面签经验,在进入使馆之前,也没有人详细查看你是否符合免面签条件。但是,其实让父母单独去面签,也并不是什么大事儿,美国签证官都是会说中文的。今年我夫人的父母(我的丈人、丈母娘)会跟我们一起去美国,两个人都是2014年新护照、白本、两个人去的、没有提交什么房产证明、旅游计划等等东西,问了两个问题就双双通过了。主要问题大约都是去干嘛?钱谁出?

递签的话,人在北京,是选择广州使馆还是北京使馆?

根据今年递签的经验,无论人在哪里,全部都选择广州使馆,否则中信银行会不接受递签申请。在填表的过程中有两个地方需要选择使馆。 第一个是填DS160的时候,最开始的页面,下拉列表中要选择广州(当然,如果你是面签的,住在北京就还是选择北京)。 DS160选择广州使馆 第二个是CGI网站,如果选择了免面签代传递服务,那么到了Step 7的时候,会自动询问你是不是选择“中国,广州”作为您此次签证的申请地点,我之前以为这是Bug,但是实际上这是正常的,直接选择“是”即可。CGI网站填写步骤参看我之前的文章

擦,眼睛都填花了好不容易填完,选的是北京,确认页都打出来了,难道还要再重新填一份DS160?

没错,是要重新生成一份DS160的申请,但是,所幸绝大部分信息都可以不用再填。操作方式是:

  • 如上一条所述,在一开始下拉列表中选择广州;
  • 然后选择下面的“Retrieve An Application”;
  • 输入Application ID(这在之前的DS160确认页上就有);
  • 输入姓、出生年份、安全问题的答案(希望你还记得);
  • 这时候会出现两个选项,说确认页已经出了,要不就是查看确认页,要不就是创建一个新申请;
  • 毫不犹豫选择创建新申请。

Screen Shot 2014-08-25 at 2.36.37 PM 在新申请中,几乎90%的原来填过的信息都会自动填好,只需要点Next就可以,需要重新填的大概是行程计划部分和最后数十个无脑选No的安全问题。

签证的照片有什么要求?

官方要求是6个月之内的近照,如果还在使用一年前的照片,特别是一看就跟上一次签证一模一样的照片,中信银行是会拒收的。虽然我上一年混过去了,但是强烈建议大家每年申请的时候都现拍一张,没几个钱。注意:美国签证的照片是特殊尺寸,不是普通的2寸,而是51*51的size。

给孩子办签证有什么要注意的?

基本上没有特别需要注意的,未满14周岁的孩子是免面签的,但是递交给中信银行的材料中需要有出生证明的复印件和父母4年内的美国签证复印页(我不确认如果父母都没有去过美国需要提交什么?但是我想未满14周岁应该是不可以一个人就去美国的吧,所以总得有个陪同者)。之前不知道,又跑了一趟复印店。

今年美国签证系统崩溃,对签证批准时间有影响吗?

似乎没有看出太多影响,中信银行员工明确告知最多2周就可以拿到签证,这跟之前的说法是一样的。

之前文章遗漏的一个美国租车的问题。在美国租车,用国内的驾照可以吗?

可以,一点儿问题没有。并且无需翻译英文版本,无需公证(如果你细心,可以看到国内的驾照上本来就是双语的,有英文)。拿着就去,去了就租,租了就开走,违反交规还能不掏钱(最后这句当我没说)。

在机场免税店买化妆品便宜,还是在梅西百货买便宜?

这问题不应该由我一个大老爷们儿来回答,不过如果真要回答,我的答案是:It depends。梅西百货是有税的,纯看单品价格确实是机场免税店便宜一点点;但是梅西百货通常会有各种促销优惠活动,比如买几送几啥的,这样一算,就还是梅西百货便宜。依我说,你也不是玩代购倒货的,就别纠结这点儿钱了,痛痛快快逛逛旧金山,觉得好那就买吧,不管怎么样,也比国内便宜。别非等到回去的机场免税店,万一没想要的款呢?

在Outlets买什么?

好像没见到在Outlets买化妆品的,都是去买仔裤、衣服、包。大的旅行箱在梅西百货买会便宜一些,而Outlets嘛,中国人去没有不看Coach的,憋坏了的中国人,一口气买十几个甚至几十个Coach的包很常见;仔裤的话,Levis的价格只有国内的三分之一;AF的T恤、圆领衫也绝对物超所值;其它的什么Prada、Gucci、LV,不是哪个Outlets都有的,要看。这里是美国Premium Outlets的网站,去之前可以先搜一下有没有想买的品牌。加州有11个Premium Outlets,其中旧金山旁边的是Petaluma Village和Gilroy。

Oracle Policy-Managed Cluster – Growing for DBaaS

Policy-Managed Cluster在Oracle 11gR2中被引进,在Oracle 12c中使用dbca创建RAC数据库的时候,Policy-Managed选项已然成为默认值。 Policy-Managed RAC

那么到底什么是Policy-Managed方式的集群和数据库呢?与以前的Admin-Managed方式有何区别?何种环境适合使用这种新的方式进行管理?本文尝试回答这些问题,并且做出简单的测试。

什么是Policy-Managed方式?

基于策略的管理方式,是以服务器池(Server Pools)为基础的,简单地说,就是先定义一些服务器池,池中包含一定量的服务器,然后再定义一些策略,根据这些策略Oracle会自动决定让多少数据库实例运行在池中的几台机器上。数据库实例名后缀、数据库实例个数、所运行的主机,这些都是通过策略决定的,而不是数据库管理员事先定好的。

与Admin-Managed方式有何区别?

实际上上面的表述已经明确说明了,Policy-Managed和Admin-Managed方式的差别。让我们再回顾一下,在以往我们创建一个RAC数据库大概是怎样的方法,我们在dbca的界面中会选择要将数据库实例运行在整个集群中的几台机器上,或者是2台或者是3台,甚或是更多,但是只要在安装的时候选定几台机器,那么以后如果不做增减节点的操作,就始终会在这几台机器上运行。而且,通常会根据主机名称的排序自动将每台主机上的数据库实例依次命名为dbname1到dbnameN。这些在管理员安装完毕以后,都不会再自动变化,这就是Admin-Managed方式。

何种环境适合使用这种新的方式进行管理?

当管理大量的服务器集群,并且在这些集群中运行着多种不同重要程度,不同策略的RAC数据库时,为了简化管理,建议使用Policy-Managed方式,实际上Oracle也建议只有在超过3台的服务器的时候才使用Policy-Managed来管理整个数据库集群。想象一下使用Policy-Managed方式可以达到的效果:如果我们有10台服务器组成,根据不同的应用的重要性定义服务器池的关键程度,然后在其中某些机器意外停机的情况下,仍然可以自动地保持足够多的机器给重要的系统提供数据库服务,而将不关键的系统数据库服务器个数降低到最低限度。

那么Policy-Managed方式到底长什么样?

在默认安装完Oracle 12c的RAC数据库之后,发现数据库实例始终只会启动在一个节点中。检查服务器池配置。

[oracle@dbserver2 ~]$ srvctl config srvpool
Server pool name: Free
Importance: 0, Min: 0, Max: -1
Category:
Candidate server names:
Server pool name: Generic
Importance: 0, Min: 0, Max: -1
Category:
Candidate server names:
Server pool name: orcl_pool
Importance: 0, Min: 0, Max: 1
Category: hub
Candidate server names:

Free池和Generic池是默认存在的,orcl_pool池则是在dbca创建数据库的时候由我们自己定义的。其中Min: 0, Max: 1表示在这个池中最少允许有0台机器,最多允许有1台机器被使用。所以这也造成了使用这个服务器池的数据库实例始终只会启动在一个节点中,即使这在我们最初的定义中是一个RAC数据库。

当前的数据库实例启动在节点2中,比较一下节点1和节点2服务器使用情况的输出。

[grid@dbserver2 ~]$ crsctl status server dbserver1 -f
NAME=dbserver1
MEMORY_SIZE=3954
CPU_COUNT=1
CPU_CLOCK_RATE=2
CPU_HYPERTHREADING=0
CPU_EQUIVALENCY=1000
DEPLOYMENT=other
CONFIGURED_CSS_ROLE=hub
RESOURCE_USE_ENABLED=1
SERVER_LABEL=
PHYSICAL_HOSTNAME=
STATE=ONLINE
ACTIVE_POOLS=Free --此处显示未Free,表示节点1中不属于任何正在运行的服务器池资源。
STATE_DETAILS=
ACTIVE_CSS_ROLE=hub

[grid@dbserver2 ~]$ crsctl status server dbserver2 -f
NAME=dbserver2
MEMORY_SIZE=3954
CPU_COUNT=1
CPU_CLOCK_RATE=2
CPU_HYPERTHREADING=0
CPU_EQUIVALENCY=1000
DEPLOYMENT=other
CONFIGURED_CSS_ROLE=hub
RESOURCE_USE_ENABLED=1
SERVER_LABEL=
PHYSICAL_HOSTNAME=
STATE=ONLINE
ACTIVE_POOLS=ora.orcl_pool --此处显示节点2正运行在orcl_pool服务器池资源中。
STATE_DETAILS=
ACTIVE_CSS_ROLE=hub

接下来需要修改一下配置,让RAC数据库以我们熟知的方式启动在多个节点上。 –修改orcl_pool池中最少运行一台机器,最多运行2台机器,还记得我们前面说的关键程度吗?importance表示该池的关键程度,数字越大表示关键程度越高,越优先被考虑满足Min条件。

[oracle@dbserver2 ~]$ srvctl modify srvpool -serverpool orcl_pool -importance 5 -min 1 -max 2

–重新检查服务器池信息,可以看到已经修改成功,Min: 1, Max: 2

[oracle@dbserver2 ~]$ srvctl config srvpool
Server pool name: Free
Importance: 0, Min: 0, Max: -1
Category:
Candidate server names:
Server pool name: Generic
Importance: 0, Min: 0, Max: -1
Category:
Candidate server names:
Server pool name: orcl_pool
Importance: 5, Min: 1, Max: 2
Category: hub
Candidate server names:

–查看当前服务器池的状态,可以看到orcl_pool池中激活的服务器包括了节点1和节点2两台机器。

[grid@dbserver1 ~]$ crsctl status serverpool
NAME=Free
ACTIVE_SERVERS=

NAME=Generic
ACTIVE_SERVERS=

NAME=ora.orcl_pool
ACTIVE_SERVERS=dbserver1 dbserver2

在修改完毕以后,节点1中的数据库实例就会自动启动,我们可以通过crsctl命令查看服务器的状态,其中STATE_DETAILS字段显示了正在启动资源,在正常启动完毕以后该字段会显示为空。

[grid@dbserver2 ~]$ crsctl status server dbserver1 -f
NAME=dbserver1
MEMORY_SIZE=3954
CPU_COUNT=1
CPU_CLOCK_RATE=2
CPU_HYPERTHREADING=0
CPU_EQUIVALENCY=1000
DEPLOYMENT=other
CONFIGURED_CSS_ROLE=hub
RESOURCE_USE_ENABLED=1
SERVER_LABEL=
PHYSICAL_HOSTNAME=
STATE=ONLINE
ACTIVE_POOLS=ora.orcl_pool
STATE_DETAILS=STARTING RESOURCES
ACTIVE_CSS_ROLE=hub

现在就出现了一个比较尴尬的情况(对于我们以前管理RAC的常识来说),由于dbserver1中的实例是后启动的,因此实例名后缀为2,而dbserver2中的实例名后缀是1,实际上,在Policy-Managed管理的RAC环境中,无需关注到底哪个实例启动在哪台机器上,我们需要的就是通过SCAN IP,通过Service名去访问数据库就好,而不需要通过实例名访问数据库。但是这里为了测试一下功能,还是决定1归1,2归2,我有说过我是完美主义者吗?

--先将dbserver1上的数据库服务资源reolocate到dbserver2中,这样实例2就运行回到了dbserver2中。
[grid@dbserver1 ~]$ crsctl relocate resource ora.orcl12c.db -s dbserver1 -n dbserver2
--再将dbserver1中的实例启动,因为实例2已经启动在dbserver2中,因此即使此时该实例是后启动的,但是仍然还是会命名为实例1。
[oracle@dbserver1 ~]$ srvctl start instance -db orcl12c -node dbserver1

最后将这个RAC数据库再改回到只会启动一个实例的默认状态。

[oracle@dbserver2 ~] srvctl modify srvpool -serverpool orcl_pool -min 0 -max 1

以后,无论是启动在哪台机器上,数据库的实例名永远会是dbname_1(注意,这里有一个下划线,这是Policy-Managed数据库实例的命名规则)。而我们访问数据库,则不应该指定实例名。比如:

sqlplus sys/passwd@db-cluster-scan:1521/orcl12c as sysdba

因为现在你已经无需关心到底实例是启动在哪台机器上了,后面是一个资源池,是不是有些熟悉这样的表述,是的,没错,Cloud! 我们也贴上了Cloud这个红到发紫的词,这就是Oracle私有云解决方案的构成组件之一。

How to cleanup ADRCI Homes

ADRCI (ADR Command Interpreter) 是伴随Oracle 11g推出的新工具,由于各种log的位置纷繁复杂,因此在Oracle 11g中,Oracle将数据库、监听、客户端等的Log统一转到了diagnostic_dest初始化参数所指向的目录下的diag目录中,并且提供了ADRCI实用程序来进行高效查询及管理。在这之前我曾经写过两篇跟ADRCI相关的文章。
ADRCI Purge is NOT What You Think It Should Be:ADRCI的Purge命令用于清除日志文件中的内容,但是使用起来并非尽如人意。
Oracle11g for Windows ADRCI Issue:在Windows中使用ADRCI会产生的小问题,当然很久没有用过Windows,不知道这个问题是不是还依然存在。

ADRCI看上去是个很好的东西,但是除了show alert相对简单之外,其它的各种命令和查询条件的指定实际上比较繁琐,因此ADRCI在我周边的使用者寥寥可数,我算是坚持使用的为数不多者。因为使用者不多,所以对于ADRCI中一些极为简单的操作,了解者也比较少。

本文也是如此,如何删除在ADRCI中显示出来的多余的Homes?解决方法简单到令人发指。

问题重现,在命令中show homes显示出6个Home,这通常是由于安装过其它的数据库实例,然后删除的时候遗留下来的Log目录仍然存在。对于完美主义者来说这难以容忍。

[oracle@emnotech rdbms]$ adrci

ADRCI: Release 12.1.0.1.0 - Production on Mon Aug 18 01:34:31 2014

Copyright (c) 1982, 2013, Oracle and/or its affiliates.  All rights reserved.

ADR base = "/u01/app/oracle"
adrci> show home
ADR Homes:
diag/clients/user_oracle/host_266584670_80
diag/tnslsnr/emnotech/listener
diag/rdbms/orcl/orcl
diag/rdbms/slob/SLOB
diag/rdbms/slob/orcl
diag/rdbms/slob/slob

如上所示,ADR的基础目录在/u01/app/oracle,而各个Home都在其下的diag目录中,因此我们直接ls看一下目录结构。

[oracle@emnotech rdbms]$ pwd
/u01/app/oracle/diag/rdbms
[oracle@emnotech rdbms]$ ls -l
total 8
drwxr-x---. 3 oracle oinstall 4096 Jun  3 14:20 orcl
drwxr-xr-x. 5 oracle oinstall 4096 Aug  1 00:36 slob
[oracle@emnotech rdbms]$ ls -l slob
total 12
-rw-r-----.  1 oracle oinstall    0 Jun  5 03:16 i_1.mif
drwxr-xr-x. 16 oracle oinstall 4096 Jun  5 03:16 orcl
drwxr-xr-x. 16 oracle oinstall 4096 Jun  5 03:35 slob
drwxr-xr-x. 16 oracle oinstall 4096 Aug  1 00:36 SLOB

可以看到目录结构与ADRCI命令行中显示的完全一样,所以,解决方法就是直接rm掉不需要的目录。

[oracle@emnotech rdbms]$ rm -rf slob
[oracle@emnotech rdbms]$ ls -l
total 4
drwxr-x---. 3 oracle oinstall 4096 Jun  3 14:20 orcl

友情提示:一定要屡次确认才可以执行rm -rf这样危险的命令,每次对这个命令敲回车我都心惊胆战、心存敬畏。

[oracle@emnotech rdbms]$ adrci

ADRCI: Release 12.1.0.1.0 - Production on Mon Aug 18 01:35:23 2014

Copyright (c) 1982, 2013, Oracle and/or its affiliates.  All rights reserved.

ADR base = "/u01/app/oracle"
adrci> show home
ADR Homes:
diag/clients/user_oracle/host_266584670_80
diag/tnslsnr/emnotech/listener
diag/rdbms/orcl/orcl
adrci>

再次进入ADRCI中查看Homes,世界清静了。

Oracle Database In-Memory (12.1.0.2) Frequently Asked Question

Oracle Database In-Memory选件于美国时间6月10日已经发布,发布会视频参看:http://www.oracle.com/us/corporate/events/dbim/index.html

什么时候Oracle Database In-Memory选件能够发布?

该选件将包含在Oracle Database 12c的第一个Pacthset(12.1.0.2)中一起发布。

价格如何?

价格会在发布日的时候决定。

新的Oracle Database In-Memory选件是否会替代In-Memory Database Cache(就是TimesTen技术)选件?

当然不,完全不一样的应用场景。In-Memory Database Cache是在应用程序层通过TimesTen来管理的内存性质的数据库,这是为了让OLTP应用享受到对于存储在Oracle数据库中一部分表的超低延迟访问而设计的。

Oracle Database In-Memory选件是否会替代TimesTen?

当然不。像上面所说,TimesTen通常是部署在应用层的,可能是作为一个独立的数据库,也可能是作为在Oracle数据库前面的In-Memory Database Cache。TimesTen提供的是超低延迟数据访问,由此提供了极速的响应时间。由此受益的OLTP系统通常不会创建用于报表应用的索引,也因此通常无法从数据库层的Oracle Database In-Memory选件中获得多少性能提升。

Oracle Database In-Memory选件是否会替代Exalytics?

当然不。并不是所有客户都需要在他们的系统中拥有强力的分析能力,而Exalytics是Oracle的一体化策略分析平台。它提供了丰富的分析工具和可视化工具,以及大量的标准报表、预测分析、数据发现,是为了最优化地运行BI和EPM工作的平台,是对数据库层的补充而不是竞争。 Exalytics也不仅仅是针对Oracle Database 12c的,它可以处理多种数据源,包括Teradata、Oracle database 11g、SAP Netweaver等。

那么在Exalytics中使用的是什么关系型内存数据库引擎呢?

Exalytics使用的是TimesTen In-Memory Database for Exalytics。在TimesTen产品发展路线图中也包含着跟Oracle Database In-Memory选件相类似的列式存储技术,而且是使用的相同的底层列式处理架构。这项技术计划在2014年跟Oracle Database In-Memory选件一起在相同的时间窗口发布。实际上,TimesTen和Oracle Database In-Memory选件在Oracle公司内部是由同一个小组来开发的,共享相同的创新技术以及底层架构。

那么在Exalytics中使用的内存数据库引擎在未来会变成Oracle Database In-Memory选件吗?

没有这个计划。Oracle期望TimesTen会持续研发。

Oracle Database In-Memory选件是否会替代Exadata?

当然不。这个问题太幼稚,就不做解释了。

好吧,那到底什么是Oracle Database In-Memory选件呢?

这是一项新的技术,内置在Oracle数据库中,通过内存中的列式存储数据来获得超快的数据处理能力。

当使用Oracle Database In-Memory时,数据库的大小受限于服务器中的可用内存容量吗?

不。由用户来决定哪些对象会存在In-Memory列式缓存中。如果一张表太大了,无法完全缓存在In-Memory列存中,Oracle会尝试将尽可能多的数据保持在内存中(以列的形式),剩下的仍然存储在闪存或者磁盘中(以原来行的形式),这样的操作非常高效并且是完全透明的。 你可以有选择性的定义一部分数据库,比如将热表、热分区、经常访问的列放入内存中。这就让除了Oracle原有的那些跨越内存、闪存、磁盘的各种优化方式之外,Oracle Database In-Memory选件还能够专门为频繁访问的业务关键数据提供帮助。当然,如果数据库足够小,全部表都能使用Oracle Database In-Memory选件。

Oracle Database In-Memory选件只会对分析/报表类业务有帮助吗?

虽然确实分析/报表类业务能够从列式存储中获得最多的收益,但是其他类型业务也同样能够受益。比如通过减少需要的索引,就能够加速OLTP业务。通常在以前的混合负载业务中需要在表上创建很多的多字段索引来支撑报表类型的查询,如今In-Memory列存则对DML操作造成更少的影响,而又可以提供相同的查询性能。 减少索引还能够带来优化和管理的简便,DBA不再需要去rebuild那些碎片化的索引,也不再需要去研究为什么优化器选择了错误的索引。

从Oracle Database In-Memory功能中能够期望获得多少性能提升?

使用该选项能够为多种查询负载提供显著的性能提升。从一个非常简单的星型查询到一个非常复杂的具有多个子查询的SQL,就算所有的的表都在内存中,In-Memory列式缓存仍然会胜过普通的Buffer Cache。 比如说,In-Memory列存在查询很少列上的很多行时,会提供极高的性能,通常这类查询都是分析类的查询,在这类查询上使用In-Memory列存和使用普通缓存(Buffer Cache)相比,有超过100倍的性能提升。

当使用Oracle Database In-Memory选件的时候,我该对索引做些什么吗?

你可以保留以前的所有索引,就像以前那样运行你的应用。Oracle优化器完全能够感知到内存中的列式数据,只有当确认使用这些列式数据会有性能提升的时候才会使用到,如果优化器认为从索引访问中能够获得更好的性能,那么就会自动去读取普通缓存(Buffer Cache)中的数据,就跟以前一样。 不过,如果将一些多列索引设置为invisible,引导Oracle优化器更多地去选择列式缓存,可能会带来额外的速度提升。然后,你可以删除掉这些无用的索引,这样又能带来进一步的性能提升,因为Oracle数据库需要维护的索引更少了。

如果使用Oracle Database In-Memory选项,数据库现在的功能会受影响吗?

没有任何一项现有的Oracle数据库功能会受到Oracle Database In-Memory选项的限制;很多操作还能在性能上获得显著提升;一些原本为了提高性能所做的操作(比如创建索引)会变得不再重要;安全性、高可用性以及其它的所有功能都跟以前一模一样,完全不受影响。 当然在DML操作上会有一些额外消耗,因为In-Memory列存要时刻跟表数据保持同步,但是这部分列存是完全在内存中的,所以不需要额外的Logging操作。

Oracle Database In-Memory选项在高可用性上跟其它厂商的内存数据库相比如何?

因为In-Memory列存并没有给Oracle数据库施加任何限制,因此在高可用性上可以享受Oracle数据库完整的丰富的高可用解决方案,而其它厂商的高可用性均不太完备。

如何规划In-Memory列存所需要的内存大小?

In-Memory Area(用于保存In-Memory列存的内存区域)是SGA中的一块新的缓存池,该缓存大小由初始化参数INMEMORY_SIZE指定,如果一旦设定,最小是100MB。因为In-Memory Area是SGA的一部分,因此其最大大小受制于分配了其它所有内存池之后的SGA中的剩余空间。INMEMORY_SIZE默认是0,也就是不启用Oracle Database In-Memory选项。 由于In-Memory Area是一块静态区域,所以不会被自动内存管理算法所影响。 如果想要修改In-Memory Area的大小,需要重启数据库实例,在RAC数据库中,可以在各实例的内存中复制In-Memory列存,因此可以通过重启节点的方式来最小化对应用的影响。

在RAC环境中,是否In-Memory列存是在集群的所有节点中完全镜像的?

在RAC环境中,用户可以自行设定使用以下三种选项中的任何一种: 1. 在所有实例的内存中镜像相同的表数据。这样每个实例都会读取自己的本地内存,通常这用于较小的表。 2. 表数据分布在所有实例的内存中。这样每个实例内存中只会保存表中的一部分数据,没有冗余复制。 3. 表数据分布在所有实例的内存中,并且为高可用性保留2份镜像。这种工作机制很像ASM的条带化和冗余性。