貸前系統(tǒng)ElasticSearch實(shí)踐總結(jié)-創(chuàng)新互聯(lián)

貸前系統(tǒng)負(fù)責(zé)從進(jìn)件到放款前所有業(yè)務(wù)流程的實(shí)現(xiàn),其中涉及一些數(shù)據(jù)量較大、條件多樣且復(fù)雜的綜合查詢,引入ElasticSearch主要是為了提高查詢效率,并希望基于ElasticSearch快速實(shí)現(xiàn)一個(gè)簡(jiǎn)易的數(shù)據(jù)倉(cāng)庫(kù),提供一些OLAP相關(guān)功能。本文將介紹貸前系統(tǒng)ElasticSearch的實(shí)踐經(jīng)驗(yàn)。

十載的望奎網(wǎng)站建設(shè)經(jīng)驗(yàn),針對(duì)設(shè)計(jì)、前端、開(kāi)發(fā)、售后、文案、推廣等六對(duì)一服務(wù),響應(yīng)快,48小時(shí)及時(shí)工作處理。全網(wǎng)整合營(yíng)銷推廣的優(yōu)勢(shì)是能夠根據(jù)用戶設(shè)備顯示端的尺寸不同,自動(dòng)調(diào)整望奎建站的顯示方式,使網(wǎng)站能夠適用不同顯示終端,在瀏覽器中調(diào)整網(wǎng)站的寬度,無(wú)論在任何一種瀏覽器上瀏覽網(wǎng)站,都能展現(xiàn)優(yōu)雅布局與設(shè)計(jì),從而大程度地提升瀏覽體驗(yàn)。創(chuàng)新互聯(lián)公司從事“望奎網(wǎng)站設(shè)計(jì)”,“望奎網(wǎng)站推廣”以來(lái),每個(gè)客戶項(xiàng)目都認(rèn)真落實(shí)執(zhí)行。

一、索引

描述:為快速定位數(shù)據(jù)而設(shè)計(jì)的某種數(shù)據(jù)結(jié)構(gòu)。

索引好比是一本書(shū)前面的目錄,能加快數(shù)據(jù)庫(kù)的查詢速度。了解索引的構(gòu)造及使用,對(duì)理解ES的工作模式有非常大的幫助。

常用索引:

  • 位圖索引

  • 哈希索引

  • BTREE索引

  • 倒排索引

1.1 位圖索引(BitMap)

位圖索引適用于字段值為可枚舉的有限個(gè)數(shù)值的情況。

位圖索引使用二進(jìn)制的數(shù)字串(bitMap)標(biāo)識(shí)數(shù)據(jù)是否存在,1標(biāo)識(shí)當(dāng)前位置(序號(hào))存在數(shù)據(jù),0則表示當(dāng)前位置沒(méi)有數(shù)據(jù)。

下圖1 為用戶表,存儲(chǔ)了性別和婚姻狀況兩個(gè)字段;

圖2中分別為性別和婚姻狀態(tài)建立了兩個(gè)位圖索引。

例如:性別->男 對(duì)應(yīng)索引為:101110011,表示第1、3、4、5、8、9個(gè)用戶為男性。其他屬性以此類推。

貸前系統(tǒng)ElasticSearch實(shí)踐總結(jié)

使用位圖索引查詢:

  • 男性 并且已婚 的記錄 = 101110011 & 11010010 = 100100010,即第1、4、8個(gè)用戶為已婚男性。

  • 女性 或者未婚的記錄 = 010001100 | 001010100 = 011011100, 即第2、3、5、6、7個(gè)用戶為女性或者未婚。

1.2 哈希索引

顧名思義,是指使用某種哈希函數(shù)實(shí)現(xiàn)key->value 映射的索引結(jié)構(gòu)。

哈希索引適用于等值檢索,通過(guò)一次哈希計(jì)算即可定位數(shù)據(jù)的位置。

下圖3 展示了哈希索引的結(jié)構(gòu),與JAVA中HashMap的實(shí)現(xiàn)類似,是用沖突表的方式解決哈希沖突的。

貸前系統(tǒng)ElasticSearch實(shí)踐總結(jié)

1.3 BTREE索引

BTREE索引是關(guān)系型數(shù)據(jù)庫(kù)最常用的索引結(jié)構(gòu),方便了數(shù)據(jù)的查詢操作。

BTREE: 有序平衡N階樹(shù), 每個(gè)節(jié)點(diǎn)有N個(gè)鍵值和N+1個(gè)指針, 指向N+1個(gè)子節(jié)點(diǎn)。

一棵BTREE的簡(jiǎn)單結(jié)構(gòu)如下圖4所示,為一棵2層的3叉樹(shù),有7條數(shù)據(jù):

貸前系統(tǒng)ElasticSearch實(shí)踐總結(jié)

以Mysql最常用的InnoDB引擎為例,描述下BTREE索引的應(yīng)用。

Innodb下的表都是以索引組織表形式存儲(chǔ)的,也就是整個(gè)數(shù)據(jù)表的存儲(chǔ)都是B+tree結(jié)構(gòu)的,如圖5所示。

貸前系統(tǒng)ElasticSearch實(shí)踐總結(jié)

主鍵索引為圖5的左半部分(如果沒(méi)有顯式定義自主主鍵,就用不為空的唯一索引來(lái)做聚簇索引,如果也沒(méi)有唯一索引,則innodb內(nèi)部會(huì)自動(dòng)生成6字節(jié)的隱藏主鍵來(lái)做聚簇索引),葉子節(jié)點(diǎn)存儲(chǔ)了完整的數(shù)據(jù)行信息(以主鍵 + row_data形式存儲(chǔ))。

二級(jí)索引也是以B+tree的形式進(jìn)行存儲(chǔ),圖5右半部分,與主鍵不同的是二級(jí)索引的葉子節(jié)點(diǎn)存儲(chǔ)的不是行數(shù)據(jù),而是索引鍵值和對(duì)應(yīng)的主鍵值,由此可以推斷出,二級(jí)索引查詢多了一步查找數(shù)據(jù)主鍵的過(guò)程。

維護(hù)一顆有序平衡N叉樹(shù),比較復(fù)雜的就是當(dāng)插入節(jié)點(diǎn)時(shí)節(jié)點(diǎn)位置的調(diào)整,尤其是插入的節(jié)點(diǎn)是隨機(jī)無(wú)序的情況;而插入有序的節(jié)點(diǎn),節(jié)點(diǎn)的調(diào)整只發(fā)生了整個(gè)樹(shù)的局部,影響范圍較小,效率較高。

可以參考紅黑樹(shù)的節(jié)點(diǎn)的插入算法:

https://en.wikipedia.org/wiki/Red%E2%80%93black_tree

因此如果innodb表有自增主鍵,則數(shù)據(jù)寫(xiě)入是有序?qū)懭氲?,效率?huì)很高;如果innodb表沒(méi)有自增的主鍵,插入隨機(jī)的主鍵值,將導(dǎo)致B+tree的大量的變動(dòng)操作,效率較低。這也是為什么會(huì)建議innodb表要有無(wú)業(yè)務(wù)意義的自增主鍵,可以大大提高數(shù)據(jù)插入效率。

注:

  • Mysql Innodb使用自增主鍵的插入效率高。

  • 使用類似Snowflake的ID生成算法,生成的ID是趨勢(shì)遞增的,插入效率也比較高。

1.4 倒排索引(反向索引)

倒排索引也叫反向索引,可以相對(duì)于正向索引進(jìn)行比較理解。

正向索引反映了一篇文檔與文檔中關(guān)鍵詞之間的對(duì)應(yīng)關(guān)系;給定文檔標(biāo)識(shí),可以獲取當(dāng)前文檔的關(guān)鍵詞、詞頻以及該詞在文檔中出現(xiàn)的位置信息,如圖6 所示,左側(cè)是文檔,右側(cè)是索引。

貸前系統(tǒng)ElasticSearch實(shí)踐總結(jié)

反向索引則是指某關(guān)鍵詞和該詞所在的文檔之間的對(duì)應(yīng)關(guān)系;給定了關(guān)鍵詞標(biāo)識(shí),可以獲取關(guān)鍵詞所在的所有文檔列表,同時(shí)包含詞頻、位置等信息,如圖7所示。

貸前系統(tǒng)ElasticSearch實(shí)踐總結(jié)

反向索引(倒排索引)的單詞的集合和文檔的集合就組成了如圖8所示的”單詞-文檔矩陣“,打鉤的單元格表示存在該單詞和文檔的映射關(guān)系。

貸前系統(tǒng)ElasticSearch實(shí)踐總結(jié)

倒排索引的存儲(chǔ)結(jié)構(gòu)可以參考圖9。其中詞典是存放的內(nèi)存里的,詞典就是整個(gè)文檔集合中解析出的所有單詞的列表集合;每個(gè)單詞又指向了其對(duì)應(yīng)的倒排列表,倒排列表的集合組成了倒排文件,倒排文件存放在磁盤(pán)上,其中的倒排列表內(nèi)記錄了對(duì)應(yīng)單詞在文檔中信息,即前面提到的詞頻、位置等信息。

貸前系統(tǒng)ElasticSearch實(shí)踐總結(jié)

下面以一個(gè)具體的例子來(lái)描述下,如何從一個(gè)文檔集合中生成倒排索引。

如圖10,共存在5個(gè)文檔,第一列為文檔編號(hào),第二列為文檔的文本內(nèi)容。

貸前系統(tǒng)ElasticSearch實(shí)踐總結(jié)

將上述文檔集合進(jìn)行分詞解析,其中發(fā)現(xiàn)的10個(gè)單詞為:[谷歌,地圖,之父,跳槽,F(xiàn)acebook,×××,創(chuàng)始人,拉斯,離開(kāi),與],以第一個(gè)單詞”谷歌“為例:首先為其賦予一個(gè)唯一標(biāo)識(shí) ”單詞ID“, 值為1,統(tǒng)計(jì)出文檔頻率為5,即5個(gè)文檔都有出現(xiàn),除了在第3個(gè)文檔中出現(xiàn)2次外,其余文檔都出現(xiàn)一次,于是就有了圖11所示的倒排索引。

貸前系統(tǒng)ElasticSearch實(shí)踐總結(jié)

1.4.1 單詞詞典查詢優(yōu)化

對(duì)于一個(gè)規(guī)模很大的文檔集合來(lái)說(shuō),可能包含幾十萬(wàn)甚至上百萬(wàn)的不同單詞,能否快速定位某個(gè)單詞,這直接影響搜索時(shí)的響應(yīng)速度,其中的優(yōu)化方案就是為單詞詞典建立索引,有以下幾種方案可供參考:

  • 詞典Hash索引

Hash索引簡(jiǎn)單直接,查詢某個(gè)單詞,通過(guò)計(jì)算哈希函數(shù),如果哈希表命中則表示存在該數(shù)據(jù),否則直接返回空就可以;適合于完全匹配,等值查詢。如圖12,相同hash值的單詞會(huì)放在一個(gè)沖突表中。

貸前系統(tǒng)ElasticSearch實(shí)踐總結(jié)

  • 詞典BTREE索引

類似于Innodb的二級(jí)索引,將單詞按照一定的規(guī)則排序,生成一個(gè)BTree索引,數(shù)據(jù)節(jié)點(diǎn)為指向倒排索引的指針。

貸前系統(tǒng)ElasticSearch實(shí)踐總結(jié)

  • 二分查找

同樣將單詞按照一定的規(guī)則排序,建立一個(gè)有序單詞數(shù)組,在查找時(shí)使用二分查找法;二分查找法可以映射為一個(gè)有序平衡二叉樹(shù),如圖14這樣的結(jié)構(gòu)。

貸前系統(tǒng)ElasticSearch實(shí)踐總結(jié)

  • FST(Finite State Transducers )實(shí)現(xiàn)

FST為一種有限狀態(tài)轉(zhuǎn)移機(jī),F(xiàn)ST有兩個(gè)優(yōu)點(diǎn):1)空間占用小。通過(guò)對(duì)詞典中單詞前綴和后綴的重復(fù)利用,壓縮了存儲(chǔ)空間;2)查詢速度快。O(len(str))的查詢時(shí)間復(fù)雜度。

以插入“cat”、 “deep”、 “do”、 “dog” 、“dogs”這5個(gè)單詞為例構(gòu)建FST(注:必須已排序)。

貸前系統(tǒng)ElasticSearch實(shí)踐總結(jié)

如圖15 最終我們得到了如上一個(gè)有向無(wú)環(huán)圖。利用該結(jié)構(gòu)可以很方便的進(jìn)行查詢,如給定一個(gè)詞 “dog”,我們可以通過(guò)上述結(jié)構(gòu)很方便的查詢存不存在,甚至我們?cè)跇?gòu)建過(guò)程中可以將單詞與某一數(shù)字、單詞進(jìn)行關(guān)聯(lián),從而實(shí)現(xiàn)key-value的映射。

當(dāng)然還有其他的優(yōu)化方式,如使用Skip List、Trie、Double Array Trie等結(jié)構(gòu)進(jìn)行優(yōu)化,不再一一贅述。

二、ElasticSearch使用心得

下面結(jié)合貸前系統(tǒng)具體的使用案例,介紹ES的一些心得總結(jié)。

2.1 概況

目前使用的ES版本:5.6

官網(wǎng)地址:https://www.elastic.co/products/elasticsearch

ES一句話介紹:The Heart of the Elastic Stack(摘自官網(wǎng))

ES的一些關(guān)鍵信息:

  • 2010年2月首次發(fā)布

  • Elasticsearch Store, Search, and Analyze

  • 豐富的Restful接口

2.2 基本概念

  • 索引(index)

ES的索引,也就是Index,和前面提到的索引并不是一個(gè)概念,這里是指所有文檔的集合,可以類比為RDB中的一個(gè)數(shù)據(jù)庫(kù)。

  • 文檔(document)

即寫(xiě)入ES的一條記錄,一般是JSON形式的。

  • 映射(Mapping)

文檔數(shù)據(jù)結(jié)構(gòu)的元數(shù)據(jù)描述,一般是JSON schema形式,可動(dòng)態(tài)生成或提前預(yù)定義。

  • 類型(type)

由于理解和使用上的錯(cuò)誤,type已不推薦使用,目前我們使用的ES中一個(gè)索引只建立了一個(gè)默認(rèn)type。

  • 節(jié)點(diǎn)

一個(gè)ES的服務(wù)實(shí)例,稱為一個(gè)服務(wù)節(jié)點(diǎn)。為了實(shí)現(xiàn)數(shù)據(jù)的安全可靠,并且提高數(shù)據(jù)的查詢性能,ES一般采用集群模式進(jìn)行部署。

  • 集群

多個(gè)ES節(jié)點(diǎn)相互通信,共同分擔(dān)數(shù)據(jù)的存儲(chǔ)及查詢,這樣就構(gòu)成了一個(gè)集群。

  • 分片

分片主要是為解決大量數(shù)據(jù)的存儲(chǔ),將數(shù)據(jù)分割為若干部分,分片一般是均勻分布在各ES節(jié)點(diǎn)上的。需要注意:分片數(shù)量無(wú)法修改。

  • 副本

分片數(shù)據(jù)的一份完全的復(fù)制,一般一個(gè)分片會(huì)有一個(gè)副本,副本可以提供數(shù)據(jù)查詢,集群環(huán)境下可以提高查詢性能。

2.3 安裝部署

  • JDK版本: JDK1.8

  • 安裝過(guò)程比較簡(jiǎn)單,可參考官網(wǎng):下載安裝包 -> 解壓 -> 運(yùn)行

  • 安裝過(guò)程遇到的坑:

ES啟動(dòng)占用的系統(tǒng)資源比較多,需要調(diào)整諸如文件句柄數(shù)、線程數(shù)、內(nèi)存等系統(tǒng)參數(shù),可參考下面的文檔。

http://www.cnblogs.com/sloveling/p/elasticsearch.html

2.4 實(shí)例講解

下面以一些具體的操作介紹ES的使用:

2.4.1 初始化索引

初始化索引,主要是在ES中新建一個(gè)索引并初始化一些參數(shù),包括索引名、文檔映射(Mapping)、索引別名、分片數(shù)(默認(rèn):5)、副本數(shù)(默認(rèn):1)等,其中分片數(shù)和副本數(shù)在數(shù)據(jù)量不大的情況下直接使用默認(rèn)值即可,無(wú)需配置。

下面舉兩個(gè)初始化索引的方式,一個(gè)使用基于Dynamic Template(動(dòng)態(tài)模板) 的Dynamic Mapping(動(dòng)態(tài)映射),一個(gè)使用顯式預(yù)定義映射。

1) 動(dòng)態(tài)模板 (Dynamic Template)

<p ><span >curl -X PUT http://ip:9200/loan_idx -H 'content-type: application/json' <br>    -d '{"mappings":{ "order_info":{ "dynamic_date_formats":["yyyy-MM-dd HH:mm:ss||yyyy-MM-dd],<br> "dynamic_templates":[<br> {"orderId2":{<br> "match_mapping_type":"string",<br> "match_pattern":"regex",<br> "match":"^orderId$",<br> "mapping":{<br> "type":"long"<br> }<br> }<br> },<br> {"strings_as_keywords":{<br> "match_mapping_type":"string",<br> "mapping":{<br> "type":"keyword",<br> "norms":false<br> }<br> }<br> }<br> ]<br> }<br>},<br>"aliases":{<br> "loan_alias":{}<br>}}'<br></span></p>

上面的JSON串就是我們用到的動(dòng)態(tài)模板,其中定義了日期格式:dynamic_date_formats 字段;定義了規(guī)則orderId2:凡是遇到orderId這個(gè)字段,則將其轉(zhuǎn)換為long型;定義了規(guī)則strings_as_keywords:凡是遇到string類型的字段都映射為keyword類型,norms屬性為false;關(guān)于keyword類型和norms關(guān)鍵字,將在下面的數(shù)據(jù)類型小節(jié)介紹。

2)預(yù)定義映射

預(yù)定義映射和上面的區(qū)別就是預(yù)先把所有已知的字段類型描述寫(xiě)到mapping里,下圖截取了一部分作為示例:

貸前系統(tǒng)ElasticSearch實(shí)踐總結(jié)

圖16中JSON結(jié)構(gòu)的上半部分與動(dòng)態(tài)模板相同,紅框中內(nèi)容內(nèi)容為預(yù)先定義的屬性:apply.applyInfo.appSubmissionTime, apply.applyInfo.applyId, apply.applyInfo.applyInputSource等字段,type表明了該字段的類型,映射定義完成后,再插入的數(shù)據(jù)必須符合字段定義,否則ES將返回異常。

2.4.2 常用數(shù)據(jù)類型

常用的數(shù)據(jù)類型有text, keyword, date, long, double, boolean, ip

實(shí)際使用中,將字符串類型定義為keyword而不是text,主要原因是text類型的數(shù)據(jù)會(huì)被當(dāng)做文本進(jìn)行語(yǔ)法分析,做一些分詞、過(guò)濾等操作,而keyword類型則是當(dāng)做一個(gè)完整數(shù)據(jù)存儲(chǔ)起來(lái),省去了多余的操作,提高索引性能。

配合keyword使用的還有一個(gè)關(guān)鍵詞norm,置為false表示當(dāng)前字段不參與評(píng)分;所謂評(píng)分是指根據(jù)單詞的TF/IDF或其他一些規(guī)則,對(duì)查詢出的結(jié)果賦予一個(gè)分值,供展示搜索結(jié)果時(shí)進(jìn)行排序, 而一般的業(yè)務(wù)場(chǎng)景并不需要這樣的排序操作(都有明確的排序字段),從而進(jìn)一步優(yōu)化查詢效率。

2.4.3 索引名無(wú)法修改

初始化一個(gè)索引,都要在URL中明確指定一個(gè)索引名,一旦指定則無(wú)法修改,所以一般建立索引都要指定一個(gè)默認(rèn)的別名(alias):

<p ><span >"aliases":{ "loan_alias":{ }<br> }<br></span></p>

別名和索引名是多對(duì)多的關(guān)系,也就是一個(gè)索引可以有多個(gè)別名,一個(gè)別名也可以映射多個(gè)索引;在一對(duì)一這種模式下,所有用到索引名的地方都可以用別名進(jìn)行替換;別名的好處就是可以隨時(shí)的變動(dòng),非常靈活。

2.4.4 Mapping中已存在的字段無(wú)法更新

如果一個(gè)字段已經(jīng)初始化完畢(動(dòng)態(tài)映射通過(guò)插入數(shù)據(jù),預(yù)定義通過(guò)設(shè)置字段類型),那就確定了該字段的類型,插入不兼容的數(shù)據(jù)則會(huì)報(bào)錯(cuò),比如定義了一個(gè)long類型字段,如果寫(xiě)入一個(gè)非數(shù)字類型的數(shù)據(jù),ES則會(huì)返回?cái)?shù)據(jù)類型錯(cuò)誤的提示。

這種情況下可能就需要重建索引,上面講到的別名就派上了用場(chǎng);一般分3步完成:

  • 新建一個(gè)索引將格式錯(cuò)誤的字段指定為正確格式;
  • 2)使用ES的Reindex API將數(shù)據(jù)從舊索引遷移到新索引;
  • 3)使用Aliases API將舊索引的別名添加到新索引上,刪除舊索引和別名的關(guān)聯(lián)。

上述步驟適合于離線遷移,如果要實(shí)現(xiàn)不停機(jī)實(shí)時(shí)遷移步驟會(huì)稍微復(fù)雜些。

2.4.5 API

基本的操作就是增刪改查,可以參考ES的官方文檔:

https://www.elastic.co/guide/en/elasticsearch/reference/current/docs.html

一些比較復(fù)雜的操作需要用到ES Script,一般使用類Groovy的painless script,這種腳本支持一些常用的JAVA API(ES安裝使用的是JDK8,所以支持一些JDK8的API),還支持Joda time等。

舉個(gè)比較復(fù)雜的更新的例子,說(shuō)明painless script如何使用:

需求描述

appSubmissionTime表示進(jìn)件時(shí)間,lenssonStartDate表示開(kāi)課時(shí)間,expectLoanDate表示放款時(shí)間。要求2018年9月10日的進(jìn)件,如果進(jìn)件時(shí)間 與 開(kāi)課時(shí)間的日期差小于2天,則將放款時(shí)間設(shè)置為進(jìn)件時(shí)間。

Painless Script如下:

<p ><span >POST loan_idx/_update_by_query<br>    { "script":{ "source":"long getDayDiff(def dateStr1, def dateStr2){ <br>    LocalDateTime date1= toLocalDate(dateStr1); LocalDateTime date2= toLocalDate(dateStr2); ChronoUnit.DAYS.between(date1, date2);<br>  }<br>  LocalDateTime toLocalDate(def dateStr)<br>   { <br>    DateTimeFormatter formatter = DateTimeFormatter.ofPattern(\"yyyy-MM-dd HH:mm:ss\"); LocalDateTime.parse(dateStr, formatter);<br>     }<br>  if(getDayDiff(ctx._source.appSubmissionTime, ctx._source.lenssonStartDate) < 2)<br>  { <br>    ctx._source.expectLoanDate=ctx._source.appSubmissionTime<br>   }", "lang":"painless"<br> }<br> , "query":<br> { "bool":{ "filter":[<br> { "bool":{ "must":[<br> { "range":{ <br> "appSubmissionTime":<br> {<br>  "from":"2018-09-10 00:00:00", "to":"2018-09-10 23:59:59", "include_lower":true, "include_upper":true<br> }<br> }<br> }<br> ]<br> }<br> }<br> ]<br> }<br> }<br>}<br></span></p>

解釋:整個(gè)文本分兩部分,下半部分query關(guān)鍵字表示一個(gè)按范圍時(shí)間查詢(2018年9月10號(hào)),上半部分script表示對(duì)匹配到的記錄進(jìn)行的操作,是一段類Groovy代碼(有Java基礎(chǔ)很容易讀懂),格式化后如下, 其中定義了兩個(gè)方法getDayDiff()和toLocalDate(),if語(yǔ)句里包含了具體的操作:

<p ><span >long getDayDiff(def dateStr1, def dateStr2){<br> LocalDateTime date1= toLocalDate(dateStr1);<br> LocalDateTime date2= toLocalDate(dateStr2);<br> ChronoUnit.DAYS.between(date1, date2);<br>}<br>LocalDateTime toLocalDate(def dateStr){<br> DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");<br> LocalDateTime.parse(dateStr, formatter);<br>}if(getDayDiff(ctx._source.appSubmissionTime, ctx._source.lenssonStartDate) < 2){<br> ctx._source.expectLoanDate=ctx._source.appSubmissionTime<br>}<br></span></p>

然后提交該P(yáng)OST請(qǐng)求,完成數(shù)據(jù)修改。

2.4.6 查詢數(shù)據(jù)

這里重點(diǎn)推薦一個(gè)ES的插件ES-SQL:

https://github.com/NLPchina/elasticsearch-sql/wiki/Basic-Queries-And-Conditions

這個(gè)插件提供了比較豐富的SQL查詢語(yǔ)法,讓我們可以使用熟悉的SQL語(yǔ)句進(jìn)行數(shù)據(jù)查詢。其中,有幾個(gè)需要注意的點(diǎn):

  • ES-SQL使用Http GET方式發(fā)送情況,所以SQL的長(zhǎng)度是受限制的(4kb),可以通過(guò)以下參數(shù)進(jìn)行修改:http.max_initial_line_length: "8k"

  • 計(jì)算總和、平均值這些數(shù)字操作,如果字段被設(shè)置為非數(shù)值類型,直接使用ESQL會(huì)報(bào)錯(cuò),可改用painless腳本。

  • 使用Select as語(yǔ)法查詢出的結(jié)果和一般的查詢結(jié)果,數(shù)據(jù)的位置結(jié)構(gòu)是不同的,需要單獨(dú)處理。

  • NRT(Near Real Time):準(zhǔn)實(shí)時(shí)

向ES中插入一條記錄,然后再查詢出來(lái),一般都能查出最新的記錄,ES給人的感覺(jué)就是一個(gè)實(shí)時(shí)的搜索引擎,這也是我們所期望的,然而實(shí)際情況卻并非總是如此,這跟ES的寫(xiě)入機(jī)制有關(guān),做個(gè)簡(jiǎn)單介紹:

  • Lucene 索引段 -> ES 索引

寫(xiě)入ES的數(shù)據(jù),首先是寫(xiě)入到Lucene索引段中的,然后才寫(xiě)入ES的索引中,在寫(xiě)入ES索引前查到的都是舊數(shù)據(jù)。

  • commit:原子寫(xiě)操作

索引段中的數(shù)據(jù)會(huì)以原子寫(xiě)的方式寫(xiě)入到ES索引中,所以提交到ES的一條記錄,能夠保證完全寫(xiě)入成功,而不用擔(dān)心只寫(xiě)入了一部分,而另一部分寫(xiě)入失敗。

  • refresh:刷新操作,可以保證最新的提交被搜索到

索引段提交后還有最后一個(gè)步驟:refresh,這步完成后才能保證新索引的數(shù)據(jù)能被搜索到。

出于性能考慮,Lucene推遲了耗時(shí)的刷新,因此它不會(huì)在每次新增一個(gè)文檔的時(shí)候刷新,默認(rèn)每秒刷新一次。這種刷新已經(jīng)非常頻繁了,然而有很多應(yīng)用卻需要更快的刷新頻率。如果碰到這種狀況,要么使用其他技術(shù),要么審視需求是否合理。

不過(guò),ES給我們提供了方便的實(shí)時(shí)查詢接口,使用該接口查詢出的數(shù)據(jù)總是最新的,調(diào)用方式描述如下:

GET http://IP:PORT/index\_name/type\_name/id

上述接口使用了HTTP GET方法,基于數(shù)據(jù)主鍵(id)進(jìn)行查詢,這種查詢方式會(huì)同時(shí)查找ES索引和Lucene索引段中的數(shù)據(jù),并進(jìn)行合并,所以最終結(jié)果總是最新的。但有個(gè)副作用:每次執(zhí)行完這個(gè)操作,ES就會(huì)強(qiáng)制執(zhí)行refresh操作,導(dǎo)致一次IO,如果使用頻繁,對(duì)ES性能也會(huì)有影響。

2.4.7 數(shù)組處理

數(shù)組的處理比較特殊,拿出來(lái)單獨(dú)講一下。

1)表示方式就是普通的JSON數(shù)組格式,如:

[1, 2, 3]、 [“a”, “b”]、 [ { "first" : "John", "last" : "Smith" },{"first" : "Alice", "last" : "White"} ]

2)需要注意ES中并不存在數(shù)組類型,最終會(huì)被轉(zhuǎn)換為object,keyword等類型。

3)普通數(shù)組對(duì)象查詢的問(wèn)題。

普通數(shù)組對(duì)象的存儲(chǔ),會(huì)把數(shù)據(jù)打平后將字段單獨(dú)存儲(chǔ),如:

<p ><span >{ "user":[<br> { "first":"John", "last":"Smith"<br> },<br> { "first":"Alice", "last":"White"<br> }<br> ]<br>}<br></span></p>

會(huì)轉(zhuǎn)化為下面的文本

<p ><span >{ "user.first":[ "John", "Alice"<br> ], "user.last":[ "Smith", "White"<br> ]<br>}<br></span></p>

將原來(lái)文本之間的關(guān)聯(lián)打破了,圖17展示了這條數(shù)據(jù)從進(jìn)入索引到查詢出來(lái)的簡(jiǎn)略過(guò)程:

  • 組裝數(shù)據(jù),一個(gè)JSONArray結(jié)構(gòu)的文本。

  • 寫(xiě)入ES后,默認(rèn)類型置為object。

  • 查詢user.first為Alice并且user.last為Smith的文檔(實(shí)際并不存在同時(shí)滿足這兩個(gè)條件的)。

  • 返回了和預(yù)期不符的結(jié)果。

貸前系統(tǒng)ElasticSearch實(shí)踐總結(jié)

4)嵌套(Nested)數(shù)組對(duì)象查詢

嵌套數(shù)組對(duì)象可以解決上面查詢不符的問(wèn)題,ES的解決方案就是為數(shù)組中的每個(gè)對(duì)象單獨(dú)建立一個(gè)文檔,獨(dú)立于原始文檔。如圖18所示,將數(shù)據(jù)聲明為nested后,再進(jìn)行相同的查詢,返回的是空,因?yàn)榇_實(shí)不存在user.first為Alice并且user.last為Smith的文檔。

貸前系統(tǒng)ElasticSearch實(shí)踐總結(jié)

5)一般對(duì)數(shù)組的修改是全量的,如果需要單獨(dú)修改某個(gè)字段,需要借助painless script,參考:https://www.elastic.co/guide/en/elasticsearch/reference/5.6/docs-update.html

2.5 安全

數(shù)據(jù)安全是至關(guān)重要的環(huán)節(jié),主要通過(guò)以下三點(diǎn)提供數(shù)據(jù)的訪問(wèn)安全控制:

  • XPACK

XPACK提供了Security插件,可以提供基于用戶名密碼的訪問(wèn)控制,可以提供一個(gè)月的免費(fèi)試用期,過(guò)后收取一定的費(fèi)用換取一個(gè)license。

  • IP白名單

是指在ES服務(wù)器開(kāi)啟防火墻,配置只有內(nèi)網(wǎng)中若干服務(wù)器可以直接連接本服務(wù)。

  • 代理

一般不允許業(yè)務(wù)系統(tǒng)直連ES服務(wù)進(jìn)行查詢,需要對(duì)ES接口做一層包裝,這個(gè)工作就需要代理去完成;并且代理服務(wù)器可以做一些安全認(rèn)證工作,即使不適用XPACK也可以實(shí)現(xiàn)安全控制。

2.6 網(wǎng)絡(luò)

ElasticSearch服務(wù)器默認(rèn)需要開(kāi)通9200、9300 這兩個(gè)端口。

下面主要介紹一個(gè)和網(wǎng)絡(luò)相關(guān)的錯(cuò)誤,如果大家遇到類似的錯(cuò)誤,可以做個(gè)借鑒。

  • 引出異常前,先介紹一個(gè)網(wǎng)絡(luò)相關(guān)的關(guān)鍵詞,keepalive :

  • Http keep-alive和Tcp keepalive。

HTTP1.1中默認(rèn)啟用"Connection: Keep-Alive",表示這個(gè)HTTP連接可以復(fù)用,下次的HTTP請(qǐng)求就可以直接使用當(dāng)前連接,從而提高性能,一般HTTP連接池實(shí)現(xiàn)都用到keep-alive;

TCP的keepalive的作用和HTTP中的不同,TPC中主要用來(lái)實(shí)現(xiàn)連接?;睿嚓P(guān)配置主要是net.ipv4.tcp_keepalive_time這個(gè)參數(shù),表示如果經(jīng)過(guò)多長(zhǎng)時(shí)間(默認(rèn)2小時(shí))一個(gè)TCP連接沒(méi)有交換數(shù)據(jù),就發(fā)送一個(gè)心跳包,探測(cè)下當(dāng)前鏈接是否有效,正常情況下會(huì)收到對(duì)方的ack包,表示這個(gè)連接可用。

下面介紹具體異常信息,描述如下:

兩臺(tái)業(yè)務(wù)服務(wù)器,用restClient(基于HTTPClient,實(shí)現(xiàn)了長(zhǎng)連接)連接的ES集群(集群有三臺(tái)機(jī)器),與ES服務(wù)器分別部署在不同的網(wǎng)段,有個(gè)異常會(huì)有規(guī)律的出現(xiàn):

每天9點(diǎn)左右會(huì)發(fā)生異常Connection reset by peer. 而且是連續(xù)有三個(gè)Connection reset by peer

<p ><span >Caused by: java.io.IOException: Connection reset by peer <br> at sun.nio.ch.FileDispatcherImpl.read0(Native Method) <br> at sun.nio.ch.SocketDispatcher.read(SocketDispatcher.java:39) <br> at sun.nio.ch.IOUtil.readIntoNativeBuffer(IOUtil.java:223) <br> at sun.nio.ch.IOUtil.read(IOUtil.java:197)<br></span></p>

為了解決這個(gè)問(wèn)題,我們嘗試了多種方案,查官方文檔、比對(duì)代碼、抓包。。。經(jīng)過(guò)若干天的努力,最終發(fā)現(xiàn)這個(gè)異常是和上面提到keepalive關(guān)鍵詞相關(guān)(多虧運(yùn)維組的同事幫忙)。

實(shí)際線上環(huán)境,業(yè)務(wù)服務(wù)器和ES集群之間有一道防火墻,而防火墻策略定義空閑連接超時(shí)時(shí)間為例如為1小時(shí),與上面提到的linux服務(wù)器默認(rèn)的例如為2小時(shí)不一致。由于我們當(dāng)前系統(tǒng)晚上訪問(wèn)量較少,導(dǎo)致某些連接超過(guò)2小時(shí)沒(méi)有使用,在其中1小時(shí)后防火墻自動(dòng)就終止了當(dāng)前連接,到了2小時(shí)后服務(wù)器嘗試發(fā)送心跳?;钸B接,直接被防火墻攔截,若干次嘗試后服務(wù)端發(fā)送RST中斷了鏈接,而此時(shí)的客戶端并不知情;當(dāng)?shù)诙煸缟鲜褂眠@個(gè)失效的鏈接請(qǐng)求時(shí),服務(wù)端直接返回RST,客戶端報(bào)錯(cuò)Connection reset by peer,嘗試了集群中的三臺(tái)服務(wù)器都返回同樣錯(cuò)誤,所以連續(xù)報(bào)了3個(gè)相同的異常。解決方案也比較簡(jiǎn)單,修改服務(wù)端keepalive超時(shí)配置,小于防火墻的1小時(shí)即可。

作者:綜合信貸雷鵬

來(lái)源:宜信技術(shù)學(xué)院

另外有需要云服務(wù)器可以了解下創(chuàng)新互聯(lián)scvps.cn,海內(nèi)外云服務(wù)器15元起步,三天無(wú)理由+7*72小時(shí)售后在線,公司持有idc許可證,提供“云服務(wù)器、裸金屬服務(wù)器、高防服務(wù)器、香港服務(wù)器、美國(guó)服務(wù)器、虛擬主機(jī)、免備案服務(wù)器”等云主機(jī)租用服務(wù)以及企業(yè)上云的綜合解決方案,具有“安全穩(wěn)定、簡(jiǎn)單易用、服務(wù)可用性高、性價(jià)比高”等特點(diǎn)與優(yōu)勢(shì),專為企業(yè)上云打造定制,能夠滿足用戶豐富、多元化的應(yīng)用場(chǎng)景需求。

網(wǎng)頁(yè)題目:貸前系統(tǒng)ElasticSearch實(shí)踐總結(jié)-創(chuàng)新互聯(lián)
文章URL:http://www.muchs.cn/article32/dsjipc.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供網(wǎng)站維護(hù)搜索引擎優(yōu)化、網(wǎng)站改版企業(yè)建站、定制開(kāi)發(fā)、面包屑導(dǎo)航

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)

搜索引擎優(yōu)化