| hai 的个人资料Oracle & Starcraft日志列表 | 帮助 |
|
8月29日 library cache lock wait event说起library cache lock wait event就必须先说一下library cache lock是什么东西,library lock是oracle用来对library cache object进行并发控制的两种数据结构之一,另外一种数据结构是pin,lock将会在pin之前获得,并被加载到library cache handle上。library cache lock有3种形式,share,null,exclusive,具体的library cache lock的分析请看我的另一篇帖子。
通常我们举的关于library cache lock wait event的例子都是用procedure来模拟的,eygle发表过这方面的文章,今天我举的是另一个例子,也是一次真实的事故,事故发生的原因就是因为ddl导致cursor invalidation,但是ddl持续运行了很长时间,导致后来的session需要parse sql的时候不能获得library cache lock,进一步导致app服务器的connection pool满,导致应用crash。下面我们用一个简单的例子模拟一下这个场景。
session 1:
SQL 10G>alter table test add constraint uk_test unique(a,b,c,d,e) using index online compute statistics;
Table altered.
session 2:
SQL 10G>select sid from v$mystat where rownum<2;
SID
---------- 142 SQL 10G>select count(*) from test; session 3: SQL 10G> select event from v$session_wait where sid=142; EVENT -------------------------------------------------------------------------------- library cache lock 当test表过大,创建uk constraint时间过长时会有越来越多的session等待library cache lock从而进一步导致应用crash,这样提醒我们当对大表进行ddl时一定要分解ddl操作,不要一句语句搞定所有操作,象alter table test add constraint uk_test unique(a,b,c,d,e) using index online compute statistics;这种语句虽然非常便捷,但是不一定适合在大并发的时候去执行。我们应该把这个操作分解成几步来做,
1. create index ind_test on test(a,b,c,d,e) online compute statistics;
2. alter table test add constraint uk_test unique(a,b,c,d,e) using index ind_test novalidate;
3. 校验表里面的数据是否符合唯一约束
4. alter table test modify constraint uk_test validate;
这里的要点就是要把每一个ddl分解成小的操作来执行。值得指出的一点是alter table test modify constraint uk_test validate;这个步骤虽然会可能会耗时非常久,但是这个步骤是不会加载library cache exclusive lock的,所以它不会导致其他session等待library cache lock。但是constraint的状态从disable到enable的转换则还是会加载library cache exclusive lock,导致其他进程等待library cache lock。oracle可能在这个地方做了不同的处理,针对disable->enable和novalidate->validate采取了不同的lock类型,显然我们也可以知道disable->enable和novalidate->validate对library cache object的影响是不一样的,一个是可以说是从无到有,一个是只是改变了一下状态,我的猜测是disable->enable的话oracle依然加载了exclusive lock,而novalidate->validate的话oracle加载了null or share lock,有心的人可以dump一下sga来测试一下。
8月4日 dbms_stats和leaf_blocksdbms_stats是oracle用来代替原有的analyze功能的一个包,与analyze相比dbms_stats具有很多优势,比如并行,比如分区信息统计等,但是dbms_stats再分析index的时候处理方式并不是太理想,dbms_stats分析index时将会只统计leaf_blocks为当前有数据的leaf block,而analyze则会统计为所有曾经被使用过的leaf block number,很显然dbms_stats的统计结果会使index fast full scan的成本被严重低估,在某些情况下会错误得选择index fast full scan做为执行路径。下面来看一个例子:
先清空原来的表
SQL 10G>truncate table t1;
Table truncated.
插入数据
SQL 10G>insert into t1 select
2 rownum id, 3 trunc(100 * dbms_random.normal) val, 4 rpad('x',100) padding 5 from 6 all_objects 7 where 8 rownum <= 10000 9 ; 10000 rows created.
SQL 10G>commit;
Commit complete.
创建索引,并限制pctfree为99,模拟大索引的产生
SQL 10G>create index ind_t1 on t1(id) pctfree 99;
Index created.
使用analyze分析索引
SQL 10G>ANALYZE INDEX IND_T1 COMPUTE STATISTICS;
Index analyzed.
SQL 10G>SELECT INDEX_NAME,BLEVEL,LEAF_BLOCKS FROM user_indexes where table_name='T1';
INDEX_NAME BLEVEL LEAF_BLOCKS
------------------------------ ---------- ----------- IND_T1 2 10000 再使用dbms_stats分析,可以看到在这个时候基本统计数据是相同的
SQL 10G>EXEC DBMS_STATS.GATHER_INDEX_STATS('TEST','IND_T1');
PL/SQL procedure successfully completed.
SQL 10G>SELECT INDEX_NAME,BLEVEL,LEAF_BLOCKS FROM user_indexes where table_name='T1';
INDEX_NAME BLEVEL LEAF_BLOCKS
------------------------------ ---------- ----------- IND_T1 2 10010 删除数据使表中只保留一条记录
SQL 10G>delete from t1 where rownum<10000;
9999 rows deleted.
SQL 10G>commit;
Commit complete.
再用analyze分析索引,可以发现leaf_blocks依然是10000
SQL 10G>ANALYZE INDEX IND_T1 COMPUTE STATISTICS;
Index analyzed.
SQL 10G>SELECT INDEX_NAME,BLEVEL,LEAF_BLOCKS FROM user_indexes where table_name='T1';
INDEX_NAME BLEVEL LEAF_BLOCKS
------------------------------ ---------- ----------- IND_T1 2 10000 看cost这一项显示index fast full scan的成本为2679,这是正确的
SQL 10G>set autotrace trace exp;
SQL 10G>select/*+ index_ffs(t1,ind_t1)*/ count(*) from t1; Execution Plan
-------------------------------------------------------------
| Id | Operation | Name | Rows | Cost (%CPU)| ------------------------------------------------------------- | 0 | SELECT STATEMENT | | 1 | 2679 (19)| | 1 | SORT AGGREGATE | | 1 | | | 2 | INDEX FAST FULL SCAN| IND_T1 | 1 | 2679 (19)| ------------------------------------------------------------- 使用dbms_stats分析索引,leaf_blocks被统计为1,只统计了当前在用的leaf block
SQL 10G>EXEC DBMS_STATS.GATHER_INDEX_STATS('TEST','IND_T1');
PL/SQL procedure successfully completed.
SQL 10G>set autotrace off;
SQL 10G>SELECT INDEX_NAME,BLEVEL,LEAF_BLOCKS FROM user_indexes where table_name='T1'; INDEX_NAME BLEVEL LEAF_BLOCKS
------------------------------ ---------- ----------- IND_T1 2 1 看cost这一项显示index fast full scan的成本为1,这显然是出现了错误
SQL 10G>set autotrace trace;
SQL 10G>select/*+ index_ffs(t1,ind_t1)*/ count(*) from t1; Execution Plan
-------------------------------------------------------------
| Id | Operation | Name | Rows | Cost (%CPU)| ------------------------------------------------------------- | 0 | SELECT STATEMENT | | 1 | 1 (0)| | 1 | SORT AGGREGATE | | 1 | | | 2 | INDEX FAST FULL SCAN| IND_T1 | 1 | 1 (0)| ------------------------------------------------------------- 再来看看它究竟需要读取多少个块,是不是cost=1就够了
SQL 10G> ALTER SESSION SET EVENTS 'immediate trace name flush_cache';
Session altered.
SQL 10G>select/*+ index_ffs(t1,ind_t1)*/ count(*) from t1;
Execution Plan ---------------------------------------------------------- -------------------------------------------------------------
| Id | Operation | Name | Rows | Cost (%CPU)| ------------------------------------------------------------- | 0 | SELECT STATEMENT | | 1 | 1 (0)| | 1 | SORT AGGREGATE | | 1 | | | 2 | INDEX FAST FULL SCAN| IND_T1 | 1 | 1 (0)| ------------------------------------------------------------- Statistics ---------------------------------------------------------- 0 recursive calls 0 db block gets 10035 consistent gets 10016 physical reads 0 redo size 411 bytes sent via SQL*Net to client 385 bytes received via SQL*Net from client 2 SQL*Net roundtrips to/from client 0 sorts (memory) 0 sorts (disk) 1 rows processed 显然,这里发生了10016个physical reads,cost=1是远远不够的。不知道oracle会不会就这个问题有改进方案,大家拭目以待。
|
|
|