前幾天在弄Hubble鏈接Oracle數據庫,而後在mongodb中創建一個鏡像數據庫;mysql
發現一個問題,本來數據是11W,可是鏡像庫中只有6w多條;算法
剛開始覺得是沒運行好,又rebuild了一下sql
結果變成了7w多,又rebuild,又變成了6w了............mongodb
rebuild了N次,基本上每次結果都是不同的數據庫
沒辦法只能下載源碼調試一下;服務器
先把全部的dll都輸出到同一個目錄oracle
而後把Hubble的服務中止了,再把Hubble的全部文件拷貝到dll的輸出目錄ide
而後編譯生成一下,將HubbleTask設爲啓動項目,直接啓動sqlserver
這樣就至關於啓動服務器了性能
而後進入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左右的數據,如今能夠排除是插入數據失敗的緣由了
而後推測是在從Orcale獲取數據的時候有問題?
打開並行任務瞄一下...(這裏只是習慣的看一下,不必定每次都有效)
不過此次很巧的,發現了一個熟悉的方法(由於Orcale的驅動是從新實現的,因此這部分比較熟)
恰好和猜想也吻合了,這個是獲取Oracle數據的地方
點進去看一下
好吧 我認可這段時間搞Oracle比較多,也吃過很多虧,因此看到這段語句的時候就感受不太對勁了
看看這個語句是何時,在什麼狀況下生成...
仔細看這個代碼就不難發現,他但願的效果是,按照索引鍵排序,而後查找出最小的5000個記錄
並保存最後一個(最大一個)索引鍵的值到from這個變量上
再看GetSelectSql(from)方法
private string GetSelectSql(long from) { if (_DBProvider.Table.DBAdapterTypeName.IndexOf("sqlserver", StringComparison.CurrentCultureIgnoreCase) == 0) { return GetSqlServerSelectSql(from); } else if (_DBProvider.Table.DBAdapterTypeName.IndexOf("mongodb", StringComparison.CurrentCultureIgnoreCase) == 0) { return GetMongoDBSelectSql(from); } else if (_DBProvider.Table.DBAdapterTypeName.IndexOf("oracle", StringComparison.CurrentCultureIgnoreCase) == 0) { return GetOracleSelectSql(from); } else if (_DBProvider.Table.DBAdapterTypeName.IndexOf("mysql", StringComparison.CurrentCultureIgnoreCase) == 0) { return GetMySqlSelectSql(from); } else if (_DBProvider.Table.DBAdapterTypeName.IndexOf("sqlite", StringComparison.CurrentCultureIgnoreCase) == 0) { return GetSqliteSelectSql(from); } else { return GetSqlServerSelectSql(from); } }
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執行完以後才執行的
也就是說 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[0].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[0].Rows.Count <= 0) { return result; } var table = ds.Tables[0]; foreach (System.Data.DataRow row in table.Rows) { result.Add(GetOneRowDocument(row)); } from = Convert.ToInt64(table.Rows[table.Rows.Count - 1][_DBProvider.Table.DocIdReplaceField]); return result; }
至此就基本結束了,其實看了表層實現來講,仍是有不少地方能夠優化的,可是Hubble真正的性能優點在於他的高效的搜索算法,因此表層的這些都不是瓶頸點
就不作過多的改動了