天天看點

Solr與MySQL查詢性能對比

本文簡單對比下solr與mysql的查詢性能速度。

測試資料量:10407608     num docs: 10407608

這裡對mysql的查詢時間都包含了從mysql server擷取資料的時間。

在項目中一個最常用的查詢,查詢某段時間内的資料,sql查詢擷取資料,30s左右

對collecttime建立索引後,同樣的查詢,2s,快了很多。

solr索引資料:

solr查詢,同樣的條件,72ms

"status": 0,

    "qtime": 72,

    "params": {

      "indent": "true",

      "q": "collecttime:[2014-12-06t00:00:00.000z to 2014-12-10t21:31:55.000z]",

      "_": "1434617215202",

      "wt": "json"

    }

好吧,查詢性能提高的不是一點點,用solrj代碼試試:

Solr與MySQL查詢性能對比
Solr與MySQL查詢性能對比

solrj查詢并擷取結果集,結果集大小為220296,傳回5個字段,時間為12s左右。

為什麼需要這麼長時間?上面的"qtime"隻是根據索引查詢的時間,如果要從solr服務端擷取查詢到的結果集,solr需要讀取stored的字段(磁盤io),再經過http傳輸到本地(網絡io),這兩者比較耗時,特别是磁盤io。

時間對比:

查詢條件

時間

mysql(無索引)

30s

mysql(有索引)

2s

solrj(select查詢)

12s

如何優化?看看隻擷取id需要的時間:

sql查詢隻傳回id,沒有對collecttime建索引,10s左右:

sql查詢隻傳回id,同樣的查詢條件,對collecttime建索引,0.337s,很快。

solrj查詢隻傳回id,7s左右,快了一點。

    id size: 220296

    time: 7340

查詢條件(隻擷取id)

10s

0.337s

7s

繼續優化。。

關于solrj擷取大量結果集速度慢的一些類似問題:

http://stackoverflow.com/questions/28181821/solr-performance#

http://grokbase.com/t/lucene/solr-user/11aysnde25/query-time-help

http://lucene.472066.n3.nabble.com/solrj-performance-bottleneck-td2682797.html

這個問題沒有好的解決方式,基本的建議都是做分頁,但是我們需要拿到大量資料做一些比對分析,做分頁沒有意義。

偶然看到一個回答,solr預設的查詢使用的是"/select" request handler,可以用"/export" request handler來export結果集,看看solr對它的說明:

it's possible to export fully sorted result sets using a special rank query parser and response writer  specifically designed to work together to handle scenarios that involve sorting and exporting millions of records. this uses a stream sorting techniquethat begins to send records within milliseconds and continues to stream results until the entire result set has been sorted and exported.

solr中已經定義了這個requesthandler: 

Solr與MySQL查詢性能對比
Solr與MySQL查詢性能對比

使用/export需要字段使用docvalues建立索引:

使用docvalues必須要有一個用來sort的字段,且隻支援下列類型:

sort fields must be one of the following types: int,float,long,double,string

docvalues支援的傳回字段:

export fields must either be one of the following types: int,float,long,double,string

使用solrj來查詢并擷取資料:

Solr與MySQL查詢性能對比
Solr與MySQL查詢性能對比

一個bug:

org.apache.solr.client.solrj.impl.httpsolrclient$remotesolrexception: error from server at http://192.8.125.30:8985/solr/hotspot: expected mime type application/octet-stream but got application/json. 

solrj沒法正确解析出結果集,看了下源碼,原因是solr server傳回的contenttype和solrj解析時檢查時不一緻,solrj的binaryresponseparser這個content_type是定死的:

一時半會也不知道怎麼解決這個bug,還是自己寫個http請求并擷取結果吧,用httpclient寫了個簡單的用戶端請求并解析json擷取資料,測試速度:

Solr與MySQL查詢性能對比
Solr與MySQL查詢性能對比

同樣的查詢條件擷取220296個結果集,時間為2s左右,這樣的查詢擷取資料的效率和mysql建立索引後的效果差不多,暫時可以接受。

為什麼使用docvalues的方式擷取資料速度快?

docvalues是一種按列組織的存儲格式,這種存儲方式降低了随機讀的成本。

傳統的按行存儲是這樣的:

Solr與MySQL查詢性能對比

1和2代表的是docid。顔色代表的是不同的字段。

改成按列存儲是這樣的:

Solr與MySQL查詢性能對比

按列存儲的話會把一個檔案分成多個檔案,每個列一個。對于每個檔案,都是按照docid排序的。這樣一來,隻要知道docid,就可以計算出這個docid在這個檔案裡的偏移量。也就是對于每個docid需要一次随機讀操作。

那麼這種排列是如何讓随機讀更快的呢?秘密在于lucene底層讀取檔案的方式是基于memory mapped byte buffer的,也就是mmap。這種檔案通路的方式是由作業系統去緩存這個檔案到記憶體裡。這樣在記憶體足夠的情況下,通路檔案就相當于通路記憶體。那麼随機讀操作也就不再是磁盤操作了,而是對記憶體的随機讀。

那麼為什麼按行存儲不能用mmap的方式呢?因為按行存儲的方式一個檔案裡包含了很多列的資料,這個檔案尺寸往往很大,超過了作業系統的檔案緩存的大小。而按列存儲的方式把不同列分成了很多檔案,可以隻緩存用到的那些列,而不讓很少使用的列資料浪費記憶體。

注意export fields隻支援int,float,long,double,string這幾個類型,如果你的查詢結果隻包含這幾個類型的字段,那采用這種方式查詢并擷取資料,速度要快很多。

下面是solr使用“/select”和“/export”的速度對比。

solrj(export查詢)

項目中如果用分頁查詢,就用select方式,如果一次性要擷取大量查詢資料就用export方式,這裡沒有采用mysql對查詢字段建索引,因為資料量每天還在增加,當達到億級的資料量的時候,索引也不能很好的解決問題,而且項目中還有其他的查詢需求。

我們來看另一個查詢需求,假設要統計每個裝置(deviceid)上資料的分布情況:

用sql,需要33s:

同樣的查詢,在對collecttime建立索引之後,隻要14s了。

看看solr的facet查詢,隻要540ms,快的不是一點點。

Solr與MySQL查詢性能對比
Solr與MySQL查詢性能對比

查詢條件(統計)

33s

14s

solrj(facet查詢)

0.54s

如果我們要查詢某台裝置在某個時間段上按“時”、“周”、“月”、“年”進行資料統計,solr也是很友善的,比如以下按天統計裝置号為1013上的資料:

Solr與MySQL查詢性能對比
Solr與MySQL查詢性能對比

這裡為什麼solr/lucene的facet(聚合)查詢會這麼快呢?

想想solr/lucene的索引資料的方式就清楚了:反向索引。對于某個索引字段,該字段下有哪幾個值,對于每個值,對應的文檔集合是建立索引的時候就清楚的,做聚合操作的時候“統計”下就知道結果了。

如果通過docvalues建立索引,對于這類facet查詢會更快,因為這時候索引已經通過字段(列)分割好了,隻需要去對應檔案中查詢統計就行了,如上文所述,通過“記憶體映射”,将該索引檔案映射到記憶體,隻需要在記憶體裡統計下結果就出來了,是以就非常快。

水準拆分表:

由于本系統采集到的大量資料和“時間”有很大關系,一些業務需求根據“時間”來查詢也比較多,可以按“時間”字段進行拆分表,比如按每月一張表來拆分,但是這樣做應用層代碼就需要做更多的事情,一些跨表的查詢也需要更多的工作。綜合考慮了表拆分和使用solr來做索引查詢的工作量後,還是采用了solr。

總結:在mysql的基礎上,配合lucene、solr、elasticsearch等搜尋引擎,可以提高類似全文檢索、分類統計等查詢性能。

參考:

http://wiki.apache.org/solr/

https://lucidworks.com/blog/2013/04/02/fun-with-docvalues-in-solr-4-2/

本文版權歸作者和部落格園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接配接,否則保留追究法律責任的權利。

http://www.cnblogs.com/luxiaoxun/p/4696477.html