我雖然窮,但是我愛學習呀。
當然,如果我有一個億,我願意放棄這個愛好,去玩耍。
開場白:
Lucene:一個基於Java的搜索庫,採用了基於倒排表的設計原理,高效地實現文本查找。底層採用了分段的存儲模式,在讀寫時避免了鎖的出現
Elasticsearch:對 Lucene 進行了封裝,對外提供 RESTful API
Kibana:負責以圖表的形式呈現數據
Logstash:動態數據收集管道
Beats:輕量型數據採集器,負責從成百上千或成千上萬臺機器和系統向 Logstash 或 Elasticsearch 發送數據
1 爲什麼要用 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 基本概念
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 基本操作
(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,這會導致代碼,業務,數據耦合度過高
方式三:召喚中間件
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 索引
看到了這裏,依然很窮吧,不慌,還有祕笈: