深入详解Oracle data change notification

深入详解 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

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

截止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

如果感觉本文对您有帮助,请点击‘顶’支持一下,您的支持是我坚持写作最大的动力,谢谢!

时间: 2024-08-07 04:08:36

深入详解Oracle data change notification的相关文章

详解Oracle存储结构 掌握基本操作管理

2018.10.14那天我写了Oracle12C 的安装并初步了解了一下Oracle体系结构中数据库和实例.从中我们知道: 数据库是磁盘上数据的集合,位于收集和维护相关信息的数据库服务器上的一个或多个文件中.数据库由各种物理和逻辑结构组成,而表则是数据库中最重要的逻辑结构.表由包含数据的相关行和列组成. 组成数据库的文件主要分为两类:数据库文件和非数据库文件.两者之间的区别在于存储何种数据.数据库文件包含数据和元数据,非数据库文件则包含初始参数和日志记录信息等.数据库文件对于每时每刻正在进行的数

【Oracle】详解Oracle中的序列

序列: 是oacle提供的用于产生一系列唯一数字的数据库对象. 自动提供唯一的数值 共享对象 主要用于提供主键值 将序列值装入内存可以提高访问效率 创建序列: 1.  要有创建序列的权限 create sequence 或 create any sequence 2.  创建序列的语法 CREATE SEQUENCE sequence  //创建序列名        [INCREMENT BY n]  //递增的序列值是n 如果n是正数就递增,如果是负数就递减 默认是1        [STAR

详解Oracle partition分区表

随着表中行数的增多,管理和性能性能影响也将随之增加.备份将要花费更多时间,恢复也将 要花费更说的时间,对整个数据表的查询也将花费更多时间.通过把一个表中的行分为几个部分,可以减少大型表的管理和性能问题,以这种方式划分发表数据的方法称为对表的分区.分区表的优势: (1)改善查询性能:对分区对象的查询可以仅搜索自己关心的分区,提高检索速度: (2)方便数据管理:因为分区表的数据存储在多个部分中,所以按分区加载和删除数据比在大表中加载和删除数据更容易: (3)方便备份恢复:因为分区比被分区的表要小,所

【Oracle】详解Oracle常用函数

Oracle SQL 提供了用于执行特定操作的专用函数.这些函数大大增强了 SQL 语言的功能.函数可以接受零个或者多个输入参数,并返回一个输出结果. Oracle 数据库中主要使用两种类型的函数: 1.  单行函数:对每一个函数应用在表的记录中时,只能输入一行结果,返回一个结果, 比如:MOD(x,y)返回 x 除以 y 的余数(x 和 y 可以是两个整数,也可以是表中的整 数列).常用的单行函数有: Ø  字符函数:对字符串操作. Ø  数字函数:对数字进行计算,返回一个数字. Ø  转换函

【Oracle】详解Oracle中NLS_LANG变量的使用

目录结构: contents structure [-] 关于NLS_LANG参数 NSL_LANG常用的值 在MS-DOS模式和Batch模式中设置NLS_LANG 注册表中NLS_LANG和系统环境变量中的NLS_LANG 参考文章 1,关于NLS_LANG参数 Oracle provides Globalization Support that enables users to interact with a database in their own language, as defin

详解Oracle数据字典

Oracle通过数据字典来管理和展现数据库信息,数据字典通常储存数据库的元数据,是数据库的“数据库”.通常说的数据字典由4部分组成:内部RDBMS(X$)表.数据字典表.动态性能视图(V$)和(静态)数据字典视图.(两表两视图) 1,内部RDBMS(X$)表 X$表示Oracle数据库的核心部分,这些表用于跟踪数据库内部信息,维持数据库的正常运行.X$表是加密命名的,而且Oracle不做文档说明.X$表是Oracle数据库的运行基础,在数据库启动时由Oracle应用程序动态创建.比如我们熟知的X

详解oracle 12c通过数据泵expdp/impdp工具实现对数据备份、恢复

简介 Oracle Database 10g引入了最新的数据泵(Data Dump)技术,数据泵导出导入(EXPDP和IMPDP)的作用1.实现逻辑备份和逻辑恢复2.数据库用户之间移动对象3.数据库之间移动对象4.实现表空间搬移 实验环境 系统环境:centos7.4Oracle服务IP地址:192.168.100.99光盘挂载目录:/mnt/sr0安装相关目录:/opt 命令步骤 一.创建测试用户并授权 1.创建数据备份目录 [[email protected] ~]# mkdir /opt/

详解System.Data 命名空间

学习了面向对象的设计思想,我们的就应该明白分层的用法.通过机房重构和牛腩的学习我们对数据库已经掌握了一定的技能,System.Data已经是我们的老朋友了,但是我们对他又了解多少呢?下面让我们认真的了解一下System.Data命名空间.System.Data这个命名空间中又有很多的命名空间,我们最常用的就是System.Data.SqlClient,这个命名空间中有很多我们总是遇到的类,比如:SqlCommand.SqlConnection.SqlDataAdapter.SqlDataRead

详解Oracle中 DELETE、TRUNCATE 和 DROP 的区别

前言 以前做项目的时候,很少关注知识点中比较细节的东西,正好今天有这个机会,就把 Oracle 中几个常用的函数区分一下. 语法 delete from table_name truncate table table_name drop table table_name 区别 delete from后面可以写条件,truncate不可以,drop后边一般也不写条件. delete from记录是一条条删的,所删除的每行记录都会进日志,而truncate一次性删掉整个页,因此日志里面只记录页释放,