ITPub博客

首页 > Linux操作系统 > Linux操作系统 > Oracle两个使用细节知识点

Oracle两个使用细节知识点

原创 Linux操作系统 作者:realkid4 时间:2011-09-20 22:02:46 0 删除 编辑

 

大量的Oracle知识点都是需要在实际中不断发现实验获得的。本篇中介绍两个实际中发现的问题,权当记录。

 

1、慎用for update大规模数据

 

Select for update是我们有时候会使用的一种语句类型。它的特点优势是能够将筛选出的数据进行独占锁定。通常情况下的Oracle select操作,是不会对数据表进行锁定,同时也不会被其他操作所阻塞。

 

在实际应用中,我们的确是经常使用for update进行数据锁定。常用的场景是,确定符合条件的数据是否存在,如果存在就立即锁定该条记录独占。之后对独占的数据进行修改。在存在多会话并发访问的时候,for update可以起到资源/信号量抢夺的作用。

 

 

但是,for update的使用是有相应的代价的。在之前的系列文章中,我们探讨过for update要消耗更多的时间,主要在于需要在每个数据行进行加锁操作。在Oracle11g中,CBO的Query Optimizer采用了一些优化技术进行性能提升。但是,总的来说,for update是要消耗更多的资源的。

 

除了上面提到的方面,在redo size上for update也是有独特之处。通常我们继续DDL和更改类型操作的时候,才会生成redo log片段。普通的select是不会生成redo log记录的。如果使用for update,我们是可以发现select会生成redo size。

 

 

 

SQL> select * from v$version;

 

BANNER

--------------------------------------------------------------------------------

Oracle Database 11g Enterprise Edition Release 11.2.0.1.0 - Production

PL/SQL Release 11.2.0.1.0 - Production

CORE     11.2.0.1.0       Production

 

TNS for Linux: Version 11.2.0.1.0 - Production

NLSRTL Version 11.2.0.1.0 – Production

 

 

使用v$mystat可以看到当前会话累计的各种指标统计量。

 

 

 

SQL> select a.statistic#, name, value from v$mystat a left join v$statname b on a.STATISTIC#=b.STATISTIC# where b.NAME = 'redo size';

 

STATISTIC# NAME                      VALUE

---------- -------------------- ----------

       169 redo size                     0

 

 

当前,redo size生成的消耗量是0。

 

 

SQL> select bytes, blocks from dba_segments where segment_name='T' and wner='SYS';

 

     BYTES     BLOCKS

---------- ----------

   9437184       1152

 

SQL> select count(*) from t;

 

  COUNT(*)

----------

     72349

 

 

此时,我们使用一个for update语句。

 

 

SQL> select * from t where wner='SCOTT' for update;

 

(结果集合省略……)

 

 

此时,redo size的消耗可以查询到。

 

 

SQL> select a.statistic#, name, value from v$mystat a left join v$statname b on a.STATISTIC#=b.STATISTIC# where b.NAME = 'redo size';

 

STATISTIC# NAME                      VALUE

---------- -------------------- ----------

       169 redo size                     0

 

SQL> commit;

 

SQL> select a.statistic#, name, value from v$mystat a left join v$statname b on a.STATISTIC#=b.STATISTIC# where b.NAME = 'redo size';

 

STATISTIC# NAME                      VALUE

---------- -------------------- ----------

       169 redo size                  2628

 

 

一个一般的for update之后,进行commit,没有发生本质上的数据变化。但是之后,我们发现了redo size指标增加到了2628。一般的select操作,就不会产生redo size消耗。

 

 

SQL> select * from t where wner='SCOTT';

(结果集合省略……

 

SQL> select a.statistic#, name, value from v$mystat a left join v$statname b on a.STATISTIC#=b.STATISTIC# where b.NAME = 'redo size';

 

STATISTIC# NAME                      VALUE

---------- -------------------- ----------

       169 redo size                  2628

 

SQL> commit;

SQL> select a.statistic#, name, value from v$mystat a left join v$statname b on a.STATISTIC#=b.STATISTIC# where b.NAME = 'redo size';

 

STATISTIC# NAME                      VALUE

---------- -------------------- ----------

       169 redo size                  2628

 

 

可见,一般的select的操作是不会产生redo记录的。这个问题我们也可以使用autotrace工具来查看到。

 

SQL> set autotrace traceonly;

SQL> select * from t for update;

已选择72349行。

 

执行计划

----------------------------------------------------------

Plan hash value: 250223868

----------------------------------------------------------------------------

| Id  | Operation           | Name | Rows  | Bytes | Cost (%CPU)| Time     |

----------------------------------------------------------------------------

|   0 | SELECT STATEMENT    |      | 72349 |  6853K|   282   (1)| 00:00:04 |

|   1 |  FOR UPDATE         |      |       |       |            |          |

|   2 |   BUFFER SORT       |      |       |       |            |          |

|   3 |    TABLE ACCESS FULL| T    | 72349 |  6853K|   282   (1)| 00:00:04 |

----------------------------------------------------------------------------

统计信息

----------------------------------------------------------

         46  recursive calls

      73649  db block gets

       1066  consistent gets

       1026  physical reads

   15086508  redo size

    5649675  bytes sent via SQL*Net to client

      53429  bytes received via SQL*Net from client

       4825  SQL*Net roundtrips to/from client

          1  sorts (memory)

          0  sorts (disk)

      72349  rows processed

 

SQL> select * from t;

已选择72349行。

 

执行计划

----------------------------------------------------------

Plan hash value: 1601196873

--------------------------------------------------------------------------

| Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |

--------------------------------------------------------------------------

|   0 | SELECT STATEMENT  |      | 72349 |  6853K|   282   (1)| 00:00:04 |

|   1 |  TABLE ACCESS FULL| T    | 72349 |  6853K|   282   (1)| 00:00:04 |

--------------------------------------------------------------------------

统计信息

----------------------------------------------------------

          1  recursive calls

          0  db block gets

       5788  consistent gets

          0  physical reads

          0  redo size

    3286309  bytes sent via SQL*Net to client

      53429  bytes received via SQL*Net from client

       4825  SQL*Net roundtrips to/from client

          0  sorts (memory)

          0  sorts (disk)

      72349  rows processed

 

 

从统计量信息看,使用for update在处理相同数据行数据的时候,要产生相当数据量的redo size和嵌套SQL。

 

分析原因,从Oracle角度看,实现数据行的锁定依靠Select语句是不能做到的。所以,在for update操作的时候,Oracle会启动一个事务过程,将筛选出的数据行作出修改动作。

 

在这个过程中,Oracle会在条件记录对应的数据块上的事务槽作出标记,表示对应的数据行已经进行修改。这样才可能以独占的方式获取数据行。在Oracle中,只要对数据块进行了修改,无论这个修改是否最终被commit/rollback,都会生成出redo log信息,写入到online redo log中。

 

 

数据表越大,涉及的数据越多,for update的损耗和生成redo就越大。所以,如果数据表很大,同时select出的数据集合也很大,频繁的select for update就引起Oracle产生出大量的redo blocks。在归档模式下,过于频繁的对大数据进行for update操作,会带来额外的归档日志存储要求。如果没有预先的规划,容易引起数据库hange住。

 

 

2、数据列默认值:先加后加,大不相同

 

数据列默认值,也是我们经常使用的一种数据库对象技术。通过数据列默认值的设置,当插入数据的时候,如果不对数据列进行指定,那么默认值就会加入其中。

 

如果对一个已经存在数据的数据表加列,同时确定默认值和之后设置默认值有什么差别呢?

 

 

SQL> create table t (id number, name varchar2(10));

Table created

 

SQL> select * from t;

        ID NAME

---------- ----------

 

SQL> insert into t select object_id, null from dba_objects where rownum<10;

9 rows inserted

 

SQL> commit;

 

Commit complete

 

SQL> select * from t;

 

        ID NAME

---------- ----------

        20

        46

        28

        15

 

9 rows selected

 

 

如果我们首先创建数据列和定义默认值在一起,会如何呢?

 

 

SQL> set timing on;

SQL> alter table t add vname varchar2(10) default 'X';

Table altered

 

Executed in 0.39 seconds

SQL> select * from t;

 

        ID NAME       VNAME

---------- ---------- ----------

        20            X

        46            X

        28            X

        15            X

        29            X

         3            X

        25            X

        41            X

        54            X

 

9 rows selected

 

Executed in 0.093 seconds

 

 

如果我们在新添加列的时候,指定default value。效果就是将数据列加入其中,并且使用默认值填满已经为null的记录项目。Default value的含义在于补全,将不完全的数据加以不全。

 

不过,也要注意这个操作的时候消耗为0.39s,时间消耗略大。下面,如果我们只是指定数据列,不同时进行默认值指定的情况呢?

 

--新添加列

SQL> alter table t add nname varchar2(10);

Table altered

 

Executed in 0.062 seconds

 

--后设置默认值;

SQL> alter table t modify nname default 'M';

Table altered

 

Executed in 0.062 seconds

 

 

SQL> select * from t;

 

        ID NAME       VNAME      NNAME

---------- ---------- ---------- ----------

        20            X         

        46            X         

        28            X         

        15            X         

        29            X         

         3            X         

        25            X         

        41            X         

        54            X         

 

9 rows selected

 

Executed in 0.094 seconds

 

 

第一个区别,如果后设置默认值,数据列中原来为空的取值,是不会如同时设置一样补齐数据的。

 

 

SQL> insert into t (id) values (100);

1 row inserted

 

Executed in 0 seconds

SQL> commit;

Commit complete

 

Executed in 0.015 seconds

 

SQL> select * from t;

 

        ID NAME       VNAME      NNAME

---------- ---------- ---------- ----------

        41            X         

        54            X         

       100            X          M

 

10 rows selected

 

Executed in 0.094 seconds

 

 

从实验结果上看,对新插入的数据,后设置default value是其效果的。同时,从时间上看,一次性的设置新列和默认值消耗的时间巨大。这对于生产环境是一个潜在的风险。

 

我们对高消耗的操作通常的处理是均衡化负载,尽可能将一个操作划分为若干个可干预、可控制的操作。所以,对一个大表添加有默认值数据列,可以先加一个无默认值数据列,之后修改默认值,最后使用dml进行数据更新。

 

 

3、结论

 

本篇阐述介绍的两个知识点,都是一些非常零碎的经验积累。只有将这些知识通过实验进行验证,归纳为自己的所得,不断的总结提高,才能达到我们质变的目的。

 

 

来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/17203031/viewspace-707979/ ,如需转载,请注明出处,否则将追究法律责任。

下一篇: 诡异的not in
请登录后发表评论 登录
全部评论
求道~

注册时间:2010-11-30

  • 博文量
    545
  • 访问量
    8039286