Elasticsearch7學習筆記(上)

Elasticsearch7學習筆記(上)
Elasticsearch7學習筆記(中)
Elasticsearch7學習筆記(下)
Elasticsearch7學習筆記(實戰(zhàn))

概述

ELK是Elasticsearch戈抄、Logstash划鸽、Kibana的簡稱裸诽,這三者是核心套件實現(xiàn)日志采集丈冬、分析埂蕊、展示蓄氧,但并非全部喉童。

Elasticsearch是實時全文搜索和分析引擎顿天,提供搜集牌废、分析畔规、存儲數(shù)據(jù)三大功能叁扫;是一套開放REST和JAVA API等結(jié)構(gòu)提供高效搜索功能莫绣,可擴展的分布式系統(tǒng)对室。它構(gòu)建于Apache Lucene搜索引擎庫之上。

Logstash是一個用來搜集么翰、分析浩嫌、過濾日志的工具码耐。它支持幾乎任何類型的日志骚腥,包括系統(tǒng)日志桦沉、錯誤日志和自定義應用程序日志。它可以從許多來源接收日志代芜,這些來源包括 syslog挤庇、消息傳遞(例如 RabbitMQ)和JMX嫡秕,它能夠以多種方式輸出數(shù)據(jù)昆咽,包括電子郵件掷酗、websockets和Elasticsearch泻轰。

Kibana是一個基于Web的圖形界面浮声,用于搜索泳挥、分析和可視化存儲在 Elasticsearch指標中的日志數(shù)據(jù)羡洁。它利用Elasticsearch的REST接口來檢索數(shù)據(jù)筑煮,不僅允許用戶創(chuàng)建他們自己的數(shù)據(jù)的定制儀表板視圖真仲,還允許他們以特殊的方式查詢和過濾數(shù)據(jù)秸应。

一软啼、Elasticsearch基礎

現(xiàn)在主流的搜索引擎大概就是:Lucene祸挪,Solr贿条,ElasticSearch整以。這里是對ElasticSearch的學習公黑。

1.1 Elasticsearch的功能

  1. 分布式的搜索引擎和數(shù)據(jù)分析引擎

    搜索:百度凡蚜,網(wǎng)站的站內(nèi)搜索番刊,IT系統(tǒng)的檢索
    數(shù)據(jù)分析:電商網(wǎng)站芹务,最近7天牙膏這種商品銷量排名前10的商家有哪些;新聞網(wǎng)站辆床,最近1個月訪問量排名前3的新聞版塊是哪些
    分布式讼载,搜索咨堤,數(shù)據(jù)分析

  2. 全文檢索一喘,結(jié)構(gòu)化檢索凸克,數(shù)據(jù)分析

    全文檢索:我想搜索商品名稱包含牙膏的商品萎战,select * from products where product_name like "%牙膏%"
    結(jié)構(gòu)化檢索:我想搜索商品分類為日化用品的商品都有哪些,select * from products where category_id='日化用品'
    部分匹配、自動完成览祖、搜索糾錯展蒂、搜索推薦
    數(shù)據(jù)分析:我們分析每一個商品分類下有多少個商品锰悼,select category_id,count(*) from products group by category_id

  3. 對海量數(shù)據(jù)進行近實時的處理

    分布式:ES自動可以將海量數(shù)據(jù)分散到多臺服務器上去存儲和檢索
    海聯(lián)數(shù)據(jù)的處理:分布式以后箕般,就可以采用大量的服務器去存儲和檢索數(shù)據(jù)丝里,自然而然就可以實現(xiàn)海量數(shù)據(jù)的處理了
    近實時:檢索個數(shù)據(jù)要花費1小時(這就不要近實時杯聚,離線批處理幌绍,batch-processing)傀广;在秒級別對數(shù)據(jù)進行搜索和分析

    跟分布式/海量數(shù)據(jù)相反的:lucene主儡,單機應用丰捷,只能在單臺服務器上使用病往,最多只能處理單臺服務器可以處理的數(shù)據(jù)量

1.2 Elasticsearch的適用場景

國外

(1)維基百科停巷,類似百度百科畔勤,牙膏庆揪,牙膏的維基百科缸榛,全文檢索内颗,高亮均澳,搜索推薦

(2)The Guardian(國外新聞網(wǎng)站)负懦,類似搜狐新聞纸厉,用戶行為日志(點擊肯尺,瀏覽则吟,收藏氓仲,評論)+社交網(wǎng)絡數(shù)據(jù)(對某某新聞的相關(guān)看法)敬扛,數(shù)據(jù)分析啥箭,給到每篇新聞文章的作者急侥,讓他知道他的文章的公眾反饋(好坏怪,壞,熱門捉超,垃圾,鄙視况芒,崇拜)

(3)Stack Overflow(國外的程序異常討論論壇)绝骚,IT問題压汪,程序的報錯止剖,提交上去穿香,有人會跟你討論和回答皮获,全文檢索洒宝,搜索相關(guān)問題和答案待德,程序報錯了将宪,就會將報錯信息粘貼到里面去印蔗,搜索有沒有對應的答案

(4)GitHub(開源代碼管理)华嘹,搜索上千億行代碼

(5)電商網(wǎng)站耙厚,檢索商品

(6)日志數(shù)據(jù)分析薛躬,logstash采集日志,ES進行復雜的數(shù)據(jù)分析(ELK技術(shù)趴酣,elasticsearch+logstash+kibana)

(7)商品價格監(jiān)控網(wǎng)站岖寞,用戶設定某商品的價格閾值仗谆,當?shù)陀谠撻撝档臅r候胸私,發(fā)送通知消息給用戶阔涉,比如說訂閱牙膏的監(jiān)控瑰排,如果高露潔牙膏的家庭套裝低于50塊錢椭住,就通知我,我就去買

(8)BI系統(tǒng)些举,商業(yè)智能户魏,Business Intelligence叼丑。比如說有個大型商場集團李剖,BI,分析一下某某區(qū)域最近3年的用戶消費金額的趨勢以及用戶群體的組成構(gòu)成贞铣,產(chǎn)出相關(guān)的數(shù)張報表,**區(qū)酱畅,最近3年纺酸,每年消費金額呈現(xiàn)100%的增長,而且用戶群體85%是高級白領(lǐng)樊诺,開一個新商場词爬。ES執(zhí)行數(shù)據(jù)分析和挖掘,Kibana進行數(shù)據(jù)可視化

國內(nèi)

(9)國內(nèi):站內(nèi)搜索(電商权均,招聘顿膨,門戶,等等)虽惭,IT系統(tǒng)搜索(OA,CRM蛇尚,ERP芽唇,等等),數(shù)據(jù)分析(ES熱門的一個使用場景)

1.3 elasticsearch的核心概念

Elasticsearch           數(shù)據(jù)庫

-----------------------------------------

Document            行
Type                表(在7.x以后已經(jīng)移除了取劫,默認為_doc; 在6.x以后一個索引只能有一個type了匆笤,在5.x以前一個索引可以有多個type)
Index               庫

(1)Near Realtime(NRT):近實時,兩個意思谱邪,從寫入數(shù)據(jù)到數(shù)據(jù)可以被搜索到有一個小延遲(大概1秒)炮捧;基于es執(zhí)行搜索和分析可以達到秒級

(2)Cluster:集群,包含多個節(jié)點惦银,每個節(jié)點屬于哪個集群是通過一個配置(集群名稱咆课,默認是elasticsearch)來決定的末誓,對于中小型應用來說,剛開始一個集群就一個節(jié)點很正常

(3)Node:節(jié)點书蚪,集群中的一個節(jié)點喇澡,節(jié)點也有一個名稱(默認是隨機分配的),節(jié)點名稱很重要(在執(zhí)行運維管理操作的時候)殊校,默認節(jié)點會去加入一個名稱為“elasticsearch”的集群晴玖,如果直接啟動一堆節(jié)點,那么它們會自動組成一個elasticsearch集群为流,當然一個節(jié)點也可以組成一個elasticsearch集群

(4)Document&field:文檔呕屎,es中的最小數(shù)據(jù)單元,一個document可以是一條客戶數(shù)據(jù)敬察,一條商品分類數(shù)據(jù)秀睛,一條訂單數(shù)據(jù),通常用JSON數(shù)據(jù)結(jié)構(gòu)表示莲祸,每個index下的type中琅催,都可以去存儲多個document。一個document里面有多個field虫给,每個field就是一個數(shù)據(jù)字段藤抡。

(5)Index:索引,包含一堆有相似結(jié)構(gòu)的文檔數(shù)據(jù)抹估,比如可以有一個客戶索引缠黍,商品分類索引,訂單索引药蜻,索引有一個名稱瓷式。一個index包含很多document,一個index就代表了一類類似的或者相同的document语泽。比如說建立一個product index贸典,商品索引,里面可能就存放了所有的商品數(shù)據(jù)踱卵,所有的商品document廊驼。

(6)shard:單臺機器無法存儲大量數(shù)據(jù),es可以將一個索引中的數(shù)據(jù)切分為多個shard惋砂,分布在多臺服務器上存儲妒挎。有了shard就可以橫向擴展,存儲更多數(shù)據(jù)西饵,讓搜索和分析等操作分布到多臺服務器上去執(zhí)行酝掩,提升吞吐量和性能。每個shard都是一個lucene index眷柔。

(7)replica:任何一個服務器隨時可能故障或宕機期虾,此時shard可能就會丟失原朝,因此可以為每個shard創(chuàng)建多個replica副本。replica可以在shard故障時提供備用服務镶苞,保證數(shù)據(jù)不丟失喳坠,多個replica還可以提升搜索操作的吞吐量和性能。primary shard(建立索引時一次設置宾尚,不能修改,默認5個)谢澈,replica shard(隨時修改數(shù)量煌贴,默認1個),默認每個索引10個shard锥忿,5個primary shard牛郑,5個replica shard,最小的高可用配置敬鬓,是2臺服務器淹朋。

1.4 使用docker安裝Elasticsearch

  1. 拉取docker鏡像,由于國內(nèi)網(wǎng)絡原因钉答,速度可能會比較慢或者無法下載础芍;可以直接安裝對應系統(tǒng)的安裝包進行安裝即可,基本都是解壓運行即可数尿。

     docker pull elasticsearch:7.8.0
    

鏡像下載慢可以配置國內(nèi)的加速

編輯編輯/etc/docker/daemon.json文件

vi /etc/docker/daemon.json

添加鏡像加速地址(下面這個是網(wǎng)易的加速地址):

{
  "registry-mirrors": ["http://hub-mirror.c.163.com"]
}

也可以使用申請阿里云容器鏡像服務ACR[https://www.aliyun.com/product/acr]仑性;申請成功后點擊管理控制臺,選擇鏡像中心->鏡像加速獲取地址右蹦。

重啟docker

systemctl daemon-reload
systemctl restart docker
  1. 創(chuàng)建elasticsearch容器诊杆,并啟動(這里使用單機版)
    docker run -d --name es7  -p 9200:9200 -p 9300:9300 -e "discovery.type=single-node" elasticsearch:7.8.0
  1. 訪問 http://192.168.1.6:9200/

如果正常返回則說明成功,類似:

{
  "name" : "074c8527cecd",
  "cluster_name" : "docker-cluster",
  "cluster_uuid" : "YBNpiQm8Qxmd0ma7j-1uGw",
  "version" : {
    "number" : "7.8.0",
    "build_flavor" : "default",
    "build_type" : "docker",
    "build_hash" : "757314695644ea9a1dc2fecd26d1a43856725e65",
    "build_date" : "2020-06-14T19:35:50.234439Z",
    "build_snapshot" : false,
    "lucene_version" : "8.5.1",
    "minimum_wire_compatibility_version" : "6.8.0",
    "minimum_index_compatibility_version" : "6.0.0-beta1"
  },
  "tagline" : "You Know, for Search"
}

二何陆、Kibana

kibana的界面可以很方便的查看elasticsearch的信息晨汹,也可以做圖表、指標等贷盲。同時提供控制臺命令操作elasticsearch淘这。

使用docker安裝kibana

# 拉取kibana的鏡像
docker pull kibana:7.8.0
# 啟動kibana
docker run -d --name kibana --link 已經(jīng)啟動的elasticsearch的容器ID或者是名字:elasticsearch -p 5601:5601 kibana:7.8.0  
# 例如
docker run -d --name kibana --link es7:elasticsearch -p 5601:5601 kibana:7.8.0

通過http://192.168.111.44:5601訪問kibana

如果需要中文界面在kibana.yml文件中添加 i18n.locale: "zh-CN"配置重啟即可

通過kibana的Console來做elasticsearch的crud和相關(guān)配置

elasticsearch集群狀態(tài)

GET _cat/health?v

green:每個索引的primary shard和replica shard都是active狀態(tài)的
yellow:每個索引的primary shard都是active狀態(tài)的,但是部分replica shard不是active狀態(tài)巩剖,處于不可用的狀態(tài)
red:不是所有索引的primary shard都是active狀態(tài)的慨灭,部分索引有數(shù)據(jù)丟失了

后面加v是為了打印出更多的信息

索引相關(guān)操作

# 查詢所有索引
GET _cat/indices?v
# 創(chuàng)建索引
PUT /索引名稱?pretty
# 刪除索引
DELETE /索引名稱

向elasticsearch中添加和修改數(shù)據(jù);

語法, 使用POST或者PUT都可以,存在則更新否則創(chuàng)建球及;

區(qū)別在于沒有加ID值時(沒有ID會自動生成)氧骤,只能用POST表示創(chuàng)建;
需要注意的是使用PUT做更新時吃引,其實是直接覆蓋筹陵,因此需要帶上所有的數(shù)據(jù)刽锤;

POST /索引名稱/_doc
POST /索引名稱/_create

POST /索引名稱/_doc/數(shù)據(jù)的id值
POST /索引名稱/_create/數(shù)據(jù)的id值

PUT /索引名稱/_doc/數(shù)據(jù)的id值
PUT /索引名稱/_create/數(shù)據(jù)的id值

只更新指定字段的值:

POST/索引名稱/_update/數(shù)據(jù)的ID值 {
    "doc":{
        // 更新內(nèi)容
    }
}

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

# 查詢所有
GET /索引名稱/_search
# 根據(jù)ID查詢
GET /索引名稱/_doc/數(shù)據(jù)的id值

刪除數(shù)據(jù)

DELETE /索引名稱/_doc/數(shù)據(jù)的id值

刪除使用的邏輯刪除,之后會統(tǒng)一進行物理刪除

示例

# 添加或更新替換
POST /ecommerce/_doc/1
{
  "name":"小米手機",
  "desc":"支持5G朦佩、全面屏6.4",
  "price":3000,
  "producer":"小米",
  "tags":["mobile","5G"]
}

# 添加或更新替換
PUT /ecommerce/_doc/2
{
  "name":"華為MacBook",
  "desc":"支持5G并思、全面屏15.2寸",
  "price":8000,
  "producer":"Huawei",
  "tags":["筆記本電腦","huawei"]
}

# 添加或更新替換
POST /ecommerce/_create/3
{
  "name":"華為P40 pro",
  "desc":"支持5G、超清攝像",
  "price":12000,
  "producer":"Huawei 成都",
  "tags":["mobile","huawei","5G"]
}

# 添加
POST /ecommerce/_doc
{
  "name":"Ipad mini 5",
  "desc":"7.9英寸",
  "price":4000,
  "producer":"apple",
  "tags":["筆記本電腦","apple"]
}

# 更新
POST /ecommerce/_update/1
{
  "doc": {
    "price":2000
  }
}

# 查詢
GET /ecommerce/_search
GET /ecommerce/_doc/1

# 刪除
DELETE /ecommerce/_doc/4

elasticsearch查詢語句示例

query string search

query string search就是將查詢條件放到http的參數(shù)上

1语稠、查詢?nèi)?/p>

GET /ecommerce/_search

返回字段說明:

took:耗費了幾毫秒
timed_out:是否超時
_shards:數(shù)據(jù)拆成幾個分片宋彼,所以對于搜索請求,會打到所有的primary shard(或者是它的某個replica shard也可以)
hits.total:查詢結(jié)果的數(shù)量仙畦,即幾個document
hits.max_score:score的含義输涕,就是document對于一個search的相關(guān)度的匹配分數(shù),越相關(guān)慨畸,就越匹配莱坎,分數(shù)也高
hits.hits:包含了匹配搜索的document的詳細數(shù)據(jù)

2、查詢名稱包含華為的商品,并且按照售價降序排序

GET /ecommerce/_search?q=name:華為&sort=price:desc

3寸士、只返回name檐什、price字段

GET /ecommerce/_search?_source=name,price

query DSL

DSL:Domain Specified Language,特定領(lǐng)域的語言

http request body:請求體弱卡,可以用json的格式來構(gòu)建查詢語法乃正,
比較方便,可以構(gòu)建各種復雜的語法婶博,比query string search肯定強大多了

查詢所有match_all

GET /ecommerce/_search
{
    "query": { "match_all": {} }
}

查詢名稱包含華為的商品烫葬,同時按照價格降序排序

GET /ecommerce/_search
{
  "query": {
    "match": {
      "name": "華為"
    }
  }
  , "sort": [
    {
      "price": {
        "order": "desc"
      }
    }
  ]
}

分頁查詢

GET /ecommerce/_search
{
  "query": {
    "match_all": {}
  },
  "from": 2,
  "size": 2
}

from 從第幾條開始,起始為0
size 返回多少條記錄

指定返回的字段

GET /ecommerce/_search
{
  "query": {
    "match_all": {}
  },
  "_source": ["name", "price"]
}

query filter

對數(shù)據(jù)進行過濾

搜索商品名稱包含華為凡蜻,而且售價大于8000元的商品

GET /ecommerce/_search
{
"query": {
 "bool": {
   "must": [
     {
       "match": {
         "name": "華為"
       }
     }
   ],
   "filter": [
     {
       "range": {
         "price": {
           "gt": 8000
         }
       }
     }
   ]
 }
}
}

或者

GET /ecommerce/_search
{
  "query": {
    "bool": {
      "must": {
          "match": {
            "name": "華為"
          }
        }
      ,
      "filter": {
          "range": {
            "price": {
              "gt": 8000
            }
          }
        }
    }
  }
}

bool 里面可以寫多個條件

full-text search(全文檢索)

全文檢索會將輸入的搜索串拆解開來搭综,去倒排索引里面去一一匹配,只要能匹配上任意一個拆解后的單詞划栓,就可以作為結(jié)果返回

GET /ecommerce/_search
{
  "query":{
    "match": {
      "producer": "Huawei 成都"
    }
  }
}

phrase search(短語搜索)

跟全文檢索相對應相反兑巾,phrase search,要求輸入的搜索串忠荞,必須在指定的字段文本中蒋歌,完全包含一模一樣的,才可以算匹配委煤,才能作為結(jié)果返回

 GET /ecommerce/_search
 {
   "query": {
     "match_phrase": {
       "producer": "Huawei 成都"
     }
   }
 }

highlight search(高亮搜索結(jié)果)

高亮搜索結(jié)果就是將匹配的字段做標識堂油,就像百度搜索中那些匹配的內(nèi)容是紅色顯示

GET /ecommerce/_search
{
  "query": {
    "match": {
      "producer": "Huawei"
    }
  },
 "highlight": {
   "fields": {
     "producer": {}
   }
 }
}

聚合:計算每個tag下的商品數(shù)量

GET /ecommerce/_search
{
  "size": 0, 
  "aggs": {
   "group_by_tags":{
     "terms": {
       "field": "tags"
     }
   }
  }
}

group_by_tags 是隨意取的一個名字,待會的查詢統(tǒng)計結(jié)果會放到這個字段中
加size是不返回原始數(shù)據(jù)

上面那樣操作會報錯碧绞,需要先執(zhí)行下面的語句府框,更新tags字段的fielddata屬性設置為true

PUT /ecommerce/_mapping
{
  "properties":{
    "tags":{
      "type":"text",
      "fielddata":true
    }
  }
}

聚合:對名稱中包含yagao的商品,計算每個tag下的商品數(shù)量

GET /ecommerce/_search
{
  "size": 0, 
  "query": {
    "match": {
      "name": "華為"
    }
  },
  "aggs": {
    "all_tags": {
      "terms": {
        "field": "tags"
      }
    }
  }
}

先執(zhí)行query條件查詢讥邻,然后對結(jié)果做aggs聚合處理

聚合:計算每個tag下的商品的平均價格(先分組再平均)

GET /ecommerce/_search
{
  "size": 0, 
  "aggs": {
    "group_by_tags": {
      "terms": {
        "field": "tags"
      },
      "aggs": {
        "avg_price": {
          "avg": {
            "field": "price"
          }
        }
      }
    }
  }
}

計算每個tag下的商品的平均價格迫靖,并且按照平均價格降序排序

GET /ecommerce/_search
{
  "size": 0
  , "aggs": {
    "all_tags": {
      "terms": {
        "field": "tags", "order": {
          "avg_price": "desc"
        }
      },
      "aggs": {
        "avg_price": {
          "avg": {
            "field": "price"
          }
        }
      }
    }
  }
}

按照指定的價格范圍區(qū)間進行分組院峡,然后在每組內(nèi)再按照tag進行分組,最后再計算每組的平均價格

GET /ecommerce/_search
{
  "size": 0, 
  "aggs": {
    "group_by_price": {
      "range": {
        "field": "price",
        "ranges": [
          {
            "from": 0,
            "to": 5000
          },
          {
            "from": 6000
          }
        ]
      },
      "aggs": {
        "group_by_tags": {
          "terms": {
            "field": "tags"
          },
          "aggs": {
            "avg_price": {
              "avg": {
                "field": "price"
              }
            }
          }
        }
      }
    }
  }
}

cerebo

這個是個es的監(jiān)控軟件系宜,可以很方便的查詢es集群的分片等情況照激,能集中管理alias和index template;在kibana中需要使用命令才可以實現(xiàn)盹牧,可以根據(jù)自己需要來安裝俩垃。

拉取鏡像:

docker pull yannart/cerebro

啟動容器:

docker run -d -p 9000:9000 --name cerebro yannart/cerebro:latest

瀏覽器訪問9000

http://ip:9000

直接輸入es的連接地址即可,如:http://192.168.6.2:9200

在一臺機器上導致連接失敗的問題

由于資源有限汰寓,上面這些組件全部都放在了一機器上導致無法訪問口柳,需要打開防火墻開放端口才可以;

查看防火墻狀態(tài)
systemctl status firewalld

重啟防火墻
systemctl restart firewalld.service

查看已經(jīng)開放的端口
firewall-cmd --zone=public --list-ports

開放如下端口(第二個可選)
firewall-cmd --zone=public --add-port=9200/tcp --permanent && firewall-cmd --reload
firewall-cmd --zone=public --add-port=9300/tcp --permanent && firewall-cmd --reload
firewall-cmd --zone=public --add-port=5601/tcp --permanent && firewall-cmd --reload
firewall-cmd --zone=public --add-port=9000/tcp --permanent && firewall-cmd --reload

這樣容器間就可以相互訪問了(開啟防火墻后可能需要重啟docker服務才行)

三踩寇、Elasticsearch 的分布式集群

shard&replica機制

(1)index包含多個shard

(2)每個shard都是一個最小工作單元啄清,承載部分數(shù)據(jù)六水,lucene實例俺孙,完整的建立索引和處理請求的能力

(3)增減節(jié)點時,shard會自動在nodes中負載均衡

(4)primary shard和replica shard掷贾,每個document肯定只存在于某一個primary shard以及其對應的replica shard中睛榄,不可能存在于多個primary shard

(5)replica shard是primary shard的副本,負責容錯想帅,以及承擔讀請求負載

(6)primary shard的數(shù)量在創(chuàng)建索引的時候就固定了场靴,replica shard的數(shù)量可以隨時修改

(7)primary shard的默認數(shù)量是5,replica默認是1港准,默認有10個shard旨剥,5個primary shard,5個replica shard

(8)primary shard不能和自己的replica shard放在同一個節(jié)點上(否則節(jié)點宕機浅缸,primary shard和副本都丟失轨帜,起不到容錯的作用),但是可以和其他primary shard的replica shard放在同一個節(jié)點上

(9)相同primary shard的replica shard不能放在同一個節(jié)點上衩椒;(節(jié)點宕機時蚌父,replica shard副本都丟失,起不到容錯的作用)

單node環(huán)境下創(chuàng)建index

(1)單node環(huán)境下毛萌,創(chuàng)建一個index苟弛,有3個primary shard,3個replica shard

(2)集群status是yellow

(3)這個時候阁将,只會將3個primary shard分配到僅有的一個node上去膏秫,另外3個replica shard是無法分配的

(4)集群可以正常工作,但是一旦出現(xiàn)節(jié)點宕機做盅,數(shù)據(jù)全部丟失荔睹,而且集群不可用狸演,無法承接任何請求

四、Elasticsearch內(nèi)部相關(guān)實現(xiàn)

并發(fā)數(shù)據(jù)修改控制

Elasticsearch內(nèi)部是多線程異步并發(fā)的進行修改(即可能出現(xiàn)后修改的先處理)僻他,采用version進行樂觀鎖宵距;

具體原理:Elasticsearch每次執(zhí)行更新和刪除操作成功時,它的version都會自動加1吨拗,
每次執(zhí)行更新刪除時會帶上版本號满哪,如果版本號不一致,則會放棄此次操作劝篷;
這樣就保證了后修改的先執(zhí)行的情況能夠正常處理哨鸭,不會被先修改的覆蓋掉。

示例:在更新的時候帶上版本號參數(shù)

POST /ecommerce/_update/2?version=3
{
  "doc":{
    "tags":["laptop ", "Huawei"]
  }
}

當版本號version不匹配的時候會更新失敗

使用external version來進行樂觀鎖并發(fā)控制

es提供了一個feature娇妓,就是說像鸡,你可以不用它提供的內(nèi)部_version版本號來進行并發(fā)控制,可以基于你自己維護的一個版本號來進行并發(fā)控制哈恰。

舉個列子只估,假如你的數(shù)據(jù)在mysql里也有一份,然后你的應用系統(tǒng)本身就維護了一個版本號着绷,無論是什么自己生成的蛔钙,程序控制的。
這個時候荠医,你進行樂觀鎖并發(fā)控制的時候吁脱,可能并不是想要用es內(nèi)部的_version來進行控制,而是用你自己維護的那個version來進行控制彬向。

PUT /ecommerce/_doc/1?version=2&version_type=external
{
    "name" : "小米10Pro",
    "desc" : "支持5G兼贡、全面屏6.4",
    "price" : 3000,
    "producer" : "小米",
    "tags" : [
      "xiaomi",
      "mobile",
      "5G"
    ]
}

在后面多加一個version_type=external參數(shù),只有version版本比當前ES維護的版本號大就可以更新成功

partial update說明

語法(url地址后面可以加版本號?version=1):

POST /索引名稱/_update/ID值
{
  "doc":{
        // 更新字段信息
  }
}

使用partial update進行更新其實際執(zhí)行過程如下:

  1. 內(nèi)部先獲取document娃胆;
  2. 將傳過來的field更新到document的json中去遍希;
  3. 將原來的document標記為刪除狀態(tài);
  4. 將修改后的新的document創(chuàng)建出來缕棵;

實際上和傳統(tǒng)的全量替換幾乎一樣孵班。

如果document不存在會報錯

同時partial update將自動執(zhí)行基于version的樂觀鎖并發(fā)控制

設置在發(fā)送沖突時進行重試的次數(shù)

POST /ecommerce/_update/1?retry_on_conflict=2
{
  "doc": {
    "price":3000
  }
}

優(yōu)點

  1. 所有查詢、修改和寫回操作都發(fā)生在es的一個shard內(nèi)部招驴,幾乎避免了所有的網(wǎng)絡數(shù)據(jù)傳輸開銷篙程,提升性能;
  2. 減少了查詢和修改的時間間隔别厘,能夠有效的減少并發(fā)的沖突的情況虱饿;(因為其內(nèi)部操作幾乎在毫秒級別)

示例

POST /ecommerce/_update/1
{
  "doc":{
        "name" : "小米10"
  }
}

es的腳本支持:groovy

使用內(nèi)置腳本來做累加操作

將price加1

POST /ecommerce/_update/1
{
  "script": "ctx._source.price+=1"
}

外置腳本

這個相當于關(guān)系型數(shù)據(jù)庫的存儲過程,將需要執(zhí)行的腳本放到es的config/scripts目錄下

如在config/scripts目錄下創(chuàng)建一個名為add-price.groovy文件,在里面寫入如下腳本:

ctx._source.price+=add_price

執(zhí)行這個腳本:

POST /ecommerce/_update/1
{
  "script": {
    "lang": "groovy",
    "file": "add-price",
    "params": {
      "add_price":1
    }
  }
}
示例刪除document的腳本

config/scripts目錄下創(chuàng)建一個名為del-doc.groovy文件氮发,在里面寫入如下腳本:

ctx.op = ctx._source.price>price?'delete':'none'

執(zhí)行腳本

POST /ecommerce/_update/1
{
  "script": {
    "lang": "groovy",
    "file": "del-doc",
    "params": {
      "price":5000
    }
  }
}

upsert的使用

解決當在執(zhí)行更新時document不存在導致更新失敗的問題渴肉。

POST /ecommerce/_update/1
{
  "script": "ctx._source.price+=1",
  "upsert": {
    "price":0,
    "tags":[]
  }
}

upsert就是沒有的時候?qū)ocument進行初始化

_mget批量查詢

普通的查詢方式只能一條一條的查詢,使用mget可以實現(xiàn)批量查詢爽冕,減少網(wǎng)絡開銷

查詢ID為1和2的數(shù)據(jù)

不同的index

GET /_mget
{
  "docs":[
    {
      "_index":"ecommerce",
      "_id":1
    },
        {
      "_index":"goods",
      "_id":2
    }
    ]
}

同一個index

GET /ecommerce/_mget
{
  "docs":[
    {
      "_id":1
    },
    {
      "_id":2
    }
    ]
}

同一個index且相同的filed

GET /ecommerce/_mget
{
  "ids":[1,2]
  
}

對返回的source字段進行過濾

GET /ecommerce/_mget
{
  "docs":[
    {
      "_id":1,
      "_source":["price","name"]
    },
    {
      "_id":2,
      "_source":"price"
    },
    {
      "_id":3,
      "_source":false
    }
    ]
}

注意直接用ids來查詢時不能進行字段過濾

_bulk批量增刪改

create:創(chuàng)建
delete:刪除
update:更新

POST /_bulk
{"delete":{"_index":"ecommerce","_id":3}}
{"create":{"_index":"ecommerce","_id":3}}
{"price":5000}
{"update":{"_index":"ecommerce","_id":3}}
{"doc":{"price":6000}}

一條語句不能有換行這些仇祭,直接一行
在create之后可以添加需要添加的屬性
update的更新屬性需要加doc
如果在一個index中可以不寫index,直接跟在url上即可

POST /ecommerce/_bulk
{"delete":{"_id":3}}
{"create":{"_id":3}}
{"price":5000}
{"update":{"_id":3}}
{"doc":{"price":6000}}

_bulk在執(zhí)行的時候颈畸,如果其中有一條語句執(zhí)行失敗乌奇,不會影響其他的執(zhí)行,會在返回結(jié)果中將異常提示返回

bulk size最佳大小

bulk request會加載到內(nèi)存里眯娱,如果太大的話礁苗,性能反而會下降,因此需要反復嘗試一個最佳的bulk size徙缴。
一般從1000到5000條數(shù)據(jù)開始试伙,嘗試逐漸增加。另外于样,如果看大小的話疏叨,最好是在5~15MB之間。

什么是distributed document store百宇?

圍繞著document在操作考廉,其實就是把es當成了一個NoSQL存儲引擎秘豹,一個可以存儲文檔類型數(shù)據(jù)的存儲系統(tǒng)携御,操作里面的document。

適合的的應用程序類型

(1)數(shù)據(jù)量較大既绕,es的分布式本質(zhì)啄刹,可以幫助你快速進行擴容,承載大量數(shù)據(jù)

(2)數(shù)據(jù)結(jié)構(gòu)靈活多變凄贩,隨時可能會變化誓军,而且數(shù)據(jù)結(jié)構(gòu)之間的關(guān)系,非常復雜疲扎,如果我們用傳統(tǒng)數(shù)據(jù)庫昵时,那是不是很坑,因為要面臨大量的表

(3)對數(shù)據(jù)的相關(guān)操作椒丧,較為簡單壹甥,比如就是一些簡單的增刪改查,用我們之前講解的那些document操作就可以搞定

(4)NoSQL數(shù)據(jù)庫壶熏,適用的也是類似于上面的這種場景

document數(shù)據(jù)路由原理

(1)document路由到shard上是什么意思句柠?

一個index的數(shù)據(jù)會被分為多片,每片都在一個shard中,因此一個document只能存在一個shard中溯职;
當有一個document需要操作時精盅,es就需要知道這個document是放在index的那個shard上的。
這個過程就稱之為document的數(shù)據(jù)路由谜酒。

(2)路由算法:shard = hash(routing) % number_of_primary_shards

舉個例子來簡要說明哈這個算法:
一個index有3個primary shard(分別為P0叹俏,P1,P2)僻族,每次增刪改查一個document的時候她肯,都會帶過來一個routing number,
默認就是這個document的_id(可能是手動指定鹰贵,也可能是自動生成)routing = _id晴氨,假設_id=1;將這個routing值傳入一個hash函數(shù)中碉输,產(chǎn)出一個routing值的hash值籽前;
然后將hash函數(shù)產(chǎn)出的值對這個index的primary shard的數(shù)量求余數(shù),根據(jù)這個余數(shù)的值決定document放在那個shard上

決定一個document在哪個shard上敷钾,最重要的一個值就是routing值枝哄,默認是_id,也可以手動指定阻荒,保證相同的routing值债鸡,每次過來朴肺,從hash函數(shù)中,產(chǎn)出的hash值一定是相同的;
這也是為什么ES啟動后設置好primary_shards數(shù)量之后稻艰,primary_shards的數(shù)量不能再更改了的原因

document增刪改內(nèi)部原理

(1)客戶端選擇一個node發(fā)送請求過去汛闸,這個node就是coordinating node(協(xié)調(diào)節(jié)點)
(2)coordinating node卫袒,對document進行路由叁执,將請求轉(zhuǎn)發(fā)給對應的node(有primary shard)
(3)實際的node上的primary shard處理請求,然后將數(shù)據(jù)同步到replica node
(4)coordinating node油猫,如果發(fā)現(xiàn)primary node和所有replica node都搞定之后稠茂,就返回響應結(jié)果給客戶端

寫一致性原理以及quorum機制剖析

consistency,one(primary shard)情妖,all(all shard)睬关,quorum(default)

我們在發(fā)送任何一個增刪改操作的時候,比如說put /index/_doc/id毡证,都可以帶上一個consistency參數(shù)电爹,指明我們想要的寫一致性是什么?
put /index/_doc/id?consistency=quorum

one:要求我們這個寫操作情竹,只要有一個primary shard是active活躍可用的藐不,就可以執(zhí)行
all:要求我們這個寫操作匀哄,必須所有的primary shard和replica shard都是活躍的,才可以執(zhí)行這個寫操作
quorum:默認的值雏蛮,要求所有的shard中涎嚼,必須是大部分的shard都是活躍的,可用的挑秉,才可以執(zhí)行這個寫操作

quorum機制法梯,寫之前必須確保大多數(shù)shard都可用(也就是半數(shù)以上)

計算公式:

quroum = int( (primary + number_of_replicas) / 2 ) + 1,當number_of_replicas>1時才生效犀概;active狀態(tài)的的shard數(shù)>=quroum才可以執(zhí)行

舉個例子:

3個primary shard立哑,number_of_replicas=1,總共有3 + 3 * 1 = 6個shard
quorum = int( (3 + 1) / 2 ) + 1 = 3
所以姻灶,要求6個shard中至少有3個shard是active狀態(tài)的铛绰,才可以執(zhí)行寫操作

如果節(jié)點數(shù)少于quorum數(shù)量,可能導致quorum不齊全产喉,進而導致無法執(zhí)行任何寫操作

如下2個例子:

例子1:3個primary shard捂掰,replica=1,要求至少3個shard是active曾沈,3個shard按照之前學習的shard&replica機制这嚣,必須在不同的節(jié)點上,如果說只有1臺機器的話塞俱,是不是有可能出現(xiàn)說姐帚,3個shard都沒法分配齊全,此時就可能會出現(xiàn)寫操作無法執(zhí)行的情況

例子2:1個primary shard障涯,replica=3罐旗,quorum=((1 + 3) / 2) + 1 = 3,要求1個primary shard + 3個replica shard = 4個shard像樊,其中必須有3個shard是要處于active狀態(tài)的尤莺。如果這個時候只有2臺機器的話旅敷,會出現(xiàn)什么情況呢生棍?

因此es提供了一種特殊的處理場景,當number_of_replicas>1時才生效媳谁,因為假如說涂滴,就一個primary shard,replica=1晴音,此時就2個shard
(1 + 1 / 2) + 1 = 2柔纵,要求必須有2個shard是活躍的,但是可能就1個node锤躁,此時就1個shard是活躍的搁料,如果你不特殊處理的話,導致我們的單節(jié)點集群就無法工作

quorum不齊全時,wait郭计,默認1分鐘霸琴,timeout,100昭伸,30s

等待期間梧乘,期望活躍的shard數(shù)量可以增加,最后實在不行庐杨,就會timeout

我們其實可以在寫操作的時候选调,加一個timeout參數(shù),比如說put /index/type/id?timeout=30灵份,這個就是自己去設定quorum不滿足條件的時候仁堪,es的timeout時長,可以縮短填渠,也可以增長

document查詢內(nèi)部原理

  1. 客戶端發(fā)送請求到任意一個node枝笨,成為coordinate node
  2. coordinate node對document進行路由,將請求轉(zhuǎn)發(fā)到對應的node揭蜒,此時會使用round-robin隨機輪詢算法横浑,在primary shard以及其所有replica中隨機選擇一個,讓讀請求負載均衡
  3. 接收請求的node返回document給coordinate node
  4. coordinate node返回document給客戶端
  5. 特殊情況:document如果還在建立索引過程中屉更,可能只有primary shard有徙融,任何一個replica shard都沒有,此時可能會導致無法讀取到document瑰谜,
    但是document完成索引建立之后欺冀,primary shard和replica shard就都有了

bulk api的奇特json格式與底層性能優(yōu)化關(guān)系

bulk api奇特的json格式

{"action": {"meta"}}\n
{"data"}\n
{"action": {"meta"}}\n
{"data"}\n

1、bulk中的每個操作都可能要轉(zhuǎn)發(fā)到不同的node的shard去執(zhí)行

2萨脑、如果采用比較良好的json數(shù)組格式

允許任意的換行隐轩,整個可讀性非常棒,讀起來很爽渤早,es拿到那種標準格式的json串以后职车,要按照下述流程去進行處理

  • 將json數(shù)組解析為JSONArray對象,這個時候鹊杖,整個數(shù)據(jù)悴灵,就會在內(nèi)存中出現(xiàn)一份一模一樣的拷貝,一份數(shù)據(jù)是json文本骂蓖,一份數(shù)據(jù)是JSONArray對象
  • 解析json數(shù)組里的每個json积瞒,對每個請求中的document進行路由
  • 為路由到同一個shard上的多個請求,創(chuàng)建一個請求數(shù)組
  • 將這個請求數(shù)組序列化
  • 將序列化后的請求數(shù)組發(fā)送到對應的節(jié)點上去

3登下、耗費更多內(nèi)存茫孔,更多的jvm gc開銷

占用更多的內(nèi)存可能就會積壓其他請求的內(nèi)存使用量叮喳,比如說最重要的搜索請求,分析請求缰贝,等等嘲更,此時就可能會導致其他請求的性能急速下降
另外的話,占用內(nèi)存更多揩瞪,就會導致java虛擬機的垃圾回收次數(shù)更多赋朦,更頻繁,每次要回收的垃圾對象更多李破,耗費的時間更多宠哄,導致es的java虛擬機停止工作線程的時間更多

假如:一個bulk size的請求為10M,共計100個請求就是1GB的內(nèi)存占用嗤攻,假設轉(zhuǎn)為json對象后為2GB毛嫉,如果請求數(shù)量更多,那么消耗的內(nèi)存就就更多了妇菱,同時Java虛擬機的垃圾回收也會更加的耗時承粤,導致系統(tǒng)性能下降。

4闯团、使用現(xiàn)在的奇特格式的優(yōu)點

  • 不用將其轉(zhuǎn)換為json對象辛臊,不會出現(xiàn)內(nèi)存中的相同數(shù)據(jù)的拷貝,直接按照換行符切割json
  • 對每兩個一組的json房交,讀取meta彻舰,進行document路由
  • 直接將對應的json發(fā)送到node上去

5、最大的優(yōu)勢在于候味,不需要將json數(shù)組解析為一個JSONArray對象形成一份大數(shù)據(jù)的拷貝刃唤,浪費內(nèi)存空間,這樣可以盡可能地保證性能

五白群、搜索引擎

5.1 search結(jié)果解析(search timeout機制說明)

took:整個搜索請求花費了多少毫秒
hits.total:本次搜索尚胞,返回了幾條結(jié)果
hits.max_score:本次搜索的所有結(jié)果中,最大的相關(guān)度分數(shù)是多少帜慢,每一條document對于search的相關(guān)度笼裳,_score分數(shù)越大,排位越靠前
hits.hits:默認查詢前10條數(shù)據(jù)崖堤,完整數(shù)據(jù)侍咱,_score降序排序
timeout:默認無timeout,latency平衡completeness密幔,手動指定timeout,timeout查詢執(zhí)行機制
shards:shards fail的條件(primary和replica全部掛掉)撩轰,不影響其他shard胯甩。默認情況下來說昧廷,一個搜索請求,會打到一個index的所有primary shard上去偎箫,每個primary shard都可能會有一個或多個replica shard木柬,所以請求也可以到primary shard的其中一個replica shard上去。

帶上超時參數(shù):timeout=10ms淹办,timeout=1s眉枕,timeout=1m

GET /_search?timeout=10m

5.2 multi-index和multi-type搜索模式解析以及搜索原理

multi-index和multi-type搜索模式

如何一次性搜索多個index和多個type下的數(shù)據(jù)

GET /_search:               所有索引,所有type下的所有數(shù)據(jù)都搜索出來
GET /index1/_search:        指定一個index怜森,搜索其下所有的數(shù)據(jù)
GET /index1,index2/_search: 同時搜索兩個index下的數(shù)據(jù)
GET /*1,*2/_search:         按照通配符去匹配多個索引
GET /_all/_search            可以代表搜索所有index下的數(shù)據(jù)

也可以加刪除type屬性速挑,但是es

簡單的搜索原理

客戶端發(fā)送一個請求,會將請求分發(fā)到所有的primary shard上執(zhí)行副硅,因為每一個shard上都包含部分數(shù)據(jù)姥宝,所有每一個shard上都可能包含搜索請求的結(jié)果;
如果primary shard有 replica shard恐疲,那么請求也會發(fā)送到replica shard上去處理

5.3 分頁搜索以及deep paging性能問題

使用es進行分頁搜索的語法

size腊满,from

GET /_search?size=10
GET /_search?size=10&from=0
GET /_search?size=10&from=20

deep paging

搜索很深就是deep paging;會很耗費性能培己,應當盡量避免碳蛋。比如查詢臨近最后一頁的數(shù)據(jù),而數(shù)據(jù)在各個分片上省咨,最后需要將各個分片返回的數(shù)據(jù)進行綜合處理疮蹦,每個分片實際返回數(shù)據(jù)不是每頁的條數(shù)。

5.4 快速掌握query string search語法以及_all metadata

query string基礎語法

GET /demo_index/_search?q=test_field:test
GET /demo_index/_search?q=+test_field:test
GET /demo_index/_search?q=-test_field:test

使用+號和沒有+號是一樣茸炒,表示包含指定的關(guān)鍵詞愕乎;-號表示不含

_all metadata的原理和作用

# 匹配包含test的數(shù)據(jù)
GET /demo_index/_search?q=test

直接可以搜索所有的field,任意一個field包含指定的關(guān)鍵字就可以搜索出來壁公。我們在進行中搜索的時候感论,不是對document中的每一個field都進行一次搜索;

es中的_all元數(shù)據(jù)紊册,在建立索引的時候比肄,每插入一條document,它里面包含了多個field囊陡,此時芳绩,es會自動將多個field的值,全部用字符串的方式串聯(lián)起來撞反,變成一個長的字符串妥色,作為_all field的值,同時建立索引遏片;
后面如果在搜索的時候嘹害,沒有對某個field指定搜索撮竿,就默認搜索_all field,其中是包含了所有field的值的笔呀。

舉個例子

{
  "name": "tom",
  "age": 25,
  "email": "tom@1qq.com",
  "address": "beijing"
}

tom 25 tom@qq.com beijing幢踏,作為這一條document的_all field的值,同時進行分詞后建立對應的倒排索引许师;生產(chǎn)環(huán)境通常不使用

5.5 mapping到底是什么房蝉?

自動或手動為index中的type建立的一種數(shù)據(jù)結(jié)構(gòu)和相關(guān)配置,簡稱為mapping微渠。

當添加數(shù)據(jù)時會dynamic mapping搭幻,自動為我們建立index,創(chuàng)建type敛助,以及type對應的mapping粗卜,mapping中包含了每個field對應的數(shù)據(jù)類型,以及如何分詞等設置纳击。

示例

添加一些測試數(shù)據(jù)

PUT /website/_doc/1
{
  "post_date": "2020-01-01",
  "title": "my first article",
  "content": "this is my first article in this website",
  "author_id": 9527
}

PUT /website/_doc/2
{
  "post_date": "2020-01-02",
  "title": "my second article",
  "content": "this is my second article in this website",
  "author_id": 9527
}

PUT /website/_doc/3
{
  "post_date": "2020-01-03",
  "title": "my third article",
  "content": "this is my third article in this website",
  "author_id": 9527
}

嘗試如下搜索续扔,只會返回1條數(shù)據(jù):

GET /website/_search?q=2020
GET /website/_search?q=2020-01-02
GET /website/_search?q=post_date:2020-01-01
GET /website/_search?q=post_date:2020

查看mapping

GET /website/_mapping

搜索結(jié)果為什么不一致,因為es自動建立mapping的時候焕数,設置了不同的field不同的data type纱昧。不同的data type的分詞、搜索等行為是不一樣的堡赔。所以出現(xiàn)了_all field和post_date field的搜索表現(xiàn)不是我們所期望的

5.6 精確匹配與全文搜索的對比分析

精確匹配(exact value)

2020-01-01识脆,exact value,搜索的時候善已,2020-01-01灼捂,才能搜索出來;如果你輸入一個01换团,是搜索不出來的

全文搜索(full text)

  • 縮寫 vs. 全程:cn vs. china悉稠;如2020-01-01,2020 01 01艘包,搜索2020的猛,或者01,都可以搜索出來想虎;china卦尊,搜索cn,也可以將china搜索出來
  • 格式轉(zhuǎn)化:like liked likes舌厨;likes岂却,搜索like,也可以將likes搜索出來
  • 大小寫:Tom vs tom;Tom淌友,搜索tom煌恢,也可以將Tom搜索出來
  • 同義詞:like vs love骇陈;like震庭,搜索love,同義詞你雌,也可以將like搜索出來

所有全文搜索不只是匹配完整的一個值器联,而是可以對值進行拆分詞語后(分詞)進行匹配,也可以通過縮寫婿崭、時態(tài)拨拓、大小寫、同義詞等進行匹配氓栈。

5.7 倒排索引核心原理

倒排索引(Inverted Index)也叫反向索引渣磷,有反向索引必有正向索引。通俗地來講授瘦,正向索引是通過key找value醋界,反向索引則是通過value找key。

doc1:I really liked my small dogs, and I think my mom also liked them.
doc2:He never liked any dogs, so I hope that my mom will not expect me to liked him.

分詞提完,初步的倒排索引的建立

|word  |doc1|doc2|
|:     |:   |:   |
|I     | *  |  * |
|really| *  |    |
|liked | *  |    |
|my    | *  |  * |
|small | *  |    |
|dogs  | *  |    |
|and   | *  |    |
....

搜索mother like little dog形纺,不可能有任何結(jié)果

這個是不是我們想要的搜索結(jié)果,因為在我們看來徒欣,mother和mom有區(qū)別嗎逐样?同義詞,都是媽媽的意思打肝。like和liked有區(qū)別嗎脂新?沒有,都是喜歡的意思粗梭,只不過一個是現(xiàn)在時争便,一個是過去時。little和small有區(qū)別嗎楼吃?同義詞始花,都是小小的。dog和dogs有區(qū)別嗎孩锡?狗酷宵,只不過一個是單數(shù),一個是復數(shù)躬窜。

因此正常情況下在建立倒排索引的時候浇垦,會執(zhí)行一個normalization操作,對拆分出的各個單詞進行相應的處理荣挨,以提升后面搜索的時候能夠搜索到相關(guān)聯(lián)的文檔的概率

重新建立倒排索引男韧,加入normalization朴摊,再次用mother liked little dog搜索,就可以搜索到了

mother like little dog會先分詞再normalization(時態(tài)的轉(zhuǎn)換此虑,單復數(shù)的轉(zhuǎn)換甚纲,同義詞的轉(zhuǎn)換,大小寫的轉(zhuǎn)換)

mother  --> mom
like    --> like
little  --> little
dog --> dog

doc1和doc2都會搜索出來

5.8 分詞器的內(nèi)部組成到底是什么朦前,以及內(nèi)置分詞器的介紹

什么是分詞器

一個分詞器介杆,將一段文本拆分成一個一個的單個的單詞,同時對每個單詞進行normalization(時態(tài)轉(zhuǎn)換韭寸,單復數(shù)轉(zhuǎn)換)春哨,最后將處理好的結(jié)果才會拿去建立倒排索引。

切分詞語恩伺,normalization(提升召回率【recall】)赴背;具體包含如下:

recall,召回率:搜索的時候晶渠,增加能夠搜索到的結(jié)果的數(shù)量

character filter:在一段文本進行分詞之前凰荚,先進行預處理,比如說最常見的就是乱陡,過濾html標簽(<span>hello<span> --> hello)浇揩,& --> and(I&you --> I and you)

tokenizer:分詞憨颠,hello you and me --> hello, you, and, me

token filter:lowercase,stop word养盗,synonymom,dogs --> dog往核,liked --> like嚷节,Tom --> tom聂儒,a/the/an --> 干掉衩婚,mother --> mom非春,small --> little

5.9 query string的分詞以及mapping

query string必須以和index建立時相同的analyzer進行分詞

query string對exact value和full text的區(qū)別對待

不同類型的field,可能有的就是full text护侮,有的就是exact value羊初;因此上面進行搜索時查詢結(jié)果不是我們預期的

測試分詞器

GET /_analyze
{
  "analyzer": "standard",
  "text": "Text to analyze"
}

5.10 什么是mapping再次回爐透徹理解

  • 往es里面直接插入數(shù)據(jù)业踏,es會自動建立索引勤家,同時建立type以及對應的mapping
  • mapping中就自動定義了每個field的數(shù)據(jù)類型
  • 不同的數(shù)據(jù)類型(比如說text和date)伐脖,可能有的是exact value,有的是full text
  • exact value绎巨,在建立倒排索引的時候场勤,分詞的時候歼跟,是將整個值一起作為一個關(guān)鍵詞建立到倒排索引中的哈街;full text骚秦,會經(jīng)歷各種各樣的處理作箍,分詞蒙揣,normaliztion(時態(tài)轉(zhuǎn)換,同義詞轉(zhuǎn)換嗤详,大小寫轉(zhuǎn)換),才會建立到倒排索引中
  • 同時呢苍狰,exact value和full text類型的field就決定了淋昭,在一個搜索過來的時候翔忽,對exact value field或者是full text field進行搜索的行為也是不一樣的,會跟建立倒排索引的行為保持一致材失;比如說exact value搜索的時候龙巨,就是直接按照整個值進行匹配恭应,full text query string,也會進行分詞和normalization再去倒排索引中去搜索
  • 可以用es的dynamic mapping胆屿,讓其自動建立mapping非迹,包括自動設置數(shù)據(jù)類型冷离;也可以提前手動創(chuàng)建index和type的mapping需曾,自己對各個field進行設置杆兵,包括數(shù)據(jù)類型,包括索引行為旧找,包括分詞器钦讳,等等

mapping缚去,就是index的type的元數(shù)據(jù)易结,每個type都有一個自己的mapping搞动,決定了數(shù)據(jù)類型矗烛,建立倒排索引的行為瞭吃,還有進行搜索的行為

5.11 mapping的核心數(shù)據(jù)類型以及dynamic mapping

核心的數(shù)據(jù)類型

string
byte,short和蚪,integer攒霹,long
float旅薄,double
boolean
date

dynamic mapping類型推測

true or false    -->    boolean
123              -->    long
123.01           -->    double
2020-01-01       -->    date
"hello world es" -->    string/text

5.12 手動建立和修改mapping以及定制string類型數(shù)據(jù)是否分詞

如何建立索引

analyzed      ------ 分詞類型
not_analyzed  ------ 不分詞
no            ------ 不分詞同時不能被搜索

只能創(chuàng)建index時手動建立mapping少梁,或者新增field mapping,但是不能update field mapping

PUT /website
{
  "mappings": {
    "properties": {
      "author_id": {
        "type": "long"
      },
      "title": {
        "type": "text",
        "analyzer": "english"
      },
      "content": {
        "type": "text"
      },
      "post_date": {
        "type": "date"
      },
      "publisher_id": {
        "type": "keyword"
      },
      "is_del": {
        "type":"boolean",
        "index":false
      }
    }
  }
}

"index":false 表示不加入索引
"type": "keyword" 表示不分詞妨马,在7.x版本后not_analyzed已經(jīng)被取消掉了

新增filed mapping

PUT /website/_mapping
{
  "properties":{
    
    "new_filed":{
      "type":"text",
      "index":false
    }
  }
}

5.13 mapping復雜數(shù)據(jù)類型以及object類型數(shù)據(jù)底層結(jié)構(gòu)

multivalue field

{ "tags": [ "tag1", "tag2" ]}

建立索引時與string是一樣的,數(shù)據(jù)類型不能混

empty field

null滤淳,[],[null]

object field

PUT /company/employee/1
{
  "address": {
    "country": "china",
    "province": "guangdong",
    "city": "guangzhou"
  },
  "name": "jack",
  "age": 27,
  "join_date": "2020-01-01"
}

對應這種object類型的底層數(shù)據(jù)存儲示例

"authors": [
    { "age": 26, "name": "Jack White"},
    { "age": 55, "name": "Tom Jones"},
    { "age": 39, "name": "Kitty Smith"}
]

上面的會轉(zhuǎn)換成下面這種:

{
    "authors.age":    [26, 55, 39],
    "authors.name":   [jack, white, tom, jones, kitty, smith]
}

5.14 search api的基礎語法介紹

search api的基本語法

GET /search
{}

GET /index1,index2/type1,type2/search
{}

GET /_search
{
  "from": 0,
  "size": 10
}

http協(xié)議中g(shù)et是否可以帶上request body

HTTP協(xié)議屁擅,一般不允許get請求帶上request body,但是因為get更加適合描述查詢數(shù)據(jù)的操作硝皂。

碰巧,很多瀏覽器贝或,或者是服務器咪奖,也都支持GET+request body模式趟佃;如果遇到不支持的場景闲昭,也可以用POST /_search

5.15 快速上機動手實戰(zhàn)Query DSL搜索語法

示例什么是Query DSL

GET /_search
{
    "query": {
        "match_all": {}
    }
}

Query DSL的基本語法

{
    QUERY_NAME: {
        ARGUMENT: VALUE,
        ARGUMENT: VALUE,...
    }
}

{
    QUERY_NAME: {
        FIELD_NAME: {
            ARGUMENT: VALUE,
            ARGUMENT: VALUE,...
        }
    }
}

示例

GET /test_index/_search 
{
  "query": {
    "match": {
      "test_field": "test"
    }
  }
}

如何組合多個搜索條件

初始數(shù)據(jù):

PUT /website/_doc/1
{
  
  "title": "my elasticsearch article",
  "content": "es is very bad",
  "author_id": 110
}

PUT /website/_doc/2
{
  
  "title": "my elasticsearch article",
  "content": "es is very good",
  "author_id": 111
}

PUT /website/_doc/3
{
  
  "title": "my elasticsearch article",
  "content": "es is just so so",
  "author_id": 112
}
  1. title必須包含elasticsearch,content可以包含elasticsearch也可以不包含,author_id必須不為111
GET /website/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "match": {
            "title": "elasticsearch"
          }
        }
      ],
      "should": [
        {
          "match": {
            "content": "elasticsearch"
          }
        }
      ],
      "must_not": [
        {
          "match": {
            "author_id": 111
          }
        }
      ]
    }
  }
}    

示例2

GET /website/_search
{
  "query": {
    "bool": {
      "must": {
        "match": {
          "name": "tom"
        }
      },
      "should": [
        {
          "match": {
            "hired": true
          }
        },
        {
          "bool": {
            "must": {
              "match": {
                "personality": "good"
              }
            },
            "must_not": {
              "match": {
                "rude": true
              }
            }
          }
        }
      ],
      "minimum_should_match": 1
    }
  }
}

should 相當于or
bool 相當于()
must 相當于and
must_not 就是不等于

5.16 filter與query深入對比解密:相關(guān)度租幕,性能

filter與query示例

PUT /company/_doc/1
{
  "join_date": "2016-01-01",
  "age":33,
  "name":"tom cat"
}

PUT /company/_doc/2
{
  "join_date": "2016-01-01",
  "age":29,
  "name":"jerry mouse"
}

搜索請求:年齡必須大于等于30,同時join_date必須是2016-01-01

GET /company/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "match": {
            "join_date": "2016-01-01"
          }
        }
      ],
      "filter": {
        "range": {
          "age": {
            "gte": 30
          }
        }
      }
    }
  }
}

filter與query對比大解密

  • filter蝎宇,僅僅只是按照搜索條件過濾出需要的數(shù)據(jù)而已姥芥,不計算任何相關(guān)度分數(shù)凉唐,對相關(guān)度沒有任何影響
  • query淡溯,會去計算每個document相對于搜索條件的相關(guān)度咱娶,并按照相關(guān)度進行排序

一般來說屈糊,如果你是在進行搜索逻锐,需要將最匹配搜索條件的數(shù)據(jù)先返回,那么用query鳄哭;如果只是要根據(jù)一些條件篩選出一部分數(shù)據(jù),不關(guān)注其排序勺拣,那么用filter
除非是你的這些搜索條件,你希望越符合這些搜索條件的document越排在前面返回愤惰,那么這些搜索條件要放在query中宦言;如果你不希望一些搜索條件來影響你的document排序奠旺,那么就放在filter中即可

filter與query性能

  • filter,不需要計算相關(guān)度分數(shù)忿晕,不需要按照相關(guān)度分數(shù)進行排序杏糙,同時還有內(nèi)置的自動cache最常使用filter的數(shù)據(jù)
  • query宏侍,相反咱旱,要計算相關(guān)度分數(shù)吐限,按照分數(shù)進行排序,而且無法cache結(jié)果

bool狐粱,must,must_not蒋搜,should,filter
每個子查詢都會計算一個document針對它的相關(guān)度分數(shù)祷杈,然后bool綜合所有分數(shù)宿刮,合并為一個分數(shù)僵缺,當然filter是不會計算分數(shù)的

5.17 常用的各種query搜索語法

match all

GET /_search
{
    "query": {
        "match_all": {}
    }
}

match

GET /_search
{
    "query": { "match": { "title": "my elasticsearch article" }}
}

multi match

GET /test_index/_search
{
  "query": {
    "multi_match": {
      "query": "test",
      "fields": ["test_field", "test_field1"]
    }
  }
}

range query

GET /company/_search 
{
  "query": {
    "range": {
      "age": {
        "gte": 30
      }
    }
  }
}

term query

GET /test_index/_search 
{
  "query": {
    "term": {
      "test_field": "test hello"
    }
  }
}

terms query

對tag指定多個分組詞

GET /_search
{
    "query": { "terms": { "tag": [ "search", "full_text", "nosql" ] }}
}

5.18 如何定位不合法的搜索以及其原因

GET /company/_validate/query?explain
{
  "query": {
    "match": {
      "name": "cat"
    }
  }
}

一般用在那種特別復雜龐大的搜索下容贝,比如一下子寫了上百行的搜索斤富,這個時候可以先用validate api去驗證一下,搜索是否合法

5.19 如何定制搜索結(jié)果的排序規(guī)則

默認排序規(guī)則

默認情況下油额,是按照_score降序排序的;然而,某些情況下看峻,可能沒有有用的_score互妓,比如說filter

GET /_search
{
  "query": {
    "bool": {
      "filter": {
        "term": {
          "name": "cat"
        }
      }
    }
  }
}

當然,也可以是constant_score

GET /_search
{
  "query": {
    "constant_score": {
      "filter": {
        "term": {
          "author_id": 1
        }
      }
    }
  }
}

定制排序規(guī)則

使用sort來定制排序規(guī)則

GET /company/_search 
{
  "query": {
    "constant_score": {
      "filter": {
        "range": {
          "age": {
            "gte": 25
          }
        }
      }
    }
  },
  "sort": [
    {
      "join_date": {
        "order": "asc"
      }
    }
  ]
}

5.20 如何將一個field索引兩次來解決字符串排序問題

如果對一個string field進行排序,結(jié)果往往不準確,因為分詞后是多個單詞蝴簇,再排序就不是我們想要的結(jié)果了

通常解決方案是,將一個string field建立兩次索引互拾,一個分詞寄猩,用來進行搜索;一個不分詞斯辰,用來進行排序

示例:先創(chuàng)建索引

PUT /website 
{
  "mappings": {
    "properties": {
      "title": {
        "type": "text",
        "fields": {
          "raw": {
            "type": "keyword"
          }
        },
        "fielddata": true
      },
      "content": {
        "type": "text"
      },
      "post_date": {
        "type": "date"
      },
      "author_id": {
        "type": "long"
      }
    }
  }
}

設置正排索引 "fielddata": true

添加初始數(shù)據(jù)

PUT /website/_doc/1
{
  "title": "first article",
  "content": "this is my first article",
  "post_date": "2017-01-01",
  "author_id": 110
}

PUT /website/_doc/2
{
  "title": "second article",
  "content": "this is my second article",
  "post_date": "2018-01-01",
  "author_id": 111
}   

執(zhí)行查詢

GET /website/_search
{
  "query": {
    "match_all": {}
  },
  "sort": [
    {
      "title.raw": {
        "order": "desc"
      }
    }
  ]
}    

5.21 相關(guān)度評分TF&IDF算法

TF&IDF算法介紹

  • relevance score算法柄瑰,簡單來說蒲跨,就是計算出或悲,一個索引中的文本,與搜索文本男公,他們之間的關(guān)聯(lián)匹配程度;
  • Elasticsearch使用的是 term frequency/inverse document frequency算法,簡稱為TF/IDF算法;
  • Term frequency:搜索文本中的各個詞條在field文本中出現(xiàn)了多少次镀琉,出現(xiàn)次數(shù)越多屋摔,就越相關(guān);
  • Inverse document frequency:搜索文本中的各個詞條在整個索引的所有文檔中出現(xiàn)了多少次替梨,出現(xiàn)的次數(shù)越多钓试,就越不相關(guān);
舉個例子:
   搜索請求:hello world
   
   doc1:hello, today is very good
   doc2:hi world, how are you
   
   比如說副瀑,在index中有1萬條document,hello這個單詞在所有的document中糠睡,一共出現(xiàn)了1000次挽鞠;world這個單詞在所有的document中,一共出現(xiàn)了100次狈孔;那么doc2的相關(guān)度越高
  • Field-length norm:field內(nèi)容長度信认,越長,相關(guān)度越弱

5.22 內(nèi)核級知識點之doc value初步探秘

搜索的時候均抽,要依靠倒排索引嫁赏;排序的時候,需要依靠正排索引油挥,看到每個document的每個field潦蝇,然后進行排序,所謂的正排索引喘漏,其實就是doc values护蝶;
在建立索引的時候,一方面會建立倒排索引翩迈,以供搜索用持灰;一方面會建立正排索引,也就是doc values负饲,以供排序堤魁,聚合,過濾等操作使用返十;
doc values是被保存在磁盤上的妥泉,此時如果內(nèi)存足夠,os會自動將其緩存在內(nèi)存中洞坑,性能還是會很高盲链;如果內(nèi)存不足夠,os會將其寫入磁盤上;

倒排索引類似如下(對每個字段進行操作):

doc1的content字段內(nèi)容: hello world you and me
doc2的content字段內(nèi)容: hi, world, how are you

對上面的內(nèi)容進行分詞刽沾,拆分為一個個單詞(term)本慕,建立類似如下的字典目錄

term Posting List(倒排列表)
world [doc1,doc2]
hello doc1
hi doc2
... ....

Posting List(倒排列表)里面是文檔的id

這樣在搜索時根據(jù)term的索引(類似MySQL的索引)去找到符合的term進而找到對應的文檔信息;

可以這樣理解倒排索引:通過單詞找到對應的倒排列表侧漓,根據(jù)倒排列表中的倒排項進而可以找到文檔記錄锅尘;過程類型如下圖:


正排索引類似如下(對整個文檔進行操作):

doc1內(nèi)容: { "name": "jack", "age": 27 }
doc2內(nèi)容: { "name": "tom", "age": 30 }

document    name        age
doc1        jack        27
doc2        tom         30  

5.23 分布式搜索引擎內(nèi)核解密之query phase

query phase

  1. 搜索請求發(fā)送到某一個coordinate node,構(gòu)構(gòu)建一個priority queue布蔗,長度以paging操作from和size為準藤违,默認為10
  2. coordinate node將請求轉(zhuǎn)發(fā)到所有shard,每個shard本地搜索纵揍,并構(gòu)建一個本地的priority queue
  3. 各個shard將自己的priority queue返回給coordinate node顿乒,并構(gòu)建一個全局的priority queue

replica shard如何提升搜索吞吐量

一次請求要打到所有shard的一個replica/primary上去,如果每個shard都有多個replica骡男,那么同時并發(fā)過來的搜索請求可以同時打到其他的replica上去

5.24 分布式搜索引擎內(nèi)核解密之fetch phase

fetch phbase工作流程

  1. coordinate node構(gòu)建完priority queue之后淆游,就發(fā)送mget請求去所有shard上獲取對應的document
  2. 各個shard將document返回給coordinate node
  3. coordinate node將合并后的document結(jié)果返回給client客戶端

一般搜索,如果不加from和size隔盛,就默認搜索前10條犹菱,按照_score排序

5.25 搜索相關(guān)參數(shù)梳理以及bouncing results問題解決方案

preference

決定了哪些shard會被用來執(zhí)行搜索操作

_primary, _primary_first, _local, _only_node:xyz, _prefer_node:xyz, _shards:2,3

bouncing results問題,兩個document排序吮炕,field值相同腊脱;不同的shard上,可能排序不同龙亲;每次請求輪詢打到不同的replica shard上陕凹;每次頁面上看到的搜索結(jié)果的排序都不一樣。這就是bouncing result鳄炉,也就是跳躍的結(jié)果杜耙。

搜索的時候,是輪詢將搜索請求發(fā)送到每一個replica shard(primary shard)拂盯,但是在不同的shard上佑女,可能document的排序不同

解決方案就是將preference設置為一個字符串,比如說user_id谈竿,讓每個user每次搜索的時候团驱,都使用同一個replica shard去執(zhí)行,就不會看到bouncing results了

timeout

主要就是限定在一定時間內(nèi)空凸,將部分獲取到的數(shù)據(jù)直接返回嚎花,避免查詢耗時過長

routing

document文檔路由,_id路由呀洲,routing=user_id紊选,這樣的話可以讓同一個user對應的數(shù)據(jù)到一個shard上去

search_type

default:query_then_fetch

dfs_query_then_fetch啼止,可以提升revelance sort精準度

5.26 基于scoll技術(shù)滾動搜索大量數(shù)據(jù)

如果一次性要查出來10萬條數(shù)據(jù),那么性能會很差丛楚,此時一般會采取用scoll滾動查詢族壳,一批一批的查,直到所有數(shù)據(jù)都查詢完處理完趣些。

使用scoll滾動搜索,可以先搜索一批數(shù)據(jù)贰您,然后下次再搜索一批數(shù)據(jù)坏平,以此類推,直到搜索出全部的數(shù)據(jù)來

scoll搜索會在第一次搜索的時候锦亦,保存一個當時的視圖快照舶替,之后只會基于該舊的視圖快照提供數(shù)據(jù)搜索,如果這個期間數(shù)據(jù)變更杠园,是不會讓用戶看到的

采用基于_doc進行排序的方式顾瞪,性能較高

每次發(fā)送scroll請求,我們還需要指定一個scoll參數(shù)抛蚁,指定一個時間窗口陈醒,每次搜索請求只要在這個時間窗口內(nèi)能完成就可以了;

GET /website/_search?scroll=1m
{
  "query": {
    "match_all": {}
  },
  "sort": [ "_doc" ],
  "size": 3
}

獲得的結(jié)果會有一個scoll_id瞧甩,下一次再發(fā)送scoll請求的時候钉跷,必須帶上這個scoll_id

GET /_search/scroll
{
    "scroll": "1m", 
    "scroll_id" : "FGluY2x1ZGVfY29udGV4dF91dWlkDXF1ZXJ5QW5kRmV0Y2gBFHg3bnJvM01CYXBadGRjZ1FELWNqAAAAAAAADY8WdXVKQzR3TzVSMEtialVYM1gxbWkzZw=="
}

六、索引管理

6.1 索引的創(chuàng)建肚逸、修改爷辙、刪除

創(chuàng)建索引

指定分片信息、mapping信息

PUT /index_demo
{
  "settings": {
    "number_of_shards": 1,
    "number_of_replicas": 0
  },
  "mappings": {
    "properties": {
      "name":{
        "type": "text"
      }
    }
  }
}

使用默認的配置

PUT /index_pretty?pretty

修改索引

PUT /index_demo/_settings
{
  "number_of_replicas": 1
}

刪除索引

DELETE /index_demo
DELETE /index_1,index_2
DELETE /index_demo*
DELETE /_all

6.2 修改分詞器以及定制自己的分詞器

默認的分詞器standard

  • standard tokenizer:以單詞邊界進行切分
  • standard token filter:什么都不做
  • lowercase token filter:將所有字母轉(zhuǎn)換為小寫
  • stop token filer(默認被禁用):移除停用詞朦促,比如a the it等等

修改分詞器的設置

  • 啟用english停用詞token filter(創(chuàng)建索引的時候才可以)
PUT /index_demo
{
  "settings": {
    "number_of_shards": 1,
    "number_of_replicas": 0,
    "analysis": {
      "analyzer": {
        "es_std":{
          "type": "standard",
          "stopwords": "_english_"
        }
      }
    }
  },
  "mappings": {
    "properties": {
      "name":{
        "type": "text"
      }
    }
  }
}

測試定制的分詞器的效果:

# 使用定制的
GET /index_demo/_analyze
{
  "analyzer": "es_std",
  "text": "a dog is in the house"
}

# 使用默認的
GET /index_demo/_analyze
{
  "analyzer": "standard",
  "text":"a dog is in the house"
}

定制分詞器

&轉(zhuǎn)換為and膝晾,a 、the不做處理务冕,將html標簽過濾掉血当,將字符轉(zhuǎn)為小寫的

PUT /index_demo
{
  "settings": {
    "analysis": {
      "char_filter": {
        "&_to_and": {
          "type": "mapping",
          "mappings": ["&=> and"]
        }
      },
      "filter": {
        "my_stopwords":{
          "type": "stop",
          "stopwords": ["the", "a"]
        }
      },
      "analyzer": {
        "my_analyzer":{
          "type":"custom",
          "char_filter": ["html_strip", "&_to_and"],
          "tokenizer":"standard",
          "filter":["lowercase","my_stopwords"]
        }
      }
    }
  }
}    

測試定制的分詞器

GET /index_demo/_analyze
{
  "text": "tom&jerry are a friend in the house, <a>, HAHA!!",
  "analyzer": "my_analyzer"
}

6.3 深入探秘type底層數(shù)據(jù)結(jié)構(gòu)

type,是一個index中用來區(qū)分類似的數(shù)據(jù)的洒疚,類似的數(shù)據(jù)歹颓,但是可能有不同的fields,而且有不同的屬性來控制索引建立油湖、分詞器巍扛;
field的value,在底層的lucene中建立索引的時候乏德,全部是opaque bytes類型撤奸,不區(qū)分類型的吠昭;
lucene是沒有type的概念的,在document中胧瓜,實際上將type作為一個document的field來存儲矢棚,即_type,es通過_type來進行type的過濾和篩選府喳;
一個index中的多個type蒲肋,實際上是放在一起存儲的,因此一個index下钝满,不能有多個type重名兜粘,因為那樣是無法處理的;
在es7中一個index只能有一個type弯蚜,默認為_doc孔轴,不推薦去自定義了。

舉例說明

設置的mappings如下:

{
   "ecommerce": {
      "mappings": {
        "_type": {
          "type": "string",
          "index": "not_analyzed"
        },
        "name": {
          "type": "string"
        }
        "price": {
          "type": "double"
        }
        "service_period": {
          "type": "string"
        }
        "eat_period": {
          "type": "string"
        }
      }
   }
}

假設有如下2條數(shù)據(jù)存入

{
  "name": "geli kongtiao",
  "price": 1999.0,
  "service_period": "one year"
}

{
  "name": "aozhou dalongxia",
  "price": 199.0,
  "eat_period": "one week"
}

在底層的存儲是這樣子的

{
  "_type": "elactronic_goods",
  "name": "geli kongtiao",
  "price": 1999.0,
  "service_period": "one year",
  "eat_period": ""
}

{
  "_type": "fresh_goods",
  "name": "aozhou dalongxia",
  "price": 199.0,
  "service_period": "",
  "eat_period": "one week"
}

如果存入數(shù)據(jù)沒有某個filed時碎捺,將會存入一個空值路鹰;假如說,將兩個type的field完全不同收厨,放在一個index下晋柱,那么就每條數(shù)據(jù)都至少有一半的field在底層的lucene中是空值,會有嚴重的性能問題帽氓;
因此在es7中一個index只能有一個type趣斤,默認為_doc,不推薦去自定義了黎休。

6.4 mapping root object剖析

root object

就是某個type對應的mapping json浓领,包括了properties,metadata(_id势腮,_source联贩,_type),settings(analyzer)捎拯,其他settings(比如include_in_all)

PUT /index_demo
{
  "mappings": {
    "properties": {
      
    }
  }
}

properties

type泪幌,index,analyzer

PUT /index_demo/_mapping
{
  "properties": {
    "title": {
      "type": "text"
    }
  }
}

_source

優(yōu)點:

  1. 查詢的時候署照,直接可以拿到完整的document祸泪,不需要先拿document id,再發(fā)送一次請求拿document
  2. partial update基于_source實現(xiàn)
  3. reindex時建芙,直接基于_source實現(xiàn)没隘,不需要從數(shù)據(jù)庫(或者其他外部存儲)查詢數(shù)據(jù)再修改
  4. 可以基于_source定制返回field
  5. debug query更容易,因為可以直接看到_source

如果不需要上述好處禁荸,可以禁用_source右蒲;但是不建議這么做官方說明

PUT /index_demo
{
  "mappings": {
    "_source": {"enabled": false}
  }
}

標識性metadata

_index阀湿,_type,_id

6.5 定制化自己的dynamic mapping

定制dynamic策略

true:遇到陌生字段瑰妄,就進行dynamic mapping
false:遇到陌生字段陷嘴,就忽略
strict:遇到陌生字段,就報錯

示例:

PUT /index_demo
{
  "mappings": {
    "dynamic":"strict",
    "properties": {
      "title":{
        "type": "text"
      },
      "address":{
        "type": "object",
        "dynamic": true
      }
    }
  }
}

測試數(shù)據(jù)添加是否可以成功

PUT /index_demo/_doc/1
{
  "title":"this is firestone",
  "content":"this is content",
  "address":{
    "province":"北京",
    "city":"北京"
  }
}

由于做了現(xiàn)在间坐,因此上面這個會添加失敗

PUT /index_demo/_doc/1
{
  "title":"this is firestone",
  "address":{
    "province":"北京",
    "city":"北京"
  }
}

定制dynamic mapping策略

默認會按照一定格式識別date灾挨,比如yyyy-MM-dd。但是如果某個field先過來一個2017-01-01的值竹宋,就會被自動dynamic mapping成date涨醋,
后面如果再來一個"hello world"之類的值,就會報錯逝撬。可以手動關(guān)閉某個type的date_detection乓土,如果有需要宪潮,自己手動指定某個field為date類型。

PUT /index_demo/_mapping
{
    "date_detection": false
}

定制自己的dynamic mapping template

PUT /index_demo
{
  "mappings": {
    "dynamic_templates": [
      {
        "en": {
          "match": "*_en",
          "match_mapping_type": "string",
          "mapping": {
            "type": "text",
            "analyzer": "english"
          }
        }
      }
    ]
  }
}

測試

PUT index_demo/_doc/1
{
 "title":"this is my first article"
}

PUT index_demo/_doc/2
{
 "title_en":"this is my first article"
}

GET /index_demo/_search
{
  "query":{
    "match": {
      "title": "is"
    }
  }
}

title沒有匹配到任何的dynamic模板趣苏,默認就是standard分詞器狡相,不會過濾停用詞,is會進入倒排索引食磕,用is來搜索是可以搜索到的尽棕;
title_en匹配到了dynamic模板,就是english分詞器彬伦,會過濾停用詞滔悉,is這種停用詞就會被過濾掉,用is來搜索就搜索不到了单绑;

6.6 基于scoll+bulk+索引別名實現(xiàn)零停機重建索引

重建索引

一個field的設置是不能被修改的回官,如果要修改一個Field,那么應該重新按照新的mapping搂橙,建立一個index歉提,然后將數(shù)據(jù)批量查詢出來,重新用bulk api寫入index中区转;
批量查詢的時候苔巨,建議采用scroll api,并且采用多線程并發(fā)的方式來reindex數(shù)據(jù)废离,每次scoll就查詢指定日期的一段數(shù)據(jù)侄泽,交給一個線程即可;

舉個例子:

(1)一開始厅缺,依靠dynamic mapping蔬顾,插入數(shù)據(jù)宴偿,但是不小心有些數(shù)據(jù)是2017-01-01這種日期格式的,所以title這種field被自動映射為了date類型诀豁,實際上它應該是string類型的窄刘。

PUT /index_demo/_doc/1
{
  "title":"2020-01-01"
}

PUT /index_demo/_doc/2
{
  "title":"2020-01-02"
}

(2)當后期向索引中加入string類型的title值的時候,就會報錯舷胜。

PUT /index_demo/_doc/3
{
  "title":"es 入門"
}

(3)如果此時想修改title的類型娩践,是不可能的

PUT /index_demo/_mapping
{
  "properties":{
    "title":{
      "type":"text"
    }
  }
}

(4)此時,唯一的辦法烹骨,就是進行reindex翻伺,也就是說,重新建立一個索引沮焕,將舊索引的數(shù)據(jù)查詢出來吨岭,再導入新索引

(5)如果舊索引的名字是old_index,新索引的名字是new_index峦树,終端java應用辣辫,已經(jīng)在使用old_index在操作了,難道還要去停止java應用魁巩,修改使用的index為new_index急灭,才重新啟動java應用嗎?這個過程中谷遂,就會導致java應用停機葬馋,可用性降低

(6)所以說,給java應用一個別名肾扰,這個別名是指向舊索引的畴嘶,java應用先用著,java應用先用goods_index alias來操作白对,此時實際指向的是舊的my_index

PUT /index_demo/_alias/goods_index

(7)新建一個index掠廓,調(diào)整其title的類型為string

PUT /index_demo_new
{
  "mappings": {
    "properties": {
      "title":{
        "type":"text"
      }
    }
  }
}

(8)使用scroll api將數(shù)據(jù)批量查詢出來

GET /index_demo/_search?scroll=1m
{
  "query": {
    "match_all": {}
  },
  "sort": ["_doc"],
  "size": 1
}

(9)采用bulk api將scoll查出來的一批數(shù)據(jù),批量寫入新索引

POST /_bulk
{"index":{"_index":"index_demo_new", "_id":"1"}}
{"title":"2020-01-01"}

(10)反復循環(huán)8~9甩恼,查詢一批又一批的數(shù)據(jù)出來蟀瞧,采取bulk api將每一批數(shù)據(jù)批量寫入新索引

(11)將goods_index alias切換到my_index_new上去,java應用會直接通過index別名使用新的索引中的數(shù)據(jù)条摸,java應用程序不需要停機悦污,零提交,高可用

POST /_aliases
{
  "actions": [
    {
      "remove": {
        "index": "index_demo",
        "alias": "goods_index"
      }
    },
    {
      "add": {
        "index": "index_demo_new",
        "alias": "goods_index"
      }
    }
  ]
}

(12)直接通過goods_index別名來查詢钉蒲,是否ok

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末切端,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子顷啼,更是在濱河造成了極大的恐慌踏枣,老刑警劉巖昌屉,帶你破解...
    沈念sama閱讀 206,214評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異茵瀑,居然都是意外死亡间驮,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評論 2 382
  • 文/潘曉璐 我一進店門马昨,熙熙樓的掌柜王于貴愁眉苦臉地迎上來竞帽,“玉大人,你說我怎么就攤上這事鸿捧∫俾ǎ” “怎么了?”我有些...
    開封第一講書人閱讀 152,543評論 0 341
  • 文/不壞的土叔 我叫張陵匙奴,是天一觀的道長堆巧。 經(jīng)常有香客問我,道長泼菌,這世上最難降的妖魔是什么恳邀? 我笑而不...
    開封第一講書人閱讀 55,221評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮灶轰,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘刷钢。我一直安慰自己笋颤,他們只是感情好,可當我...
    茶點故事閱讀 64,224評論 5 371
  • 文/花漫 我一把揭開白布内地。 她就那樣靜靜地躺著伴澄,像睡著了一般。 火紅的嫁衣襯著肌膚如雪阱缓。 梳的紋絲不亂的頭發(fā)上非凌,一...
    開封第一講書人閱讀 49,007評論 1 284
  • 那天,我揣著相機與錄音荆针,去河邊找鬼敞嗡。 笑死,一個胖子當著我的面吹牛航背,可吹牛的內(nèi)容都是我干的喉悴。 我是一名探鬼主播,決...
    沈念sama閱讀 38,313評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼玖媚,長吁一口氣:“原來是場噩夢啊……” “哼箕肃!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起今魔,我...
    開封第一講書人閱讀 36,956評論 0 259
  • 序言:老撾萬榮一對情侶失蹤勺像,失蹤者是張志新(化名)和其女友劉穎障贸,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體吟宦,經(jīng)...
    沈念sama閱讀 43,441評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡篮洁,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,925評論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了督函。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片嘀粱。...
    茶點故事閱讀 38,018評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖辰狡,靈堂內(nèi)的尸體忽然破棺而出炊邦,到底是詐尸還是另有隱情焊切,我是刑警寧澤,帶...
    沈念sama閱讀 33,685評論 4 322
  • 正文 年R本政府宣布,位于F島的核電站鳞贷,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏枣抱。R本人自食惡果不足惜佩厚,卻給世界環(huán)境...
    茶點故事閱讀 39,234評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望吆倦。 院中可真熱鬧听诸,春花似錦、人聲如沸蚕泽。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽须妻。三九已至仔蝌,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間荒吏,已是汗流浹背敛惊。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評論 1 261
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留绰更,地道東北人瞧挤。 一個月前我還...
    沈念sama閱讀 45,467評論 2 352
  • 正文 我出身青樓,卻偏偏與公主長得像儡湾,于是被迫代替她去往敵國和親皿伺。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,762評論 2 345