學好 ElasticSearch,擺脫貧困

 

我雖然窮,但是我愛學習呀。

當然,如果我有一個億,我願意放棄這個愛好,去玩耍。

官方教程

ElasticSearch中文社區

搜索:ElasticSearch OR MySQL?

開場白:

Lucene:一個基於Java的搜索庫,採用了基於倒排表的設計原理,高效地實現文本查找。底層採用了分段的存儲模式,在讀寫時避免了鎖的出現     

Elasticsearch:對 Lucene 進行了封裝,對外提供 RESTful API

Kibana:負責以圖表的形式呈現數據

Logstash:動態數據收集管道

Beats:輕量型數據採集器,負責從成百上千或成千上萬臺機器和系統向 Logstash 或 Elasticsearch 發送數據

1 爲什麼要用 Elasticsearch?

爲什麼要使用elasticsearch

1.1  站內搜索用SQL就能實現,爲什麼要用 Elasticsearch 呢?

先來看看我們用SQL來搜索的話,有什麼缺點:

  • SQL沒有相關度排名,如綜合排序。而ES內部是有一個打分機制的。
  • SQL搜索結果沒有關健字高亮顯示

 

  • SQL搜索有時較慢,尤其是數據庫不在本地時,超慢

1.2 模糊查詢

假設有一張書籍表book,根據書籍名稱從中檢索一些書籍時,用MySQL實現,你可能會這樣寫SQL:

SELECT * FROM book WHERE book_name LIKE '%關鍵詞%'

這樣做,理論上是可以搜到一些數據的,比如和用戶輸入的關鍵詞完全匹配的就可以,如果 顛倒了詞的順序,LIKE關鍵詞肯定是匹配不到了

例:

搜索一本書籍名稱爲 “黑萌影帝妙探妻” 的書,使用 SQL

select * from r_read_book where book_name like '%影帝黑%'

搜索結果爲空。而使用 ES 可以搜索出相應的書籍

GET book/_search
{
  "query": {
    "match": {
      "book_name": "影帝黑"
    }
  }
}

另外,LIKE是全表掃描的一個操作,如果你的數據量較小,還好說,但如果你數據量在百萬、千萬甚至更多的時候,耗時將是不可想象的,而且 DB 的 like操作會導致 SQL 注入黑洞。如:

select * from book where book_name like '%1%';drop table book;#%'

標紅的爲輸入值,#表示註釋

注入的獨立語句

drop table book; 

會造成刪表

2 有了 ElasticSearch 還需要 MySQL 嗎?

通常,我們可以使用 ES 來實現自己的站內搜索引擎,但還是推薦大家使用 MySQL 來做原始數據的存儲,然後基於 MySQL 在上層部署我們的 ES 中間件來實現我們的搜索引擎。主要原因是,MySQL 雖然在數據全文檢索方面顯得有些力不從心,但是因爲它的事務功能特性,可以保證不會出現髒數據。而ES對事務方面並無建樹,所以不是很適合存儲原始數據。當然,你可以運用雙寫的策略,一方面利用 MySQL 保證原始數據的安全性,另一方面,利用ES的搜索力量。更推薦的是將兩個中間件直接結合起來,同時使用 ES 查詢數據,並結合 MySQL 做數據的增刪差改,具體實現細節,要根據實際的需求來制定最優的解決方案。

2.1 ElasticSearch 作爲主要的後端系統

適用於新啓動的項目

2.2 將 Elasticsearch 添加到現有的系統

對於一個正在運作的複雜系統,但想加入搜索功能,可以採用該方式

例子:

有一家在線零售商店,商品的信息都存在 SQL 數據庫中,需要快速且相關性良好的搜索,於是安裝了 Elasticsearch。爲了索引數據,需要部署同步機制,既可以是 Elasticsearch 的插件,也可以是自行構建的定製化服務。同步的機制可以將每個商品對應的數據拉取出來,並將其索引到 Elasticsearch 中,每個商品存儲爲一篇 Elasticsearch 的文檔。信息的插入或更新仍可以在 SQL 數據庫上進行,可以使用 Elasticsearch 僅來處理搜索。保持 Elasticsearch 更新最近的變化取決於同步的機制。

3 開始動手了

3.1 安裝(WIndows)

下載地址

下載壓縮包,解壓,進入bin,運行 elasticsearch.bat

訪問:Elasticsearch 9200 端口,Kibana 5601端口。Kibana 控制檯地址

3.2 基本概念

全文搜索引擎 Elasticsearch 入門教程

Elasticsearch-基礎介紹及索引原理分析

3.2.1 Index

An index is a collection of documents that have somewhat similar characteristics. For example, you can have an index for customer data, another index for a product catalog, and yet another index for order data

ES 索引是文檔的集合,類似於數據庫。

3.2.2 Document

Elasticsearch 是面向文檔型數據庫,Index 裏面單條的記錄稱爲 Document(文檔)。許多條 Document 構成了一個 Index。用 JSON 作爲文檔序列化的格式,如:

{ "name" : "John",

"age" : 25,

"about" : "I love to go rock climbing",

"interests": [ "sports", "music" ] }

3.2.3 Type

Document 可以分組,比如 book 這個 Index 裏面,可以按類型分組(言情和玄幻),也可以按作者分組。這種分組就叫做 Type,它是虛擬的邏輯分組,用來過濾 Document。

如上,type 類型中的 book_name 字段與 author 類型中的 book_name 字段是被存儲在同一個字段中,而且兩個 book_name 字段在這兩種映射類型中都有相同的定義,如類型都是 text。

當你希望在一個索引中的兩個映射類型,一個映射類型中的 deleted 字段映射爲一個日期數據類型的字段,而在另一個映射類型中的 deleted 字段映射爲一個布爾數據類型的字段,這就會失敗。

而在一個關係型數據庫中,表之間是相互獨立的。一個表中的列與另一個表中同名的列沒有關係。

版本 8.x 後,廢棄了類型 Type,迴歸到一般意義上的索引定義,索引定位文檔。

Elasticsearch和關係型數據術語大致的對照表

關係數據庫 ⇒ 數據庫 ⇒ 表 ⇒ 行 ⇒ 列(Columns)

Elasticsearch ⇒ 索引(Index) ⇒ 類型(type) ⇒ 文檔(Docments) ⇒ 字段(Fields)

3.3 基本操作

基本操作API

什麼是mapping

(1) 查看 ES 的基本信息

GET /

(2) 新建一個 Index,並指定需要分詞的字段。

PUT /book
{
  "mappings": {
    "properties": {
      "book_name":    { "analyzer": "standard","type": "text"},  
      "author":  { "type": "keyword"  }, 
      "desc":   { "analyzer": "standard","type": "text"},
      "comment_count":{"type":"integer"}
    }
  }
}

上面代碼中,首先新建一個名稱爲book的 Index,裏面有 3 個字段

bookName:類型爲text文本,指定了一個標準的分詞器,用於全文搜索。

author:類型爲keyword關鍵字,不需要分詞,用於聚合查詢,統計分析。

desc:類型爲text文本,指定了一個標準的分詞器,用於全文搜索。

(3) 添加文檔,book 爲索引,_doc 爲類型。7.x 版本,一個索引只有一個類型。

POST book/_doc/1
{
  "book_name": "大人物的小萌妻", 
  "author": "秋如意",
  "desc": "她跟他相差了十歲,她,在學校,視做平庸無奇的眼鏡妹;在家族裏,被人笑沒人要的書呆子;他,商界大佬對他俯首稱臣,帝國上將奉他爲座上賓,偏偏有了交集!大叔,我會好好做你的小萌妻!你可不能反悔!大叔,愛你無悔!",
  "comment_count":57
}

POST book/_doc/2
{
  "book_name": "黑萌影帝妙探妻", 
  "author": "犬犬",
  "desc": "第一回合,他被她推得頭破血流,第二回合,他被她摔得滿目暈眩,但他始終知道,她就是那個他要的女人。",
  "comment_count":107
}

POST book/_doc/3
{
  "book_name": "西晉小廚師", 
  "author": "亂石蘭竹",
  "desc": "亂世將臨,且看衛平攬財富,聚美人,收天下英雄,扶大廈於將傾。",
  "comment_count":11
}

(4) 根據 id 查看文檔

GET book/_doc/1

(5) 修改文檔用 PUT,修改文檔 id 爲 1 的文檔

PUT book/_doc/1
{
  "book_name": "大人物的小萌妻", 
  "author": "秋如意",
  "desc": "她跟他相差了十歲",
  "comment_count":57
}

(6) 根據字段匹配文檔,再做修改。如下:找到 “author” 爲 “秋如意” 的文檔,再修改其 “desc” 字段值。

POST book/_update_by_query
{
  "script":{
    "source":"ctx._source.desc = params.desc",
    "lang":"painless",
    "params":{
      "desc":"她跟他相差了十歲"
    }
  },
  "query":{
    "match":{
      "author":"秋如意"
    }
  }
}

(7) 根據書籍名稱匹配查詢

GET book/_search
{
  "query": {
    "match": {
      "book_name": "影帝黑"
    }
  }
}

(8) 多個字段匹配查詢

GET book/_search
{
  "query": {
    "bool":{
      "must":[
          {"match":{
            "author":"秋如意"
          }},{
            "match":{
              "book_name":"小"
            }
          }
        ]
    }
  }
}

(9) 統計數量

GET book/_count
{
  "query": {
    "bool":{
      "must":[
          {"match":{
            "author":"秋如意"
          }},{
            "match":{
              "book_name":"小"
            }
          }
        ]
    }
  }
}

(10) 按某字段排序

GET user/_search
{
  "query": {
    "range":{
      "age":{
        "gte":30,
        "lte":40
      }
    }
  },"sort":[
    {
      "age":{
        "order":"desc"
      }
    }
  ]
}

(11) 查詢結果高亮顯示

GET book/_search
{
    "query" : {
        "match": { "book_name": "影帝黑" }
    },
    "highlight" : {
        "fields" : {
            "book_name" : {}
        }
    }
}

(12) 聚合查詢 

GET book/_search
{
  "size": 0,
  "aggs": {
    "range": {
      "field": "comment_count",
      "ranges": [
        {
          "from": 100,
          "to": 110
        },
        {
          "from": 50,
          "to": 60
        }
      ]
    }
  }
}

(13) 分組查詢,如下按 “author” 分組查詢

GET book/_search
{
  "size": 0,
  "aggs": {
    "author": {
      "terms":{
        "field":"author"
      }
    }
  }
}

3.4 SpringBoot 集成 Elasticsearch

參考

elasticsearch入門 springboot2集成elasticsearch 實現全文搜索

結合spring boot和mysql快速實現elasticsearch7簡單功能

3.5 MySQL的數據同步到 ES

全量同步:搭建好 ES 後,把 MySQL 的數據一次性同步過去

增量同步:MySQL 產生新的數據後同步到 ES,包括:新插入的數據,更新了老數據,刪除了老數據

靈魂拷問:你會怎樣去實現捏?

方式一:

  • select * from table,然後在程序中遍歷,對應生成一條條文檔插入到 ES。中途又有了新數據,咋辦捏?每一次同步都保留最大的 last_update_time,select * from table where updat_time >= last_update_time。

方式二:

  • 在操作數據庫的 CRUD 方法裏對應加上 ES 的CRUD,這會導致代碼,業務,數據耦合度過高

方式三:召喚中間件

go-mysql-elasticsearch

3.5.1 Binlog 訂閱

二進制日誌 binlog:binlog是記錄所有數據庫表結構變更(例如CREATE、ALTER TABLE…)以及表數據修改(INSERT、UPDATE、DELETE…)的二進制日誌。binlog不會記錄SELECT和SHOW這類操作,因爲這類操作對數據本身並沒有修改,但你可以通過查詢通用日誌來查看MySQL執行過的所有語句。

阿里巴巴 MySQL binlog 增量訂閱&消費組件Canal

3.5.2 Logstash

Logstash 是開源的服務器端數據處理管道,能夠同時從多個來源採集數據,轉換數據,然後將數據發送到您最喜歡的“存儲庫”中。官網

 

下面表演將 house 表及其關聯表的數據同步到 ES 的 house 索引

 

 

 

 

 

看到了這裏,依然很窮吧,不慌,還有祕笈:

學好 ElasticSearch,奔向小康

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章