ElasticSearch最廣泛的使用場景,是提供垂直搜索功能。什么是垂直搜索呢?
垂直搜索引擎是針對某一個行業(yè)的專業(yè)搜索引擎,是搜索引擎的細(xì)分和延伸,是對網(wǎng)頁庫中的某類專門的信息進行一次整合,定向分字段抽取出需要的數(shù)據(jù)進行處理后再以某種形式返回給用戶。垂直搜索是相對通用搜索引擎的信息量大、查詢不準(zhǔn)確、深度不夠等提出來的新的搜索引擎服務(wù)模式,通過針對某一特定領(lǐng)域、某一特定人群或某一特定需求提供的有一定價值的信息和相關(guān)服務(wù)。其特點就是“專、精、深”,且具有行業(yè)色彩,相比較通用搜索引擎的海量信息無序化,垂直搜索引擎則顯得更加專注、具體和深入。其實說白了就一句話,垂直搜索是在企業(yè)內(nèi)部使用的搜索引擎。這種搜索引擎的特點是,內(nèi)容可能是一些結(jié)構(gòu)化的數(shù)據(jù),而不像大搜索那樣都是雜亂的內(nèi)容。一般被拿來解決一些什么樣的問題?
數(shù)據(jù)庫字段太多,查詢太慢,索引沒有辦法再做優(yōu)化;數(shù)據(jù)庫一個count就拖死全表;MySQL的limit翻到幾十幾百萬頁后實在是太慢;數(shù)據(jù)庫like實在太慢,每次like整個服務(wù)器cpu內(nèi)存飆高,拖慢整個線上服務(wù);想要對外/內(nèi)提供db里的數(shù)據(jù)的全文檢索服務(wù);提供日志(程序運行)查詢功能;下面來針對上面幾方面的問題逐一進行說明。
數(shù)據(jù)庫方面MySQL對于一些較為固定,字段較少的查詢方式,可以通過簡單的增加索引來完成優(yōu)化。在大多數(shù)公司,即使對索引優(yōu)化不熟悉,也有專門的dba來幫忙完成一些簡單的優(yōu)化。甚至有些公司要求程序中不允許出現(xiàn)orm,必須用純sql來完成業(yè)務(wù)邏輯,這樣dba可以直接介入到代碼中來。
不過到字段太多的時候,這種方法就失靈了。字段越多,查詢自然就越慢(比如單條記錄可能都超過了4k)。
MySQL表在普通查詢過程中,比如select * from xxx limit 100w, 100;這種,數(shù)據(jù)量小的時候隨便寫sql,可能不會體會到翻頁的痛。但在一個單表3000w的系統(tǒng)中寫了limit 100w, 10。那數(shù)據(jù)庫服務(wù)器就哭了。因為實際上數(shù)據(jù)庫為了取出想要的那幾條數(shù)據(jù),需要把所有的數(shù)據(jù)也就是10000010條都取到內(nèi)存中,復(fù)雜一點的select再加上order by則可能會同時涉及到多次磁盤讀取和文件排序,慢上加慢。
除此之外,現(xiàn)在最流行的innodb之類的存儲引擎在計算count的時候非常的慢。當(dāng)然了,網(wǎng)絡(luò)上會有人從亂七八糟的文章里看到換myisam應(yīng)該就會更快的結(jié)論,但這其實是錯的。如果在select語句的where條件中也有表達式時,這兩種存儲引擎本質(zhì)上都是一樣的,都會很慢很慢。
還有MySQL的like,其實沒什么玄幻的,每次做like本質(zhì)還是查詢內(nèi)容去和數(shù)據(jù)庫字段做字符串匹配。非常地慢。
現(xiàn)在一般的互聯(lián)網(wǎng)系統(tǒng)都是普遍的寫少讀多的系統(tǒng),寫/讀搞不好會有1/5以上。但因為數(shù)據(jù)量龐大,為了讀取效率而去做拆表或者拆庫的話,有時候?qū)嵲谑怯悬c得不償失。而且拆表拆庫對業(yè)務(wù)代碼來說也并不透明,還可能會對本來支持的功能造成額外的影響。只是為了查詢而去拆分的話,不是很合適。
上面這些問題,ES都可以解決。企業(yè)里對數(shù)據(jù)的查詢一般可以分為三種:列表查詢、詳情查詢和統(tǒng)計查詢。列表一般就是列表頁對應(yīng)的查詢,詳情查詢一般就是具體id對應(yīng)的詳情查詢,而統(tǒng)計查詢一般都是在看一些數(shù)值之類的報表,也就是一堆count值。
這三種查詢里,MySQL做起來最困難的是1和3,即列表查詢和統(tǒng)計查詢。列表查詢這種場景也會對應(yīng)各種各樣的查詢條件,例如字段等于/小于/大于/不等判斷,或者像字符串的嚴(yán)格匹配/前后綴模糊查詢,時間字段的范圍查詢,in查詢等等。這些查詢都可以翻譯為ES中的bool查詢,舉一個簡單的例子:
例如上面這個es中的bool查詢,就是從這種sql翻譯過來的:
對應(yīng)到業(yè)務(wù)里,常用的查詢其實大多數(shù)都是這些很簡單的條件并列,A && B && C && D。所以翻譯起來也比較簡單。
單表的count放在ES里做也非常的快,為什么呢?因為ES本身會把單個字段的一種值當(dāng)作一個term,然后會記錄這個term出現(xiàn)的所有文檔和出現(xiàn)次數(shù)。舉個例子,我們公司的業(yè)務(wù),可能會去查詢某個業(yè)務(wù)線下的所有工單。那么查詢條件就類似于where business_type is 6這樣??赡苤恍枰缓撩刖头祷亓私Y(jié)果。很費解是不是?其實ES也只是去讀了一下這個business_type是6的term出現(xiàn)的文檔數(shù),邏輯上是很簡單的。
這是不是說明ES就是萬能的了?
并不是。
首先是翻頁的問題,ES里有上億數(shù)據(jù),翻到最后一頁的時候還是會比較慢,并且會影響到整個系統(tǒng)的load,然后系統(tǒng)響應(yīng)變慢。因為其原理還是拿一堆數(shù)據(jù)來做merge。
從傳統(tǒng)的sql思維翻譯到es的dsl過程也稍微有點痛苦。因為ES畢竟是從搜索引擎的角度去做這些事情,所以如果當(dāng)DB來用的話,其DSL設(shè)計就顯得很別扭。雖然有了上面的轉(zhuǎn)換規(guī)則,但實際上業(yè)務(wù)轉(zhuǎn)換起來并沒有這么方便,比如在通常的查詢里還可能會有where a = 1 or b = 2。顯然想轉(zhuǎn)成DSL就沒有這么方便了。
ES不是數(shù)據(jù)庫,所以如果想要實現(xiàn)聯(lián)表查詢也會變得很麻煩。如果還想實現(xiàn)事務(wù),那么還是放棄吧。
在企業(yè)里用ES提供查詢服務(wù)的話,一般都會做一層查詢封裝。直接提供sql接口。
但插件支持的功能也是有限的,并不是所以的特性都能很好的支持,比如join。所以也有一些公司的人會用druid之類的東西做一個sql parser層,然后來支持這些需求。不過即使是直接用這種插件,也不能認(rèn)為它就能一勞永逸,還是需要對ES內(nèi)部的機制(例如mapping)和通常的查詢方式(term/query_string/wild_card等)很了解才行。
比如必須知道wildcard查詢必須對字符串字段設(shè)置為not_analyzed。還得知道term什么時候代表的是分詞后的詞,什么時候代表的是整個字段的值。
在了解了這些之后才會了解到ES的高性能like,其實也還是有一些限制。例如輸入的字符串會被分詞,這也就是說,想要高性能的時候只能用ES默認(rèn)提供的基于詞的字符串like,而且一旦分詞,就沒辦法實現(xiàn)類似sql里的 x= "Hello world"這種準(zhǔn)確匹配的邏輯。也就是說,在ES里查詢hello world,hello world fuck也會出現(xiàn)在結(jié)果當(dāng)中。不過這個對于大多數(shù)的業(yè)務(wù)來說實際上是無所謂的。
檢索服務(wù)方面搜索是人類的自然需求。如果不是的話,那Google和百度就不會誕生了。
而檢索/搜索的基本原理就是對語句進行分詞,然后再形成倒排索引,再根據(jù)詞項出現(xiàn)次數(shù)對文檔進行打分,最終按分?jǐn)?shù)倒序展示給用戶。
對于海量數(shù)據(jù)的公司來說,一個單機的方案很快就會遇到瓶頸,而去尋求或自行開發(fā)更好的解決方案。在ES之前solr更流行一些吧,不過solr的配置還是稍微麻煩,而es的集群搭建只要改改yml就好了。
有了ES以后,集群便可以非常方便地進行動態(tài)擴展。只要加硬盤加機器改配置就好,因為本身的副本分布策略比較科學(xué)。所以只要別一半以上的節(jié)點都掛掉,數(shù)據(jù)就不會丟失。而且還會在某些結(jié)點掛掉的時候自動進行分片relocate。
由于ES本身帶的分詞不是很科學(xué),這樣的話對doc打分可能會有一些影響。比如中國人可能不正確地分成了中/國人之類的?,F(xiàn)在很多人會選擇以插件的形式把ik分詞器之類的插件掛載到es上來改善分詞效果。這些插件的本質(zhì)其實還是一個非常龐大的中文詞庫。內(nèi)部設(shè)計有鏈接可以直接查看語句的分詞結(jié)果,可以方便地直接查看效果。
所以要是有幾億的文檔需要做些檢索,那五六臺配置不錯的ES機器就足夠了,甚至都不用ssd。
日志方面企業(yè)里的系統(tǒng)一般都是分布式系統(tǒng),所以無論是接入,還是api,還是db,都不太可能在一臺機器上完成需求。
對于某一個服務(wù)模塊來說,多臺機器最麻煩的就是去查問題。在沒有日志系統(tǒng)的時代,程序員大概只能登陸到機器去一臺一臺尋找可能的錯誤日志,然而因為負(fù)載均衡算法(比如可能是一致性哈希望/隨機/RR/WR)的問題,可能一個用戶在一次訪問會話(session)中的請求都不是一臺而是多臺機器完成的響應(yīng)。
所以日志系統(tǒng)的工作就是把日志匯集到一起,并提供統(tǒng)一的查詢?nèi)肟凇?/p>
要收集日志一般會自行搭建一個elk平臺,elasticsearch/logstash/kibana必不可少。
不過拿來的東西總會有那么一些問題,比如kibana里的按地圖出數(shù)據(jù)默認(rèn)用的是googlemap,在墻內(nèi)使會有些問題,這個問題github上也有人已經(jīng)解決了。再比如logstash這個程序可能只考慮了簡單的收集 ,如果是大公司的業(yè)務(wù)講究一個嚴(yán)謹(jǐn)。例如想要對日志收集端的資源使用做一些限制,不能隨便占用系統(tǒng)資源而影響到業(yè)務(wù)系統(tǒng)。再比如還希望日志不要因為網(wǎng)絡(luò)閃斷之類的問題導(dǎo)致日志丟失什么的,所以還可能會在logstash后面再加一個kafka/redis。不管怎么說,工作基礎(chǔ)還是elk。
日志系統(tǒng)還存在一個問題,因為海量的數(shù)據(jù)和海量的訪問,日志的數(shù)據(jù)量一般都非常地龐大。所以一般數(shù)據(jù)都會有一個過期時間,一般來說,日志數(shù)據(jù)其實一般也就一周或者一個月。畢竟即使是一個邊緣部門,一周的日志也都已經(jīng)幾個億(100+GB)了。
查詢起來也不希望太慢,所以還是盡量把日志索引的大小控制在一個范圍內(nèi)。當(dāng)然,也有按照日期來生成索引的。每一天在一個獨立的索引下,這樣查詢性能也會好一些。
同時又是因為這海量的數(shù)據(jù),在寫入到ES的時候必須使用bulk端口,相信使用過ES的人都知道使用和不使用分別意味著什么。