Scaling Solr(Solr的擴展) solr優化深刻學習前端
Solr 的擴展 (Scaling)java
當你的索引數量愈來愈大,你會發現你的搜索響應時間變得更慢,索引新內容的時間也會愈來愈長,那麼,到了作出一些改變的時候了,幸運的是, solr 很好的考慮到了這些狀況,你只須要改變你的配置就能夠了。算法
如下將從三個方面講述 solr 的 scaling :shell
l 調優某個 Solr 服務器 (Scale High)數據庫
經過緩存和內存管理優化某個單實例的 Solr 。將 Solr 部署到一個擁有快速的 CPU 和硬件的專用服務器,經過調優,最大化的將單個服務器的性能達到最高。apache
l 使用多 Solr 服務器 (Scale Wide)緩存
使用多 Solr 服務器。若是你的 avgTimePerRequest 參數在你可接受的範圍內(數據量通常在數百萬),那麼能夠經過配置將你的 master 上的索引完整地複製到 slave 機器上;若是你的查詢已經很慢,那麼使用分片來說你的單個查詢的負載分發到多個 Solr 服務器上。服務器
l 使用複製 (replication) 和分片 (sharding)(Scale Deep)session
當你的數據量足夠大,你須要同時使用複製和分片,那麼每一個分片將對應一個 master 和若干 slave ,這將是一個最複雜的架構。多線程
咱們將會對三個性能參數進行優化:
l TPS(Transaction Per Second) 每秒事務處理量,能夠查看 http://localhost:8983/solr/mbtracks/admin/stats.jsp 或者查看 requesHandler 的 avgTimePerRequest 和 avgRequestsPerSecond 參數。
l CPU Usage CPU 使用狀況,在 Windows 下可使用 PerfMon 得到 CPU 使用的相關信息,而在 Unix 類操做系統上使用 top 。
l Memory Usage 內存使用狀況,可使用 PrefMon 、 top 和 jConsole 來查看。
接下來將會介紹對於 Solr 的 scaling 。
調優某個 Solr 服務器 (Scale High)
Solr 提供了一系列可選的配置來加強性能,具體怎麼使用將取決於你的應用程序。下面將對其中最經常使用的進行介紹
JVM 配置
Solr 運行在 JVM 之上,所以對 JVM 的調優將直接影響 Solr 的性能,不過對於 JVM 參數的改變要慎重,由於,極可能一丁點改變會引起很大的問題。
能夠在啓動的時候指定 JVM 參數:
java -Xms512M -Xmx1024M -server -jar start.jar
你的 Xmx 參數應當爲你的操做系統以及運行在服務器上的其餘進程預留足夠的內存,好比你有 4G 的索引文件,你能夠指定 6G 的 RAM (並指定較大的緩存)那麼你就能取得比較好的性能。
另外,在可能的狀況下,儘可能使用版本較高的 Java 版本,由於新版本的 Java 虛擬機性能愈來愈好。
HTTP 緩存
由於 Solr 的許多操做都是基於 HTTP 的,所以 Solr 對 HTTP 緩存有很大的支持。若是你想使用 HTTP 緩存,那麼你須要在 solrconfig.xml 中作以下配置:
<httpCaching lastModifiedFrom="openTime" etagSeed="Solr" never304="false">
<cacheControl>max-age=43200, must-revalidate</cacheControl>
</httpCaching>
默認狀況下, Solr 是不使用 304 not modified 狀態給客戶端的,而是始終返回 200 OK ,上面的配置指明 max-age 是 43200 秒。下面是例子:
>> curl -v http://localhost:8983/solr/mbartists/select/?q=Smashing+Pumpkins
< HTTP/1.1 200 OK
< Cache-Control: max-age=43200
< Expires: Thu, 11 Jun 2009 15:02:00 GMT
< Last-Modified: Thu, 11 Jun 2009 02:55:39 GMT
< ETag: "YWFkZWIyNjVmODgwMDAwMFNvbHI="
< Content-Type: text/xml; charset=utf-8
< Content-Length: 1488
< Server: Jetty(6.1.3)
很顯然, HTTP 緩存配置生效了,那麼,咱們也能夠指定 If-modified-since 參數,這樣服務器會比較,若是在最新更改時間以後,那麼服務器會返回最新數據。
>>curl -v -z "Thu, 11 Jun 2009 02:55:40 GMT"
http://localhost:8983/solr/mbartists/select/?q=Smashing+Pumpkins
* About to connect() to localhost port 8983 (#0)
* Trying ::1... connected
* Connected to localhost (::1) port 8983 (#0)
> GET /solr/mbartists/select/?q=Smashing+Pumpkins HTTP/1.1
> User-Agent: curl/7.16.3 (powerpc-apple-darwin9.0) libcurl/7.16.3
OpenSSL/0.9.7l zlib/1.2.3
> Host: localhost:8983
> Accept: */*
> If-Modified-Since: Thu, 11 Jun 2009 02:55:40 GMT
>
< HTTP/1.1 304 Not Modified
< Cache-Control: max-age=43200
< Expires: Thu, 11 Jun 2009 15:13:43 GMT
< Last-Modified: Thu, 11 Jun 2009 02:55:39 GMT
< ETag: "YWFkZWIyNjVmODgwMDAwMFNvbHI="
< Server: Jetty(6.1.3)
Entity tag 也是一種新的方法來進行鑑別,它比使用 last modified date 更加的強健和靈活。 ETag 是一個字符串。在 Solr 的索引更新之後,當前的 ETag 會隨之改變。
Solr 緩存
Solr 爲緩存使用了 LRU 算法,緩存存放在內存中,緩存和 Index Searcher 關聯在一塊兒,維持了一個數據的快照 (a snapshot view of data). 在一個 commit 以後,新的 index searcher 打開,並會自動預熱 (auto-warmed). 自動預熱指的是以前搜索的緩存會被拷貝到新的 searcher 。接着,預先在 solrconfig.xml 中定義的 searcher 會運行。爲那些須要排序的字段 (field) 加入一些典型的 query 到 newSearcher 和 firstSearcher ,這樣,新的 searcher 就能爲新的搜索提供服務了。
Solr1.4 使用了 FastLRUCache, 它比 LRUCache 要更快,由於它無需單獨的線程來移除無用的 items 。
經過 Solr 的 statistics 頁面,你能夠看到你的緩存有多大,而且能夠根據實際狀況對緩存的大小進行調整以適應最新的狀況。
設計更好的 Schema
你須要考慮是否 indexed ,是否 stored 等等,這些將決定於你應用程序的具體狀況。若是你存儲很大的文本到你的索引中,你最好使用 field 的 compressed 選項配置對其進行壓縮。若是你不是總須要讀取全部的 fields ,那麼在 solrconfig.xml 中配置使用 field 延遲加載: <enableLazyFieldLoading>true</enableLazyFieldLoading>
這會起到很好的做用。
注意:若是你使用了 compressed ,那麼你可能須要使用 field 延遲加載,同時還要下降解壓縮的代價。另外下降文本分析的數量將有效提升性能,由於文本分析會消耗大量的 CPU 時間,而且使得你的索引大幅增大。
索引策略
一種加速索引的方式是分批索引,這樣將會顯著加速性能。可是,隨着你的 document 增長,性能仍是會開始降低。根據經驗,對於大的 document ,每批索引 10 個,而對於小的 document ,每批索引 100 個,並分批提交。
另外,使用多線程進行索引將會再次提升性能。
取消 document 惟一性檢查 (Disable unique document check)
默認狀況下,索引的時候 Solr 會檢查主鍵是否有重複的,以免不一樣的 document 使用相同的主鍵。若是你確認你的 document 不會有重複的主鍵,將參數 allowDups=true 加到 url 上能夠取消檢查,對於 scv 文檔,使用 overwrite=false 。
Commit/optimize 因子 ( factors)
對於大的索引以及頻繁的更新,使用較大的 mergeFactor ,它決定了 Lucene 會在 segments 數量達到多少時將它們合併 (merge) 。
優化 Faceting( 分組查詢 ) 的性能
使用 Term Vectors
Term Vectors 是某 field 經文本分析以後的一系列 terms 。它通常包括了 term 的頻率, document 的頻率和在文本中的數值偏移量,啓用它有可能會加強 MoreLikeThis 查詢和 Hignlight 查詢的性能。
可是啓用 tern vectors 會增長索引的大小,而且可能根本不會在 MoreLikeThis 和 Highlight 查詢結果中。
提高 phrase 查詢的性能
在大索引的查詢中, phrase 查詢的性能會很慢,由於,某個 phrase 可能會出如今不少的 document 中,一種解決辦法是使用 filter 過濾掉諸如「 the 」這樣沒有意義的詞語。可是這樣會使得搜索出現歧義,解決方案是使用 Shingling ,它使用相似 n-gram 的方法將搜索句子切分,如「 The quick brown fox jumped over the lazy dog 」將會變爲 "the quick", "quick brown",
"brown fox", "fox jumped", "jumped over", "over the", "the lazy", "lazy dog". 粗糙的測試代表,這樣至少能夠提升 2-3 倍的性能。
使用多 Solr 服務器 (Scale wide)
當你對單臺 Solr 服務器的調優仍然沒法知足性能需求的時候,接下來你應該考慮拆分查詢請求到不一樣的機器上,具有橫向擴展 (Scale wide) 是可擴展系統的最基本的特色,所以, solr 也具有了該特色。
Script VS Java replication
在 Solr1.4 以前, replication 是經過使用 Unix 腳本進行的。通常來講,這種方案還算不錯,可是可能有一些複雜了,須要編寫 shell 腳本, cron jobs 和 resync daemon 。
從 1.4 開始, Solr 實現了基於 Java 的複製策略,不用再編寫複雜的 shell 腳本,而且運行得更快。
Replication 的配置在 solrconfig.xml 之中,而且配置文件自己能夠在 master 和 slave 服務器之間被複制。 Replication 目前已經支持 Unix 和 Windows 系統而且已經集成到了 Admin interface 之中。 Admin interface 目前能夠控制複製 --- 例如,強制開始 replication 或者終止失效( stalled )的複製。複製是經過 ReplicationHandler 提供的 REST API 進行的。
開始體驗多 Solr 服務器
若是你在多個 Solr 服務器之間使用了同一個 solrconfig.xml 文件,那麼你須要在啓動的時候指定如下幾個參數:
l -Dslave=disabled :指定當前 solr 服務器是 Master 。 Master 將負責推送索引文件到全部 slave 服務器。你將會存儲 document 到 master 上,而在 slave 服務器上進行查詢。
l -Dmaster=disabled :指定當前 solr 服務器是 Slave 。 Slave 要麼按期輪詢 Master 服務器來更新索引,要麼手動的經過 Admin interface 觸發更新操做。一組 slave 將會被負載均衡(多是 HAProxy 之類)器管理着來對外提供搜索。
若是你想在同一機器上運行多個 Solr 服務器,那麼你須要經過 -Djetty.port=8984 指定不一樣的端口,而且經過 -Dsolr.data.dir=./solr/data8984 指定不一樣的 data 目錄。
配置 Replication
配置 replication 很簡單,在 ./examples/cores/mbreleases/solrconfig.xml 中就有示例配置 :
<requestHandler name="/replication" class="solr.ReplicationHandler" >
<lst name="${master:master} ">
<str name="replicateAfter">startup</str>
<str name="replicateAfter">commit</str>
<str name="confFiles">stopwords.txt</str>
</lst>
<lst name="${slave:slave}">
<str name="masterUrl">http://localhost:8983/solr/replication</str>
<str name="pollInterval">00:00:60</str>
</lst>
</requestHandler>
注意 ${} 將可以運行期進行配置,它將經過 -Dmaster=disabled 或 -Dslave=disabled 決定這裏的參數是 master 仍是 slave 。 Master 機器已經配置了在每次 commit 以後進行 replication 。而且可經過 confFiles 屬性以指定複製配置文件。複製配置文件很是有用,由於你能夠在運行期修改配置而無需從新部署。在 master 上修改配置文件, replication 到 slave 後, Slave 將會知道配置文件被修改了,並 reload core 。
能夠參考 http://wiki.apache.org/solr/SolrReplication
Replication 的實現
Master 是感知不到 Slave 的存在的, Slave 會週期性的輪詢 Master 來查看當前的索引版本。若是 Slave 發現有新的版本,那麼 Slave 啓動複製進程。步驟以下:
1. Slave 發出一個 filelist 命令來收集文件列表。這個命令將返回一系列元數據( size , lastmodified , alias 等等)
2. Slave 查看它本地是否有這些文件,而後它會開始下載缺失的文件 ( 使用命令 filecontent) 。若是鏈接失敗,則下載終止。它將重試 5 次,若是仍然失敗則放棄。
3. 文件被下載到了一個臨時目錄。所以,下載中途出錯不會影響到 slave 。
4. 一個 commit 命令被 ReplicationHandler 執行,而後新的索引被加載進來
跨多個 Slave 的分佈式搜索
索引一些文件到 Master 上
你能夠用 SSH 運行兩個 session ,一個開啓 Solr 服務,另外一個索引一些文件:
>> curl http://localhost:8983/solr/mbreleases/update/csv -F f.r_
attributes.split=true -F f.r_event_country.split=true -F f.r_event_
date.split=true -F f.r_attributes.separator=' ' -F f.r_event_country.
separator=' ' -F f.r_event_date.separator=' ' -F commit=true -F stream.
file=/root/examples/9/mb_releases.csv
上面的命令索引了一個 csv 文件。你能夠經過 Admin interface 監控這個操做。
配置 Slave
以前已經索引了文件,而且經過複製已經到了 slave 上,接下來,須要使用 SSH 到 slave 機器上,配置 masterUrl 以下:
<lst name="${slave:slave}">
<str name="masterUrl">
http://ec2-67-202-19-216.compute-1.amazonaws.com:8983/solr/mbreleases/replication
</str>
<str name="pollInterval">00:00:60</str>
</lst>
你能夠到 Admin interface 上查看當前的 replication 情況。
分發搜索請求到不一樣的 Slave 上
因爲使用了多個 Slave ,因此咱們沒有一個固定的請求 URl 給客戶的,所以,咱們須要使用負載均衡,這裏使用了 HAProxy 。
在 master 機器上,配置 /etc/haproxy/haproxy.cfg ,將你的 salve 機器的 url 放入:
listen solr-balancer 0.0.0.0:80
balance roundrobin
option forwardfor
server slave1 ec2-174-129-87-5.compute-1.amazonaws.com:8983
weight 1 maxconn 512 check
server slave2 ec2-67-202-15-128.compute-1.amazonaws.com:8983
weight 1 maxconn 512 check
solr-balancer 處理器將會監聽 80 端口,並將根據權重將請求重定向到每一個 Slave 機器,運行
>> service haproxy start
來啓動 HAProxy 。
固然, SolrJ 也提供了 API 來進行負載均衡, LBHttpSolrServer 須要客戶端知道全部 slave 機器的地址,而且它沒有 HAProxy 這樣的強健,由於它在架構上實現得很簡略。能夠參考:
http://wiki.apache.org/solr/LBHttpSolrServer
索引分片 (Sharding indexes)
Sharding 是一種當你的數據太多時很廣泛的對單臺數據庫進行擴展的策略。在 Solr 中, sharding 有兩種策略,一是將一個單一的 Solr core 分紅多個 Solr 服務器,二是將單核的 Solr 變成多核的。 Solr 具有將單一的查詢請求分發到多個 Solr shards 上,並彙集結果到一個單一的 result 裏返回調用者。
當你的查詢在單臺服務器上執行太慢時你就須要組合多臺 solr 服務器的能力來共同完成一個查詢。若是你的查詢已經足夠快了,而你只是想擴展覺得更多用戶服務,那麼建議你使用全索引而使採用 replication 的方法。
使用 Sharding 並非一個徹底透明的操做。關鍵的約束就是當索引一個 document ,你須要決定它應當在哪一個 Shards 上。 Solr 對於分佈式索引並無相關的邏輯支持。那麼當你搜索的時候你須要加上 shards 參數到 url ,以肯定須要到哪些 shards 上收集結果。這意味着客戶端須要知道 Solr 的架構。另外,每一個 document 須要一個惟一的 id ,由於你是基於行將其拆分的,須要一個 id 來區分彼此。
分發 documents 到 shards
一種比較好的辦法是對 id 進行 hash ,在模分片的大小來決定應當分發到哪一個 shards 上:
SHARDS = ['http://ec2-174-129-178-110
.compute-1.amazonaws.com:8983/solr/mbreleases',
'http://ec2-75-101-213-59
.compute-1.amazonaws.com:8983/solr/mbreleases']
unique_id = document[:id]
if unique_id.hash % SHARDS.size == local_thread_id
# index to shard
end
這樣,在你的 shards 不變化的狀況下,你的 document 將會很好的找到它的 shards 。
跨多個 shards 搜索 (Searching across shards)
這個功能是已經配置在 query request handler 裏面的。所以你無需作額外的配置,若是你想在兩個 shards 上面進行查詢,那麼你只須要在 url 跟相關的參數便可:
>> http://[SHARD_1]:8983/solr/select?shards=ec2-174-129-178-110.
compute-1.amazonaws.com:8983/solr/mbreleases,ec2-75-101-213-59.compute-
1.amazonaws.com:8983/solr/mbreleases&indent=true&q=r_a_name:Joplin
注意 shards 後的參數不能跟 http 這樣的傳輸協議,而且你能夠跟儘可能多的 shards ,只要你不超過 GET URL 的最大字符數 4000.
在使用 Shards 的時候,請務必注意如下內容:
l Shards 僅僅支持如下組件 (Component):Query,faceting,Hignlighting,Stats 和 Debug
l 每一個 document 必須有一個惟一的 id 。 Solr 是根據這個來合併搜索結果 document 的。
l 若是多個 shards 返回了相同 id 的 document ,那麼第一個會被選中,而餘下的被忽略。
聯合使用 Replication 和 Shards(Scale Deep)
若是你使用了前面的方法,仍然發現性能沒法知足要求,那麼是到了將兩個聯合起來組成更高層次的架構來達到你的須要的時候了。
你須要使用一樣的方法配置一組 Master ,這樣最終你將有一棵樹同樣的 Masters 和 Slaves 。你甚至能夠有一個專用的 Solr 服務器,它沒有索引,只負責將查詢分發的 shards 上,並在最後合併結果返回用戶。
數據的更新將經過在 Master 機器上更新並 replication 到 slave 機器上實現。前端須要相關的負載均衡支持,可是這樣一來, Solr 將可以處理極大的數據。
關於 Solr 的下一步 scaling ,在 Solr 的郵件列表裏面已經討論了不少次,一些調查研究顯示, Hadoop 可以提供強健可靠的文件系統。另外一個有趣的項目是 ZooKeeper ,它將能對分佈式系統進行集中式的管理,對於將 ZooKeeper 集成進來已經有很多努力。
參考文件 :
Solr 1.4 Enterprise Search Server.pdf
http://wiki.apache.org/solr/首頁