0x00 開頭照例扯淡
自從各種脫褲門事件開始層出不窮,在下就學乖了,各個地方的密碼全都改成不一樣的,重要帳號的密碼定期更換,生怕被人社出祖宗十八代的我,甚至開始用起了假名字,我給自己起一新網名」興才」,這個看起來還不錯的名字,其實是我們家鄉罵人土話,意思是腦殘人士…. -_-|||額好吧,反正是假的,不要在意這些細節。
這只是名,至于姓氏麼,每個帳號的註冊資料那裡,照著百家姓上趙錢孫李周吳鄭王的依次往下排,什麼張興才、李興才、王興才……於是也不知道我這樣」興才」了多久,終於有一天,我接到一個陌生電話:您好,請問是馬興才先生嗎?
好麼,該來的終於還是來了,於是按名索驥,得知某某網站我用了這個名字,然後通過各種途徑找,果然,那破站被脫褲子了。
果斷Down了那個褲子,然後就一發不可收拾,走上了收藏褲子的不歸路,直到有一天,我發現收藏已經非常豐富了,粗略估計得好幾十億條數據,拍腦袋一想,這不能光收藏啊,我也搭個社工庫用吧……
0x01 介紹社工庫怎麼搭呢,這種海量數據的東西,並不是簡單的用mysql建個庫,然後做個php查詢select * from sgk where username like 『%xxxxx%』這樣就能完事的,也不是某些幼稚騷年想的隨便找個4g內存,amd雙核的破電腦就可以帶起來的,上面這樣的語句和系統配置,真要用於社工庫查詢,查一條記錄恐怕得半小時。好在這個問題早就被一種叫做全文搜尋引擎的東西解決了,更好的消息是,全文搜尋引擎大部分都是開源的,不需要花錢。
目前網上已經搭建好的社工庫,大部分是mysql+coreseek+php架構,coreseek基於sphinx,是一款優秀的全文搜尋引擎,但缺點是比較輕量級,一旦數據量過數億,就會有些力不從心,並且搭建集群做分布式性能並不理想,如果要考慮以後數據量越來越大的情況,還是得用其他方案,為此我使用了solr。
Solr的基礎是著名的Lucene框架,基於java,通過jdbc接口可以導入各種資料庫和各種格式的數據,非常適合開發企業級的海量數據搜索平臺,並且提供完善的solr cloud集群功能,更重要的是,solr的數據查詢完全基於http,可以通過簡單的post參數,返回json,xml,php,python,ruby,csv等多種格式。
以前的solr,本質上是一組servlet,必須放進Tomcat才能運行,從solr5開始,它已經自帶了jetty,配置的好,完全可以獨立使用,並且應付大量並發請求,具體的架構我們後面會講到,現在先來進行solr的安裝配置。
0x02 安裝和配置以下是我整個搭建和測試過程所用的硬體和軟體平臺,本文所有內容均在此平臺上完成:
軟體配置: solr5.5,mysql5.7,jdk8,Tomcat8 Windows10/Ubuntu14.04 LTS
硬體配置: i7 4770k,16G DDR3,2T西數黑盤
2.1 mysql資料庫Mysql資料庫的安裝和配置我這裡不再贅述,只提一點,對於社工庫這種查詢任務遠遠多於插入和更新的應用來說,最好還是使用MyISAM引擎。
搭建好資料庫後,新建一個庫,名為newsgk,然後創建一個表命名為b41sgk,結構如下:
id bigint 主鍵 自動增長
username varchar 用戶名
email varchar 郵箱
password varchar 密碼
salt varchar 密碼中的鹽或者第二密碼
ip varchar ip、住址、電話等其他資料
site varchar 資料庫的來源站點
接下來就是把收集的各種褲子全部導入這個表了,這裡推薦使用navicat,它可以支持各種格式的導入,具體過程相當的枯燥乏味,需要很多的耐心,這裡就不再廢話了,列位看官自己去搞就是了,目前我初步導入的數據量大約是10億條。
2.2 Solr的搭建和配置首先下載solr:
解壓縮:
安裝jdk8:
因為是java跨平臺的,Windows下和linux下solr是同一個壓縮包,windows下jdk的安裝這裡不再說明。
進入解壓縮後的solr文件夾的bin目錄,solr.cmd和solr分別是windows和linux下的啟動腳本:
因為社工庫是海量大數據,而jvm默認只使用512m的內存,這遠遠不夠,所以我們需要修改,打開solr.in.sh文件,找到這一行:
SOLR_HEAP="512m"
依據你的數據量,把它修改成更高,我這裡改成4G,改完保存. 在windows下略有不同,需要修改solr.in.cmd文件中的這一行:
set SOLR_JAVA_MEM=-Xms512m -Xmx512m
同樣把兩個512m都修改成4G。
Solr的啟動,重啟和停止命令分別是:
在linux下還可以通過install_solr_service.sh腳本把solr安裝為服務,開機後臺自動運行。
Solr安裝完成,現在我們需要從mysql導入數據,導入前,我們需要先創建一個core,core是solr的特有概念,每個core是一個查詢、數據,、索引等的集合體,你可以把它想像成一個獨立資料庫,我們創建一個新core:
在solr-5.5.0/server/solr子目錄下面建立一個新文件夾,命名為solr_mysql,這個是core的名稱,在下面創建兩個子目錄conf和data,把solr-5.5.0/solr-5.5.0/example/example-DIH/solr/db/conf下面的所有文件全部拷貝到我們創建的conf目錄中.接下來的配置主要涉及到三個文件, solrconfig.xml, schema.xml和db-data-config.xml。
首先打開db-data-config.xml,修改為以下內容:
這個文件是負責配置導入數據源的,請按照mysql實際的設置修改datasource的內容,下面entity的內容必須嚴格按照mysql中社工庫表的結構填寫,列名要和資料庫中的完全一樣。
然後打開solrconfig.xml,先找到這一段:
把它全部注釋掉,加上一行,改成這樣:
這是因為solr5 以上默認使用managed-schema管理schema,需要更改為可以手動修改。
然後我們還需要關閉suggest,它提供搜索智能提示,在社工庫中我們用不到這樣的功能,重要的是,suggest會嚴重的拖慢solr的啟動速度,在十幾億數據的情況下,開啟suggest可能會導致solr啟動加載core長達幾個小時!
同樣在solrconfig.xml中,找到這一段:
把這些全部刪除,然後保存solrconfig.xml文件。
接下來把managed-schema拷貝一份,重命名為schema.xml (原文件不要刪除),打開並找到以下位置:
只保留_version_和_root_節點,然後把所有的field,dynamicField和copyField全部刪除,添加以下的部分:
這裡的uniqueKey是配置文件中原有的,用來指定索引欄位,必須保留。新建了一個欄位名為keyword,它的用途是聯合查詢,即當需要同時以多個欄位做關鍵字查詢時,可以用這一個欄位名代替,增加查詢效率,下面的copyField即用來指定複製哪些欄位到keyword。注意keyword這樣的欄位,後面的multiValued屬性必須為true。
username和email以及keyword這三個欄位,用來檢索查詢關鍵字,它們的類型我們指定為text_ik,這是一個我們創造的類型,因為solr雖然內置中文分詞,但效果並不好,我們需要添加IKAnalyzer中文分詞引擎來查詢中文。在https://github.com/EugenePig/ik-analyzer-solr5下載IKAnalyzer for solr5的源碼包,然後使用Maven編譯,得到一個文件IKAnalyzer-5.0.jar,把它放入solr-5.5.0/server/solr-webapp/webapp/WEB-INF/lib目錄中,然後在solrconfig.xml的fieldType部分加入以下內容:
保存後,core的配置就算完成了,不過要導入mysql數據,我們還需要在mysql網站上下載mysql-connector-java-bin.jar庫文件,連同solr-5.5.0/dist目錄下面的solr-dataimporthandler-5.5.0.jar,solr-dataimporthandler-extras-5.5.0.jar兩個文件,全部拷貝到solr-5.5.0/server/solr-webapp/webapp/WEB-INF/lib目錄中,然後重啟solr,就可以開始數據導入工作了。
2.3 數據導入確保以上配置完全正確且solr已經運行,打開瀏覽器,訪問http://localhost:8983/solr/#/ ,進入solr的管理頁面,點擊左側Core Admin,然後Add Core:
name和instanceDir都填寫前面我們命名的core名稱solr_mysql,點擊add core後,稍等片刻,core就建立完成了。此時在左邊的下拉菜單選擇創建的core,然後進一步選擇Dataimport項,按照如下設置:
點擊Execute,就會開始從mysql導入數據,選中Auto-Refresh Status會自動刷新進度,接下來就是漫長的等待……
導入完成後,我們就可以開始查詢了,solr的查詢全部使用post參數,比如:
http://localhost:8983/solr/solr_mysql/select?q=keyword:12345678&start=10&rows=100&wt=json&indent=true
因為前面已經建立了複合欄位keyword,所以這裡我們直接用keyword:12345678會自動查找username和email中包含12345678的所有結果,start=10&rows=100指定查詢結果返回第11行到第110行的內容,因為solr採用的是分頁查詢,wt=json指定查詢結果是json格式的,還可以是xml、php、python、ruby以及csv。
上圖返回結果中的numfound:111892代表一共返回的結果數,不指定 start和rows的情況下默認只顯示前十個結果。還需要注意IKAnalyzer引擎的幾個問題,在以純數字或者純字母關鍵字查詢時,IKAnalyzer會返回正確的結果,但在查詢數字字母混合關鍵字時,需要在後面加*號,查詢漢字時.默認會進行分詞,即把一段關鍵字分成幾個詞查詢,而社工庫必須精確查詢,所以漢字查詢必須給關鍵字加雙引號。
到這一步,如果只是搭建一個本地庫,供自己使用,那麼我們接下來只需寫一個查詢程序,post關鍵字,然後顯示返回的結果即可,比如這樣:
秒查,速度非常快,但如果要架設成伺服器,提供給其他人使用,我們還有很多工作要做。
0x03 伺服器架構和數據增量更新儘管solr現在已經自帶了jetty,jetty並不弱於tomcat,且沒有後者那麼臃腫,但是很多人在構建web應用時還是喜歡用以前的習慣,把solr整合進tomcat,然後和後臺程序一鍋亂燉,坦白說,在下並不喜歡這樣的架構,對於大數據應用來說,各個功能組件各自獨立,互相配合遠比大雜燴要有效率和易於維護的多,所以,我理想中的社工庫查詢伺服器,應該是以下的架構:
以上架構中,mysql只負責存儲整理好的數據,並不提供查詢服務,整理和導入新資料庫時,只需操作mysql,solr利用自帶的jetty獨立運行,定期從mysql導入增量更新的數據,Tomcat作為應用伺服器,運行提供查詢的servlet應用,此應用通過http向solr post數據並獲取結果,返回給前端頁面,相互獨立又相輔相成。
並且,solr並不依賴於mysql,它本身就是資料庫可以獨立運行,而社工庫這種東西,並不是經常有新數據的,獲取新數據的間隔可能很長,所以上面的定時增量更新可以改為手動增量更新,沒有新數據時mysql完全可以關閉以節約資源。
那麼我們先開始著手增量更新的設置,我們現在已有的數據表b41sgk並不動,在此基礎上建立一個和b41sgk結構基本相同的表b41new,不同之處是增加了一個欄位updatetime,用來自動存儲添加數據的時間,以後的新數據全寫入這個表,b41sgk不再做更新:
CREATE TABLE b41new (
id bigint NOT NULL auto_increment,
username varchar(255),
email varchar(255),
password varchar(255),
salt varchar(255),
ip varchar(255),
site varchar(255),
updatetime timestamp NOT NULL ON UPDATE CURRENT_TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY ('id')
)
用和前面同樣的步驟建立一個core命名為solr_newsgk,在db-data-config.xml中做如下設置:
last_index_time和delta.id是兩個自動變化的參數,分別記錄最後一次導入數據的時間和已導入的最大id值,存儲於當前core的conf目錄下dataimporter.properties文件中,以上設置保存後,提交如下連結:
http://localhost:8983/solr/solr_newsgk/dataimport?command=delta-import&clean=false&commit=true
如果此時數據表b41new中已經添加了新數據,就會自動增量同步到solr中,如果要每天定時自動增量更新,執行:
增加一條:
保存後執行:
Solr就會在每天的零時自動增量導入數據,如果是windows系統,可以利用powershell和計劃任務達到同樣的目的。
現在我們的伺服器搭建還剩下最後一件事:既然現在我們準備把solr查詢提供給別人用,那麼問題來了,我們只希望別人通過tomcat裡的servlet查詢,而不希望直接調用solr,我們需要屏蔽外部查詢:
如果把solr和tomcat放在不同的伺服器,只需把127.0.0.1改成tomcat伺服器的ip地址。接下來保存:
編輯/etc/network/interfaces,加入下面這行:
pre-up iptables-restore < /etc/iptables.up.rules
0x04 編寫查詢應用Solr除了可以通過http post數據來查詢之外,還提供了一套完整的api solrj,其實solrj底層還是通過http訪問的,但如果你是用java開發,使用它會比直接http訪問方便的多。
我們啟動eclipse,配置好和tomcat的連接,新建一個項目sgk,在構建路徑中添加solr-5.5.0/dist/solrj-lib下的全部jar包,然後添加solr-5.5.0/server/lib/ext中的log4j-1.2.17.jar,slf4j-api-1.7.7.jar,slf4j-log4j12-1.7.7.jar三個包。
在web.xml中添加如下設置:
新建一個servlet命名為searcher:
<查看更多代碼,閱讀原文>
編譯運行,提交http://localhost:8080/sgk/searcher?keyword=xxxxxxxxxxxx這樣的地址,就會返回json數據,接下來只需寫一個前端頁面,解析並顯示這些結果即可.
0x05尾聲終於全都完成了,我在把狐朋狗友加基友加妹紙在社工庫裡查了個遍之後,心滿意足的睡覺去了,夢中,我經過一條小河上的獨木橋,剛走到橋正中,突然腰帶一松,褲子掉進了河裡,我正在發愁的時候,河面上冒出了一個猥瑣的河神,手中拿著三件東西笑眯眯的問我:
「善良的少年喲! 你掉的,是這條金褲子,還是這條銀褲子,還是這個裝滿了褲子的硬碟呢?」
「哈哈哈哈哈哈哈…..」
我對著河神哈哈大笑,笑著笑著,猛然從夢中醒來,睜眼一看,已經天光大亮了,回頭看床頭時鐘,媽蛋! 上班要遲到了!!!
*作者b41k3r,本文屬FreeBuf原創獎勵計劃,未經許可禁止轉載