小程聊微服务-数据抽取那点事(一)

一、前言

我们在《微服务是在双刃剑 http://www.jianshu.com/p/82ec12651d2d 》中提到了当我们将应用服务化以后,很多在单块系统中能够开展的数据统计和分析业务将会受到很大程度的影响,本文将延续上一篇文章深入分析服务化后,作为后端的数据统计和分析如何做。

注:本文的数据库是基于Oracle数据库

二、服务化后的现状分析

拿一个简单的快捷支付系统为例,服务化后的系统调用图如下所示:


image.png

通过上图我们可以看到,单块系统根据业务进行服务化后,每个系统功能单一、职责明确并且独立布署,这只是从系统的角度描述了服务化后的调用关系,那么从微服务的角度讲,还有一点是去中心化,也就是将数据库也按服务进行拆分,下图所示的正是每个服务与其对应的数据库间的关系。

image.png

上面我们可以看到,每个服务对应一个数据库,这样从上到下就已经全部拆分开了,再结合康威定律的理论,每个服务由一个团队负责管理,团队之间彼此协作和沟通。

三、数据抽取的技术选型

关于后台的数据统计需求,因为服务化后数据库已经拆分开,于是对后台数据统计造成了一定的困扰,针对这个问题我首先想到的是利用数据库同步来解决,将不同库或者表的数据统一汇总到一起。那么接下来,我将和大家一起逐步探讨和分析。

1、使用Oracle Golden Gate(简称OGG)工具

OGG的实现原理是抽取源端的redo log和archive log,然后通过TCP/IP协议投递到目标端,最后解析还原同步到目标端,使目标端实现源端的数据同步,如下图所示:

image.png
1.1 使用OGG的优点:

1、对生产系统影响小:实时读取交易日志,以低资源占用实现大交易量数据实时复制。
2、以交易为单位复制,保证交易一致性:只同步已提交的数据。
3、灵活的拓扑结构:支持一对一、一对多、多对一、多对多和双向复制等。
4、可以自定义基于表和行的过滤规则,可以对实时数据执行灵活影射和变换。

1.2 使用OGG需要注意的问题点:

1、在二个库之间做数据同步的时候,如果我们要在表中新加字段,必须要将OGG停下来加字段,然后再启动,新字段同步才会生效。
2、使用OGG做数据同步的时候,工具不是很稳定,经常会出现假死或者退出的情况。
3、OGG偶尔出现在同步过程中丢数据的时候。

2、使用Oracle Logminer

Logminer是oracle从8i开始提供的用于分析重做日志信息的工具,它包括DBMS_LOGMNR和DBMS_LOGMNR_D两个package,后边的D是字典的意思。它既能分析redo log file,也能分析归档后的archive log file。通过LogMiner可以跟踪Oracle数据库的所有DML、DDL和DCL操作。

2.1 使用LogMiner进行数据同步的框架图如下所示:
image.png
2.2 数据同步流程图如下所示:
image.png

同步流程说明:

通过指定源端、目标端数据库信息、LogMiner 同步时间等配置信息,获取源端同步数据。

1、通过定时轮询的方式检测是否到达数据同步时间,如果是则进行数据同步,否则继续进行轮询。
2、定时加载数据库归档日志文件到动态表 v$logmnr_contents 中。
3、根据条件读取指定 sql 语句。
4、执行 sql 语句。

基于JAVA写的LogMiner的数据同步部分核心代码如下所示:

try {
    ResultSet resultSet = null;
    // 获取源数据库连接
    sourceConn = DataBase.getSourceDataBase(); Statement statement = sourceConn.createStatement();
    // 添加所有日志文件,本代码仅分析联机日志 StringBuffer sbSQL = new StringBuffer(); sbSQL.append(" BEGIN");
    sbSQL.append("
    dbms_logmnr.add_logfile(logfilename=>'"+Constants.LOG_PATH+"\\REDO01.LOG', options=>dbms_logmnr.NEW);");
    sbSQL.append(" dbms_logmnr.add_logfile(logfilename=>'"+Constants.LOG_PATH+"\\REDO02.LOG', options=>dbms_logmnr.ADDFILE);");
    sbSQL.append(" dbms_logmnr.add_logfile(logfilename=>'"+Constants.LOG_PATH+"\\REDO03.LOG', options=>dbms_logmnr.ADDFILE);");
    sbSQL.append(" END;");
    CallableStatement callableStatement = sourceConn.prepareCall(sbSQL+""); callableStatement.execute();
    // 打印获分析日志文件信息
    resultSet = statement.executeQuery("SELECT db_name, thread_sqn, filename FROM v$logmnr_logs");

    while(resultSet.next()) {
        System.out.println("已添加日志文件==>"+resultSet.getObject(3)); 
    }

    System.out.println("开始分析日志文件,起始scn号:"+Constants.LAST_SCN);
    callableStatement = sourceConn.prepareCall("BEGINdbms_logmnr.start_logmnrstartScn=>'"+Constants.LAST_SCN+"',dictfilename=>'"+Constants.DATA_DICTIONARY+"\\dictionary.ora',OPTIONS =>DBMS_LOGMNR.COMMITTED_DATA_ONLY+dbms_logmnr.NO_ROWID_IN_STMT);END;");
    callableStatement.execute(); 
    System.out.println("完成分析日志文件");
    // 查询获取分析结果 System.out.println("查询分析结果"); 
    resultSet = statement.executeQuery("SELECT scn,operation,timestamp,status,sql_redo FROM v$logmnr_contents WHERE seg_owner='"+Constants.SOURCE_CLIENT_USERNAME+"' AND seg_type_name='TABLE' AND operation !='SELECT_FOR_UPDATE'");

    // 连接到目标数据库,在目标数据库执行redo语句
    targetConn = DataBase.getTargetDataBase();
    Statement targetStatement = targetConn.createStatement();
    String lastScn = Constants.LAST_SCN; String operation = null;
    String sql = null;
    boolean isCreateDictionary = false; while(resultSet.next()){
    lastScn = resultSet.getObject(1)+"";
    if( lastScn.equals(Constants.LAST_SCN) ) {
        continue; 
    }
    operation = resultSet.getObject(2)+""; 
    if( "DDL".equalsIgnoreCase(operation) ) {
        isCreateDictionary = true;
    }
    sql = resultSet.getObject(5)+"";
    // 替换用户
    sql = sql.replace("\""+Constants.SOURCE_CLIENT_USERNAME+"\".", ""); System.out.println("scn="+lastScn+",自动执行sql=="+sql+"");
    try {
        targetStatement.executeUpdate(sql.substring(0, sql.length()-1));
    } catch (Exception e) {
        System.out.println("测试一下,已经执行过了"); }
    }
    // 更新scn
    Constants.LAST_SCN = (Integer.parseInt(lastScn))+"";
    // DDL发生变化,更新数据字典 
    if( isCreateDictionary ){
        System.out.println("DDL发生变化,更新数据字典");
        createDictionary(sourceConn); 
        System.out.println("完成更新数据字典"); 
        isCreateDictionary = false;
    } 

    System.out.println("完成一个工作单元");
} finally {
    if( null != sourceConn ) { 
        sourceConn.close();
    } 
    if( null != targetConn ) {
        targetConn.close();
    }
    sourceConn = null;
    targetConn = null; 
    }
}
2.3 使用LogMiner做数据同步需要注意的点:

1、LogMiner是针对数据库级别的同步。
2、LogMiner工具的时效性较差,同步延时时间很长。
3、目标库必须与源库版本相同,或者比源库版本更高;目标库与源库字符集一致,或者是源库字符集的超集。
4、源数据库与目标库,必须运行在相同的硬件平台。
5、通过LogMiner方式获取日志的,通过oracle提供工具读取redo日志的信息,然后解析成SQL队列。有些特殊的数据类型,数据的变化是不记录到redo的,比如LOB字段的变化

3、总结

上面二种方案各有优缺点,但是实际工作中更需要同步延时小,同时稳定性极佳并且数据丢失率极低的方案,可以看到这二个方案并不适合做真正的数据抽取工具,来实现一个如下的方案:

image.png

在下一文中,我将结合工作实战为大家介绍一款高效的数据库同步工具,最终解决微服务实施中所带来的数据统计的痛点。

注:本文参考了 http://www.cnblogs.com/shishanyuan/p/3142788.html

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,189评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,577评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,857评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,703评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,705评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,620评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,995评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,656评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,898评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,639评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,720评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,395评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,982评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,953评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,195评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,907评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,472评论 2 342

推荐阅读更多精彩内容