|
(源码的下载在第三楼)
很久没有给大家贡献点东西了,大家没有忘了我吧。去年我曾经对ITPUBER许诺要为大家开发一个直接读SGA的程序。但是先是身体不太好,后来又开始忙于备课、写教材,后来又骨折了,我哭。这个“SGA直接读”就暂时耽置了。前段时间VAGE考完OCM后比较空闲,就找到我一起开发这个“日志挖掘”。我们打算先把“日志挖掘”做成个监控软件,因为所有的修改都在日志中,监控日志中的内容,也等于监控了数据库所有的修改。而且监控软件对于数据的准确性要求不是十分高,少量的错误是允许的。等到它比较完善后,再把它做成ETL、或数据同步的工具。我和VAGE都一致认为,将这个程序开源更有意义,有兴趣的朋友可以直接下载源代吗进行研究。在开发的过程上,VAGE曾做过系统分析员,我们就按照他的过程开发。根据他的说法,无论多完美的设计,到开始编写程序时,都会出现各种意想不到的问题,如果在这个基础不断修修补补,最终的程序虽然可以完成,但BUG一定是巨多的。所以最好的方法是简单的设计一下,然后立即开始编写,快速的将第一遍编写完成(或因问题不得不中止),将所有的问题集中起来,进行再次的设计,然后重新开始编写。这样,第二遍编写出来的东西,BUG会比较少。他的这个看法我也同意,Windows是直到3.0后才可以正常使用的(小学时我还见过鼎鼎有名的Windows3.1),Vista也是一个臭名着著的产品,而下一版Windows 7则让人期待。我们现在发布的,就是第一次编写的研究版,它的确有很多问题,例如,没有自己的内存管理函数,而是用的malloc和free。经常会报出来Segmentation fault(这个后来发现是“事务梳理”造成的),但是,它已经可以从日志中准确的读出内容了。相信第二次正式编写时,BUG会大大减少。
开发这样的东西,其实只是出于对Oracle内部研究的兴趣。至于研究内部的作用,这里不再争论,所谓“仁者乐山,智者乐水”。或许我们追求的,只是一种感觉,一种清清楚楚、明明白白的感觉。不过,看这个充满错误的研究版可以把语句还原出来,还是很有成就感的。
好了,闲话少叙,先把大概思路讲说一下,因为写的快,整个过程用了两个多星期,而且,单是破译日志中各种操作的格式,就用了将近一个星期的时间,编写程序只用了十天左右,所以程序中没有加太多注释,有兴趣的朋友了解了我们的编写思路,对阅读代码会会有帮助的。
对了,这里替VAGE发个求职广告,有需要高级DBA的,可以找他联系,目前他正在求职中。他有5年专职DBA经历,带过小型DBA团队,经验还是比较丰富的。
他是“光荣”的七零后,计算机的衷实粉丝。最早曾在1997年元月在电脑报上发表过一遍在DOS3.2(我记不太清是不是这个版本了)下自己用C编写个attrib命令(隐藏文件)的程序,据他说这一版的DOS没有这个attrib命令,我没什么印象了。这一年一直被他称为他的“IT元年”。在毕业后他曾历任程序员、高级程序员、系统分析员,从2004年起专职作DBA。改行的浅层原因很简单,一是单位的内部需要,二是相对开发者,DBA相对比较轻松一些,“可以让和每根头发说再见的日子延后些”。
至于他的水平,哪个公司有兴趣的话可以去自己去面试。对了,“非诚勿扰”。
除了技术,我比较佩服他的有两点。第一,他从不开QQ与MSN,也就是说他从不聊天。是的,是从不。当然,他有QQ,但只是为了和家人联系,QQ号也经常的换。第二,他来北京快一年了,没有去过天安门、故宫、长城、香山,……。他除了上班就是在家学习,甚至在去Oracle参加OCM的培训、考试时,也没拐到天安门看看(OU离天安门坐地铁只有几站路了)。专业精神堪比董仲书的目不窥园。
他的QQ:714729313 有意者可以在QQ上留言。
好,下面开始介绍程序,程序在Linux下用C和号称访问Oracle速度最快的OCI开发。
这个程序首先分两大部分,分别是元数据抽取模块和读日志模块。元数据抽取模块有两个文件,extract.c和extract.h。编译它们的MAKE文件是extract.mk,如下的命令就可以编译它:
$ make -f extract.mk
显示结果如下:
gcc -c -I/opt/ora10g/product/10.2.0/db_1/rdbms/demo -I/opt/ora10g/product/10.2.0/db_1/rdbms/public -I/opt/ora10g/product/10.2.0/db_1/plsql/public -I/opt/ora10g/product/10.2.0/db_1/network/public -I/opt/ora10g/product/10.2.0/db_1/precomp/public extract.c
gcc -L/opt/ora10g/product/10.2.0/db_1/lib/ -L/opt/ora10g/product/10.2.0/db_1/rdbms/lib/ -o extract extract.o -lclntsh -ldl -lm -lpthread -lnsl -lirc
这个里面使用到了C的OCI接口,要设置如下环境变量才行:
export LD_LIBRARY_PATH=$ORACLE_HOME/lib:$ORACLE_HOME/bin:/bin:/usr/bin:/usr/local/bin:/usr/X11R6/bin/
export CLASSPATH=$ORACLE_HOME/JRE:$ORACLE_HOME/jlib:$ORACLE_HOME/rdbms/jlib
它的执行方式:
$ ./extract system/oracle@myone drlog.par
drlog.par是参数文件,这个我已经写好了,通常不必更改。
此程序的作用是把数据库的元数据,也就是对象ID、对象名、簇号、列号、列名等信息写入metafile_name参数指定的文件中(metafile_name的值在drlog.par中指定)。
我原计划在一次性的从数据库中抽取元数据后,再有新对象产生,此程序就不必再执行了,因为创建表、索引这些操作也会在日志中留下珠丝马迹。比如创建表将会在对象编号为8的表中插入行,这是一个簇表,对簇表日志的解析还有一点问题,这个功能就放在第二次开发时完成吧。
此程序执行后,只要没有新创建对象,此程序不必再执行。另外,执行此程序需要的打开监听器。
读日志模块包含两个文件,drlog.c和drlog.h,MAKE文件是drlog.mk,编译方式如下:
$ make -f drlog.mk
执行非常简单,drlog.par是参数文件,17962.log是要挖掘的日志文件,注意,还不能挖掘当前重做日志文件。
$ ./drlog drlog.par 17962.log
参数文件中有个list_all_record参数,等于大写Y时,将不按事务,顺序从日志中挖出每一条语句。设置为N时,将按事务顺序显示语句。如果日志中有事务没有提交,相关的语句不会被显示。另外,如果有事务开始自另一个日志文件,在此要挖掘的文件中提交,这样的半截事务相关的语句,也不会被挖掘。日志中的事务处理是重点,也是第一遍编写主要测试的对象,为了它能高效的处理,这一块我们是很费了心思的。
事务的开始,可以通过编号为5.2的操作(修改回滚段头的后映像)所在的重做记录来识别。虽然在事务的进行中还会再有5.2操作,但后面的5.2操作明显和事务开始的5.2操作不同。在5.2操作中,可以提取出来回滚段头DBA、槽号、和序列号,但没有事务的XID。在5.4操作(提交操作)中,也可以提取出来回滚段头DBA、槽号、和序列号。和事务开始的5.2在同一重做记录中的5.1操作(回滚块后映像)或DML相关的操作中,可以提取事务的XID信息,但没有回滚段头DBA这些信息。如果事务在一条重做记录中没有结束,同一事务相关的后续重做记录中的第一条DML操作或5.1操作中,会记录有事务的XID,总结一下,事务的识别,主要靠XID和三元组(回滚段头DBA,槽号,序列号),分别根据它们创建两个HASH链,HASH Bucket的数量可以用参数文件中的transaction_hash_bucket参数设置。这两个HASH链指向同一个保存语句的链表,这一块有点复杂,这一块程序中用到了“指向指针的指针的指针”,一些段无效的错误就是这一块代码引起的,在第二遍开发时,会对这一块进行改善。下面用个图说明一下事务处理。
(图在最后)
事务开始时,根据从5.2中抽取的三元组(回滚段头DBA,槽号,序列号)信息,计算出HASH值,将事务加进图中。每次有一条语句进入,根据XID计算HASH值,将它加进DML语句链表中。当事务提交时,根据从5.4(提交操作)中抽取的三元组信息,计算出HASH值,在HASH链中找到具体位置,根据指针找到事务语句链表的开始,顺序就可以把所有语句都读出来了。再根据语句链表最开始的XID,做一些清除工作(现在主要是这个清除工作容易引起段无效错误)。
HASH函数经过比较,使用了类似于OpenSSL中的HASH函数,去掉了低效的strlen()函数,把长度除2操作也去掉了,这样在HASH函数内部就只是作一次二进制移位、与和异域运算,这样可以保证比较高效。
好了,关于程序的介绍先说这么多吧,谁有兴趣的话,咱们再讨论。
下面这是我挖掘出的结果:
$ ./drlog drlog.par 17962.log
LoadMeta:max(n)=10950
(………………)
update T2 set ID1=111111,C1=5a5958575655 where rowid='AAACYJAABAAAG4SAAl'
update T2 set ID1=111111,C1=5a5958575655 where rowid='AAACYJAABAAAG4SAAm'
update T2 set ID1=111111,C1=5a5958575655 where rowid='AAACYJAABAAAG4SAAn'
delete T2 where rowid='AAACYJAABAAAG3qAAA'
delete T2 where rowid='AAACYJAABAAAG3qAAB'
VLD has other value,Log file is end,7 pos=28b610
对了,对于数据类型,我们只对NUMBER作出来解释,字符型、日期型还没有解析。
程序的终止,通常是读到一个错误的类型后自动终止,所以最后都会有一些什么找不到类的错误,“VLD has other value”,VLD是重做记录的类型,最后读到了一个无法识别的类型,自动终止运行了。
不好意思啊,原来的extract.c的头文件写错了,编译不了,我重新传了份。感谢blackeye提醒。
[ 本帖最后由 晶晶小妹 于 2009-5-9 14:48 编辑 ] |
|