学好 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,奔向小康

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