oracle 11g归档日志研究_4

change的内容,是oracle日志转化为SQL语句的核心,也是最麻烦,变化最多的地方。

先说opcode,opcode的含义网上随便一搜有很多,真正对我有用的,只有增删改,至于什么搜索、索引等操作,我根本就不关心。

5.1:包含信息较多,每个增删改一定对应一个有效的5.1,这个5.1中将包含原始数据,用来在回滚(undo)时使用。另外还会有大量的5.1操作,目前我并不理解其他5.1操作的含义,并将这些我认为“无效”的5.1忽略。5.1的数据内容按照如下顺序排列:

typedef struct ktudb {
    uint16_t siz;
    uint16_t spc;
    uint16_t flg;
    uint16_t unknown0;
    uint16_t xid0;
    uint16_t xid1;
    uint32_t xid2;
    uint16_t seq;
    uint8_t rec;
    uint8_t ufo;    //0x84: ktubu; 0x00: ktubl
}Redo_ktudb;
这是5.1的change向量表之后的第一段数据,其中的xid也许可以作为txn的id,其他值我并不知道有什么用。
typedef struct ktubl {
    uint32_t objn;
    uint32_t objd;
    uint32_t tsn;       //maybe
    uint32_t noname;    //maybe
    uint8_t opc0;
    uint8_t opc1;
    uint16_t slt;
    uint32_t unknown1;
    uint32_t unknown0;  //00 00 00 00
    uint32_t uba0;
    uint16_t uba1;
    uint8_t uba2;
    uint8_t unknown2;   //00
    uint32_t max_scn1;
    uint16_t max_scn0;
    uint16_t unknown3;
    uint32_t tx_scn1;
    uint16_t tx_scn0;
    uint16_t unknown4;
    uint32_t unknown5;  //00 00 00 00
    uint32_t txn_scn1;  //位置不确定
    uint16_t txn_scn0;
    uint16_t unknown6;  //00 00
    uint32_t brb;
    uint32_t unknown7;  //00 00 00 00
    uint8_t user;
    uint8_t bcl;    //maybe
    uint8_t idx;    //maybe
    uint8_t flg2;   //maybe
}Redo_ktubl;
这是第二段,其中opc0.opc1应该为11.1,以对应我们的增删改操作,其他值被我忽略。objd被我当做obj,在数据字典中查找对应项。虽然ktubl的结构被我定义成这样,但实际数据是变长的,其可能只有24字节,故可能使用时只有前几个元素的值才是真实的。
typedef struct KTB {
    uint8_t op0;
    uint8_t unknown0[7];
    uint16_t xid0;
    uint16_t xid1;
    uint32_t xid2;
    uint32_t uba0;
    uint16_t uba1;
    uint8_t uba2;
    uint8_t unknown1;
    uint16_t unknown2;
    uint16_t scn0;
    uint32_t scn1;
}Redo_KTB;
本段也是变长的,事实上很多操作都会有KTB段,但是它们的结构却不相同。
typedef struct KDO {
    uint32_t bdba;
    uint32_t hdba;
    uint16_t maxfr;
    uint8_t unop0;
    uint8_t unop1;  //maybe
    uint8_t itli;
    uint8_t unknown1[3];
    uint8_t slot;
    uint8_t unknown2[3];
}Redo_KDO;

本段也是变长的,好吧,看来5.1里面没有什么是固定的了。

在这4段之后的数据,就属于undo段了,其数据摆放格式都差不多,可以按照向量表将其内容打印分析。

 

5.2:包含的信息较少,可能应该作为一个事务(txn)的开始,但目前被我忽略。

typedef struct ktudh {
    uint16_t xid1;      //slt
    uint16_t unknown0;
    uint32_t xid2;      //sqn
    uint32_t uba0;
    uint16_t uba1;
    uint8_t uba2;
    uint8_t unknown1;
    uint16_t flg;
    uint16_t siz;
    uint8_t unknown3[12];
}Redo_ktudh;

这似乎是5.2包含的唯一一段数据,固定为32个字节,似乎5.2应该包含xid作为一个txn的标示,但它只包含了xid的一部分。

 

5.4:表示一个事务(txn)的结束,应该是意味着用户提交了一次commit,可能应该与5.2对应。

typedef struct ktucm {
    uint16_t slt;
    uint16_t unknown0;
    uint32_t sqn;
    uint32_t srt;   //长度1-4
    uint32_t sta;   //长度1-4
    uint32_t flg;   //长度1-4
}Redo_ktucm;
5.4的第一段数据,固定为20字节。
typedef struct ktucf {
    uint32_t uba0;
    uint16_t uba1;
    uint8_t uba2;
    uint8_t unknown0;   //00
    uint16_t ext;
    uint16_t spc;
    uint16_t unknown1;
    uint16_t fbi;   //maybe
}Redo_ktucf;

5.4的第二段数据,固定为16字节。

之后5.4可能还会有其他数据,我并不知道那是什么,并且,似乎最后一段总是以4字节数据结束。

 

11.2:插入,对应insert语句。

11.2的第一段数据是KTB,其结构可以使用5.1的,但是长度却不同,可能比5.1的要短。

typedef struct KDO_11_2 {
    uint32_t bdba;
    uint32_t hdba;
    uint16_t maxfr;
    uint16_t unknown0;
    uint8_t itli;
    uint8_t unknown1[3];
    uint16_t unknown2;
    uint16_t cc;
    uint8_t unknown3[20];
    uint16_t size_delt;
    uint8_t slot;
    uint8_t unknown4[10];
}Redo_KDO112;

第二段是11.2的KDO。

从第三段开始,就是insert的内容了,每一段对应一个字段,对于insert语句来说,所有的字段都会在11.2中列出。

 

11.3:删除,对应delete语句。

typedef struct KTB_11_3 {
    uint8_t op0;
    uint8_t unknown0[7];
    uint16_t xid0;
    uint16_t xid1;
    uint32_t xid2;
    uint32_t uba0;
    uint16_t uba1;
    uint8_t uba2;
    uint8_t unknown1;
    uint32_t zero0;
    uint32_t zero1;
    uint32_t zero2;
    uint32_t zero3;
    uint32_t zero4;
    uint32_t unknown2;
    uint32_t unknown3;
    uint32_t unknown4;
    uint16_t unknown5;
    uint16_t scn0;
    uint32_t scn1;
}Redo_KTB113;

第一段,虽然名字都叫KTB,内容却不一样。

第二段是KDO,可以使用5.1的KDO结构。

 

11.5:修改,对应update语句。

typedef struct KTB_11_5 {
    uint8_t op0;
    uint8_t unknown0[7];
    uint32_t uba0;
    uint16_t uba1;
    uint8_t uba2;
    uint8_t unknown1;
}Redo_KTB115;
第一段,又是一只独特的KTB。
typedef struct KDO_11_5 {
    uint32_t bdba;
    uint32_t hdba;
    uint16_t maxfr;
    uint16_t unknown0;
    uint8_t itli;
    uint8_t unknown1[3];
    uint8_t flag;
    uint8_t lock;
    uint8_t unknown2[2];
    uint8_t slot;
    uint8_t size;   //maybe
    uint8_t ncol;
    uint8_t nnew;
    uint8_t unknown4[5];
}Redo_KDO115;

以及独特的KDO。

第三段开始是update的数据,第三段本身,是类似于向量表的一个数组,只是它其中存储的是要修改的数据字段id-1。

 

11.17:对应于有LOB字段的增、改操作,用来表示LOB字段的总长度。还有一种不包含LOB长度的11.17,其意义不明,被我忽略。

typedef struct LOB_11_17 {
    uint16_t xid0;
    uint16_t xid1;
    uint32_t xid2;
    uint32_t OBJ;
    uint32_t unknown2;
    uint32_t unknown3;      //00
    uint32_t unknown4;      //00
    uint32_t lobsize;
    uint32_t unknown5;      //00
}Redo_LOB_11_17;

这是11.17的第三段,前两段被我忽略掉了,我并不知道它们是什么。并不是所有11.17都有这个结构,所以需要进行判断,如果此段的长度不是32字节,则认为这个11.17不是我们需要的。本段中lobsize就是本次操作的LOB字段的总长度。

 

19.1:对应于有LOB字段的增、改操作,存储着LOB字段的内容,目前我所操作的oracle 11g,会将LOB切割成8168-36(LOB头)=8132字节的块,每块分别放在一个19.1中。LOB的总长度需要通过11.17获取,然后通过19.1获取LOB数据。LOB头格式:

typedef struct LOB_19_1_1 {
    uint32_t unknown0;
    uint32_t unknown1;
    uint16_t unknown2;
    uint32_t lob_set;
    uint16_t unknown3;
    uint32_t seq;           //2 or 4 byte, lob编号, 1 to n
    uint32_t unknown5;      //00
    uint32_t subseq;        //2 or 4 byte, lob分段编号, 0 to n
    uint32_t unknown7;
    uint32_t unknown8;      //00
}Redo_LOB_19_1;

LOB头中有2个编号,这两个编号表示LOB的排列顺序,而且,由于insert和update使用的方式不同(java脚本、sqldeveloper等),LOB数据可能有重复(此时LOB第1个编号会+1,第2个编号会从0开始)。

 

以上是增删改对应的基本数据结构。以下进行简单总结说明:

我们进行的是增删改操作,即11.2、11.3、11.5是我们真正操作的内容。

如果我们的增、改操作带有LOB字段,则会出现11.17和19.1。

每个操作(指的是11.2、11.3、11.5),一定有其对应的5.1,其中含有undo所需的内容。

11.3最简单,因为其中没有需要解析的数据,只要通过5.1的undo段确定所在行即可,而通常只需要一个主键就解决了。

11.2中等,需要插入的数据都在11.2的数据段,其5.1的undo段是空的,因为插入的undo就是删除。

11.5最复杂,需要修改的数据在11.5的数据段,而需要定位的行则必须从其对应的5.1的undo段解析,undo段存的其实就是11.5中对应字段的原值。

 

程序运行示例:

程序指定数据字典为dictionary.ora,归档日志文件为o1_mf_1_3279_brn6w2fm_.arc,程序运行结果非常巨大,只贴出其中一小部分。


解析数据字典,得出用户名、表名、字段名。从日志文件只能解出对应obj即id,需要通过数据字典查到名称。其中数据字典中的字段id从1开始,日志文件中从0开始,故需对日志文件中的字段id+1,其他id不需要。


以5.2、5.1开始,5.4结束,中间11.2表示insert操作。图片中的ERR是DEBUG输出,请无视~~

解析结果存为SQL语句。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值