在介紹Elasticsearch的用法之前先講講為什么要用它吧。首先學習搜索引擎,肯定不可避免的都聽過lucene,solr和Elasticsearch都是基於它的。spinx文章很多,但是數據庫的入侵性太強(插件模式)。Elasticsearch是當下最流行的分布式搜索引擎之一。solr也稍微玩過,文章也多。同時也希望能通過Elasticsearch進一步學習完善自己對於分布式的學習。更深入的同學可以考慮開始學習ELK(Elasticsearch, Logstash, Kibana)。
推薦:《Elasticsearch-definitive-guide》
看過這本書之后入門就成了很簡單的事,書很淺顯易懂,沒什么特別難的地方。
其中有一個例子寫的非常好,可以作為全局引導大家學習,摘抄一部分過來:
我們首先要做的是存儲員工數據,每個文檔代表一個員工。在Elasticsearch中存儲數據的行為就叫做索引(indexing),不過在索引之前,我們需要明確數據應該存儲在哪里。
在Elasticsearch中,文檔歸屬於一種類型(type),而這些類型存在於索引(index)中,我們可以畫一些簡單的對比圖來類比傳統關系型數據庫:
Relational DB -> Databases -> Tables -> Rows -> Columns Elasticsearch -> Indices -> Types -> Documents -> Fields
Elasticsearch集群可以包含多個索引(indices)(數據庫),每一個索引可以包含多個類型(types)(表),每一個類型包含多個文檔(documents)(行),然后每個文檔包含多個字段(Fields)(列)。
「索引」含義的區分
你可能已經注意到索引(index)這個詞在Elasticsearch中有着不同的含義,所以有必要在此做一下區分:
- 索引(名詞) 如上文所述,一個索引(index)就像是傳統關系數據庫中的數據庫,它是相關文檔存儲的地方,index的復數是indices 或indexes。
- 索引(動詞) 「索引一個文檔」表示把一個文檔存儲到索引(名詞)里,以便它可以被檢索或者查詢。這很像SQL中的
INSERT
關鍵字,差別是,如果文檔已經存在,新的文檔將覆蓋舊的文檔。- 倒排索引 傳統數據庫為特定列增加一個索引,例如B-Tree索引來加速檢索。Elasticsearch和Lucene使用一種叫做倒排索引(inverted index)的數據結構來達到相同目的。
默認情況下,文檔中的所有字段都會被索引(擁有一個倒排索引),只有這樣他們才是可被搜索的。
理解上面的索引介紹后,接下來就是很入門的開工工作了。安裝Elasticsearch:
- 安裝JDK(下載地址)
-
安裝ElasticSearch( 下載地址)
- 解壓,運行\bin\elasticsearch.bat.
-
瀏覽器輸入http://localhost:9200/,可以看到如圖:
- 安裝成功。
-
安裝ElasticSearch – header plugin https://github.com/mobz/elasticsearch-head
這個插件主要是用來管理索引數據和狀態,文檔中有詳細說明,安裝步驟也很簡單,用命令行轉bin目錄下運行插件安裝
完成后,打開http://localhost:9200/_plugin/head/就可以看到下面這個管理界面
這樣子安裝算告一段落了,可以開始寫點代碼做點實事了,我用的是NEST這個Client工具,大家可以根據自己的語言選擇工具。
一般而言,我們用到搜索的功能都是全文索引,否則也沒必要用搜索引擎了:
static void Main(string[] args) { //var person = new Person //{ // Id = System.Guid.NewGuid().ToString("N"), // Firstname = "中文測試一下 天才", // Lastname = "哈哈 蠢的不行?" //}; //CraateIndex(person); var items = GetPagingForSearch<Person>("天才", "firstname", SortOrder.Descending, 0, 10); foreach (var item in items) { Console.WriteLine("item:" + Newtonsoft.Json.JsonConvert.SerializeObject(item)); } Console.ReadLine(); } public static IList<T> GetPagingForSearch<T>(string key, string orderbyKey, SortOrder orderType, int index, int size) where T : class { var client = GetClient(); //query_string只是其中一種最常用查詢,Operator枚舉的Or和And可以根據全文檢索的業務要求使用,以下查詢就是為了最簡單的分頁全文檢索接口 var searchResults = client.Search<T>(s => s .Index("sample-index") .Type("sample-type") .Query(q => q.QueryString(qs => qs.Query(key).DefaultOperator(Operator.And))) .Sort(sort => sort.OnField(orderbyKey).Order(orderType)) .From(index * size) //分頁頁碼 .Size(size) //分頁尺寸 ); return searchResults.Documents.ToList(); } public static void CraateIndex<T>(T model) where T : class { var client = GetClient(); var index = client.Index(model, i => i .Index("sample-index") .Type("sample-type") .Id(System.Guid.NewGuid().ToString("N")) .Refresh() .Ttl("1m") ); } public static ElasticClient GetClient() { var node = new Uri("http://localhost:9200"); var settings = new ConnectionSettings( node, defaultIndex: "my-application" ); return new ElasticClient(settings); }
運行結果:

以上只用到了QueryString,當然它的查詢方法遠不只這些,具體的查詢方法我這里就不班門弄斧了。推薦去看文檔。附上NEST的文檔:http://nest.azurewebsites.net/nest/quick-start.html
Elasticsearch致力於隱藏分布式系統的復雜性。以下這些操作都是在底層自動完成的:
- 將你的文檔分區到不同的容器或者分片(shards)中,它們可以存在於一個或多個節點中。
- 將分片均勻的分配到各個節點,對索引和搜索做負載均衡。
- 冗余每一個分片,防止硬件故障造成的數據丟失。
- 將集群中任意一個節點上的請求路由到相應數據所在的節點。
- 無論是增加節點,還是移除節點,分片都可以做到無縫的擴展和遷移。
你可以看到以下默認的分片情況:
很明顯以上分片都在同一個node上,而分片又組成了集群。其中node本身是有可能存在主分片和復制分片,而節點與節點之間又有可能通過分片聯系。
還有一些很關鍵的集群模塊,集群健康監控,Elasticsearch健康有三種狀態:green
、yellow
或red
。限制query執行時占用的JVM Heap sized等等。
對於分布式這塊這里就不能再往下展開了,具體可學習文中提到的那本書,里面有詳細介紹。而性能方面,由於現在公司的業務根本達不到這個數量級,暫時無法給出較好的對比。千百萬級別,不管solr,還是ES都是OK的。上億恐怕要斟酌和優化了。