Elasticsearch學(xué)習(xí)筆記

Why Elasticsearch?

由于需要提升項目的搜索質(zhì)量,最近研究了一下Elasticsearch,一款非常優(yōu)秀的分布式搜索程序。最開始的一些筆記放到github,這里只是歸納總結(jié)一下。

首先,為什么要使用Elasticsearch?最開始的時候,我們的項目僅僅使用MySQL進行簡單的搜索,然后一個不能索引的like語句,直接拉低MySQL的性能。后來,我們曾考慮過sphinx,并且sphinx也在之前的項目中成功實施過,但想想現(xiàn)在的數(shù)據(jù)量級,多臺MySQL,以及搜索服務(wù)本身HA,還有后續(xù)擴容的問題,我們覺得sphinx并不是一個最優(yōu)的選擇。于是自然將目光放到了Elasticsearch上面。

根據(jù)官網(wǎng)自己的介紹,Elasticsearch是一個分布式搜索服務(wù),提供Restful API,底層基于Lucene,采用多shard的方式保證數(shù)據(jù)安全,并且提供自動resharding的功能,加之github等大型的站點也采用Elasticsearch作為其搜索服務(wù),我們決定在項目中使用Elasticsearch。

對于Elasticsearch,如果要在項目中使用,需要解決如下問題:

  1. 索引,對于需要搜索的數(shù)據(jù),如何建立合適的索引,還需要根據(jù)特定的語言使用不同的analyzer等。
  2. 搜索,Elasticsearch提供了非常強大的搜索功能,如何寫出高效的搜索語句?
  3. 數(shù)據(jù)源,我們所有的數(shù)據(jù)是存放到MySQL的,MySQL是唯一數(shù)據(jù)源,如何將MySQL的數(shù)據(jù)導(dǎo)入到Elasticsearch?

對于1和2,因為我們的數(shù)據(jù)都是從MySQL生成,index的field是固定的,主要做的工作就是根據(jù)業(yè)務(wù)場景設(shè)計好對應(yīng)的mapping以及search語句就可以了,當(dāng)然實際不可能這么簡單,需要我們不斷的調(diào)優(yōu)。

而對于3,則是需要一個工具將MySQL的數(shù)據(jù)導(dǎo)入Elasticsearch,因為我們對搜索實時性要求很高,所以需要將MySQL的增量數(shù)據(jù)實時導(dǎo)入,筆者唯一能想到的就是通過row based binlog來完成。而近段時間的工作,也就是實現(xiàn)一個MySQL增量同步到Elasticsearch的服務(wù)。

Lucene

Elasticsearch底層是基于Lucene的,Lucene是一款優(yōu)秀的搜索lib,當(dāng)然,筆者以前仍然沒有接觸使用過。:-)

Lucene關(guān)鍵概念:

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

Lucene使用Inverted index來存儲term在document中位置的映射關(guān)系。
譬如如下文檔:

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

使用inverted index存儲,一個簡單地映射關(guān)系:

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的映射關(guān)系,并記錄token出現(xiàn)的總次數(shù)。這樣就得到了一個簡單的inverted index。

Elasticsearch關(guān)鍵概念

要使用Elasticsearch,筆者認為,只需要理解幾個基本概念就可以了。

在數(shù)據(jù)層面,主要有:

  • Index:Elasticsearch用來存儲數(shù)據(jù)的邏輯區(qū)域,它類似于關(guān)系型數(shù)據(jù)庫中的db概念。一個index可以在一個或者多個shard上面,同時一個shard也可能會有多個replicas。
  • Document:Elasticsearch里面存儲的實體數(shù)據(jù),類似于關(guān)系數(shù)據(jù)中一個table里面的一行數(shù)據(jù)。
    document由多個field組成,不同的document里面同名的field一定具有相同的類型。document里面field可以重復(fù)出現(xiàn),也就是一個field會有多個值,即multivalued。
  • Document type:為了查詢需要,一個index可能會有多種document,也就是document type,但需要注意,不同document里面同名的field一定要是相同類型的。
  • Mapping:存儲field的相關(guān)映射信息,不同document type會有不同的mapping。

對于熟悉MySQL的童鞋,我們只需要大概認為Index就是一個db,document就是一行數(shù)據(jù),field就是table的column,mapping就是table的定義,而document type就是一個table就可以了。

Document type這個概念其實最開始也把筆者給弄糊涂了,其實它就是為了更好的查詢,舉個簡單的例子,一個index,可能一部分?jǐn)?shù)據(jù)我們想使用一種查詢方式,而另一部分?jǐn)?shù)據(jù)我們想使用另一種查詢方式,于是就有了兩種type了。不過這種情況應(yīng)該在我們的項目中不會出現(xiàn),所以通常一個index下面僅會有一個type。

在服務(wù)層面,主要有:

  • Node: 一個server實例。
  • Cluster:多個node組成cluster。
  • Shard:數(shù)據(jù)分片,一個index可能會存在于多個shards,不同shards可能在不同nodes。
  • Replica:shard的備份,有一個primary shard,其余的叫做replica shards。

Elasticsearch之所以能動態(tài)resharding,主要在于它最開始就預(yù)先分配了多個shards(貌似是1024),然后以shard為單位進行數(shù)據(jù)遷移。這個做法其實在分布式領(lǐng)域非常的普遍,codis就是使用了1024個slot來進行數(shù)據(jù)遷移。

因為任意一個index都可配置多個replica,通過冗余備份的方式保證了數(shù)據(jù)的安全性,同時replica也能分擔(dān)讀壓力,類似于MySQL中的slave。

Restful API

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

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

而我們使用http標(biāo)準(zhǔn)method來操作這些資源,POST新增,PUT更新,GET獲取,DELETE刪除,HEAD判斷是否存在。

這里,友情推薦httpie,一個非常強大的http工具,個人感覺比curl還用,幾乎是命令行調(diào)試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類型并建立合適的索引,但筆者仍然推薦自己設(shè)置相關(guān)索引規(guī)則,這樣才能更好為后續(xù)的搜索服務(wù)。

我們通過定制mapping的方式來設(shè)置不同field的索引規(guī)則。

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

索引和搜索是Elasticsearch非常重要的兩個方面,直接關(guān)系到產(chǎn)品的搜索體驗,但筆者現(xiàn)階段也僅僅是大概了解了一點,后續(xù)在詳細介紹。

同步MySQL數(shù)據(jù)

Elasticsearch是很強大,但要建立在有足量數(shù)據(jù)情況下面。我們的數(shù)據(jù)都在MySQL上面,所以如何將MySQL的數(shù)據(jù)導(dǎo)入Elasticsearch就是筆者最近研究的東西了。

雖然現(xiàn)在有一些實現(xiàn),譬如elasticsearch-river-jdbc,或者elasticsearch-river-mysql,但筆者并不打算使用。

elasticsearch-river-jdbc的功能是很強大,但并沒有很好的支持增量數(shù)據(jù)更新的問題,它需要對應(yīng)的表只增不減,而這個幾乎在項目中是不可能辦到的。

elasticsearch-river-mysql倒是做的很不錯,采用了python-mysql-replication來通過binlog獲取變更的數(shù)據(jù),進行增量更新,但它貌似處理MySQL dump數(shù)據(jù)導(dǎo)入的問題,不過這個筆者真的好好確認一下?話說,python-mysql-replication筆者還提交過pull解決了minimal row image的問題,所以對elasticsearch-river-mysql這個項目很有好感。只是筆者決定自己寫一個出來。

為什么筆者決定自己寫一個,不是因為筆者喜歡造輪子,主要原因在于對于這種MySQL syncer服務(wù)(增量獲取MySQL數(shù)據(jù)更新到相關(guān)系統(tǒng)),我們不光可以用到Elasticsearch上面,而且還能用到其他服務(wù),譬如cache上面。所以筆者其實想實現(xiàn)的是一個通用MySQL syncer組件,只是現(xiàn)在主要關(guān)注Elasticsearch罷了。

項目代碼在這里go-mysql-elasticsearch,現(xiàn)已完成第一階段開發(fā),內(nèi)部對接測試中。

go-mysql-elasticsearch的原理很簡單,首先使用mysqldump獲取當(dāng)前MySQL的數(shù)據(jù),然后在通過此時binlog的name和position獲取增量數(shù)據(jù)。

一些限制:

  • binlog一定要變成row-based format格式,其實我們并不需要擔(dān)心這種格式的binlog占用太多的硬盤空間,MySQL 5.6之后GTID模式都推薦使用row-based format了,而且通常我們都會把控SQL語句質(zhì)量,不允許一次性更改過多行數(shù)據(jù)的。
  • 需要同步的table最好是innodb引擎,這樣mysqldump的時候才不會阻礙寫操作。
  • 需要同步的table一定要有主鍵,好吧,如果一個table沒有主鍵,筆者真心會懷疑設(shè)計這個table的同學(xué)編程水平了。多列主鍵也是不推薦的,筆者現(xiàn)階段不打算支持。
  • 一定別動態(tài)更改需要同步的table結(jié)構(gòu),Elasticsearch只能支持動態(tài)增加field,并不支持動態(tài)刪除和更改field。通常來說,如果涉及到alter table,很多時候已經(jīng)證明前期設(shè)計的不合理以及對于未來擴展的預(yù)估不足了。

更詳細的說明,等到筆者完成了go-mysql-elasticsearch的開發(fā),并通過生產(chǎn)環(huán)境中測試了,再進行補充。

總結(jié)

最近一周,筆者花了不少時間在Elasticsearch上面,現(xiàn)在算是基本入門了。其實筆者覺得,對于一門不懂的技術(shù),找一份靠譜的資料(官方文檔或者入門書籍),蛋疼的對著資料敲一遍代碼,不懂的再問google,最后在將其用到實際項目,這門技術(shù)就算是初步掌握了,當(dāng)然精通還得在下點功夫。

現(xiàn)在筆者只是覺得Elasticsearch很美好,上線之后鐵定會有坑的,那時候只能慢慢填了。話說,筆者是不是要學(xué)習(xí)下java了,省的到時候看不懂代碼就慘了。:-)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,517評論 6 539
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,087評論 3 423
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 177,521評論 0 382
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,493評論 1 316
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 72,207評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,603評論 1 325
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,624評論 3 444
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,813評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,364評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 41,110評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,305評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,874評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,532評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,953評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,209評論 1 291
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,033評論 3 396
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 48,268評論 2 375

推薦閱讀更多精彩內(nèi)容