深入详解Oracle data change notification

简介: 0、什么是 Oracle data change notification ? 当有多个应用程序或者进程操作同一个数据库时,其中进程1对Oracle中的某个表Table1进行插入、删除、修改等操作,进程2想在第一个进程操作完成后进行相应的操作。有没有什么方法让进程2获取到进程1的操作?

1、支持Oracle版本

Oracle 10gR2 或者以上版本。


2、  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

 用Database Change Notification有三个步骤:


    1)注册: 指定数据库要监听的查询。数据库可以监听DML(Data Manipulation Language)事件,DDL(Data Definition Language)事件,和global 事件(例如关闭数据库)。


 2)通知:一旦数据库中的数据发生变化,数据库将自动发出通知,我们要在我们的程序中定义事件处理操作。


 3)响应:在我们的程序中,一旦收到通知,我们一般情况下会自动更新data cache,当然我们可以通知用户数据发生改变,由他来决定是否进行更新。



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

http://stackoverflow.com/questions/30847188/when-use-change-notification-interface-the-ora-24912-listener-thread-failed-l


截止2015-6-16,根本原因还没有找到,即该Demo未测试成功。

2015-6-23, 发现错误原因: 和代码中通过putenv()设置有关,不使用putenv(),改成直接将文件、库包含到工程中,该Bug便不存在。


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 usr@db...\n\n");

//1.连接 第一个参数格式:【IP:端口/服务名】,第二个参数:登录用户名,第三个参数:密码。

con = OCI_ConnectionCreate("100.200.10.50:1521/ts", "tss", "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效果截图

如下图的三个步骤:image.png

注意:测试 空表insert一条记录的测试情况,需要 点下SQLDeveloper右面的“ 提交 ”按钮!


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为什么存在,作为程序员还是要抽业余时间追根究底,最终解决掉。



image.png

相关文章
|
Oracle 机器学习/深度学习 数据库
深入详解Oracle data change notification
深入详解 Oracle data change notification
1513 0
|
8月前
|
Oracle 关系型数据库
Oracle 中data与timstamp互转
Oracle 中data与timstamp互转
|
10月前
|
存储 Oracle Java
[亲测可用]hibernate调用Oracle存储过程|Spring Data JPA调用Oracle存储过程方法
[亲测可用]hibernate调用Oracle存储过程|Spring Data JPA调用Oracle存储过程方法
|
存储 SQL 监控
Oracle BCT(Block Change Tracking)与增量备份---发表在数据和云
BCT(Block Change Tracking)是Oracle从10g开始有的特性。BCT的原理是记录数据文件里每个数据块的变化,并把这些变化信息保存在BCT的跟踪文件中。
355 0
|
存储 运维 Oracle
【大数据开发运维解决方案】Oracle Data Redaction数据加密测试
最近有个做Java开发的网友问我,怎么在Oracle进行数据加密呢?我给他推荐了Data Redaction。Oracle Database 12c中加入了Data Redaction这个新的安全特性。当然在11g的Database Advanced Security Administrator’s Guide官方文档中就介绍了。
【大数据开发运维解决方案】Oracle Data Redaction数据加密测试
|
23小时前
|
存储 SQL Oracle
|
4天前
|
SQL Oracle 关系型数据库
Oracle数据库基础教程:从安装到基本操作的全面介绍
Oracle数据库基础教程:从安装到基本操作的全面介绍