Solr與MongoDB集成,實時增量索引
一. 概述
大量的數據存儲在MongoDB上,需要快速搜索出目標內容,於是搭建Solr服務。
另外一點,用Solr索引數據后,可以把數據用在不同的項目當中,直接向Solr服務發送請求,返回xml、json等形式的內容,使得對數據的使用更靈活。
對於MongoDB與Solr的描述不在這里闡釋了,本文旨在給大家提供整個Solr與MongoDB接合的方法,實現實時的增量索引。
MongoDB的官網:http://www.mongodb.org/
Solr項目的主頁:http://lucene.apache.org/solr/
二. 尋找解決方案
既然有了目標是講Solr跟MongoDB接到一起,那么就開始想想解決的方案。
網上搜了一些資料之后,匯總了下面三個方案:
1. 使用Solr的DataImport功能(Data Import)
我們先來看看Solr Wiki上對於DataImport功能的描述http://wiki.apache.org/solr/DataImportHandler
Most applications store data in relational databases or XML files and searching over such data is a common use-case.The DataImportHandler is a Solr contrib that provides a configuration driven way to import this data into Solr in both "full builds" and using incremental delta imports.
對於存儲在關系型數據庫及XML上的數據,Solr提供了DataImportHandler去實現全量和增量索引。
啥?人家沒說支持NoSQL啊,不信,我看清楚一點。
Wiki中的內容只給出了Usage with RDBMS 和 Usage with XML/HTTP Datasource,貌似Solr目前是不支持對於NoSQL的DataImport的。
有興趣的朋友可以嘗試給Solr添加一個Mongo的DataImportHandler,可能還要寫底層的Mongo驅動,工程量可能很大。
關鍵是這個方案不可控,成本可能會很大,因此我就不采取這種方式了。
在這里給大家分享一篇文章,Solr與MySQL集成指南
確實DataImport這個功能還是比較強大的,而且對MySQL也支持得很好,本人嘗試一下將Solr跟MySQL集成起來,配置過程也很簡單。
不過MySQL不是本文的重點,跑題了,因此只是嘗試了一下,沒有深入。
2. 使用腳本語言讀取MongoDB中的數據(Script Update)
說白了就是讀取整個Collection中的數據,遍歷。
這種方案是最直觀的,但不優雅,復用性、可維護性低,
最嚴重的問題在於性能,當數量級在百萬以下,那還是可以接受的,一旦數據繼續增長,那性能問題就凸顯出來了。
然而要是你還想用這種方案,那這里還有一個需要考慮的問題,你是打算每次遍歷的時候對Solr進行全量還是增量索引呢?
全量的話就直接overwrite,事情也好辦;增量的話,Mongo中刪除了的數據你咋整呢?
總而言之,不推薦這種方案,它的復雜度問題很明顯,無論是時間上還是空間上。
3. 使用MongoDB的oplog功能(Oplog Update)
MongoDB支持集群,集群中的實例進行通信,很自然地想到它們會記錄log,在MongoDB中稱之為oplog(operation log),類似與MySQL的binlog。
我們可以看看MongoDB官網上對oplog的描述 http://docs.mongodb.org/manual/reference/program/mongooplog/
如果現在你還想用上面方案2的話,那oplog的存在必然是對你的工作帶來極大便利的。
其一,oplog是實時記錄的,配合tailable cursor,可以實現實時的更新Solr索引,見http://derickrethans.nl/mongodb-and-solr.html
其二,實現優雅,增量的新增刪除的判斷時間復雜度變為O(1)
看到這里,你想用oplog來實現Solr與MongoDB的集成,那需要理清下面幾個問題:
(1)mongooplog如何開啟,怎么配置才適合
(2)Mongo Tailable Cursor是怎么一回事
(3)使用什么語言,選擇合適的Solr Client
(4)服務器宕機恢復后的處理
三. 最終方案,mongo-connector
當我性高彩烈地動手實現方案3的時候,我看到了這個http://blog.mongodb.org/post/29127828146/introducing-mongo-connector
竟然找到了一個mongo-solr的connector,當時那個心情真叫欣喜若狂啊。
它完全就是方案3的實現啊!提到的問題它都解決了,而且使用Python正好適合這個項目,一切來得太突然。
Git地址:https://github.com/10gen-labs/mongo-connector
但是配置的過程都搞了我很久,后文將整個過程記錄下來
四. 項目環境及工具版本
在本地測試,服務器:Windows7 32-bit
MongoDB:mongodb-win32-i386-2.4.5
Tomcat 6
Python:2.7.4
Solr:4.5.1
mongo-connector:沒有提供版本號
Python pysolr模塊
Python pymongo模塊
Python lxml模塊:lxml-3.2.3.win32-py2.7
可能還需要一些模塊,但由於我在之前已經安裝了,沒有列舉出來。如果運行的過程中報module not found,就去安裝吧~
五. Solr端准備
這里默認你已經部署Solr成功,詳細的部署過程自行Google。
這里主要是講述與本次測試相關的配置。
使用的是solr example中的multicore例子,以其中的core0為例子
schema.xml文件如下:修改_id與Mongo對應,只留下一個name字段,為String類型
其它的配置不需要修改
把它放到Tomcat中運行吧,檢查是否已經配置成功
六. MongoDB端准備
看到mongo-connector項目中的說明,
Since the connector does real time syncing, it is necessary to have MongoDB running, although the connector will work with both sharded and non sharded configurations. It requires a replica set setup.
就算我們開啟了oplog也不行,還需要在Mongo中啟動一個replica set
1. 配置replica set
(1)
我的MONGO_HOME為 D:\mongodb
目錄樹如下:
-rs (d)
|----db (d) mongo數據文件文件存放的目錄
|----rs1 (d) rs1實例數據文件存放的目錄
|----rs2 (d) rs2實例數據文件存放的目錄
|----log (d) log文件存放的目錄
|----rs1.log (f) rs1實例的log文件
|----rs2.log (f) rs2實例的log文件
|----mongod-rs1.bat rs1實例的啟動腳本
|----mongod-rs2.bat rs2實例的啟動腳本
mongod-rs1.bat內容如下:
D:\mongodb\bin\mongod --port 27001 --oplogSize 100 --dbpath db\rs1 --logpath log\rs1.log --replSet rs/127.0.0.1:27002 --journal
pause
mongod-rs2.bat內容如下:
D:\mongodb\bin\mongod --port 27002 --oplogSize 100 --dbpath db\rs2 --logpath log\rs2.log --replSet rs/127.0.0.1:27001 --journal
pause
(2)執行兩個腳本,啟動兩個mongod實例
(3)但這時它們還沒組成一個replica set,還需要進行配置,開啟mongo,連上localhost:27001,也就是實例rs1
至此,配置完成。
七. mongo-connector准備
如果是在mongo example中的multicore默認的配置上修改的話,訪問http://localhost:8080/solr/core0/admin/luke?show=Schema&wt=json
應該是能看到JSON形式的core0的schema
打開mongo_connector/doc_managers/solr_doc_manager.py
進行如下修改:1.從util引入verify_url;2. ADMIN_URL修改為獲取Solr核core0的JSON形式schema的URL的后半部分,因為要根據schema中的fields進行索引
在Solr多核的情況下啟動mongo-connector,會報出Solr URL訪問錯誤,它期望你傳入http://localhost:8080/solr,
但http://localhost:8080/solr/core0才是實際起作用的,因此我們需要傳入這個作為BASE_URL
解決辦法如下:屏蔽掉url檢查就行了
接下來就是啟動mongo-connector了,啟動命令如下:
-m Mongod實例的訪問路徑
-t Solr的BASE_URL
-o 記錄oplog處理時間戳的文件
-n mongo命名空間,是監聽哪個database哪個collection的設置,以逗號分隔多個命名空間,這里是監聽test庫中的test集合
-d 就是處理doc的py文件
啟動結果如下:說明你的配置已經成功了
八. 測試增量索引
先看看Solr中core0的狀態:現在是沒有記錄的,Num Docs為0
往MongoDB中插入一條數據:需要包含name字段,還記得我們上面的schema.xml嗎?
查看mongo-connector的輸出:update了一條記錄
看看Solr現在的狀態:我們看到了剛才插入的
嘗試刪除掉剛才那條記錄,connector輸出如下:多了一條update的記錄,這次是<delete>
再看看Solr的狀態:剛才那條記錄沒了,刪除成功!
九. 一些說明
mongo-connector會在oplog_progress.txt中記錄時間戳,可以在服務器宕機恢復后索引Mongo oplog新增的數據,記錄如下:
["Collection(Database(MongoClient([u'127.0.0.1:27001', u'127.0.0.1:27002']), u'local'), u'oplog.rs')", 5941530871067574273]
mongo-connector的代碼並不復雜,想想上面上面方案3怎么實現,那它就是怎么做的了。
有些地方還是要根據我們項目進行一些修改的。