Know Unknown Through Known

2 23rd, 2009 | Posted by jacky | Filed under 大话技术

在学习Oracle的过程中,我们不光要知道某个功能如何使用,而且要经常思考Oracle为什么要这样设计。假如我们是Oracle的开发人员,我们会怎么设计这个功能?通过这样的思考,能够帮助我们更深层次的了解其设计原理。就算一个你从来没有用过的功能,花几分钟查阅一下文档,你就可以迅速上手,完全不需要死记命令。

学习的过程是由点到面,由浅到深。最初是学习每个知识点,然后每个点不断深入,再融汇贯通后,就可以形成连成一个网状的知识面。学习的最高境界是Know Unknown Through Known(通过已知知未知)。当你到达一定的高度时,你会发现很多设计原理都是相通的,比如Oracle启动和OS的启动过程,表面虽然不同,但原理很相似。甚至我们可以通过已知的知识来推断出一个未知的东西是如何设计的。

昨天和一个同事聊到了flashback这个东西,问题是在flashback table的时候,Oracle要求必须打开enable row movement,文档上只是说明这个是前提条件,但是为什么却没有说明。我从来没有用过flashback,但是我基本了解实现原理,flashback有两种实现方式:一类是通过undo来实现的,比如flashback query,其实就是利用Oracle的一致性读实现的,因为undo不记录ddl操作,所以ddl操作比如truncate table,是不能flashback的。另一类是10g中引入的flashback log,这个log定期记录了每个block变化的前镜像,其原理实际上是找到变化的block在需要恢复时间点之前的前镜像,然后再用redo将数据库前滚到一致的状态,所以不管什么操作都可以恢复,所以flashback database就是利用它来实现的。

flashback table和flashback query一样,都是通过undo去实现的,所以它同样不支持DDL操作,但是flashback table会自动同步索引,也就是说索引在flashback后,不会变成invalid状态。有两种可能的实现方案:第一种是根据undo生成了一系列的递归的undo sql,然后在表上应用这些SQL,达到flashback的目的(很明显,这个方案很复杂)。第二种是直接利用flashback query生成那个时间点的一致读状态,然后删除原有的数据(delete或truncate),再根据flashback query的结果插入数据。

以上都是我们的猜测,通过10046 trace可以验证Oracle实际上是采用了第二个方案(flashback query+delete+insert),为什么使用delete而不是truncate,因为truncate会生成新的data_object_id.

step1:先利用flashback query生成一个临时表,这个表上有一个字段表明操作的类型(D代表删除,I代表插入):

INSERT /*+ APPEND */into SYS_TEMP_FBT
SELECT /*+ FBTSCAN FULL  PARALLEL(S, DEFAULT) */:1, :2, :3, rowid, SYS_FBT_INSDEL
FROM "SYSTEM"."TEST" as of SCN :4 S

step2:然后关联两个表,删除D类型的操作:

DELETE /*+ BYPASS_UJVC */
FROM (SELECT /*+ ORDERED USE_NL  PARALLEL(S, DEFAULT) PARALLEL(T, DEFAULT) */ S.rowid
FROM SYS_TEMP_FBT T, "SYSTEM"."TEST" S
WHERE T.rid = S.rowid
and T.action = 'D'
and T.object# = :1) V

step3:插入flashback query的结果,完成flashback table:

INSERT /*+ PARALLEL(S, DEFAULT) PARALLEL(T, DEFAULT) */
INTO "SYSTEM"."TEST_1"
SELECT /*+ USE_NL  ORDERED PARALLEL(S, DEFAULT) PARALLEL(T, DEFAULT) */ S.*
FROM SYS_TEMP_FBT T, "SYSTEM"."TEST" as of SCN :1 S
WHERE T.rid = S.rowid
and T.action = 'I'
and T.object# = :2

这下全明白了,因为都是普通的DML操作,这样就可以理解为什么Oracle要求必须enable row movement,而且flashback table后索引也处于一个一致的状态。

Flashback Table happens in place by rolling back only the changes made to the table or tables and their dependent objects, such as indexes. Note that Flashback Table is different from Flashback Drop: Flashback Table undoes recent transactions to an existing table whereas Flashback Drop recovers a dropped table; Flashback Table uses data in the undo tablespace whereas Flashback Drop uses the recycle bin.

The FLASHBACK TABLE command brings one or more tables back to a point in time before any number of logical corruptions have occurred on the tables. To be able to flashback a table, you must enable row movement for the table; because DML operations are used to bring the table back to its former state, the ROWIDs in the table change. As a result, Flashback Table is not a viable option for applications that depend on the table’s ROWIDs to remain constant.

多谢棉花糖兄的trace结果,大胆猜测,小心论证,就完成了整个结论。

–EOF–

后记:这个事情还没完,有人问我:flashback table到底和enable row movement有什么关系?
回答:flashback table虽然可以保证数据不变,但是实际上里面的行的rowid已经发生了变化,也就是说样子虽然没变,但是内部已经发生了好大的变化(比如:一个update操作,flashback是通过delete,insert来实现的,所以行的位置已经变化了)。enable row movement常用于分区表,但是其实际的含义是允许行的rowid发生变化,因为普通的DML操作是不会导致rowid发生变化的(不要拿delete,再insert说事,那是两个行,不是一个行,rowid当然会变)。因为Oracle有这个承诺,所以当rowid会变化时,要求必须打开enable row movement这个开关。

标签:
  1. 谭理想
    2 23rd, 200921:25

    Know Unknown Through Known,说的好!!!

  2. vogts
    2 24th, 200920:29

    大师级别了!!
    顶!!