深入详解 Oracle
data change notification
1、什么是 Oracle data change notification ?
当有多个应用程序或者进程操作同一个数据库时,其中进程1对Oracle中的某个表Table1进行插入、删除、修改等操作,进程2想在第一个进程操作完成后进行相应的操作。有没有什么方法让进程2获取到进程1的操作?
类似进程、多线程的同步机制,或者消息响应机制。在Oracle中也有类似的实现,该机制名称即为:data change notification。
2、支持Oracle版本
Oracle 10gR2 或者以上版本。
3、 data change notification支持的操作
(1) 数据库状态变化 Database status changes : startup and shutdown
(2) 数据库对象变化 Database objects changes :
1) DDL changes : alter or drop actions
2) DML changes : insert, delete, update actions
3、两种使用方法
3.1 OTL data change notification源码详解
示例源码参考资料:
【1】http://otl.sourceforge.net/otl4_subscriber.htm
【2】http://otl.sourceforge.net/otl4_ex585.htm
使用中在subs.subscribe()接口会出现Bug:
bug号及详情——ORA-24912: Listener thread failed. Listen failed.
Google提供的解决方案:The client needs to be restarted。(但测试不凑效)
错误及详见我的提问:http://bbs.csdn.net/topics/391054125
截止2015-6-16,根本原因还没有找到,即该Demo未测试成功。
3.2 ocilib data change notification源码详解
示例源码参考资料:
http://orclib.sourceforge.net/doc/html/group___ocilib_c_api_subscriptions.html
//代码解析(VS2010 C++实现,已经验证过)
#include "stdafx.h" #include "ocilib.h" #pragma comment (lib, "ociliba.lib") #ifdef _WINDOWS #define sleep(x) Sleep(x*1000) #endif #define wait_for_events() sleep(5000) void event_handler(OCI_Event *event); void error_handler(OCI_Error *err); int main(void) { OCI_Connection *con; OCI_Subscription *sub; OCI_Statement *st; printf("=> Initializing OCILIB in event mode...\n\n"); //0.第二个参数为原有的oracle的DLL所在的路径名称。 if (!OCI_Initialize(error_handler, "oracle", OCI_ENV_EVENTS)) return EXIT_FAILURE; printf("=> Connecting to [email protected]\n\n"); //1.连接 第一个参数格式:【IP:端口/服务名】,第二个参数:登录用户名,第三个参数:密码。 con = OCI_ConnectionCreate("100.200.10.50:1521/ts54", "tss54", "psdts**", OCI_SESSION_DEFAULT); OCI_SetAutoCommit(con, TRUE); printf("=> Creating statement...\n\n"); st = OCI_StatementCreate(con); printf("=> Creating tables...\n\n"); //2.创建数据表 OCI_ExecuteStmt(st, "create table table1(code number)"); OCI_ExecuteStmt(st, "create table table2(str varchar2(10))"); printf("=> Registering subscription...\n\n"); //3.注册通知事件 sub-00 为通知名称. sub = OCI_SubscriptionRegister(con, "sub-00", OCI_CNT_ALL, event_handler, 5468, 0); printf("=> Adding queries to be notified...\n\n"); OCI_Prepare(st, "select * from table1"); OCI_SubscriptionAddStatement(sub, st); OCI_Prepare(st, "select * from table2"); OCI_SubscriptionAddStatement(sub, st); //等待响应注册事件 wait_for_events(); //可以Sleep足够时间一直等待,当有其他进程进行修改表的操作,该处就会有响应。 // 以下为测试用,可以不用执行。 // #if 0 // //4.执行对应数据库alter操作 // printf("=> Executing some DDL operation...\n\n"); // OCI_ExecuteStmt(st, "alter table table1 add price number"); //alter事件 // //等待5s,等待打印输出. // wait_for_events(); // // // //5.执行数据库的inser,update操作。 // printf("=> Executing some DML operation...\n\n"); // OCI_ExecuteStmt(st, "insert into table1 values(1, 10.5)"); // OCI_ExecuteStmt(st, "insert into table2 values('shoes')"); // OCI_ExecuteStmt(st, "update table1 set price = 13.5 where code = 1"); // OCI_ExecuteStmt(st, "delete from table2 "); // wait_for_events(); // // //6.执行drop数据库表操作。 // printf("=> Droping tables...\n\n"); // OCI_ExecuteStmt(st, "drop table table1"); // OCI_ExecuteStmt(st, "drop table table2"); // wait_for_events(); // // printf("=> Disconnecting from DB...\n\n"); // OCI_ConnectionFree(con); // printf("=> Stopping the remote database...\n\n"); // OCI_DatabaseShutdown("db", "sys", "sys", // OCI_SESSION_SYSDBA, // OCI_DB_SDM_FULL, // OCI_DB_SDF_IMMEDIATE); // #endif printf("=> Unregistering subscription...\n\n"); OCI_SubscriptionUnregister(sub); printf("=> Cleaning up OCILIB resources...\n\n"); OCI_Cleanup(); printf("=> Done...\n\n"); return EXIT_SUCCESS; } void error_handler(OCI_Error *err) { int err_type = OCI_ErrorGetType(err); const char *err_msg = OCI_ErrorGetString(err); printf("** %s - %s\n", err_type == OCI_ERR_WARNING ? "Warning" : "Error", err_msg); } void event_handler(OCI_Event *event) { unsigned int type = OCI_EventGetType(event); unsigned int op = OCI_EventGetOperation(event); OCI_Subscription *sub = OCI_EventGetSubscription(event); printf("** Notification : %s\n\n", OCI_SubscriptionGetName(sub)); printf("...... Database : %s\n", OCI_EventGetDatabase(event)); switch (type) { case OCI_ENT_STARTUP: printf("...... Event : Startup\n"); break; case OCI_ENT_SHUTDOWN: printf("...... Event : Shutdown\n"); break; case OCI_ENT_SHUTDOWN_ANY: printf("...... Event : Shutdown any\n"); break; case OCI_ENT_DROP_DATABASE: printf("...... Event : drop database\n"); break; case OCI_ENT_DEREGISTER: printf("...... Event : deregister\n"); break; case OCI_ENT_OBJECT_CHANGED: printf("...... Event : object changed\n"); printf("........... Object : %s\n", OCI_EventGetObject(event)); switch (op) { case OCI_ONT_INSERT: printf("........... Action : insert\n"); break; case OCI_ONT_UPDATE: printf("........... Action : update\n"); break; case OCI_ONT_DELETE: printf("........... Action : delete\n"); break; case OCI_ONT_ALTER: printf("........... Action : alter\n"); break; case OCI_ONT_DROP: printf("........... Action : drop\n"); break; } if (op < OCI_ONT_ALTER) printf("........... Rowid : %s\n", OCI_EventGetRowid(event)); break; } printf("\n"); }
//data change notification效果截图
如下图的三个步骤:
4、两种思路对比及Bug分析反思
周一下午临危受命,预期周一晚上完成data change notification的验证。
但是最终测试发现有RA-24912: Listener thread failed. Listen failed的Bug。
Google及Stackoverflow了N多资料,都没有解决方案。
一直持续到周二下午4点,在OTL尝试了N多方法都没有解决。
试验思路包括:
1)授权本地用户具有grant 权限,即能执行成功“grant change notifiation to 用户名”,已经授权,但Bug依然存在。
2)Stackoverflow老外提供思路,可能和服务有关,重启Oracle数据库,Bug依然存在。
3)从程序subscribe接口分析,但由于第三方接口,没有在深层打印日志,没有理清根本原因。
且subscirbe接口为void类型,无返回值,只能通过捕获异常,得到错误。且错误信息就只有监听失败。
综上,既然OTL这条路不通,为何不去尝试下其他思路。
当在Google输入"oracle data change notification C++" 关键词,便找到了3.2的实现。
反思:
1、对于不熟悉的领域,不能“一棵树上吊死”,当尝试N久一条路不通, 且业界大牛也没有好的方案的时候,可以考虑换换思路,说不定会柳暗花明。
2、当然,对于第一条路的Bug为什么存在,作为程序员还是要抽业余时间追根究底,最终解决掉。
2015-6-16 22:44 思于家中床前
作者:铭毅天下
转载请标明出处,原文地址:http://blog.csdn.net/laoyang360/article/details/46524519
如果感觉本文对您有帮助,请点击‘顶’支持一下,您的支持是我坚持写作最大的动力,谢谢!