1. 程式人生 > >HBase Indexer導致Solr與HBase資料不一致問題解決

HBase Indexer導致Solr與HBase資料不一致問題解決

1. 問題描述

經過對HBase Indexer一段時間的使用、測試、觀察,發現通過Phoenix端匯入到HBase表的資料與Solr那邊的資料會產生不一致的現象,具體體現為Solr那邊的資料會比HBase表資料少幾千條或者更多。在公司測試環境以及試點專案生產環境下都會出現這個問題。

公司的測試環境如下描述:
1) C_PICRECORD表,擁有兩個二級索引表C_PICRECORD_IDX和C_PICRECORD_IDX_COLLISION
2) 測試資料80+w條
3) psql方式匯入,發現Solr會比HBase源表少幾千條到上萬條

【注意】

這個現象使用了現場的部分資料集並且在HBase表建立了二級索引表的情況下會很容易重現。

2. 發現問題

HBase indexer內建會有一些監控資料,監控SepEvent事件處理次數、Solr索引新增次數、Solr索引刪除次數,同時還會記錄一些操作失敗的次數。通過官方Github上Wiki對監控資料這部分的介紹:
https://github.com/NGDATA/hbase-indexer/wiki/Metrics

我們採用JMX方式來獲取這部分監控資料,具體操作如下:
1) 在CDH Cloudera Manager管理介面裡面配置Key Value Store Indexer服務開啟JMX服務配置:

hbase-indexer-jmx-config

配置項包括:

-Dcom.sun.management.jmxremote
.port=8999 -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false

配置儲存後重啟Key Value Store Indexer服務,接下來我們就可以通過JConsole連線到這個埠,來獲取HBase Indexer的監控資料了。

2) 本地測試使用JConsole來獲取HBase Indexer相關監控資料,如下圖所示:

jconsole-config

輸入需要監控的機器IP及埠(前面的配置採用8999埠),點選“連線”進入監控介面。

jconsole-show-mbean

在“MBean”面板下我們可以看到有“hbaseindexer”的相關監控資料,根據HBase Indexer官網Wiki介紹,在“DirectSolrInputDocumentWriter”下儲存了HBase Indexer對Solr進行操作的監控資料,比如新增索引次數“Indexer adds”和刪除索引次數“Indexer deletes”,裡面分別記錄了索引新增次數和索引刪除次數。我們正是通過監控這兩個資料,分析出Solr和HBase源表資料產生不一致的原因——部分HBase新增在HBase Indexer被當成“Indexer deletes”操作處理,從而使得Solr的資料少於HBase源表資料。少掉的資料等於“Indexer deletes”裡面記錄的次數,即有:Hbase Row Count - Solr numfound = Indexer deletes count。

3. 除錯並定位問題

針對上面觀察並分析的現象,我們需要定位到Hbase Indexer原始碼進行分析:

【注意】

需要使用CDH5.4裡面的Key Value Store Indexer原始碼,而不是Github上面HBase Indexer的原始碼。當然,兩者的區別僅在於hbase版本的不同。(CDH使用的是自己封裝改造過的HBase版本)

定位到Indexer類的indexRowData方法,這個方法會呼叫calculateIndexUpdates方法,針對我們的配置採用的是RowBasedIndexer(Indexer的子類,並且是內部類)。因此我們定位到RowBasedIndexer的calculateIndexUpdates實現方法上。

@Override
        protected void calculateIndexUpdates(List<RowData> rowDataList, SolrUpdateCollector updateCollector) throws IOException {

            Map<String, RowData> idToRowData = calculateUniqueEvents(rowDataList); // 選取需要處理的行(包括那些要刪除的)

            for (RowData rowData : idToRowData.values()) {
                String tableName = new String(rowData.getTable(), Charsets.UTF_8);

                Result result = rowData.toResult(); // 對於需要delete(delete or delete family)的行,其kvs為空

                if (conf.getRowReadMode() == RowReadMode.DYNAMIC) {
                    if (!mapper.containsRequiredData(result)) {
                        if(log.isDebugEnabled()) {
                            log.debug("Row " + Bytes.toString(rowData.getRow()) + " need to re-read from hbase");
                        }
                        result = readRow(rowData, result);
                    }
                }

                boolean rowDeleted = result.isEmpty(); // 由此判斷是否為delete row

                String documentId;
                if (uniqueKeyFormatter instanceof UniqueTableKeyFormatter) {
                    documentId = ((UniqueTableKeyFormatter) uniqueKeyFormatter).formatRow(rowData.getRow(),
                            rowData.getTable());
                } else {
                    documentId = uniqueKeyFormatter.formatRow(rowData.getRow());
                }

                if (rowDeleted) {
                    // Delete row from Solr as well
                    updateCollector.deleteById(documentId);
                    if (log.isDebugEnabled()) { 
                        log.debug("Row " + Bytes.toString(rowData.getRow()) + ": deleted from Solr, kvs : " + rowData.getKeyValues());
                    }
                } else {
                    IdAddingSolrUpdateWriter idAddingUpdateWriter = new IdAddingSolrUpdateWriter(
                            conf.getUniqueKeyField(),
                            documentId,
                            conf.getTableNameField(),
                            tableName,
                            updateCollector);
                    mapper.map(result, idAddingUpdateWriter);
                }
            }
        }

出現不一致的根源在於

conf.getRowReadMode() == RowReadMode.DYNAMIC

我們所建立的Indexer預設情況下RowReadMode是採用DYNAMIC的,那麼在程式碼裡面會進入這段條件邏輯,這部分程式碼簡單說明如下:

判斷Result裡面是否包含了所有我們需要對映到Solr裡面的列,如否,則需要呼叫readRow方法從HBase裡面獲取我們需要對映的資料(根據rowkey,呼叫HTable.get(Get))。

出現不一致的現象就在這部分邏輯裡面,我們考慮下面這種情況:

1) HBase RegionServer 將Put操作先寫WAL (這個時候Put還沒儲存到Region)
2) 非同步處理的HBase Indexer獲取到這個WAL日誌,對資料進行處理,進入了我們上面說的這段條件邏輯程式碼,恰巧Result裡面沒有一部分Solr索引列,那麼需要呼叫readRow方法從HBase重新讀取資料,這個時候呼叫HTable.get(Get) 並沒有獲取到資料(Result.isEmpty()為真)
3) HBase RegionServer把Put儲存到Region
4) 那麼對於2) 裡面的HBase Indexer,那條記錄將被當成delelet操作,所以在後面的邏輯將其當成solr delete document的操作

經過以上分析再加上我們通過新增debug日誌進行除錯,驗證了我們上面的猜測。

hbase-indexer-log4j-config

配置CDH Key Value Store Indexer log4j配置,加入需要DEBUG的類:

log4j.logger.com.ngdata.hbaseindexer.indexer.Indexer$RowBasedIndexer=DEBUG
log4j.logger.com.ngdata.hbaseindexer.indexer.Indexer$ColumnBasedIndexer=DEBUG

我們通過在原始碼裡面新增輸出自定義DEBUG資訊來除錯(需要編譯並替換叢集上面的hbase-indexer-engine-xxx.jar)。

4. 解決問題

根據上面的驗證以及對程式碼的初步瞭解,我們給出以下兩種解決方案:

4.1 方案1

建立hbase indexer的時候,對morphline-hbase-mapper.xml裡面<indexer>節點增加read-row屬性配置,配置read-row=”never”,關於這個引數的說明參考:

配置為never,將不會進入上面提到的條件邏輯程式碼,那麼自然也不會出現不同步的現象。這種解決方案只需要修改建立indexer的配置,而不需要修改HBase Indexer原始碼,優於後面提到的【方案2】

4.2 方案2

在readRow()方法裡面加入retry邏輯,合理的設定重試次數併合理休眠,以此來保證能夠獲取到正確的Result。這種方式相對比較麻煩,首先需要修改原始碼,並進行編譯,替換叢集上相應jar包。而且,重試和休眠會對效能造成一定影響,建議不到萬不得已不要使用這種方式。

相關推薦

HBase Indexer導致SolrHBase資料一致問題解決

1. 問題描述 經過對HBase Indexer一段時間的使用、測試、觀察,發現通過Phoenix端匯入到HBase表的資料與Solr那邊的資料會產生不一致的現象,具體體現為Solr那邊的資料會比HBase表資料少幾千條或者更多。在公司測試環境以及試點專案生產

hbase修復.META.表HDFS檔案一致問題

在實際環境中遇到hbase fbck檢查報hdfs資料塊與META表資訊不一致的錯誤。表現就是資料寫入無法進行。 經過檢查,發現在.META.表中對應的一些region塊的子列少了regioninfo這一列;同時在hdfs的出錯region資料夾下檢視發現本來該是.reg

NHibernate 查詢檢視資料資料庫資料一致

NHibernate 查詢資料與資料庫資料不一致 開發中遇到,當更改一條資料之後,查詢到的檢視列表資料沒有更新,實際資料庫中資料已經更改,檢視中的資料也幾經更改。 初步懷疑是 session 快取的

mysql主從同步、資料一致解決辦法

提示報錯: Could not execute Write_rows event on table injured_dataplatform.injurysite; Duplicate entry '6864' for key 'PRIMARY', Error_code: 1062; handl

c# java base64 一致解決方案

不一致的問題不是編碼的問題  而是json字串的問題通常我們會json 巢狀  我們先來看連個字串 {"contentType":"","httpMethod":"POST","paramMap":"{\"keyword\":\"華為\"}","url":"https:\/\/bizapi.jd.com\/a

【問題記錄】控制檯解析preview和response資料一致解決JS處理後臺返回的Long型資料精度丟失

問題描述: 後端返回資料preview和response不一致 (翻譯成專業術語就是:JS處理後臺返回的Long型資料精度丟失) 問題分析: JS在處理返回資料型別是Long的時候,精度會丟失一部分!!! 問題原因: JS內建有32位整數,而number型

elasticsearch-6.1.2部署時JDK版本應用版本一致解決辦法

elasticsearch-6.1.2需要JDK1.8版本,通長會出現與我們應用程式JDK不匹配現象,其實解決方法很簡單,只需要再安裝JDK1.8,環境變數新增ES_JAVA_HOME,指定JDK1.8的安裝路徑,下面具體介紹:安裝jdk1.8,配置環境變數vi /etc/p

微信支付 錯誤碼10003 redirect_url域名後臺配置一致解決方法

這個問題是微信公眾號網址配置的問題,解決方法如下第一步:登陸微信公眾平臺網址:點選開啟連結https://mp.weixin.qq.com/第二步:找到介面許可權/功能服務/網頁授權/修改第三步:修改成自己的域名(注意前面不能加https/http)第四步:到微信支付的產品中

Redis和DB資料一致解決方案

大多情況下,我們使用快取都是這樣的策略:先讀快取,讀取不到就讀資料庫然後同步到快取中。 問題出現場景 問題就是在併發訪問中,不論是先寫庫,再刪除快取;還是先刪快取,再寫庫,都有可能出現數據不一致的情況 1、在併發中是無法保證讀寫的先後順序的,如果刪掉了

碼雲提交本地倉庫遠端倉庫一致解決方法。

造成這個錯誤是因為我在github上修改了自己的檔案,在上傳本地檔案之前沒有把github上的檔案拉倒本地,意思是本地和遠端的檔案應該合併後才能上傳本地的新檔案具體方法:1、先拉下來,會自動合併的(不用操心)git pull origin master2、再上傳git push -u origin maste

Android studio匯入工程java檔案出現紅色J,gradle外掛gradle版本一致 解決辦法

     在這個程式碼搬運的時代,合理運用開源優秀程式碼,已經成為了每一個程式設計師必不可少的技能。      我相信大家在匯入第三方module或者第三方工程的時候,都遇到過,開啟的工程JAVA檔案

使用hbase hbck修復region中資料一致問題

# 檢視執行日誌 20171227_tried_repaired_test_tony 發現上述的三種錯誤中的兩種已修復,但是 Region not deployed on any region server錯誤還沒能修復 日誌摘要如下ERROR: Region { meta => test_tony,P

namenode namespaceIDdatanode namespaceID 一致導致datanode無法啟動的問題

csdn sdn enter .net oot 改版 more version 進入 在啟動hadoop之後,發現如下datanode何在?????在重新啟動hadoop的時候,發現了問題,如下這說明datanode啟動時出錯了我們跟蹤這個問題,進入到log文件夾下,找到h

導致資料庫中資料一致的根本原因

資料庫中很有可能存在不一致的資料。   一般導致資料庫中資料不一致的根本原因有三種情況。第一種是資料冗餘造成的,第二種是併發控制不當造成的,第三種是由於某種原因(比如軟硬體故障或者操作錯誤)導致資料丟失或資料損壞。   讓我們具體講講這三種情況: 第一種情況:資料冗餘 假如資料庫

MongoDB學習筆記~資料結構實體物件一致時,它會怎麼樣?

回到目錄 對於MongoDB這個文件型弱型別資料庫,它在操作時有時給我們帶來了一些方便,如當你的mongodb資料表與現有的類實體物件不一致時,會發生一些情況,總結如下 1:mongodb資料表不存在,實體類有,進行獲取資料時,程式不出錯 2:mongodb資料表沒有某些屬性欄位,實體類有,進行獲取

HBase--通過Java APIHBase互動(增刪改)

import java.io.IOException; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.hbase.HBaseConfiguration; import org.apache.hadoop

VMWare 虛擬網路連線設定主機設定一致導致虛擬機器無法連線網路的解決辦法

最近閒來無事,將家裡的電腦帶到了公司。開啟虛擬機器打算玩玩Linux。 (~ ̄▽ ̄)→))* ̄▽ ̄*)o發現我的Ubuntu竟然連不上網了。 這時候習慣性的google此問題,哎,忘記公司沒有vpn。哭死。好吧那就問問度娘吧,不問不知道,一問嚇一跳啊,這種問題

Tomcat中因時區設定問題導致系統時間一致問題

今天在除錯一個專案時發現使用者那裡伺服器的Tomcat命令列中log4j輸出的時間與作業系統時間不一致,因此日誌記錄的時間與作業系統時間也不一致,且正好相差8個小時。         產生原因是因為Tomcat中的時區設定與作業系統的時區設定不一致,通過修改Tomcat根

solr分片後副本資料一致的問題

技術交流qq群: 659201069概述:solr支援單機和cloud兩種執行模式,在cloud模式,會出現同一個查詢條件資料不一致的問題,這其實就是分散式系統資料一致性問題,根據Eric Brewer教授的CAP定理,一個布式系統不可能同時滿足一致性(C:Consisten

hibernate手動更新資料 查詢資料更新資料同步

最近在專案中,二級密碼驗證一直出問題,本來利用的是Ajax非同步提交驗證,程式剛才開始執行時候沒什麼問題,但是一旦使用者修改一下二級密碼之後,當再需要輸入二級密碼的時候就會一直驗證不成功,得到的密碼一直都是更新之前的資料,查了很久資料,才發現原來是由於快取的問題,由於JQue