初識 ElasticSearch

場景:最近有同事分享了ElasticSearch Inverted Index,因此本身也瞭解一下基於Lucene的ES。node

轉載自:http://www.jianshu.com/p/05cff717563cpython

Why Elasticsearch?

因爲須要提高項目的搜索質量,最近研究了一下Elasticsearch,一款很是優秀的分佈式搜索程序。最開始的一些筆記放到github,這裏只是概括總結一下。mysql

首先,爲何要使用Elasticsearch?最開始的時候,咱們的項目僅僅使用MySQL進行簡單的搜索,而後一個不能索引的like語句,直接拉低MySQL的性能。後來,咱們曾考慮過sphinx,而且sphinx也在以前的項目中成功實施過,但想一想如今的數據量級,多臺MySQL,以及搜索服務自己HA,還有後續擴容的問題,咱們以爲sphinx並非一個最優的選擇。因而天然將目光放到了Elasticsearch上面。git

根據官網本身的介紹,Elasticsearch是一個分佈式搜索服務,提供Restful API,底層基於Lucene,採用多shard的方式保證數據安全,而且提供自動resharding的功能,加之github等大型的站點也採用Elasticsearch做爲其搜索服務,咱們決定在項目中使用Elasticsearch。github

對於Elasticsearch,若是要在項目中使用,須要解決以下問題:算法

  1. 索引,對於須要搜索的數據,如何創建合適的索引,還須要根據特定的語言使用不一樣的analyzer等。
  2. 搜索,Elasticsearch提供了很是強大的搜索功能,如何寫出高效的搜索語句?
  3. 數據源,咱們全部的數據是存放到MySQL的,MySQL是惟一數據源,如何將MySQL的數據導入到Elasticsearch?

對於1和2,由於咱們的數據都是從MySQL生成,index的field是固定的,主要作的工做就是根據業務場景設計好對應的mapping以及search語句就能夠了,固然實際不可能這麼簡單,須要咱們不斷的調優。sql

而對於3,則是須要一個工具將MySQL的數據導入Elasticsearch,由於咱們對搜索實時性要求很高,因此須要將MySQL的增量數據實時導入,筆者惟一能想到的就是經過row based binlog來完成。而近段時間的工做,也就是實現一個MySQL增量同步到Elasticsearch的服務。數據庫

Lucene

Elasticsearch底層是基於Lucene的,Lucene是一款優秀的搜索lib,固然,筆者之前仍然沒有接觸使用過。編程

Lucene關鍵概念:json

  • Document:用來索引和搜索的主要數據源,包含一個或者多個Field,而這些Field則包含咱們跟Lucene交互的數據。
  • Field:Document的一個組成部分,有兩個部分組成,name和value。
  • Term:不可分割的單詞,搜索最小單元。
  • Token:一個Term呈現方式,包含這個Term的內容,在文檔中的起始位置,以及類型。

Lucene使用Inverted index來存儲term在document中位置的映射關係。
譬如以下文檔:

  • Elasticsearch Server 1.0 (document 1)
  • Mastring Elasticsearch (document 2)
  • Apache Solr 4 Cookbook (document 3)

使用inverted index存儲,一個簡單地映射關係:

Term Count Docuemnt
1.0 1 <1>
4 1 <3>
Apache 1 <3>
Cookbook 1 <3>
Elasticsearch 2 <1>.<2>
Mastering 1 <2>
Server 1 <1>
Solr 1 <3>

對於上面例子,咱們首先經過分詞算法將一個文檔切分紅一個一個的token,再獲得該token與document的映射關係,並記錄token出現的總次數。這樣就獲得了一個簡單的inverted index。

Elasticsearch關鍵概念

要使用Elasticsearch,筆者認爲,只須要理解幾個基本概念就能夠了。

在數據層面,主要有:

  • Index:Elasticsearch用來存儲數據的邏輯區域,它相似於關係型數據庫中的db概念。一個index能夠在一個或者多個shard上面,同時一個shard也可能會有多個replicas。
  • Document:Elasticsearch裏面存儲的實體數據,相似於關係數據中一個table裏面的一行數據。
    document由多個field組成,不一樣的document裏面同名的field必定具備相同的類型。document裏面field能夠重複出現,也就是一個field會有多個值,即multivalued。
  • Document type:爲了查詢須要,一個index可能會有多種document,也就是document type,但須要注意,不一樣document裏面同名的field必定要是相同類型的。
  • Mapping:存儲field的相關映射信息,不一樣document type會有不一樣的mapping。

對於熟悉MySQL的童鞋,咱們只須要大概認爲Index就是一個db,document就是一行數據,field就是table的column,mapping就是table的定義,而document type就是一個table就能夠了。

Document type這個概念其實最開始也把筆者給弄糊塗了,其實它就是爲了更好的查詢,舉個簡單的例子,一個index,可能一部分數據咱們想使用一種查詢方式,而另外一部分數據咱們想使用另外一種查詢方式,因而就有了兩種type了。不過這種狀況應該在咱們的項目中不會出現,因此一般一個index下面僅會有一個type。

在服務層面,主要有:

  • Node: 一個server實例。
  • Cluster:多個node組成cluster。
  • Shard:數據分片,一個index可能會存在於多個shards,不一樣shards可能在不一樣nodes。
  • Replica:shard的備份,有一個primary shard,其他的叫作replica shards。

Elasticsearch之因此能動態resharding,主要在於它最開始就預先分配了多個shards(貌似是1024),而後以shard爲單位進行數據遷移。這個作法其實在分佈式領域很是的廣泛,codis就是使用了1024個slot來進行數據遷移。

由於任意一個index均可配置多個replica,經過冗餘備份的方式保證了數據的安全性,同時replica也能分擔讀壓力,相似於MySQL中的slave。

Restful API

Elasticsearch提供了Restful API,使用json格式,這使得它很是利於與外部交互,雖然Elasticsearch的客戶端不少,但筆者仍然很容易的就寫出了一個簡易客戶端用於項目中,再次印證了Elasticsearch的使用真心很容易。

Restful的接口很簡單,一個url表示一個特定的資源,譬如/blog/article/1,就表示一個index爲blog,type爲aritcle,id爲1的document。

而咱們使用http標準method來操做這些資源,POST新增,PUT更新,GET獲取,DELETE刪除,HEAD判斷是否存在。

這裏,友情推薦httpie,一個很是強大的http工具,我的感受比curl還用,幾乎是命令行調試Elasticsearch的絕配。

一些使用httpie的例子:

# create http POST :9200/blog/article/1 title="hello elasticsearch" tags:='["elasticsearch"]' # get http GET :9200/blog/article/1 # update http PUT :9200/blog/article/1 title="hello elasticsearch" tags:='["elasticsearch", "hello"]' # delete http DELETE :9200/blog/article/1 # exists http HEAD :9200/blog/article/1

索引和搜索

雖然Elasticsearch能自動判斷field類型並創建合適的索引,但筆者仍然推薦本身設置相關索引規則,這樣才能更好爲後續的搜索服務。

咱們經過定製mapping的方式來設置不一樣field的索引規則。

而對於搜索,Elasticsearch提供了太多的搜索選項,就不一一律述了。

索引和搜索是Elasticsearch很是重要的兩個方面,直接關係到產品的搜索體驗,但筆者現階段也僅僅是大概瞭解了一點,後續在詳細介紹。

同步MySQL數據

Elasticsearch是很強大,但要創建在有足量數據狀況下面。咱們的數據都在MySQL上面,因此如何將MySQL的數據導入Elasticsearch就是筆者最近研究的東西了。

雖然如今有一些實現,譬如elasticsearch-river-jdbc,或者elasticsearch-river-mysql,但筆者並不打算使用。

elasticsearch-river-jdbc的功能是很強大,但並無很好的支持增量數據更新的問題,它須要對應的表只增不減,而這個幾乎在項目中是不可能辦到的。

elasticsearch-river-mysql卻是作的很不錯,採用了python-mysql-replication來經過binlog獲取變動的數據,進行增量更新,但它貌似處理MySQL dump數據導入的問題,不過這個筆者真的好好確認一下?話說,python-mysql-replication筆者還提交過pull解決了minimal row image的問題,因此對elasticsearch-river-mysql這個項目頗有好感。只是筆者決定本身寫一個出來。

爲何筆者決定本身寫一個,不是由於筆者喜歡造輪子,主要緣由在於對於這種MySQL syncer服務(增量獲取MySQL數據更新到相關係統),咱們不光能夠用到Elasticsearch上面,並且還能用到其餘服務,譬如cache上面。因此筆者其實想實現的是一個通用MySQL syncer組件,只是如今主要關注Elasticsearch罷了。

項目代碼在這裏go-mysql-elasticsearch,現已完成第一階段開發,內部對接測試中。

go-mysql-elasticsearch的原理很簡單,首先使用mysqldump獲取當前MySQL的數據,而後在經過此時binlog的name和position獲取增量數據。

一些限制:

  • binlog必定要變成row-based format格式,其實咱們並不須要擔憂這種格式的binlog佔用太多的硬盤空間,MySQL 5.6以後GTID模式都推薦使用row-based format了,並且一般咱們都會把控SQL語句質量,不容許一次性更改過多行數據的。
  • 須要同步的table最好是innodb引擎,這樣mysqldump的時候纔不會阻礙寫操做。
  • 須要同步的table必定要有主鍵,好吧,若是一個table沒有主鍵,筆者真心會懷疑設計這個table的同窗編程水平了。多列主鍵也是不推薦的,筆者現階段不打算支持。
  • 必定別動態更改須要同步的table結構,Elasticsearch只能支持動態增長field,並不支持動態刪除和更改field。一般來講,若是涉及到alter table,不少時候已經證實前期設計的不合理以及對於將來擴展的預估不足了。

更詳細的說明,等到筆者完成了go-mysql-elasticsearch的開發,並經過生產環境中測試了,再進行補充。

總結

對於一門不懂的技術,找一份靠譜的資料(官方文檔或者入門書籍),蛋疼的對着資料敲一遍代碼,不懂的再問google,最後在將其用到實際項目,這門技術就算是初步掌握了,固然精通還得在下點功夫。

相關文章
相關標籤/搜索