轉(zhuǎn)自:http://xargin.com/some-things-about-es/
談一談es的優(yōu)勢(shì)和限制
企業(yè)內(nèi)部使用的elasticsearch是提供垂直搜索的一種方案,什么是垂直搜索呢。
先抄個(gè)百度百科:
垂直搜索引擎是針對(duì)某一個(gè)行業(yè)的專業(yè)搜索引擎,是搜索引擎的細(xì)分和延伸,是對(duì)網(wǎng)頁(yè)庫(kù)中的某類專門(mén)的信息進(jìn)行一次整合,定向分字段抽取出需要的數(shù)據(jù)進(jìn)行處理后再以某種形式返回給用戶。垂直搜索是相對(duì)通用搜索引擎的信息量大、查詢不準(zhǔn)確、深度不夠等提出來(lái)的新的搜索引擎服務(wù)模式,通過(guò)針對(duì)某一特定領(lǐng)域、某一特定人群或某一特定需求提供的有一定價(jià)值的信息和相關(guān)服務(wù)。其特點(diǎn)就是“專、精、深”,且具有行業(yè)色彩,相比較通用搜索引擎的海量信息無(wú)序化,垂直搜索引擎則顯得更加專注、具體和深入。
其實(shí)說(shuō)白了就一句話,垂直搜索是在企業(yè)內(nèi)部使用的搜索引擎。這種搜索引擎的特點(diǎn)是,內(nèi)容可能是一些結(jié)構(gòu)化的數(shù)據(jù),而不像大搜索那樣都是雜亂的內(nèi)容。
一般被拿來(lái)解決一些什么樣的問(wèn)題:
1.數(shù)據(jù)庫(kù)字段太多,查詢太慢,索引沒(méi)有辦法再做優(yōu)化
2.數(shù)據(jù)庫(kù)一個(gè)count就拖死全表。
3.mysql的limit翻到幾十幾百萬(wàn)頁(yè)后實(shí)在是太慢
4.數(shù)據(jù)庫(kù)like實(shí)在太慢,每次like整個(gè)服務(wù)器cpu內(nèi)存飆高,拖慢整個(gè)線上服務(wù)
5.想要對(duì)外/內(nèi)提供db里的數(shù)據(jù)的全文檢索服務(wù)。
6.提供日志(程序運(yùn)行)查詢功能。
所以這個(gè)工具對(duì)于海量的訂單、工單、倉(cāng)庫(kù)調(diào)撥單、用戶信息的系統(tǒng)非常有用。而這些單據(jù)什么的對(duì)于電商來(lái)說(shuō)則非常重要。其實(shí)也不只是電商,大多數(shù)互聯(lián)網(wǎng)公司內(nèi)的數(shù)據(jù)不就是這樣的“單據(jù)”么。
來(lái)針對(duì)上面幾方面的問(wèn)題逐一進(jìn)行說(shuō)明:
數(shù)據(jù)庫(kù)方面
mysql對(duì)于一些較為固定,字段較少的查詢方式,可以通過(guò)簡(jiǎn)單的增加索引來(lái)完成優(yōu)化,在大多數(shù)公司,即使你對(duì)索引優(yōu)化不熟悉,也有專門(mén)的dba來(lái)幫你完成一些簡(jiǎn)單的優(yōu)化。甚至有些電商公司要求程序中不允許出現(xiàn)orm,必須用純sql來(lái)完成業(yè)務(wù)邏輯,這樣dba可以直接介入到代碼中來(lái)。
不過(guò)到字段太多的時(shí)候這招就不靈了,字段越多,查詢自然就越慢(比如單條記錄可能都超過(guò)了4k什么,甚至有些oracle的表里,一個(gè)字段就有幾兆信息,當(dāng)然幾兆信息的這種這里也解決不了,嗯)。
mysql表在普通查詢過(guò)程中,比如select * from xxx limit 100w, 100;這種,小網(wǎng)站隨便寫(xiě)sql,可能不會(huì)體會(huì)到翻頁(yè)的痛。但你在一個(gè)單表3000w的系統(tǒng)中寫(xiě)了limit 10000000, 10。那數(shù)據(jù)庫(kù)服務(wù)器就哭了。因?yàn)閷?shí)際上數(shù)據(jù)庫(kù)為了取出你想要的那幾條數(shù)據(jù),需要把所有的數(shù)據(jù)也就是10000010條都取到內(nèi)存中,復(fù)雜一點(diǎn)的select再加上order by則可能會(huì)同時(shí)涉及到多次磁盤(pán)讀取和文件排序。慢上加慢。
除此之外,現(xiàn)在最流行的innodb之類的存儲(chǔ)引擎在計(jì)算count的時(shí)候非常的慢。當(dāng)然了,網(wǎng)絡(luò)上會(huì)有人從亂七八糟的文章里看到換myisam應(yīng)該就會(huì)更快的結(jié)論,但這其實(shí)是錯(cuò)的。如果在select語(yǔ)句的where條件中也有表達(dá)式時(shí),這兩種存儲(chǔ)引擎本質(zhì)上都是一樣的,都會(huì)很慢很慢。
還有mysql的like,其實(shí)沒(méi)什么玄幻的,每次做like本質(zhì)還是查詢內(nèi)容去和數(shù)據(jù)庫(kù)字段做字符串匹配。非常地慢。
現(xiàn)在一般的互聯(lián)網(wǎng)系統(tǒng)都是普遍的寫(xiě)少讀多的系統(tǒng),寫(xiě)/讀搞不好會(huì)有1/5以上?但因?yàn)閿?shù)據(jù)量龐大,為了讀取效率而去做拆表或者拆庫(kù)的話,有時(shí)候?qū)嵲谑怯悬c(diǎn)得不償失。而且拆表拆庫(kù)對(duì)業(yè)務(wù)代碼來(lái)說(shuō)也并不透明,還可能會(huì)對(duì)本來(lái)支持的功能造成額外的影響。只是為了查詢而去拆分的話,不是很合適。
上面這些問(wèn)題,es都可以解決。企業(yè)里對(duì)數(shù)據(jù)的查詢一般可以分為三種:列表查詢、詳情查詢和統(tǒng)計(jì)查詢??疵忠部梢岳斫獾牟畈欢嗔?,列表一般就是列表頁(yè)對(duì)應(yīng)的查詢,詳情查詢一般就是具體id對(duì)應(yīng)的詳情查詢,而統(tǒng)計(jì)查詢一般都是在看一些數(shù)值之類的報(bào)表,也就是一堆count值。
這三種查詢里,mysql做起來(lái)最困難的是1和3,即列表查詢和統(tǒng)計(jì)查詢。列表查詢這種場(chǎng)景也會(huì)對(duì)應(yīng)各種各樣的查詢條件,例如字段等于/小于/大于/不等判斷,或者像字符串的嚴(yán)格匹配/前后綴模糊查詢,時(shí)間字段的范圍查詢,in查詢等等。這些查詢都可以翻譯為es中的bool查詢,舉一個(gè)簡(jiǎn)單的例子:
{
? ? "from" : 0,
? ? "size" : 15,
? ? "query" : {
? ? ? ? "bool": {
? ? ? ? ? ? "must" : [{"range" : {"create_time" : {"from":"2016-08-11T00:00:00+0800","to":"2016-08-11T23:59:00+0800"}}},{"term" : {"user_id" : "37540"}},{"terms" : {"status" : [2,1,0]}}]
? ? ? ? }
? ? },
? ? "sort" : [{"create_time" : "desc"}],
? ? "highlight" : {
? ? }
}
例如上面這個(gè)es中的bool查詢,就是從這種sql翻譯過(guò)來(lái)的:
select * from db/table where create_time between ('2016-08-11 00:00:00' and '2016-08-11 23:59:59')
and user_id = 37540 and status in (2, 1, 0);
對(duì)應(yīng)到業(yè)務(wù)里,常用的查詢其實(shí)大多數(shù)都是這些很簡(jiǎn)單的條件并列,A && B && C && D。所以翻譯起來(lái)也比較簡(jiǎn)單。
單表的count放在es里做也非常的快,為什么呢?因?yàn)閑s本身會(huì)把單個(gè)字段的一種值當(dāng)作一個(gè)term,然后會(huì)記錄這個(gè)term出現(xiàn)的所有文檔和出現(xiàn)次數(shù)。舉個(gè)例子,我們公司的業(yè)務(wù),你可能會(huì)去查詢某個(gè)業(yè)務(wù)線下的所有工單。那么查詢條件就類似于where business_type is 6這樣??赡苤恍枰缓撩刖头祷亓私Y(jié)果。很費(fèi)解是不是?其實(shí)es也只是去讀了一下這個(gè)business_type是6的term出現(xiàn)的文檔數(shù)。邏輯上是很簡(jiǎn)單的。
這是不是說(shuō)明es就是萬(wàn)能的了?
并不是。
首先是翻頁(yè)的問(wèn)題,es里有上億數(shù)據(jù),翻到最后一頁(yè)的時(shí)候還是會(huì)比較慢,并且會(huì)影響到整個(gè)系統(tǒng)的load,然后系統(tǒng)響應(yīng)變慢。因?yàn)槠湓磉€是拿一堆數(shù)據(jù)來(lái)做merge。
從傳統(tǒng)的sql思維翻譯到es的dsl過(guò)程也稍微有點(diǎn)痛苦。因?yàn)閑s畢竟是從搜索引擎的角度去做這些事情,所以如果你當(dāng)DB來(lái)用的話,其DSL設(shè)計(jì)就顯得很別扭。雖然有了上面的轉(zhuǎn)換規(guī)則,但實(shí)際上業(yè)務(wù)轉(zhuǎn)換起來(lái)并沒(méi)有這么方便,比如在通常的查詢里還可能會(huì)有where a = 1 or b = 2。顯然想轉(zhuǎn)成DSL就沒(méi)有這么方便了。
es不是數(shù)據(jù)庫(kù),所以如果你想要實(shí)現(xiàn)聯(lián)表查詢也會(huì)變得很麻煩。如果你還想實(shí)現(xiàn)事務(wù),我勸你還是別想了。
在企業(yè)里用es提供查詢服務(wù)的話,一般都會(huì)做一層查詢封裝。直接提供sql接口。例如下面的這個(gè)es插件:
https://github.com/NLPchina/elasticsearch-sql
但插件也是人寫(xiě)的,并不是所以的特性都能很好的支持,比如join啊什么的。所以也有一些公司的人會(huì)用druid之類的東西做一個(gè)sql parser層,然后來(lái)支持這些亂七八糟的需求。
不過(guò)即使是直接用這種插件,也不能認(rèn)為它就能讓你一勞永逸,你還是需要對(duì)es內(nèi)部的機(jī)制(例如mapping)和通常的查詢方式(term/query_string/wild_card等)很了解才行。
比如你必須知道wildcard查詢必須對(duì)字符串字段設(shè)置為not_analyzed。你還得知道term什么時(shí)候代表的是分詞后的詞,什么時(shí)候代表的是整個(gè)字段的值。
在了解了這些之后才會(huì)了解到es的高性能like,其實(shí)也還是有一些限制。例如輸入的字符串會(huì)被分詞,這也就是說(shuō),想要高性能的時(shí)候只能用es默認(rèn)提供的基于詞的字符串like,而且一旦分詞,你就沒(méi)辦法實(shí)現(xiàn)類似sql里的 x= "Hello world"這種準(zhǔn)確匹配的邏輯。也就是說(shuō),你在es里查詢hello world,hello world fuck也會(huì)出現(xiàn)在你的結(jié)果當(dāng)中。不過(guò)這個(gè)對(duì)于大多數(shù)的業(yè)務(wù)來(lái)說(shuō)實(shí)際上是無(wú)所謂的~
所以懶還是偷不了的orz。在你了解所有特性的時(shí)候,這些工具才能切實(shí)地幫助到你。這時(shí)候即使你想自己寫(xiě)一個(gè),也并不是那么麻煩。
檢索服務(wù)方面
搜索是人類的自然需求。
如果不是的話,那Google和百度就不會(huì)誕生了。畢竟現(xiàn)在是信息爆炸的時(shí)代吶。
而檢索/搜索的基本原理就是對(duì)語(yǔ)句進(jìn)行分詞,然后再形成倒排索引,再根據(jù)詞項(xiàng)出現(xiàn)次數(shù)對(duì)文檔進(jìn)行打分,最終按分?jǐn)?shù)倒序展示給用戶。
好幾年前大家用php做個(gè)論壇的時(shí)候,搜索帖子就已經(jīng)是很普遍的需求了。php程序員一般首先冒出來(lái)的想法是使用sphinx來(lái)解決問(wèn)題,不過(guò)這個(gè)工具對(duì)于小公司來(lái)說(shuō)也確實(shí)足夠了。
而對(duì)于真海量數(shù)據(jù)的公司來(lái)說(shuō),一個(gè)單機(jī)的方案很快就會(huì)遇到瓶頸,而去尋求或自行開(kāi)發(fā)更好的解決方案。
其實(shí)在es之前solr應(yīng)該要更流行一些吧(瞎說(shuō)的),不過(guò)solr的配置還是稍微麻煩,而es的集群搭建只要改改yml就好了(拍飛
有了es以后,集群便可以非常方便地進(jìn)行動(dòng)態(tài)擴(kuò)展。只要加硬盤(pán)加機(jī)器改配置就好啦,因?yàn)楸旧淼母北痉植疾呗员容^科學(xué)。所以只要?jiǎng)e一半以上的節(jié)點(diǎn)都掛掉,數(shù)據(jù)就不會(huì)丟失。而且還會(huì)在某些結(jié)點(diǎn)掛掉的時(shí)候自動(dòng)進(jìn)行分片relocate。
由于es本身帶的分詞不是很科學(xué),這樣的話對(duì)doc打分可能會(huì)有一些影響。比如中國(guó)人可能不正確地分成了中/國(guó)人之類的?,F(xiàn)在很多人會(huì)選擇以插件的形式把ik分詞器之類的插件掛載到es上來(lái)改善分詞效果。這些插件的本質(zhì)其實(shí)還是一個(gè)非常龐大的中文詞庫(kù),沒(méi)什么可說(shuō)的。es內(nèi)部設(shè)計(jì)有鏈接可以直接查看語(yǔ)句的分詞結(jié)果,可以方便地直接查看效果。
所以你要是有幾億的文檔需要做些檢索,那五六臺(tái)配置不錯(cuò)的es機(jī)器就足夠了,甚至都不用ssd硬盤(pán)哦~
日志方面
企業(yè)里的系統(tǒng)一般都是分布式系統(tǒng),所以無(wú)論是接入,還是api,還是db,都不太可能在一臺(tái)機(jī)器上完成需求。先不說(shuō)他們是怎么從一臺(tái)機(jī)器橫向擴(kuò)展到現(xiàn)在這樣的規(guī)模的。事實(shí)是我們的面前已經(jīng)有這么多的機(jī)器了。
對(duì)于某一個(gè)服務(wù)模塊來(lái)說(shuō),多臺(tái)機(jī)器最麻煩的就是去查問(wèn)題。在沒(méi)有日志系統(tǒng)的洪荒時(shí)代,程序員大概只能登陸到機(jī)器去一臺(tái)一臺(tái)尋找可能的錯(cuò)誤日志,然而因?yàn)樨?fù)載均衡算法(比如可能是一致性哈希望/隨機(jī)/RR/WR)的問(wèn)題,可能一個(gè)用戶在一次訪問(wèn)會(huì)話(session)中的請(qǐng)求都不是一臺(tái)而是多臺(tái)機(jī)器完成的響應(yīng)。
要吐血了。
所以日志系統(tǒng)的工作就是把日志匯集到一起,并提供統(tǒng)一的查詢?nèi)肟?。?dāng)然,也有人會(huì)參考M/R的思想去實(shí)現(xiàn)了一個(gè)分布式的grep。不過(guò)這是邪道。在干凈的網(wǎng)頁(yè)上看日志多爽啊。
要收集日志一般會(huì)自行搭建一個(gè)elk平臺(tái),elasticsearch/logstash/kibana必不可少。國(guó)內(nèi)也有人專門(mén)做了一個(gè)網(wǎng)站
來(lái)做一些科普工作,還挺不錯(cuò)的。不過(guò)拿來(lái)的東西總會(huì)有那么一些問(wèn)題,比如kibana里的按地圖出數(shù)據(jù)默認(rèn)用的是googlemap,在墻內(nèi)使會(huì)有些問(wèn)題,這個(gè)問(wèn)題github上也有人已經(jīng)解決了,相信你可以搜得到。再比如logstash這個(gè)程序可能只考慮了簡(jiǎn)單的收集 ,如果是大公司的業(yè)務(wù)講究一個(gè)嚴(yán)謹(jǐn)。例如他們想要對(duì)日志收集端的資源使用做一些限制,不能讓你隨便占用系統(tǒng)資源而影響到業(yè)務(wù)系統(tǒng)。再比如他們還希望日志不要因?yàn)榫W(wǎng)絡(luò)閃斷之類的問(wèn)題導(dǎo)致日志丟失什么的,所以還可能會(huì)在logstash后面再加一個(gè)kafka/redis。不管怎么說(shuō),工作基礎(chǔ)還是elk。或者是e自己的l和自己的k。
如果是正兒八經(jīng)的日志系統(tǒng)的話還有一個(gè)問(wèn)題,因?yàn)楹A康臄?shù)據(jù)和海量的訪問(wèn),日志的數(shù)據(jù)量一般都非常地龐大。所以一般數(shù)據(jù)都會(huì)有一個(gè)過(guò)期時(shí)間,對(duì)于我們這里來(lái)說(shuō),日志數(shù)據(jù)其實(shí)一般也就一周或者一個(gè)月。畢竟即使是一個(gè)邊緣部門(mén),一周的日志也都已經(jīng)幾個(gè)億(100+GB)了。
查詢起來(lái)你也不希望它太慢,所以還是盡量把日志索引的大小控制在一個(gè)范圍內(nèi)。當(dāng)然,也有按照日期來(lái)生成索引的。每一天在一個(gè)獨(dú)立的索引下,這樣查詢性能也會(huì)好一些。
同時(shí)又是因?yàn)檫@海量的數(shù)據(jù),你在寫(xiě)入到es的時(shí)候必須使用bulk端口,相信使用過(guò)es的人都知道使用和不使用分別意味著什么。
以上。