【单元目标】
-
什么是elasticsearch?
-
elasticsearch Analysis(分词器)概念及使用
-
go实现elasticsearch 搜索封装
【教学内容】
1. 什么是elasticsearch?
Elasticsearch 是一个实时的分布式存储、搜索、分析的引擎。
Elasticsearch is a real-time, distributed storage, search, and analytics engine
介绍那儿有几个关键字:
-
实时
-
分布式
-
搜索
-
分析
特点和优势
分布式实时文件存储,可将每一个字段存入索引,使其可以被检索到。
近乎实时分析的分布式搜索引擎。
分布式:索引分拆成多个分片,每个分片可有零个或多个副本。集群中的每个数据节点都可承载一个或多个分片,并且协调和处理各种操作;
负载再平衡和路由在大多数情况下自动完成。
可以扩展到上百台服务器,处理PB级别的结构化或非结构化数据(官网是这么说的)。也可以运行在单台PC上(已测试)。
支持插件机制,分词插件、同步插件、Hadoop插件、可视化插件等。
ES 基本概念
1)节点(Node)
运行了单个实例的ES主机称为节点,它是集群的一个成员,可以存储数据、参与集群索引及搜索操作。节点通过为其配置的ES集群名称确定其所要加入的集群。
2)集群(cluster)
ES可以作为一个独立的单个搜索服务器。不过,一般为了处理大型数据集,实现容错和高可用性,ES可以运行在许多互相合作的服务器上。这些服务器的集合称为集群。
3)分片(Shard)
ES的"分片(shard)"机制可将一个索引内部的数据分布地存储于多个节点,它通过将一个索引切分为多个 底层物理的Lucene索引完成索引数据的分割存储功能,这每一个物理的Lucene索引称为一个分片(shard)。
这样的好处是可以把一个大的索引拆分成多个,分布到不同的节点上 。降低单服务器的压力,构成分布式搜索,提高整体检索的效率(分片数的最优值与硬件参数和数据量大小有关)。 分片的数量只能在索引创建前指定,并且索引创建后不能更改。
4)副本(Replica)
副本是一个分片的精确复制 ,每个分片可以有零个或多个副本。副本的作用一是提高系统的容错性 ,当某个节点某个分片损坏或丢失时可以从副本中恢复。二是提高es的查询效率,es会自动对搜索请求进行负载均衡。
ES的数据架构
1)索引(index)
ES将数据存储于一个或多个索引中,索引是具有类似特性的文档的集合。类比传统的关系型数据库领域来说,索引相当于SQL中的一个数据库。
一个ES集群中可以按需创建任意数目的索引,但根据不同的硬件配置,索引数有一个建议范围
2)类型(Type)
类型是索引内部的逻辑分区(category/partition),然而其意义完全取决于用户需求。因此,一个索引内部可定义一个或多个类型(type)。一般来说,类型就是为那些拥有相同的域的文档做的预定义。类比传统的关系型数据库领域来说,类型相当于"表"。
特别注意的是, 根据官网信息:在Elasticsearch 6.0.0或更高版本中创建的索引只能包含一个映射类型 。在5.x中创建的具有多种映射类型的索引将继续像在Elasticsearch 6.x中一样工作。类型将在Elasticsearch 7.0.0中的API中弃用,并在8.0.0中完全删除。
3)文档(Document)
文档是Lucene索引和搜索的原子单位,它是包含了一个或多个域的容器,基于JSON格式进行表示。文档由一个或多个域组成,每个域拥有一个名字及一个或多个值,有多个值的域通常称为"多值域"。每个文档可以存储不同的域集,但同一类型下的文档至应该有某种程度上的相似之处。相当于mysql表中的row。
4)映射(Mapping)
映射是定义文档及其包含的字段如何存储和索引的过程。
例如,使用映射来定义:
哪些字符串字段应该被视为全文字段。
哪些字段包含数字、日期或地理位置。
文档中所有字段的值是否应该被索引到catch-all _all字段中。
日期值的格式。
用于控制动态添加字段的映射的自定义规则。
elasticsearch和mysql的对比
2. elasticsearch Analysis(分词器)概念及使用
什么是 Analysis?
顾名思义,文本分析就是把全文本转换成一系列单词(term/token)的过程 ,也叫分词 。在 ES 中,Analysis 是通过分词器(Analyzer) 来实现的,可使用 ES 内置的分析器或者按需定制化分析器。
举一个分词简单的例子:比如你输入 Mastering Elasticsearch
,会自动帮你分成两个单词,一个是 mastering
,另一个是 elasticsearch
,可以看出单词也被转化成了小写的。
分词器的组成
分词器是专门处理分词的组件,分词器由以下三部分组成:
Character Filters: 针对原始文本处理,比如去除 html 标签
Tokenizer: 按照规则切分为单词,比如按照空格切分
Token Filters: 将切分的单词进行加工,比如大写转小写,删除 stopwords,增加同义语
同时 Analyzer 三个部分也是有顺序的,从图中可以看出,从上到下依次经过 Character Filters
,Tokenizer
以及 Token Filters
,这个顺序比较好理解,一个文本进来肯定要先对文本数据进行处理,再去分词,最后对分词的结果进行过滤。
其中,ES 内置了许多分词器:
-
Standard Analyzer - 默认分词器,按词切分,小写处理
-
Simple Analyzer - 按照非字母切分(符号被过滤),小写处理
-
Stop Analyzer - 小写处理,停用词过滤(the ,a,is)
-
Whitespace Analyzer - 按照空格切分,不转小写
-
Keyword Analyzer - 不分词,直接将输入当做输出
-
Pattern Analyzer - 正则表达式,默认 \W+
-
Language - 提供了 30 多种常见语言的分词器
-
Customer Analyzer - 自定义分词器
-
Customer Analyzer - 中文分词器
下面详细介绍下:中文分词
分词∶
即把一段中文或者别的划分成一个个的关键字,我们在搜索时候会把自己的信息进行分词,会把数据库中或者索引库中的数据进行分词,然后进行一个匹配操作,默认的中文分词是将每个字看成一个词,比如**"我爱中国"会被分为"我""爱""中""国"**,这显然是不符合要求的,所以我们需要安装中文分词器ik来解决这个问题。
IK提供了两个分词算法:ik_smart
和ik_max_word
,其中ik smart
为最少切分,ik_max_word
为最细粒度划分!
ik_max_word
: 会将文本做最细粒度的拆分,比如会将"中华人民共和国国歌"拆分为"中华人民共和国,中华人民,中华,华人,人民共和国,人民,人,民,共和国,共和,和,国国,国歌",会穷尽各种可能的组合;
ik_smart
: 会做最粗粒度的拆分,比如会将"中华人民共和国国歌"拆分为"中华人民共和国,国歌"。
谈谈为什么要使用es搜索?
MySQL 的不足
MySQL 架构天生不适合海量数据查询,它只适合海量数据存储,但无法应对海量数据下各种复杂条件的查询,有人说加索引不是可以避免全表扫描,提升查询速度吗,为啥说它不适合海量数据查询呢,有两个原因:
*1、*加索引确实可以提升查询速度,但在 MySQL 中加多个索引最终在执行 SQL 的时候它只会选择成本最低的那个索引,如果没有索引满足搜索条件,就会触发全表扫描,而且即便你使用了组合索引,也要符合最左前缀原则才能命中索引,但在海量数据多种查询条件下很有可能不符合最左前缀原则而导致索引失效,而且我们知道存储都是需要成本的,如果你针对每一种情况都加索引,以 innoDB 为例,每加一个索引,就会创建一颗 B+ 树,如果是海量数据,将会增加很大的存储成本,之前就有人反馈说他们公司的某个表实际内容的大小才 10G, 而索引大小却有 30G!这是多么巨大的成本!所以千万不要觉得索引建得越多越好。
*2、*有些查询条件是 MySQL 加索引都解决不了的,比如我要查询商品中所有 title 带有「格力空调」的关键词,如果你用 MySQL 写,会写出如下代码
SELECT * FROM product WHERE title like '%格力空调%'
这样的话无法命中任何索引,会触发全表扫描,而且你不能指望所有人都能输对他想要的商品,是人就会犯错误,我们经常会犯类似把「格力空调」记成「格空调」的错误,那么 SQL 语句就会变成:
SELECT * FROM product WHERE title like '%格空调%'
这种情况下就算你触发了全表扫描也无法查询到任何商品,综上所述,MySQL 的查询确实能力有限
与其说上面列的这些点是 MySQL 的不足,倒不如说MySQL 本身就不是为海量数据查询而设计的
ES查询高效的原因
ES 中的索引为何如此高效,能在海量数据下达到秒级的效果呢?它采用了多种优化手段,最主要的原因是它采用了一种叫做倒排索引 的方式来生成索引,避免了全文档扫描,那么什么是倒排索引呢,通过文档来查找关键词等数据的我们称为正排索引,返之,通过关键词来查找文档的形式我们称之为倒排索引
假设有以下三个文档(Document)
要在其中找到含有 comming 的文档,如果要正排索引,那么要把每个文档的内容拿出来查找是否有此单词,毫无疑问这样的话会导致全表扫描,那么用倒排索引会怎么查找呢,它首先会将每个文档内容进行分词,小写化等,然后建立每个分词与包含有此分词的文档之前的映射关系,如果有多个文档包含此分词,那么就会按重要程度即文档的权重(通常是用 TF-IDF 给文档打分)将文档进行排序,于是我们可以得到如下关系
这样的话我们我要查找所有带有 comming 的文档,就只需查一次,而且这种情况下查询多个单词性能也是很好的,只要查询多个条件对应的文档列表,再取交集即可,极大地提升了查询效率。
3. go实现elasticsearch curd封装
go 操作elasticsearch
文档和教程
esapi https://pkg.go.dev/github.com/elastic/go-elasticsearch/v6@v6.8.5#section-readme
Go Elasticsearch 快速入门 Go Elasticsearch 快速入门-CSDN博客
注: 由于elasticsearch扩展包兼容问题,以下es操作会通过http方式来实现。
启动header-master,访问http://localhost:9100
添加索引
添加fang_demo索引的mapping
Go
{
"properties": {
"id": {
"type": "integer"
},
"title": {
"type": "text",
"analyzer":"ik_max_word",
"search_analyzer":"ik_max_word"
}
}
}
添加fang_video索引的mapping
Go
{
"properties": {
"id": {
"type": "integer"
},
"title": {
"type": "text",
"analyzer": "ik_max_word",
"search_analyzer": "ik_max_word"
},
"sub_title": {
"type": "text",
"analyzer": "ik_max_word",
"search_analyzer": "ik_max_word"
},
"status": {
"type": "integer"
},
"add_time": {
"type": "integer"
},
"img ": {
"type": "keyword"
},
"img1": {
"type": "keyword"
},
"channel_id": {
"type": "integer"
},
"type_id": {
"type": "integer"
},
"region_id": {
"type": "integer"
},
"user_id": {
"type": "integer"
},
"episodes_count": {
"type": "integer"
},
"episodes_update_time": {
"type": "integer"
},
"is_end": {
"type": "integer"
},
"is_hot": {
"type": "integer"
},
"is_recommend": {
"type": "integer"
}
}
}
检查index索引状态
封装搜索方法
es 搜索语法 http://events.jianshu.io/p/77f27c3176d0
es搜索时请求url和参数
Go
GET /demo/doc/_search?_source
{
"query" : {
"match" : {
"content" : "科技发展"
}
},
"highlight" : {
"pre_tags" : ["<font color='red'>"],
"post_tags" : ["</font>"],
"fields" : {
"content" : {}
}
},
"from": 0,
"size": 20,
"sort": [
{
"age": {
"order": "asc"
}
}
]
}
根据请求参数格式,封装搜索函数
在services/es下创建es.go,并初始化es url连接
定义es返回格式
封装es搜索方法
定义路由
访问接口,获得数据
定义文章格式,将数据绑定在结构体上
查看效果