Chinaunix首页 | 论坛 | 博客
  • 博客访问: 2833307
  • 博文数量: 599
  • 博客积分: 16398
  • 博客等级: 上将
  • 技术积分: 6875
  • 用 户 组: 普通用户
  • 注册时间: 2009-11-30 12:04
个人简介

WINDOWS下的程序员出身,偶尔也写一些linux平台下小程序, 后转行数据库行业,专注于ORACLE和DB2的运维和优化。 同时也是ios移动开发者。欢迎志同道合的朋友一起研究技术。 数据库技术交流群:58308065,23618606

文章分类

全部博文(599)

文章存档

2014年(12)

2013年(56)

2012年(199)

2011年(105)

2010年(128)

2009年(99)

分类: Oracle

2010-07-26 13:52:12

ASSM通过增加数据的分布的随机性来减少争用。在ASSM下,并发的进程基本都选择不同的块来插入数据行。因此,在我们使用序列或者以
日期作为插入顺序的表并在其上面建立索引的话,可能将会导致索引的聚簇因子比较大。

SQL> SELECT TABLESPACE_NAME,SEGMENT_SPACE_MANAGEMENT FROM DBA_TABLESPACES WHERE TABLESPACE_NAME IN ('ASSM','MSSM');

TABLESPACE_NAME      SEGMENT_SPAC
-------------------- ------------
ASSM                 AUTO
MSSM                 MANUAL

SQL> create sequence assm_s;

Sequence created.

SQL> create sequence mssm_s;

Sequence created.

SQL> create table assm_test(id int,flag char(1)) tablespace assm;

Table created.

SQL> create table mssm_test(id int,flag char(1)) tablespace mssm;

Table created.


首先创建2序列和2个表,一个创建在ASSM管理的表空间下,另一个创建在手工段空间管理的表空间下。

SQL>  create or replace procedure load_assm_data(v_flag char)
  2  as
  3  begin
  4    for i in 1..1000 loop
  5      insert into assm_test values(assm_s.nextval,v_flag);
  6    end loop;
  7    commit;
  8* end;

Procedure created.

SQL> create or replace procedure load_mssm_data(v_flag char)
  2  as
  3  begin
  4    for i in 1..1000 loop
  5      insert into mssm_test values(mssm_s.nextval,v_flag);
  6    end loop;
  7    commit;
  8  end;
  9  /

Procedure created.

分别创建2个过程用来为表ASSM_TEST和MSSM_TEST加载数据。


SQL> declare
  2   jobname varchar2(30);
  3  BEGIN
  4    FOR I IN 0..4 LOOP
  5      JOBNAME:=DBMS_SCHEDULER.GENERATE_JOB_NAME;
  6      DBMS_SCHEDULER.CREATE_JOB(
  7      job_name=>JOBNAME,
  8      job_type=>'STORED_PROCEDURE',
  9      job_action=>'LOAD_ASSM_DATA',
 10      auto_drop=>TRUE,
 11      enabled=>FALSE,
 12      number_of_arguments=>1);
 13      DBMS_SCHEDULER.SET_JOB_ARGUMENT_VALUE(JOBNAME,1,CHR(65+I));
 14      DBMS_SCHEDULER.ENABLE(JOBNAME);
 15    END LOOP;
 16  END;
 17  /

PL/SQL procedure successfully completed.

SQL> declare
  2   jobname varchar2(30);
  3  BEGIN
  4    FOR I IN 0..4 LOOP
  5      JOBNAME:=DBMS_SCHEDULER.GENERATE_JOB_NAME;
  6      DBMS_SCHEDULER.CREATE_JOB(
  7      job_name=>JOBNAME,
  8      job_type=>'STORED_PROCEDURE',
  9      job_action=>'LOAD_MSSM_DATA',
 10      auto_drop=>TRUE,
 11      enabled=>FALSE,
 12      number_of_arguments=>1);
 13      DBMS_SCHEDULER.SET_JOB_ARGUMENT_VALUE(JOBNAME,1,CHR(65+I));
 14      DBMS_SCHEDULER.ENABLE(JOBNAME);
 15    END LOOP;
 16  END;
 17  /

PL/SQL procedure successfully completed.

使用JOB的方式,同时启动5个并发进程,向表assm_test和mssm_test插入数据。

SQL> CREATE INDEX ASSM_TEST_IDX ON ASSM_TEST(ID);

Index created.

SQL> CREATE INDEX MSSM_TEST_IDX ON MSSM_TEST(ID);

Index created.


分别在2个表的ID列上建立索引。

SQL> EXEC DBMS_STATS.GATHER_TABLE_STATS(USER,'ASSM_TEST',CASCADE=>TRUE);

PL/SQL procedure successfully completed.

SQL> EXEC DBMS_STATS.GATHER_TABLE_STATS(USER,'MSSM_TEST',CASCADE=>TRUE);

PL/SQL procedure successfully completed.


SQL> COL TABLE_NAME FORMAT A20

SQL> SELECT TABLE_NAME,BLOCKS,NUM_ROWS FROM USER_TABLES WHERE TABLE_NAME IN ('ASSM_TEST','MSSM_TEST');

TABLE_NAME               BLOCKS   NUM_ROWS
-------------------- ---------- ----------
ASSM_TEST                    28       5000
MSSM_TEST                    10       5000

SQL> SELECT INDEX_NAME,BLEVEL,LEAF_BLOCKS,CLUSTERING_FACTOR FROM USER_INDEXES WHERE TABLE_NAME IN ('ASSM_TEST','MSSM_TEST');

INDEX_NAME               BLEVEL LEAF_BLOCKS CLUSTERING_FACTOR
-------------------- ---------- ----------- -----------------
MSSM_TEST_IDX                 1          11                 8
ASSM_TEST_IDX                 1          11              2441

很明显,ASSM下的空间管理方式下的聚簇因子远远大于手工段空间管理模式下的。

 

SQL> SELECT DISTINCT DBMS_ROWID.ROWID_BLOCK_NUMBER(ROWID)BLOCK#
  2  FROM YSP.ASSM_TEST;

    BLOCK#
----------
       437
       440
       436
       739
       438
      1442
       740
      1459
       439

9 rows selected.


可以看到,表ASSM_TEST只有28个BLOCKS,实际表的数据只占用了9个BLOCK,但是聚簇因子却达到了2441。 这就是ASSM在避免了表争用问题
带来的一个副作用。

SQL> SELECT BLOCK#,COUNT(*)
  2  FROM (
  3  SELECT DISTINCT DBMS_ROWID.ROWID_BLOCK_NUMBER(ROWID)BLOCK# ,FLAG
  4  FROM YSP.ASSM_TEST)
  5  GROUP BY BLOCK#;

    BLOCK#   COUNT(*)
---------- ----------
       437          1
       440          3
       436          3
       739          1
       438          2
      1442          1
      1459          1
       740          1
       439          1

9 rows selected.

可以看到大部分块,被一个进程使用,减少了争用。

而在MSSM下,一个块基本被2个进程争用。

SQL> SELECT BLOCK#,COUNT(*)
  2  FROM (
  3  SELECT DISTINCT DBMS_ROWID.ROWID_BLOCK_NUMBER(ROWID)BLOCK# ,FLAG
  4  FROM YSP.MSSM_TEST)
  5  GROUP BY BLOCK#;

    BLOCK#   COUNT(*)
---------- ----------
       397          3
       400          3
       395          2
       401          2
       399          3
       396          2
       394          1
       398          3

8 rows selected.


下面看看,ASSM对执行计划的影响:


SQL> SELECT * FROM  MSSM_TEST WHERE ID BETWEEN 1 AND 50;

50 rows selected.


Execution Plan
----------------------------------------------------------
Plan hash value: 1865800780

---------------------------------------------------------------------------------------------
| Id  | Operation                   | Name          | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT            |               |    50 |   400 |     3   (0)| 00:00:01 |
|   1 |  TABLE ACCESS BY INDEX ROWID| MSSM_TEST     |    50 |   400 |     3   (0)| 00:00:01 |
|*  2 |   INDEX RANGE SCAN          | MSSM_TEST_IDX |    50 |       |     2   (0)| 00:00:01 |
---------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   2 - access("ID">=1 AND "ID"<=50)


Statistics
----------------------------------------------------------
          1  recursive calls
          0  db block gets
         11  consistent gets
          0  physical reads
          0  redo size
       1561  bytes sent via SQL*Net to client
        525  bytes received via SQL*Net from client
          5  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
         50  rows processed

我们从5000行的数据里选取了50行,选择率大约为1/100。此时索引扫描相对较好。

SQL> SELECT * FROM  ASSM_TEST  WHERE ID BETWEEN 1 AND 50;

50 rows selected.


Execution Plan
----------------------------------------------------------
Plan hash value: 957178735

-------------------------------------------------------------------------------
| Id  | Operation         | Name      | Rows  | Bytes | Cost (%CPU)| Time     |
-------------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |           |    50 |   400 |     7   (0)| 00:00:01 |
|*  1 |  TABLE ACCESS FULL| ASSM_TEST |    50 |   400 |     7   (0)| 00:00:01 |
-------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   1 - filter("ID"<=50 AND "ID">=1)


Statistics
----------------------------------------------------------
          1  recursive calls
          0  db block gets
         35  consistent gets
          0  physical reads
          0  redo size
       1561  bytes sent via SQL*Net to client
        525  bytes received via SQL*Net from client
          5  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
         50  rows processed

但是在ASSM下,ORACLE却选择了成本较高的全表扫描。
这就是ASSM的特性带来的对执行计划的影响。

如果使用索引,ORACLE计算出来的成本会比全表扫描高。
但实际走索引的效率会更好,从逻辑读取也可以看得出来。
SQL> SELECT /*+INDEX(ASSM_TEST)*/  * FROM  ASSM_TEST  WHERE ID BETWEEN 1 AND 50;

50 rows selected.


Execution Plan
----------------------------------------------------------
Plan hash value: 2637445088

---------------------------------------------------------------------------------------------
| Id  | Operation                   | Name          | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT            |               |    50 |   400 |    27   (0)| 00:00:01 |
|   1 |  TABLE ACCESS BY INDEX ROWID| ASSM_TEST     |    50 |   400 |    27   (0)| 00:00:01 |
|*  2 |   INDEX RANGE SCAN          | ASSM_TEST_IDX |    50 |       |     2   (0)| 00:00:01 |
---------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   2 - access("ID">=1 AND "ID"<=50)


Statistics
----------------------------------------------------------
          1  recursive calls
          0  db block gets
         11  consistent gets
          0  physical reads
          0  redo size
       1561  bytes sent via SQL*Net to client
        525  bytes received via SQL*Net from client
          5  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
         50  rows processed


我们可以通过如下方式校正这种不正确的执行计划:


1、重新组织表

SQL> CREATE TABLE ASSM_TEST_2 TABLESPACE ASSM AS SELECT * FROM ASSM_TEST ORDER BY ID;

Table created.

SQL> CREATE INDEX ASSM_TEST_2_IDX ON ASSM_TEST_2(ID);

Index created.

我们复制了一个与ASSM_TEST一模一样的表。

SQL> EXEC DBMS_STATS.GATHER_TABLE_STATS(USER,'ASSM_TEST_2',CASCADE=>TRUE);;

PL/SQL procedure successfully completed.

SQL> SELECT TABLE_NAME,BLOCKS,NUM_ROWS FROM USER_TABLES WHERE TABLE_NAME LIKE '%SSM%';

TABLE_NAME               BLOCKS   NUM_ROWS
-------------------- ---------- ----------
MSSM_TEST                    10       5000
ASSM_TEST_2                  12       5000
ASSM_TEST                    28       5000

SQL> SELECT INDEX_NAME,BLEVEL,LEAF_BLOCKS,CLUSTERING_FACTOR FROM USER_INDEXES WHERE TABLE_NAME LIKE '%SSM%';

INDEX_NAME               BLEVEL LEAF_BLOCKS CLUSTERING_FACTOR
-------------------- ---------- ----------- -----------------
ASSM_TEST_2_IDX               1          11                 8
MSSM_TEST_IDX                 1          11                 8
ASSM_TEST_IDX                 1          11              2441

SQL> SET AUTOT TRACEONLY
SQL> SELECT * FROM  ASSM_TEST_2 WHERE ID BETWEEN 1 AND 50;

50 rows selected.


Execution Plan
----------------------------------------------------------
Plan hash value: 3232560642

-----------------------------------------------------------------------------------------------
| Id  | Operation                   | Name            | Rows  | Bytes | Cost (%CPU)| Time     |
-----------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT            |                 |    50 |   400 |     3   (0)| 00:00:01 |
|   1 |  TABLE ACCESS BY INDEX ROWID| ASSM_TEST_2     |    50 |   400 |     3   (0)| 00:00:01 |
|*  2 |   INDEX RANGE SCAN          | ASSM_TEST_2_IDX |    50 |       |     2   (0)| 00:00:01 |
-----------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   2 - access("ID">=1 AND "ID"<=50)


Statistics
----------------------------------------------------------
          0  recursive calls
          0  db block gets
         11  consistent gets
          0  physical reads
          0  redo size
       1561  bytes sent via SQL*Net to client
        525  bytes received via SQL*Net from client
          5  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
         50  rows processed

可以看到,重新组织表后,使ORACLE从全表扫描走了索引扫描。

2、使用SYS_OP_COUNTCHG()来校正CLUSTERING_FACTOR


SQL> ALTER SESSION SET SQL_TRACE TRUE;

Session altered.

SQL> EXEC DBMS_STATS.GATHER_TABLE_STATS(USER,'ASSM_TEST',CASCADE=>TRUE);

PL/SQL procedure successfully completed.

SQL> ALTER SESSION SET SQL_TRACE FALSE;

Session altered.


启用SQL跟踪,格式化跟踪文件,并在跟踪文件中找到如下SQL语句。
select /*+ no_parallel_index(t,"ASSM_TEST_IDX") dbms_stats
  cursor_sharing_exact use_weak_name_resl dynamic_sampling(0) no_monitoring
  no_expand index(t,"ASSM_TEST_IDX") */ count(*) as nrw,count(distinct
  sys_op_lbid(60257,'L',t.rowid)) as nlb,count(distinct "ID") as ndk,
  sys_op_countchg(substrb(t.rowid,1,15),1) as clf
from
 "YSP"."ASSM_TEST" t where "ID" is not null

修改 sys_op_countchg(substrb(t.rowid,1,15),1) as clf  第二个参数为5(并发的进程数),并在SQLPLUS执行,
将得到的CLF值写回到数据字典中。。

select /*+ no_parallel_index(t,"ASSM_TEST_IDX") dbms_stats
  cursor_sharing_exact use_weak_name_resl dynamic_sampling(0) no_monitoring
  no_expand index(t,"ASSM_TEST_IDX") */ count(*) as nrw,count(distinct
  sys_op_lbid(60257,'L',t.rowid)) as nlb,count(distinct "ID") as ndk,
  sys_op_countchg(substrb(t.rowid,1,15),5) as clf
from
 "YSP"."ASSM_TEST" t where "ID" is not null

其中,NRW=USER_INDEXES.NUM_ROWS,NLB=USER_INDEXES.LEAF_BLOCKS,NDK=USER_INDEXES.NUM_DISTINCT,CLF=USER_INDEXES.CLUSTERING_FACTOR。

 


SQL> select /*+ no_parallel_index(t,"ASSM_TEST_IDX") dbms_stats
  2    cursor_sharing_exact use_weak_name_resl dynamic_sampling(0) no_monitoring
  3    no_expand index(t,"ASSM_TEST_IDX") */ count(*) as nrw,count(distinct
  4    sys_op_lbid(60257,'L',t.rowid)) as nlb,count(distinct "ID") as ndk,
  5    sys_op_countchg(substrb(t.rowid,1,15),5) as clf
  6  from
  7   "YSP"."ASSM_TEST" t where "ID" is not null;

       NRW        NLB        NDK        CLF
---------- ---------- ---------- ----------
      5000         11       5000          9

SQL> SELECT INDEX_NAME,BLEVEL,LEAF_BLOCKS,CLUSTERING_FACTOR FROM USER_INDEXES WHERE TABLE_NAME LIKE '%SSM%';

INDEX_NAME               BLEVEL LEAF_BLOCKS CLUSTERING_FACTOR
-------------------- ---------- ----------- -----------------
ASSM_TEST_2_IDX               1          11                 8
MSSM_TEST_IDX                 1          11                 8
ASSM_TEST_IDX                 1          11              2441

SQL> exec dbms_stats.set_index_stats(user,'ASSM_TEST_IDX',CLSTFCT=>9);

PL/SQL procedure successfully completed.

SQL> SELECT INDEX_NAME,BLEVEL,LEAF_BLOCKS,CLUSTERING_FACTOR FROM USER_INDEXES WHERE TABLE_NAME LIKE '%SSM%';

INDEX_NAME               BLEVEL LEAF_BLOCKS CLUSTERING_FACTOR
-------------------- ---------- ----------- -----------------
ASSM_TEST_2_IDX               1          11                 8
MSSM_TEST_IDX                 1          11                 8
ASSM_TEST_IDX                 1          11                 9

SQL> SET AUTOT TRACEONLY
SQL> SELECT * FROM  ASSM_TEST  WHERE ID BETWEEN 1 AND 50;

50 rows selected.


Execution Plan
----------------------------------------------------------
Plan hash value: 2637445088

---------------------------------------------------------------------------------------------
| Id  | Operation                   | Name          | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT            |               |    50 |   400 |     3   (0)| 00:00:01 |
|   1 |  TABLE ACCESS BY INDEX ROWID| ASSM_TEST     |    50 |   400 |     3   (0)| 00:00:01 |
|*  2 |   INDEX RANGE SCAN          | ASSM_TEST_IDX |    50 |       |     2   (0)| 00:00:01 |
---------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   2 - access("ID">=1 AND "ID"<=50)


Statistics
----------------------------------------------------------
          0  recursive calls
          0  db block gets
         35  consistent gets
          0  physical reads
          0  redo size
       1561  bytes sent via SQL*Net to client
        525  bytes received via SQL*Net from client
          5  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
         50  rows processed

此时,ORACLE也是从全表扫描转为索引扫描。

此处有点小疑问,使用索引ASSM_TEST_IDX的逻辑读是35。如果正常的话,应该和前面使用索引得到的一致,应该是11才对。
但是在此,不知道为什么ORACLE得出的是35。
如果加个提示,逻辑读就是11了。
SQL> SELECT /*+INDEX(ASSM_TEST)*/ * FROM ASSM_TEST  WHERE ID BETWEEN 1 AND 50;

50 rows selected.


Execution Plan
----------------------------------------------------------
Plan hash value: 2637445088

---------------------------------------------------------------------------------------------
| Id  | Operation                   | Name          | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT            |               |    50 |   400 |     3   (0)| 00:00:01 |
|   1 |  TABLE ACCESS BY INDEX ROWID| ASSM_TEST     |    50 |   400 |     3   (0)| 00:00:01 |
|*  2 |   INDEX RANGE SCAN          | ASSM_TEST_IDX |    50 |       |     2   (0)| 00:00:01 |
---------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   2 - access("ID">=1 AND "ID"<=50)


Statistics
----------------------------------------------------------
          1  recursive calls
          0  db block gets
         11  consistent gets
          0  physical reads
          0  redo size
       1561  bytes sent via SQL*Net to client
        525  bytes received via SQL*Net from client
          5  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
         50  rows processed

明显都是使用的同一个索引。不知道ORACLE怎么计算出一个是35,一个是11。

阅读(1825) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~