上接:【ElasticSearch】從了解到使用
目錄
- ElasticSearch 原理
14.1 es 的分布式架構(gòu)
14.1.1 分布式架構(gòu)的透明隱藏特性
14.1.2 擴(kuò)容機(jī)制
14.1.3 rebalance
14.1.4 master節(jié)點(diǎn)
14.1.5 節(jié)點(diǎn)對等
14.2 分片和副本機(jī)制
14.3 單節(jié)點(diǎn)環(huán)境下創(chuàng)建索引分析
14.4 兩個(gè)節(jié)點(diǎn)環(huán)境下創(chuàng)建索引分析
14.5 水平擴(kuò)容過程
14.6 ElasticSearch 的容錯(cuò)機(jī)制
14.7 文檔的核心元數(shù)據(jù)
14.8 文檔id生成方式
14.9 _source 元數(shù)據(jù)
14.10 改變文檔內(nèi)容解析
14.11 基于groovy腳本執(zhí)行partial update
14.12 partial update 處理并發(fā)沖突
14.13 文檔數(shù)據(jù)路由原理
14.14 文檔增刪改原理
14.15 寫一致性原理和quorum機(jī)制
14.16 文檔查詢原理
14.17 bulk批量操作的json格式解析
14.18 查詢結(jié)果分析
14.19 多index查詢
14.20 分頁查詢中的deep paging問題
14.21 query string查詢及copy_to解析
14.22 字符串排序問題
14.23 如何計(jì)算相關(guān)度分?jǐn)?shù)
14.24 Doc Values 解析
14.25 基于scroll技術(shù)滾動搜索大量數(shù)據(jù)
14.26 dynamic mapping策略
14.27 重建索引
14.28 索引不可變的原因
14. ElasticSearch 原理
14.1 es 的分布式架構(gòu)
14.1.1 分布式架構(gòu)的透明隱藏特性
ElasticSearch 是一個(gè)分布式系統(tǒng),隱藏了復(fù)雜的處理機(jī)制,如:
- 分片和副本機(jī)制:
一個(gè)索引要對應(yīng)若干個(gè)分片,每個(gè)分片都有相應(yīng)的副本,這個(gè)分片是怎么分的,分片和副本放在哪個(gè)節(jié)點(diǎn)上,都不需要關(guān)心 - 集群發(fā)現(xiàn)機(jī)制(cluster discovery):
比如當(dāng)前我們啟動了一個(gè)es進(jìn)程,當(dāng)啟動了第二個(gè)es進(jìn)程時(shí),這個(gè)進(jìn)程作為一個(gè)node自動就發(fā)現(xiàn)了集群,并且加入了進(jìn)去 - shard 負(fù)載均衡:
比如現(xiàn)在有10shard,集群中有3個(gè)節(jié)點(diǎn),es會進(jìn)行均衡的進(jìn)行分配,以保持每個(gè)節(jié)點(diǎn)均衡的負(fù)載請求 - 請求路由
14.1.2 擴(kuò)容機(jī)制
垂直擴(kuò)容:服務(wù)器的臺數(shù)不變,購置新的機(jī)器,替換已有的機(jī)器
水平擴(kuò)容:直接增加機(jī)器
14.1.3 rebalance
增加或減少節(jié)點(diǎn)時(shí)會自動均衡分配分片
14.1.4 master節(jié)點(diǎn)
主節(jié)點(diǎn)的主要職責(zé)是和集群操作相關(guān)的內(nèi)容,如創(chuàng)建或刪除索引,跟蹤哪些節(jié)點(diǎn)是群集的一部分,并決定哪些分片分配給相關(guān)的節(jié)點(diǎn)。穩(wěn)定的主節(jié)點(diǎn)對集群的健康是非常重要的。
14.1.5 節(jié)點(diǎn)對等
每個(gè)節(jié)點(diǎn)都能接收請求
每個(gè)節(jié)點(diǎn)接收到請求后都能把該請求路由到有相關(guān)數(shù)據(jù)的其它節(jié)點(diǎn)上
接收原始請求的節(jié)點(diǎn)負(fù)責(zé)采集數(shù)據(jù)并返回給客戶端
14.2 分片和副本機(jī)制
- 一個(gè) index 可包含多個(gè) shard
- 每個(gè)shard都是一個(gè)最小工作單元,承載部分?jǐn)?shù)據(jù);每個(gè)shard都是一個(gè)lucene實(shí)例,有完整的建立索引和處理請求的能力
- 增減節(jié)點(diǎn)時(shí),shard會自動在nodes中負(fù)載均衡
- primary shard(主分片)和replica shard(副本),每個(gè)document肯定只存在于某一個(gè)primary shard以及其對應(yīng)的replica shard中,不可能存在于多個(gè)primary shard
- replica shard是primary shard的副本,負(fù)責(zé)容錯(cuò),以及承擔(dān)讀請求負(fù)載
- primary shard的數(shù)量在創(chuàng)建索引的時(shí)候就固定了,replica shard的數(shù)量可以隨時(shí)修改
- primary shard的默認(rèn)數(shù)量是5,replica默認(rèn)是1,默認(rèn)有10個(gè)shard,5個(gè)primary shard,5個(gè)replica shard
- primary shard不能和自己的replica shard放在同一個(gè)節(jié)點(diǎn)上(否則節(jié)點(diǎn)宕機(jī),primary shard和副本都丟失,起不到容錯(cuò)的作用),但是可以和其他primary shard的replica shard放在同一個(gè)節(jié)點(diǎn)上
14.3 單節(jié)點(diǎn)環(huán)境下創(chuàng)建索引分析
PUT /myindex
{
"settings" : {
"number_of_shards" : 3,
"number_of_replicas" : 1
}
}
這個(gè)時(shí)候,只會將3個(gè)primary shard分配到僅有的一個(gè)node上去,另外3個(gè)replica shard是無法分配的(一個(gè)shard的副本replica,他們兩個(gè)是不能在同一個(gè)節(jié)點(diǎn)的)。集群可以正常工作,但是一旦出現(xiàn)節(jié)點(diǎn)宕機(jī),數(shù)據(jù)全部丟失,而且集群不可用,無法接收任何請求。
14.4 兩個(gè)節(jié)點(diǎn)環(huán)境下創(chuàng)建索引分析
將3個(gè)primary shard分配到一個(gè)node上去,另外3個(gè)replica shard分配到另一個(gè)節(jié)點(diǎn)上
primary shard 和replica shard 保持同步
primary shard 和replica shard 都可以處理客戶端的讀請求
14.5 水平擴(kuò)容過程
- 擴(kuò)容后primary shard和replica shard會自動的負(fù)載均衡
- 擴(kuò)容后每個(gè)節(jié)點(diǎn)上的shard會減少,那么分配給每個(gè)shard的CPU,內(nèi)存,IO資源會更多,性能提高
- 擴(kuò)容的極限,如果有6個(gè)shard,擴(kuò)容的極限就是6個(gè)節(jié)點(diǎn),每個(gè)節(jié)點(diǎn)上一個(gè)shard,如果想超出擴(kuò)容的極限,比如說擴(kuò)容到9個(gè)節(jié)點(diǎn),那么可以增加replica shard的個(gè)數(shù)
- 6個(gè)shard,3個(gè)節(jié)點(diǎn),最多能承受幾個(gè)節(jié)點(diǎn)所在的服務(wù)器宕機(jī)?(容錯(cuò)性)
任何一臺服務(wù)器宕機(jī)都會丟失部分?jǐn)?shù)據(jù)
為了提高容錯(cuò)性,增加shard的個(gè)數(shù):
9個(gè)shard,(3個(gè)primary shard,6個(gè)replicashard),這樣就能容忍最多兩臺服務(wù)器宕機(jī)了
總結(jié):擴(kuò)容是為了提高系統(tǒng)的吞吐量,同時(shí)也要考慮容錯(cuò)性,也就是讓盡可能多的服務(wù)器宕機(jī)還能保證數(shù)據(jù)不丟失
14.6 ElasticSearch 的容錯(cuò)機(jī)制
以9個(gè)shard,3個(gè)節(jié)點(diǎn)為例:
如果master node 宕機(jī),此時(shí)不是所有的primary shard都是Active status,所以此時(shí)的集群狀態(tài)是red。
- 容錯(cuò)處理的第一步:
是選舉一臺服務(wù)器作為master - 容錯(cuò)處理的第二步:
新選舉出的master會把掛掉的primary shard的某個(gè)replica shard 提升為primary shard,此時(shí)集群的狀態(tài)為yellow,因?yàn)樯倭艘粋€(gè)replica shard,并不是所有的replica shard都是active status - 容錯(cuò)處理的第三步:
重啟故障機(jī),新master會把所有的副本都復(fù)制一份到該節(jié)點(diǎn)上,(同步一下宕機(jī)后發(fā)生的修改),此時(shí)集群的狀態(tài)為green,因?yàn)樗械膒rimary shard和replica shard都是Active status
14.7 文檔的核心元數(shù)據(jù)
1._index:
說明了一個(gè)文檔存儲在哪個(gè)索引中
同一個(gè)索引下存放的是相似的文檔(文檔的field多數(shù)是相同的)
索引名必須是小寫的,不能以下劃線開頭,不能包括逗號
2._type(7.2.0 只有默認(rèn)的 _doc):
表示文檔屬于索引中的哪個(gè)類型
一個(gè)索引下只能有一個(gè)type
類型名可以是大寫也可以是小寫的,不能以下劃線開頭,不能包括逗號
3._id:
文檔的唯一標(biāo)識,和索引,類型組合在一起唯一標(biāo)識了一個(gè)文檔
可以手動指定值,也可以由es來生成這個(gè)值
14.8 文檔id生成方式
1.手動指定
put /index/_doc/66
通常是把其它系統(tǒng)的已有數(shù)據(jù)導(dǎo)入到 es 時(shí)使用
2.由es生成id值
post /index/_doc
es生成的id長度為20個(gè)字符,使用的是base64編碼,URL安全,使用的是GUID算法,分布式下并發(fā)生成id值時(shí)不會沖突
14.9 _source 元數(shù)據(jù)
其實(shí)就是我們在添加文檔時(shí)request body中的內(nèi)容
指定返回的結(jié)果中含有哪些字段:
get /index/_doc/1?_source=name
14.10 改變文檔內(nèi)容解析
替換方式:
PUT /lib/_doc/4
{
"first_name": "Jane",
"last_name": "Lucy",
"age": 24,
"about": "I like to collect rock albums",
"interests": [
"music"
]
}
修改方式(partial update):
POST /lib/_doc/2/_update
{
"doc": {
"age": 26
}
}
刪除文檔:標(biāo)記為deleted,隨著數(shù)據(jù)量的增加,es會選擇合適的時(shí)間刪除掉
14.11 基于groovy腳本執(zhí)行partial update
es有內(nèi)置的腳本支持,可以基于groovy腳本實(shí)現(xiàn)復(fù)雜的操作
1.修改年齡
POST /lib/_doc/4/_update
{
"script": "ctx._source.age+=1"
}
2.修改名字
POST /lib/_doc/4/_update
{
"script": "ctx._source.last_name+='hehe'"
}
3.添加愛好
POST /lib/_doc/4/_update
{
"script": {
"source": "ctx._source.interests.add(params.tag)",
"params": {
"tag": "picture"
}
}
}
4.刪除愛好
POST /lib/_doc/4/_update
{
"script": {
"source": "ctx._source.interests.remove(ctx._source.interests.indexOf(params.tag))",
"params": {
"tag": "picture"
}
}
}
5.刪除文檔
POST /lib/_doc/4/_update
{
"script": {
"source": "ctx.op=ctx._source.age==params.count?'delete':'none'",
"params": {
"count": 29
}
}
}
6.upsert
POST /lib/_doc/4/_update
{
"script": "ctx._source.age += 1",
"upsert": {
"first_name": "Jane",
"last_name": "Lucy",
"age": 20,
"about": "I like to collect rock albums",
"interests": [
"music"
]
}
}
14.12 partial update 處理并發(fā)沖突
使用的是樂觀鎖: _version
retry_on_conflict: POST /lib/user/4/_update?retry_on_conflict=3
重新獲取文檔數(shù)據(jù)和版本信息進(jìn)行更新,不斷的操作,最多操作的次數(shù)就是retry_on_conflict的值
14.13 文檔數(shù)據(jù)路由原理
1.文檔路由到分片上:
一個(gè)索引由多個(gè)分片構(gòu)成,當(dāng)添加(刪除,修改)一個(gè)文檔時(shí),es就需要決定這個(gè)文檔存儲在哪個(gè)分片上,這個(gè)過程就稱為數(shù)據(jù)路由(routing)
2.路由算法:
shard=hash(routing) % number_of_pirmary_shards
示例:一個(gè)索引,3個(gè)primary shard
(1)每次增刪改查時(shí),都有一個(gè)routing值,默認(rèn)是文檔的_id的值
(2)對這個(gè)routing值使用哈希函數(shù)進(jìn)行計(jì)算
(3)計(jì)算出的值再和主分片個(gè)數(shù)取余數(shù)
余數(shù)肯定在0---(number_of_pirmary_shards-1)之間,文檔就在對應(yīng)的shard上
routing值默認(rèn)是文檔的_id的值,也可以手動指定一個(gè)值,手動指定對于負(fù)載均衡以及提高批量讀取的性能都有幫助
3.primary shard個(gè)數(shù)一旦確定就不能修改了
14.14 文檔增刪改原理
- 發(fā)送增刪改請求時(shí),可以選擇任意一個(gè)節(jié)點(diǎn),該節(jié)點(diǎn)就成了協(xié)調(diào)節(jié)點(diǎn)(coordinating node)
- 協(xié)調(diào)節(jié)點(diǎn)使用路由算法進(jìn)行路由,然后將請求轉(zhuǎn)到primary shard所在節(jié)點(diǎn),該節(jié)點(diǎn)處理請求,并把數(shù)據(jù)同步到它的replica shard
- 協(xié)調(diào)節(jié)點(diǎn)對客戶端做出響應(yīng)
14.15 寫一致性原理和quorum機(jī)制
1.任何一個(gè)增刪改操作都可以跟上一個(gè)參數(shù) consistency
可以給該參數(shù)指定的值:
one: (primary shard)只要有一個(gè)primary shard是活躍的就可以執(zhí)行
all: (all shard)所有的primary shard和replica shard都是活躍的才能執(zhí)行
quorum: (default) 默認(rèn)值,大部分shard是活躍的才能執(zhí)行 (例如共有6個(gè)shard,至少有3個(gè)shard是活躍的才能執(zhí)行寫操作)
2.quorum機(jī)制:多數(shù)shard都是可用的
int((primary+number_of_replica)/2)+1
例如:3個(gè)primary shard,1個(gè)replica
int((3+1)/2)+1=3
至少3個(gè)shard是活躍的
注意:可能出現(xiàn)shard不能分配齊全的情況
比如:1個(gè)primary shard,1個(gè)replica
int((1+1)/2)+1=2
但是如果只有一個(gè)節(jié)點(diǎn),因?yàn)閜rimary shard和replica shard不能在同一個(gè)節(jié)點(diǎn)上,所以仍然不能執(zhí)行寫操作
再舉例:1個(gè)primary shard,3個(gè)replica,2個(gè)節(jié)點(diǎn)
int((1+3)/2)+1=3
最后:當(dāng)活躍的shard的個(gè)數(shù)沒有達(dá)到要求時(shí),
es默認(rèn)會等待一分鐘,如果在等待的期間活躍的shard的個(gè)數(shù)沒有增加,則顯示timeout
put /index/type/id?timeout=60s
14.16 文檔查詢原理
- 第一步:
查詢請求發(fā)給任意一個(gè)節(jié)點(diǎn),該節(jié)點(diǎn)就成了coordinating node,該節(jié)點(diǎn)使用路由算法算出文檔所在的primary shard - 第二步:
協(xié)調(diào)節(jié)點(diǎn)把請求轉(zhuǎn)發(fā)給primary shard也可以轉(zhuǎn)發(fā)給replica shard(使用輪詢調(diào)度算法(Round-Robin Scheduling,把請求平均分配至primary shard 和replica shard) - 第三步:
處理請求的節(jié)點(diǎn)把結(jié)果返回給協(xié)調(diào)節(jié)點(diǎn),協(xié)調(diào)節(jié)點(diǎn)再返回給應(yīng)用程序
特殊情況:請求的文檔還在建立索引的過程中,primary shard上存在,但replica shar上不存在,但是請求被轉(zhuǎn)發(fā)到了replica shard上,這時(shí)就會提示找不到文檔
14.17 bulk批量操作的json格式解析
bulk的格式:
{action:{metadata}}
{requstbody}
為什么不使用如下格式:
[{
"action": {
},
"data": {
}
}]
這種方式可讀性好,但是內(nèi)部處理就麻煩了:
1.將json數(shù)組解析為JSONArray對象,在內(nèi)存中就需要有一份json文本的拷貝,另外還有一個(gè)JSONArray對象。
2.解析json數(shù)組里的每個(gè)json,對每個(gè)請求中的document進(jìn)行路由
3.為路由到同一個(gè)shard上的多個(gè)請求,創(chuàng)建一個(gè)請求數(shù)組
4.將這個(gè)請求數(shù)組序列化
5.將序列化后的請求數(shù)組發(fā)送到對應(yīng)的節(jié)點(diǎn)上去
會導(dǎo)致耗費(fèi)更多內(nèi)存,增加java虛擬機(jī)開銷
現(xiàn)有格式的優(yōu)點(diǎn):
1.不用將其轉(zhuǎn)換為json對象,直接按照換行符切割json,內(nèi)存中不需要json文本的拷貝
2.對每兩個(gè)一組的json,讀取meta,進(jìn)行document路由
3.直接將對應(yīng)的json發(fā)送到node上去
14.18 查詢結(jié)果分析
{
"took": 419,
"timed_out": false,
"_shards": {
"total": 3,
"successful": 3,
"skipped": 0,
"failed": 0
},
"hits": {
"total": 3,
"max_score": 0.6931472,
"hits": [
{
"_index": "lib3",
"_id": "3",
"_score": 0.6931472,
"_source": {
"address": "bei jing hai dian qu qing he zhen",
"name": "lisi"
}
},
{
"_index": "lib3",
"_id": "2",
"_score": 0.47000363,
"_source": {
"address": "bei jing hai dian qu qing he zhen",
"name": "zhaoming"
}
}
]
}
}
took:查詢耗費(fèi)的時(shí)間,單位是毫秒
_shards:共請求了多少個(gè)shard
total:查詢出的文檔總個(gè)數(shù)
max_score: 本次查詢中,相關(guān)度分?jǐn)?shù)的最大值,文檔和此次查詢的匹配度越高,_score的值越大,排位越靠前
hits:默認(rèn)查詢前10個(gè)文檔
timed_out:
GET /lib3/_doc/_search?timeout=10ms
{
"_source": ["address","name"],
"query": {
"match": {
"interests": "changge"
}
}
}
14.19 多index查詢
GET _search
GET /lib/_search
GET /lib,lib3/_search
GET /3,4/_search
GET /_all/_search
14.20 分頁查詢中的deep paging問題
GET /lib3/_doc/_search
{
"from":0,
"size":2,
"query":{
"terms":{
"interests": ["hejiu","changge"]
}
}
}
GET /_search?from=0&size=3
deep paging:查詢的很深,比如一個(gè)索引有三個(gè)primary shard,分別存儲了6000條數(shù)據(jù),我們要得到第100頁的數(shù)據(jù)(每頁10條),類似這種情況就叫deep paging
如何得到第100頁的10條數(shù)據(jù)?
在每個(gè)shard中搜索990到999這10條數(shù)據(jù),然后用這30條數(shù)據(jù)排序,排序之后取10條數(shù)據(jù)就是要搜索的數(shù)據(jù),這種做法是錯(cuò)的,因?yàn)?個(gè)shard中的數(shù)據(jù)的_score分?jǐn)?shù)不一樣,可能這某一個(gè)shard中第一條數(shù)據(jù)的_score分?jǐn)?shù)比另一個(gè)shard中第1000條都要高,所以在每個(gè)shard中搜索990到999這10條數(shù)據(jù)然后排序的做法是不正確的。
正確的做法是每個(gè)shard把0到999條數(shù)據(jù)全部搜索出來(按排序順序),然后全部返回給coordinate node,由coordinate node按_score分?jǐn)?shù)排序后,取出第100頁的10條數(shù)據(jù),然后返回給客戶端。
deep paging性能問題
1.耗費(fèi)網(wǎng)絡(luò)帶寬,因?yàn)樗阉鬟^深的話,各shard要把數(shù)據(jù)傳送給coordinate node,這個(gè)過程是有大量數(shù)據(jù)傳遞的,消耗網(wǎng)絡(luò),
2.消耗內(nèi)存,各shard要把數(shù)據(jù)傳送給coordinate node,這個(gè)傳遞回來的數(shù)據(jù),是被coordinate node保存在內(nèi)存中的,這樣會大量消耗內(nèi)存。
3.消耗cpu coordinate node要把傳回來的數(shù)據(jù)進(jìn)行排序,這個(gè)排序過程很消耗cpu.
鑒于deep paging的性能問題,所以應(yīng)盡量減少使用。
14.21 query string查詢及copy_to解析
GET /lib3/_doc/_search?q=interests:changge
GET /lib3/_doc/_search?q=+interests:changge
GET /lib3/_doc/_search?q=-interests:changge
copy_to字段是把其它字段中的值,以空格為分隔符組成一個(gè)大字符串,然后被分析和索引,但是不存儲,也就是說它能被查詢,但不能被取回顯示。
注意:copy_to指向的字段字段類型要為:text
當(dāng)沒有指定field時(shí),就會從copy_to字段中查詢
GET /lib3/_doc/_search?q=changge
14.22 字符串排序問題
對一個(gè)字符串類型的字段進(jìn)行排序通常不準(zhǔn)確,因?yàn)橐呀?jīng)被分詞成多個(gè)詞條了
解決方式:對字段索引兩次,一次索引分詞(用于搜索),一次索引不分詞(用于排序)
GET /lib3/_search
{
"query": {
"match_all": {}
},
"sort": [
{
"interests": {
"order": "desc"
}
}
]
}
GET /lib3/_search
{
"query": {
"match_all": {}
},
"sort": [
{
"interests.raw": {
"order": "asc"
}
}
]
}
DELETE lib3
PUT /lib3
{
"settings": {
"number_of_shards": 3,
"number_of_replicas": 0
},
"mappings": {
"properties": {
"name": {
"type": "text"
},
"address": {
"type": "text"
},
"age": {
"type": "integer"
},
"birthday": {
"type": "date"
},
"interests": {
"type": "text",
"fields": {
"raw": {
"type": "keyword"
}
},
"fielddata": true
}
}
}
}
14.23 如何計(jì)算相關(guān)度分?jǐn)?shù)
使用的是TF/IDF算法(Term Frequency&Inverse Document Frequency)
1.Term Frequency:我們查詢的文本中的詞條在document本中出現(xiàn)了多少次,出現(xiàn)次數(shù)越多,相關(guān)度越高
搜索內(nèi)容: hello world
Hello,I love china.
Hello world,how are you!
2.Inverse Document Frequency:我們查詢的文本中的詞條在索引的所有文檔中出現(xiàn)了多少次,出現(xiàn)的次數(shù)越多,相關(guān)度越低
搜索內(nèi)容:hello world
hello,what are you doing?
I like the world.
hello 在索引的所有文檔中出現(xiàn)了500次,world出現(xiàn)了100次
3.Field-length(字段長度歸約) norm:field越長,相關(guān)度越低
搜索內(nèi)容:hello world
{"title":"hello,what's your name?","content":{"owieurowieuolsdjflk"}}
{"title":"hi,good morning","content":{"lkjkljkj.......world"}}
查看分?jǐn)?shù)是如何計(jì)算的:
GET /lib3/_search?explain=true
{
"query": {
"match": {
"interests": "duanlian,changge"
}
}
}
查看一個(gè)文檔能否匹配上某個(gè)查詢:
GET /lib3/_doc/2/_explain
{
"query":{
"match":{
"interests": "duanlian,changge"
}
}
}
14.24 Doc Values 解析
DocValues 是 Lucene 在構(gòu)建倒排索引時(shí),會額外建立一個(gè)有序的正排索引(基于document => field value的映射列表)
{"birthday":"1985-11-11",age:23}
{"birthday":"1989-11-11",age:29}
| document | age | birthday |
|---|---|---|
| doc1 | 23 | 1985-11-11 |
| doc2 | 29 | 1989-11-11 |
存儲在磁盤上,節(jié)省內(nèi)存
對排序,分組和一些聚合操作能夠大大提升性能
注意:默認(rèn)對不分詞的字段是開啟的,對分詞字段無效(需要把fielddata設(shè)置為true)
PUT /lib3
{
"settings":{
"number_of_shards" : 3,
"number_of_replicas" : 0
},
"mappings":{
"user":{
"properties":{
"name": {"type":"text"},
"address": {"type":"text"},
"age": {
"type":"integer",
"doc_values":false
},
"interests": {"type":"text"},
"birthday": {"type":"date"}
}
}
}
}
14.25 基于scroll技術(shù)滾動搜索大量數(shù)據(jù)
如果一次性要查出來比如10萬條數(shù)據(jù),那么性能會很差,此時(shí)一般會采取用scoll滾動查詢,一批一批的查,直到所有數(shù)據(jù)都查詢完為止。
- scoll 搜索會在第一次搜索的時(shí)候,保存一個(gè)當(dāng)時(shí)的視圖快照,之后只會基于該舊的視圖快照提供數(shù)據(jù)搜索,如果這個(gè)期間數(shù)據(jù)變更,是不會讓用戶看到的
- 采用基于_doc(不使用_score)進(jìn)行排序的方式,性能較高
- 每次發(fā)送scroll請求,我們還需要指定一個(gè)scoll參數(shù),指定一個(gè)時(shí)間窗口,每次搜索請求只要在這個(gè)時(shí)間窗口內(nèi)能完成就可以了
GET /lib3/user/_search?scroll=1m
{
"query": {
"match_all": {}
},
"sort":["_doc"],
"size":3
}
GET /_search/scroll
{
"scroll": "1m",
"scroll_id": "DnF1ZXJ5VGhlbkZldGNoAwAAAAAAAAAdFkEwRENOVTdnUUJPWVZUd1p2WE5hV2cAAAAAAAAAHhZBMERDTlU3Z1FCT1lWVHdadlhOYVdnAAAAAAAAAB8WQTBEQ05VN2dRQk9ZVlR3WnZYTmFXZw=="
}
14.26 dynamic mapping策略
dynamic**:
1.true:遇到陌生字段就 dynamic mapping
2.false:遇到陌生字段就忽略
3.strict:約到陌生字段就報(bào)錯(cuò)
PUT /lib8
{
"settings": {
"number_of_shards": 3,
"number_of_replicas": 0
},
"mappings": {
"dynamic": "strict",
"properties": {
"name": {
"type": "text"
},
"address": {
"type": "object",
"dynamic": true
}
}
}
}
會報(bào)錯(cuò)
PUT /lib8/_doc/1
{
"name":"lisi",
"age":20,
"address":{
"province":"beijing",
"city":"beijing"
}
}
date_detection:默認(rèn)會按照一定格式識別date,比如yyyy-MM-dd
可以手動關(guān)閉某個(gè)type的date_detection
PUT /lib8
{
"settings":{
"number_of_shards" : 3,
"number_of_replicas" : 0
},
"mappings":{
"date_detection": false,
}
}
定制 dynamic mapping template(type)
PUT /my_index
{
"mappings": {
"dynamic_templates": [
{
"en": {
"match": "*_en",
"match_mapping_type": "string",
"mapping": {
"type": "text",
"analyzer": "english"
}
}
}
]
}
}
使用了模板
PUT /my_index/_doc/3
{
"title_en": "this is my dog"
}
沒有使用模板
PUT /my_index/_doc/5
{
"title": "this is my cat"
}
GET my_index/_doc/_search
{
"query": {
"match": {
"title": "is"
}
}
}
14.27 重建索引
一個(gè)field的設(shè)置是不能修改的,如果要修改一個(gè)field,那么應(yīng)該重新按照新的mapping,建立一個(gè)index,然后將數(shù)據(jù)批量查詢出來,重新用bulk api寫入到index中。
批量查詢的時(shí)候,建議采用scroll api,并且采用多線程并發(fā)的方式來reindex數(shù)據(jù),每次scroll就查詢指定日期的一段數(shù)據(jù),交給一個(gè)線程即可。
PUT /index1/_doc/4
{
"content":"1990-12-12"
}
GET /index1/_doc/_search
GET /index1/_doc/_mapping
報(bào)錯(cuò)
PUT /index1/_doc/4
{
"content":"I am very happy."
}
修改content的類型為string類型,報(bào)錯(cuò),不允許修改
PUT /index1/_mapping/
{
"properties": {
"content":{
"type": "text"
}
}
}
創(chuàng)建一個(gè)新的索引,把index1索引中的數(shù)據(jù)查詢出來導(dǎo)入到新的索引中
但是應(yīng)用程序使用的是之前的索引,為了不用重啟應(yīng)用程序,給index1這個(gè)索引起個(gè)別名
PUT /index1/_alias/index2
創(chuàng)建新的索引,把content的類型改為字符串
PUT /newindex
{
"mappings": {
"properties": {
"content":{
"type": "text"
}
}
}
}
使用scroll批量查詢
GET /index1/_search?scroll=1m
{
"query": {
"match_all": {}
},
"sort": ["_doc"],
"size": 2
}
使用bulk批量寫入新的索引
POST /_bulk
{"index":{"_index":"newindex","_type":"type1","_id":1}}
{"content":"1982-12-12"}
將別名index2和新的索引關(guān)聯(lián),應(yīng)用程序不用重啟
POST /_aliases
{
"actions": [
{"remove": {"index":"index1","alias":"index2"}},
{"add": {"index": "newindex","alias": "index2"}}
]
}
GET index2/_search
14.28 索引不可變的原因
倒排索引包括:
文檔的列表,文檔的數(shù)量,詞條在每個(gè)文檔中出現(xiàn)的次數(shù),出現(xiàn)的位置,每個(gè)文檔的長度,所有文檔的平均長度
索引不變的原因:
1.不需要鎖,提升了并發(fā)性能
2.可以一直保存在緩存中(filter)
3.節(jié)省cpu和io開銷