PostgreSQL 9.5 new feature highlight: BRIN indexes

前几天PostgreSQL 9.5 Alpha 1版本刚刚发布,在新版本中吸引我注意的是BRIN index。为什么引人注意?因为这就是活脱脱的Oracle Exadata中的Storage Index和Oracle Database 12.1.0.2中的新功能Zone Maps。

Exadata的Storage Index不说了,因为那并非数据库范畴的解决方案,而Oracle数据库12.1.0.2中的新功能Zone Maps曾让我非常激动,但是最终发现该功能也只能在运行于Exadata上的Oracle中才能启用,略失望。

Zone Maps的解释如下:

Zone maps in an Oracle Database store minimum and maximum values of columns for a range of blocks (known as a zone). In addition to performing I/O pruning based on predicates of clustered fact tables, zone maps prune on predicates of dimension tables provided the fact tables are attribute-clustered by the dimension attributes though outer joins with the dimension tables.

BRIN index的解释如下:

BRIN stands for Block Range INdexes, and store metadata on a range of pages. At the moment this means the minimum and maximum values per block…So if a 10GB table of order contained rows that were generally in order of order date, a BRIN index on the order_date column would allow the majority of the table to be skipped rather than performing a full sequential scan.

同样的思路,在一个类索引结构中存储一定范围的数据块中某个列的最小和最大值,当查询语句中包含该列的过滤条件时,就会自动忽略那些肯定不包含符合条件的列值的数据块,从而减少IO读取量,提升查询速度。

以下借用Pg wiki中的例子解释BRIN indexes的强大。

 -- 创建测试表orders
 CREATE TABLE orders (
     id int,
     order_date timestamptz,
     item text);

 -- 在表中插入大量记录,Pg的函数generate_series非常好用。
 INSERT INTO orders (order_date, item)
 SELECT x, 'dfiojdso' 
 FROM generate_series('2000-01-01 00:00:00'::timestamptz, '2015-03-01 00:00:00'::timestamptz,'2 seconds'::interval) a(x);

 -- 该表目前有13GB大小,算是大表了。
 # \dt+ orders
                    List of relations
  Schema |  Name  | Type  | Owner | Size  | Description 
 --------+--------+-------+-------+-------+-------------
  public | orders | table | thom  | 13 GB | 
 (1 row)

 -- 以全表扫描的方式查询两天内的记录,注意这里预计需要30s,这是一个存储在SSD上Pg数据库,因此速度已经很理想了。
 # EXPLAIN ANALYSE SELECT count(*) FROM orders WHERE order_date BETWEEN '2012-01-04 09:00:00' and '2014-01-04 14:30:00';
                                                                          QUERY PLAN                                                                          
 -------------------------------------------------------------------------------------------------------------------------------------------------------------
  Aggregate  (cost=5425021.80..5425021.81 rows=1 width=0) (actual time=30172.428..30172.429 rows=1 loops=1)
    ->  Seq Scan on orders  (cost=0.00..5347754.00 rows=30907121 width=0) (actual time=6050.015..28552.976 rows=31589101 loops=1)
          Filter: ((order_date >= '2012-01-04 09:00:00+00'::timestamp with time zone) AND (order_date <= '2014-01-04 14:30:00+00'::timestamp with time zone))
          Rows Removed by Filter: 207652500
  Planning time: 0.140 ms
  Execution time: 30172.482 ms 
 (6 rows)

 -- 接下来在order_date列上创建一个BRIN index
 CREATE INDEX idx_order_date_brin
   ON orders
   USING BRIN (order_date);

 -- 查看这个BRIN index占多少物理空间,13GB的表,而BRIN index只有504KB大小,非常精简。
 # \di+ idx_order_date_brin
                               List of relations
  Schema |        Name         | Type  | Owner | Table  |  Size  | Description 
 --------+---------------------+-------+-------+--------+--------+-------------
  public | idx_order_date_brin | index | thom  | orders | 504 kB | 
 (1 row)

 -- 再次执行相同的SQL,看看性能提升多少。速度上升到只需要6秒钟,提升了5倍。如果这是存储在HDD上的Pg库,这个效果还能更明显。
 # EXPLAIN ANALYSE SELECT count(*) FROM orders WHERE order_date BETWEEN '2012-01-04 09:00:00' and '2014-01-04 14:30:00';
                                                                               QUERY PLAN                                                                               
 -----------------------------------------------------------------------------------------------------------------------------------------------------------------------
  Aggregate  (cost=2616868.60..2616868.61 rows=1 width=0) (actual time=6347.651..6347.651 rows=1 loops=1)
    ->  Bitmap Heap Scan on orders  (cost=316863.99..2539600.80 rows=30907121 width=0) (actual time=36.366..4686.634 rows=31589101 loops=1)
          Recheck Cond: ((order_date >= '2012-01-04 09:00:00+00'::timestamp with time zone) AND (order_date <= '2014-01-04 14:30:00+00'::timestamp with time zone))
          Rows Removed by Index Recheck: 6419
          Heap Blocks: lossy=232320
          ->  Bitmap Index Scan on idx_order_date_brin  (cost=0.00..309137.21 rows=30907121 width=0) (actual time=35.567..35.567 rows=2323200 loops=1)
                Index Cond: ((order_date >= '2012-01-04 09:00:00+00'::timestamp with time zone) AND (order_date <= '2014-01-04 14:30:00+00'::timestamp with time zone))
  Planning time: 0.108 ms
  Execution time: 6347.701 ms
 (9 rows)

 --能够让用户自行设定一个range中可以包含的数据块数,也是很体贴的设计。默认情况下一个range包含128个page,我们可以修改为更小或者更大,包含的page越少则精度越细,相应的BRIN index也就会越大;反之则精度粗,BRIN index小。
 -- 创建一个每个range包含32 pages的索引。
 CREATE INDEX idx_order_date_brin_32
 ON orders
 USING BRIN (order_date) WITH (pages_per_range = 32);
 
 -- 再创建一个每个range包含512 pages的索引。
 CREATE INDEX idx_order_date_brin_512
 ON orders
 USING BRIN (order_date) WITH (pages_per_range = 512);

--比较一下各个索引的大小。
 # \di+ idx_order_date_brin*
                                  List of relations
  Schema |          Name           | Type  | Owner | Table  |  Size   | Description 
 --------+-------------------------+-------+-------+--------+---------+-------------
  public | idx_order_date_brin     | index | thom  | orders | 504 kB  | 
  public | idx_order_date_brin_32  | index | thom  | orders | 1872 kB | 
  public | idx_order_date_brin_512 | index | thom  | orders | 152 kB  | 
 (3 rows)

延展阅读:
Postgres 9.5 feature highlight: BRIN indexes
WAITING FOR 9.5 – BRIN: BLOCK RANGE INDEXES

Compare PostgreSQL Standard Statistics Views with Oracle Dynamic Performance (V$) Views

Oracle数据库的性能视图几乎可以说是最引以为骄傲的功能,在那样细粒度的采样统计强度下,依然保持卓越的性能,基于这些性能数据采样之后形成的AWR,更是Oracle DBA分析数据库性能问题的最重要手段之一。

那么在誉为最接近Oracle的开源数据库PostgreSQL中,如果要诊断性能问题,又有哪些视图可以使用呢?作为Oracle DBA,在学习PostgreSQL的时候,不可避免地会将PostgreSQL和Oracle进行比较。

以下SQL命令,在mydb=#提示符下的均为在PostgreSQL中执行的,在SQL>提示符下的均为在Oracle中执行的。

先看一下在PostgreSQL中存在那些统计信息视图。PostgreSQL中数据字典的命名还是很规范的,所有统计信息基本上都以pg_stat_开头。

mydb=# select relname from pg_class where relname like 'pg_stat_%';
             relname              
----------------------------------
 pg_statistic
 pg_stats
 pg_stat_all_tables
 pg_stat_xact_all_tables
 pg_stat_sys_tables
 pg_stat_xact_sys_tables
 pg_stat_user_tables
 pg_stat_xact_user_tables
 pg_statio_all_tables
 pg_statio_sys_tables
 pg_statio_user_tables
 pg_statio_all_indexes
 pg_statio_sys_indexes
 pg_statio_user_indexes
 pg_statio_all_sequences
 pg_statio_sys_sequences
 pg_statio_user_sequences
 pg_stat_activity
 pg_stat_replication
 pg_stat_database
 pg_stat_database_conflicts
 pg_stat_user_functions
 pg_stat_xact_user_functions
 pg_stat_archiver
 pg_stat_bgwriter
 pg_stat_all_indexes
 pg_stat_sys_indexes
 pg_stat_user_indexes
 pg_statistic_relid_att_inh_index
(29 rows)

pg_stat_activity
该视图显示了连接入一个Cluster下所有数据库的会话的统计信息,每个会话一行记录,类似于Oracle中的V$SESSION视图。
pg_stat_activity.query字段直接显示了该会话正在执行的SQL或者上次执行的SQL语句文本。在Oracle中检查一个会话正在执行的SQL语句文本,则需要通过V$SESSION和V$SQL视图Join才可以。
pg_stat_activity.pid字段直接显示了该会话在操作系统上的进程ID,这样通过top命令看到的繁忙操作系统进程,可以很简单地通过该字段定位,来作进一步的诊断。在Oracle中则需要通过V$SESSION和V$PROCESS视图Join才可以。

pg_stat_archiver
该视图始终只有一条记录,显示了负责一个cluster下所有数据库的重做日志(PostgreSQL中称为WAL file)归档进程的统计信息,记录项比较简单。last_archived_wal和last_archived_time分别显示了最近一次归档的文件名和最近一次归档时间。
类似于Oracle中的V$ARCHIVE_DEST_STATUS。由于PostgreSQL中的归档实现实在是太简单了,所以几乎跟Oracle没有太多可比性。

pg_stat_bgwriter
该视图始终只有一条记录,显示了负责一个cluster下所有数据库的后台写进程的统计信息,也就是在操作系统中看到的postgres: writer process。该进程每隔bgwriter_delay初始化参数定义的间隔(默认200ms)会唤醒,将Buffer Pool中修改过的页写入到磁盘。跟Oracle的后台进程DBWR非常相仿。
在Oracle中没有专门记录DBWR进程的性能视图,V$BGPROCESS视图也同样没有提供类似的信息,但是在V$SYSSTAT却记录了DBWR的统计信息,这部分跟pg_stat_bgwriter中记录的信息相仿。Oracle 11gR2中有超过600项的统计信息记录在V$SYSSTAT视图中。

SQL> select NAME,VALUE from v$sysstat where upper(name) like '%DBWR%';

NAME                                                                  VALUE
---------------------------------------------------------------- ----------
flash cache insert skip: DBWR overloaded                                  0
DBWR checkpoint buffers written                                     1564210
DBWR thread checkpoint buffers written                                    0
DBWR tablespace checkpoint buffers written                             2852
DBWR parallel query checkpoint buffers written                            0
DBWR object drop buffers written                                        324
DBWR transaction table writes                                         81619
DBWR undo block writes                                               485016
DBWR revisited being-written buffer                                       0
DBWR lru scans                                                            0
DBWR checkpoints                                                       4158
DBWR fusion writes                                                        0

12 rows selected.

pg_stat_database
该视图对于每个database显示一行记录,PostgreSQL中的Cluster类似于Oracle的一个Instance,一个Cluster下可以创建多个database。
该视图中记录了每个数据库提交了多少事务,回滚了多少事务,读了多少数据块,查询、插入、更新、删除了多少记录(在PostgreSQL中用Tuple这个奇怪的词表示跟Row相同的概念),产生过多少死锁。总之这是一个数据库级别相对很简单的统计信息。
但是,在Oracle中还真没有与此类似的性能视图,实际上Oracle没有一个视图简单地记录了一个Schema下面总共查询或者DML了多少条记录,但是却有DBA_TAB_MODIFICATIONS这样的视图详细记录每一张表的DML数量。查询了多少数据?可能Oracle认为这个数字是太不重要了,或者说实在是太大了,完全没必要记录。
对于事务级别的统计,同样可以在Oracle的V$SYSSTAT视图中查询包含“ROLLBACK”和“COMMIT”字样的统计值,远比PostgreSQL中记录地要更多样。

pg_stat_all_tables/pg_stat_sys_tables/pg_stat_user_tables
在PostgreSQL的统计信息视图中,all表示一个数据库下所有的表,sys表示所有的系统表,user表示所有用户创建的表,这三个配套的视图我们放在一起看。以下类似的也相同。
该视图对于每张表显示一条记录,显示了一张表上进行过多少全表扫描,多少索引扫描,查询、插入、更新、删除过多少记录,表中现在有多少记录,表的分析时间等。
在Oracle中表的分析信息存储在DBA_TABLES中,而对于每个表上DML的信息如前所述,可以从DBA_TAB_MODIFICATIONS视图中查询,而经历过怎样的IO则又可以从V$SEGSTAT视图中查询。好吧,实际上,在Oracle中根本也不关注一个表上读取过多少记录这样的数字,所以在PostgreSQL中但凡跟Tuple相关的统计值在Oracle中都找不到对应的记录。对于Oracle来说,IO都以Block为单位,所以读取一条记录还是读取一个块,在IO消耗上没有区别。而至于对于返回记录数等的优化,则归结到SQL层面,那则可以通过V$SQLSTAT等一系列视图作更详细的分析。

Oracle在视图层面从Table概念和Segment概念上做了详细的区分,看似复杂,实际清晰而且详尽,而在PostgreSQL中则混为一谈了,当然在PostgreSQL中通过后面会谈到的pg_statio_系列视图又对表和索引上的IO统计信息进行了记录。

pg_stat_xact_all_tables/pg_stat_xact_sys_tables/pg_stat_xact_user_tables
该系列视图与上述相仿,只是增加了xact前缀,xact表示transaction,统计的是当前会话对于表操作的信息,这部分信息通常还没有更新到pg_stat_all_tables视图中。
在Oracle中由于性能数据的抓取粒度是如此之细,所以并未区分当前会话还是已经结束的会话,要知道V$SEGSTAT中的信息几乎是real-time在更新的。所以,在Oracle中无需此类视图。

pg_stat_all_indexes/pg_stat_sys_indexes/pg_stat_user_indexes
该视图对于每个索引显示一条记录,显示的信息如下:

mydb=# select * from pg_stat_all_indexes where relname='t1';   
 relid | indexrelid | schemaname | relname | indexrelname | idx_scan | idx_tup_read | idx_tup_fetch 
-------+------------+------------+---------+--------------+----------+--------------+---------------
 24604 |      24613 | public     | t1      | t1_index     |        3 |        58960 |         47817
(1 row)

可见记录的信息非常简单,就是一个索引上进行过多少次扫描,通过这个索引扫描读取了多少记录,返回了多少记录。
在Oracle中,由于索引是Segment的一种,因此类似的统计信息都可以从V$SEGSTAT中获取。

pg_statio_all_tables/pg_statio_sys_tables/pg_statio_user_tables
pg_statio_all_indexes/pg_statio_sys_indexes/pg_statio_user_indexes

这两部分放在一起描述,具有statio前缀的视图显示的是表或索引在数据块级别的IO统计信息,而stat前缀的视图(如前面看到的)则显示的是表或索引在记录级别的IO统计信息。以pg_statio_all_indexes为例:

mydb=# select * from pg_statio_all_indexes where relname='t1';
 relid | indexrelid | schemaname | relname | indexrelname | idx_blks_read | idx_blks_hit 
-------+------------+------------+---------+--------------+---------------+--------------
 24604 |      24613 | public     | t1      | t1_index     |           150 |          453
(1 row)

显示了读取过多少个数据块,这些读取中有多少数据块是直接命中缓存的。
在Oracle中是我们提到了多次的V$SEGSTAT视图。

pg_statio_all_sequences/pg_statio_sys_sequences/pg_statio_user_sequences
PostgreSQL对sequence上的IO独立给出了一系列视图,PostgreSQL中的sequence跟Oracle中的sequence概念基本一致,为存储序列号等的字段生成序列值。
该视图对于每个序列显示一条记录,显示的信息如下:

mydb=# select * from pg_statio_all_sequences;
 relid | schemaname |   relname    | blks_read | blks_hit 
-------+------------+--------------+-----------+----------
 24614 | public     | users_id_seq |         1 |        3
(1 row)

非常简单,显示了读取过多少个数据块,多少数据块的读取是直接命中缓存的。
在Oracle中,由于序列是系统自身对象的一部分,因此如果要诊断跟序列相关的问题,通常要依赖等待事件,比如“enq: SQ – contention”或者“row cache lock”,另外在V$ROWCACHE视图中存储了与序列相关的整体统计值。

SQL> select PARAMETER,GETS,GETMISSES from v$rowcache where PARAMETER='dc_sequences';

PARAMETER                              GETS  GETMISSES
-------------------------------- ---------- ----------
dc_sequences                           2145         54

pg_stat_user_functions/pg_stat_xact_user_functions
有xact前缀和没有前缀的区别在前面描述pg_stat_xact_all_tables系列视图时已经提过,因此放在一起描述。
该视图对于每个指定要跟踪的用户自定义函数显示一条记录,这通过初始化参数track_functions来控制,默认不开启任何跟踪,视图结构如下:

mydb=# \d pg_stat_user_functions
 View "pg_catalog.pg_stat_user_functions"
   Column   |       Type       | Modifiers 
------------+------------------+-----------
 funcid     | oid              | 
 schemaname | name             | 
 funcname   | name             | 
 calls      | bigint           | 
 total_time | double precision | 
 self_time  | double precision | 

calls字段记录了对于用户函数进行过多少次调用;
total_time字段记录了运行这个函数总共消耗了多长时间(毫秒为单位),包括调用其它函数的时间;
self_time字段记录了运行这个函数本身消耗了多长时间(毫秒为单位),不包括调用其它函数的时间。

Oracle中没有类似的视图,Oracle的关于函数或者存储过程的执行统计信息,都是详细到其中每一条SQL语句的,实际上如果像PostgreSQL这样能有一个函数或者存储过程级别的性能统计值,也是极好的。

pg_stat_replication
在设置了复制的环境中,该视图对于每个WAL sender进程(WAL sender进程负责将本机的重做日志发送到远端复制环境)显示一条记录,显示内容大致如下:

postgres=# select pid,application_name,client_addr,state,sent_location,replay_location from pg_stat_replication;
  pid  | application_name |  client_addr   |   state   | sent_location | replay_location 
-------+------------------+----------------+-----------+---------------+-----------------
 27855 | walreceiver      | 192.168.56.105 | streaming | 0/50188CE8    | 0/50188CE8
(1 row)

每个视图中都能直接显示操作系统进程ID,实在是很方便的事情。在操作系统上可以直接查看pid=27855的进程。

[root@pg1-enmotech-com ~]# ps -ef|grep 2785|grep postgres
postgres 27855  1119  0 00:45 ?        00:00:00 postgres: wal sender process postgres 192.168.56.105(57046) streaming 0/50188CE8

从操作系统的ps命令中看到实际上已经将视图中的这些字段内容更新到了该进程描述中,在进程描述中会更新一些很有用的信息(比如server进程的状态,是等待还是空闲等),这也是PostgreSQL非常方便的一个地方。

在Oracle中与PostgreSQL的复制相类似的功能是Physical Data Guard,在DG中重做日志的传输是通过归档路径来完成的,因此类似的信息可以从V$ARCHIVE_DEST_STATUS和V$MANAGED_STANDBY视图中获取。

pg_stat_database_conflicts
该视图仅对于Standby数据库有效,对于每个数据库显示一条记录,显示内容如下:

postgres=# select * from pg_stat_database_conflicts;
 datid |  datname  | confl_tablespace | confl_lock | confl_snapshot | confl_bufferpin | confl_deadlock 
-------+-----------+------------------+------------+----------------+-----------------+----------------
     1 | template1 |                0 |          0 |              0 |               0 |              0
 13051 | template0 |                0 |          0 |              0 |               0 |              0
 13056 | postgres  |                0 |          0 |              0 |               0 |              0
 16384 | mydb      |                0 |          0 |              0 |               0 |              0
 24587 | mydb_bak  |                0 |          0 |              0 |               0 |              0
(5 rows)

由于PostgreSQL的机制,在备库上的查询会跟一些诸如删除表空间、删除数据库、vacuum cleanup的操作相冲突,为了不让备库的WAL replay操作延时太久,PostgreSQL内建了强制取消当前备库上运行的查询以避免跟应用重做日志这样更重要的动作相冲突的机制。而该视图则是记录由于不同原因取消掉的查询的次数。对于每个数据库显示一条记录。
Oracle中不会出现这样的问题,因此也没有相应的视图。

总结
当然,PostgreSQL中除了这些统计信息视图之外,还有不少类似于pg_tables,pg_users这样与Oracle中的数据字典视图相仿的视图,另外还有比如pg_locks这样用于记录锁信息的诊断视图。但是仅仅用一篇文章的长度就可以将所有的统计信息视图全部介绍完毕,PostgreSQL确实是很简洁的数据库。

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私有云解决方案的构成组件之一。