起因

前几天在弄Hubble连接Oracle数据库,然后在mongodb中建立一个镜像数据库;

发现一个问题,原本数据是11W,但是镜像库中只有6w多条;

刚开始以为是没运行好,又rebuild了一下

结果变成了7w多,又rebuild,又变成了6w了............

rebuild了N次,基本上每次结果都是不一样的

准备调试

没办法只能下载源码调试一下;

先把所有的dll都输出到同一个目录

然后把Hubble的服务停止了,再把Hubble的所有文件拷贝到dll的输出目录

然后编译生成一下,将HubbleTask设为启动项目,直接启动

这样就相当于启动服务器了

然后进入Hubble的安装目录,启动QueryAnalyzer.exe

开始调试

因为是建立mongodb的镜像,所以找到Hubble.Code项目中的DBAdapter文件夹中的MongoAdapter.cs

找到方法public void MirrorInsert(IList<Document> docs)  插入镜像数据

设置断点,就可以了,然后找到QueryAnalyzer.exe中的rebuild,执行

其中的docs就是需要插入到monogodb的数据,每次5000个,运行几次后就发现了,每到5,6w左右数据的时候,就会传入一个1000多的数据,然后就没有下次了

就是后面的数据查询不到了,每次只能查到6w左右的数据,现在可以排除是插入数据失败的原因了

排除了monogodb插入错误的原因后,开始检查Oracle查询数据是否准确

然后推测是在从Orcale获取数据的时候有问题?

打开并行任务瞄一下...(这里只是习惯的看一下,不一定每次都有效)

不过这次很巧的,发现了一个熟悉的方法(因为Orcale的驱动是重新实现的,所以这部分比较熟)

刚好和猜测也吻合了,这个是获取Oracle数据的地方

点进去看一下

查询语句似乎有些问题看,这时还不确定

好吧 我承认这段时间搞Oracle比较多,也吃过不少亏,所以看到这段语句的时候就感觉不太对劲了

看看这个语句是什么时候,在什么情况下生成...

继续顺着代码的思路往下排查

仔细看这个代码就不难发现,他希望的效果是,按照索引键排序,然后查找出最小的5000个记录

并保存最后一个(最大一个)索引键的值到from这个变量上

再看GetSelectSql(from)方法

        private string GetSelectSql(long from)
{
if (_DBProvider.Table.DBAdapterTypeName.IndexOf("sqlserver", StringComparison.CurrentCultureIgnoreCase) == )
{
return GetSqlServerSelectSql(from);
}
else if (_DBProvider.Table.DBAdapterTypeName.IndexOf("mongodb", StringComparison.CurrentCultureIgnoreCase) == )
{
return GetMongoDBSelectSql(from);
}
else if (_DBProvider.Table.DBAdapterTypeName.IndexOf("oracle", StringComparison.CurrentCultureIgnoreCase) == )
{
return GetOracleSelectSql(from);
}
else if (_DBProvider.Table.DBAdapterTypeName.IndexOf("mysql", StringComparison.CurrentCultureIgnoreCase) == )
{
return GetMySqlSelectSql(from);
}
else if (_DBProvider.Table.DBAdapterTypeName.IndexOf("sqlite", StringComparison.CurrentCultureIgnoreCase) == )
{
return GetSqliteSelectSql(from);
}
else
{
return GetSqlServerSelectSql(from);
} }

GetSelectSql

private string GetOracleSelectSql(long from)
{
string fields = GetOracleFieldNameList(from); StringBuilder sb = new StringBuilder();
sb.AppendFormat("select {0} from {1} where {4} > {2} and rownum <= {3} order by {4} ",
fields, _DBProvider.Table.DBTableName, from, _Step, Oracle8iAdapter.GetFieldName(_DBProvider.Table.DocIdReplaceField));
return sb.ToString();
}

猜想已经得到验证了, 下一次查询的时候 使用上次得到的from作为条件,继续搜索前5000条记录

现在如果接触过Oracle的应该知道了,Orcale中的rownum+orderby的效果和sqlserver中的top+orderby是完全不一样的

Oracle中orderby是等rownum执行完之后才执行的

产生bug的原因

也就是说 orderby 搜索出前5000条记录,然后对这5000条记录排序!而不是对全表排序,取出前5000条记录

假设我有数据 5,8,4,3,10,2,9,1,6,7

每次取出5条 5,8,4,3,10 然后排序 3,4,5,8,10 ,得到最后一个记录的id=10

下次搜索的时候获取比10大的5条记录..结果就是0 所以2,9,1,6,7数据丢失了......

开始修改代码

第一次修改:

private string GetOracleSelectSql(long from)
{
string fields = GetOracleFieldNameList(from); StringBuilder sb = new StringBuilder();
sb.AppendFormat("select * from (select {0} from {1} where {4} > {2} order by {4}) where rownum <= {3}",
fields, _DBProvider.Table.DBTableName, from, _Step, Oracle8iAdapter.GetFieldName(_DBProvider.Table.DocIdReplaceField));
return sb.ToString();
}

测试得到的结果就是 数据是正确的,但是效率很低

第二次修改

所以进过第二次修改:

private string GetOracleSelectSql(long from)
{
string fields = GetOracleFieldNameList(from);
string indexerField = Oracle8iAdapter.GetFieldName(_DBProvider.Table.DocIdReplaceField);
string table = _DBProvider.Table.DBTableName;
string where = indexerField + " > " + from; string sql = Oracle8iAdapter.GetOracleSelectSql(table, fields, indexerField, _Step, where);
return sql;
}
/// <summary>
/// 组成Oracle可用的Sql语句
/// </summary>
/// <param name="table">操作表</param>
/// <param name="fields">返回列列名</param>
/// <param name="indexerField">索引列列名</param>
/// <param name="rowsCount">返回条数</param>
/// <param name="where">额外的where条件,不包含where关键字</param>
/// <returns></returns>
public static string GetOracleSelectSql(string table, string fields, string indexerField, System.Decimal rowsCount, string where)
{
string sql = @"
SELECT {1}
FROM {0} A
WHERE EXISTS (SELECT 1 FROM (SELECT {2}
FROM (SELECT B.{2}
FROM {0} B
{4}{5}
ORDER BY B.{2})
WHERE ROWNUM <= {3}) C
WHERE C.{2} = A.{2})
ORDER BY {2}
"; if (where == null)
{
return string.Format(sql, table, fields, indexerField, rowsCount, null, null, null);
}
else
{
return string.Format(sql, table, fields, indexerField, rowsCount, " WHERE ", where, " AND ");
}
}

虽然还是有一些不合理的地方,不过我们讲究的是最小的改动

关联改动

一共有4个方法是需要改动的

Hubble.Core.Service.SynchronizeCanUpdate.GetOracleSelectSql

Hubble.Core.Service.SynchronizeCanUpdate.GetOracleTriggleSql

Hubble.Core.Service.SynchronizeAppendOnly.GetOracleSelectSql

QueryAnalyzer.FormRebuildTableOld.GetOracleSelectSql

另外之前的方法也有一定的修改

private List<Document> GetDocumentsForInsert(IDBAdapter dbAdapter, ref long from)
{
List<Document> result = new List<Document>(); dbAdapter.DBProvider = this._DBProvider; System.Data.DataSet ds = dbAdapter.QuerySql(GetSelectSql(from)); StringBuilder sb = new StringBuilder(); foreach (System.Data.DataRow row in ds.Tables[].Rows)
{
result.Add(GetOneRowDocument(row));
from = long.Parse(row[_DBProvider.Table.DocIdReplaceField].ToString());
} return result;
}

改为

private List<Document> GetDocumentsForInsert(IDBAdapter dbAdapter, ref long from)
{
List<Document> result = new List<Document>(); dbAdapter.DBProvider = this._DBProvider; System.Data.DataSet ds = dbAdapter.QuerySql(GetSelectSql(from)); if (ds.Tables[].Rows.Count <= )
{
return result;
} var table = ds.Tables[]; foreach (System.Data.DataRow row in table.Rows)
{
result.Add(GetOneRowDocument(row));
} from = Convert.ToInt64(table.Rows[table.Rows.Count - ][_DBProvider.Table.DocIdReplaceField]); return result;
}

结束了

至此就基本结束了,其实看了表层实现来说,还是有很多地方可以优化的,但是Hubble真正的性能优势在于他的高效的搜索算法,所以表层的这些都不是瓶颈点

就不做过多的改动了

记录排查解决Hubble.Net连接Oracle数据库建立镜像库数据丢失的问题的更多相关文章

  1. 64位程序,利用ADO连接Oracle数据库

        刚好手头项目解决了ADO连接Oracle数据库的问题,记录下来,防止忘记. 项目情况:用32位环境完成算法动态库,结果后来需要升级到64位环境,由64位的软件来调用,则在64位设置下生成算法动 ...

  2. Windows server2008 搭建ASP接口访问连接oracle数据库全过程记录--备用

    真的是太不容易了,以前的时候在window server 2003上面搭建了一套asp+oracle的接口系统,就费了好大的劲儿,其实那会迷迷瞪瞪的也不知道怎么的就弄好了,也懒得管了.OK,从昨天到今 ...

  3. Windows server2008 搭建ASP接口訪问连接oracle数据库全过程记录

    真的是太不easy了,曾经的时候在window server 2003上面搭建了一套asp+oracle的接口系统.就费了好大的劲儿,事实上那会迷迷瞪瞪的也不知道怎么的就弄好了,也懒得管了.OK,从昨 ...

  4. navicat连接oracle数据库报ORA-28547: connection to server failed, probable Oracle Net admin error错误的解决方法

    原文:navicat连接oracle数据库报ORA-28547: connection to server failed, probable Oracle Net admin error错误的解决方法 ...

  5. 解决ODBC连接Oracle数据库报Unable to connect SQLState=08004问题

    今天用ODBC连接Oracle数据库时,报了这么一个错“Unable to connect SQLState=08004 Oracle ODBC Ora-12154”,上网查了好久都说PowerDes ...

  6. 解决Navicat Premium 12 连接oracle数据库出现ORA-28547的问题

    1. 出现的问题... 下午工作时想连接Oracle数据库,使用的是Navicat Premium 12 . 数据库地址.用户名.密码.端口号都没有问题,但出现了ORA-28547:connectio ...

  7. Asp.Net 应用程序在IIS发布后无法连接oracle数据库问题的解决方法

    asp.net程序编写完成后,发布到IIS,经常出现的一个问题是连接不上Oracle数据库,具体表现为Oracle的本地NET服务配置成功:用 pl/sql 等工具也可以连接上数据库,但是通过浏览器中 ...

  8. linux系统下php通过php_oci8扩展连接oracle数据库 Nginx

    相关版本信息: PHP Version 5.6.30 nginx version: nginx/1.10.3 Linux version 2.6.32-358.el6.x86_64 (mockbuil ...

  9. 关于Java_Web连接Oracle数据库

    1.前提条件 1>装有Oracle数据库(因为连接的时候需要开启两项服务) 2>myeclipse或eclipse(支持WebProject的版本)开发环境,本机以myeclipse为例, ...

随机推荐

  1. PS:蓝天白云的制作

    方法一: 1.新建(ctrl+N),根据自己的需求设置画面大小: 2.设置前景色为蓝天颜色,alt+delete,填充为天空蓝颜色: 3.滤镜-渲染-云彩,得出蓝天白云效果: 4.根据需求再调整亮度. ...

  2. Process 执行shell 脚本

    概述: Process类是一个抽象类(所有的方法均是抽象的),封装了一个进程(即一个执行程序). Process 类提供了执行从进程输入.执行输出到进程.等待进程完成.检查进程的退出状态以及销毁(杀掉 ...

  3. [转] 利用SET STATISTICS IO和SET STATISTICS TIME 优化SQL Server查询性能

    首先需要说明的是这篇文章的内容并不是如何调节SQL Server查询性能的(有关这方面的内容能写一本书),而是如何在SQL Server查询性能的调节中利用SET STATISTICS IO和SET ...

  4. gulp-rev-orig

    给客户演示项目时,老是会出现由于缓存,造成的最新的样式或者效果出不来的情况,还得需要手动清除缓存操作,一方面呢,会给客户留下不好的印象,而且也会多了清缓存这一过程,和同事商量过后,决定使用在css或者 ...

  5. angular中的自定义过滤器

    <!DOCTYPE HTML> <html ng-app="myApp"> <head> <meta http-equiv="C ...

  6. Log4j的ConversionPattern无缝适配到Logback

    为了能将log4j的ConversionPattern无缝应用到logback上来,需要对两个Conversion做适配,具体可以参考:Log4j 与 Logback的ConversionPatter ...

  7. 转:关于BFC的初步了解以及常见使用

    在学习CSS的过程中,掌握一些常用方法或效果实现的原理对于我们的学习来说是很有帮助的.如最常见的清除浮动和取消外边距塌陷时使用overflow:hidden;,在学习初期往往只知道有这种用法,且使用时 ...

  8. MySQL数据导出

    1,打开命令行窗口“运行”-->输入CMD 2,进入自己MySQL Server安装目录的bin目录(我的安装目录如下) cd C:\Program Files\MySQL\MySQL Serv ...

  9. c#编程指南(十) 平台调用P-INVOKE完全掌握, 字符串和指针

    可以说新手使用P-INVOKE最开始的头疼就是C#和C++的字符串传递,因为这里涉及到两个问题. 第一:C#的string和C++的字符串首指针如何对应. 第二:字符串还有ANSI和UNICODE(宽 ...

  10. CentOS7 服务器 JDK+TOMCAT+MYSQL+redis 安装日志

    防火墙配置(参考 CentOS7安装iptables防火墙) 检查是否安装iptables #先检查是否安装了iptables service iptables status #安装iptables ...