由于工作中接触到了ES,并且惊喜的发现具有很好的性能,所以特意对其相关知识点进行总结,参考了很多网上大神的总结,这里主要是加深自己对es的了解。
1.简介
ES是基于lucene的构建的高扩展的分布式全文检索引擎,可以做到近乎实时的存储,全文检索。
注:Lucene 是 apache 下的一个开放源代码的全文检索引擎工具包,提供了完整的查询引擎和索引引擎,部分文本分析引擎
2.主要概念
集群cluster | es是分布式全文检索引擎,可以由多台服务器组成,它们的集合即es集群。 |
节点node | 每一个服务器,即es节点。 |
分片shard | 当文档的数据较大的时候,单个节点的内存或者磁盘不足,需要多个节点,可以将数据切分成多个分片,然后分布在不同的节点上。 分片作用: 1.能够水平扩展容量。 2.使得各个分片能够分布式,并行操作,提高性能。 |
副本replia | 每一个分片可以有零个或一个以上的副本,每一个副本是分片的完整复制。 副本作用: 1.备用,当主分片失效后,副本将升级为主分片,提高可用性。 2.当大量并发请求时,提高es负载能力,实现高吞吐量(可以理解为多一套可以查询到库) |
接下来的基本概念通过与传统数据进行比对,大家应该就比较好理解了。
ES | 传统数据库 |
Index 索引 | database 数据库 |
type 类型 | table 表 |
document 文档 | row 行 |
field 字段 | column 列名 |
mapping 映射 | schema 表结构 |
es会对每个词建索引 | index 索引 |
3.倒排索引
说到ES,我们不得不提到倒排索引了,这个应该是es为什么在全文检索中这么强大的重要因素了。
假设有一篇文档,文档为id,文档中有很多单词。传统数据库,先找到该文档,然后在该文档中查看有哪些单词。倒排索引正好相反,采用的是,单词->文档 的模式,文档中的所有单词为索引,通过单词直接定位到文档。
例如:
1.我们都是好孩子。
2.今天天气特别好。
3.我们好孩子比他们孩子多。
转化成倒排索引就是
我们 1,3 都是 1 好 1,2,3 今天 2 天气 2 特别 2 孩子 1,3 他们 3 多 3
事实上,倒排索引还记录了,单词在文档中出现的位置以及词频。
我们 (1,1,1),(3,1,1) 都是 (1,1,2) 好 (1,1,3),(2,1,4),(3,1,2) 今天 (2,1,1) 天气 (2,1,2) 特别 (2,1,3) 孩子 (1,1,4),(3,2,<3,6>) 他们 (3,1,5) 多 (3,1,7)比如孩子的
(3,2,<3,6>)
表示出现在文档3,2次,同时第3个和第6个位置出现(这里的位置指的是词的位置)
当我们将数据导入到es,es便开始按照倒排索引建立索引。
4.工作使用到的知识点
前面我们主要介绍了es的主要概念,这一节就比较零碎了,主要记录工作中使用到的es的相关知识点。
4.1.string字段的index属性
工作中的,我的字段类型都是string,但是根据具体业务逻辑,有些需要分词有些不需要分词。
"Std_Typ": {
"index": "not_analyzed",
"type": "string"
},
"Seg_Nm": {
"analyzer": "ik_max_word",
"type": "string"
}
比如上面的两个字段,其中第一个表示不分词,第二个表示分词,同时按照ik分词(es默认的string类型都是会分词的,但是中文的分词是每个字一个,不适合)
这里我们就重点提一下,字段的string的index属性设置
(1)analyzed
先分词,再索引
(2)not_analyzed
不分词,直接索引
(3)no
不索引该字段,也不会被搜出来
4.2.ES存储方式
上面提到了string类型,这里就讲讲es的存储方式。
当我们通过此条对es进行文档搜索对时候,利用的是es的倒排索引,但是当我们进行排序或者聚合的时候,正好相反,是先找到文档,查找文档中的词条,es采用的列式存储来进行排序和聚合的。
文档值(doc_value)数据结构是列式存储风格的结构。默认情况下,es中除了字符类型的分析字段外,其他字段都会启用文档值,存储在硬盘中。该默认值可以改变,节省硬盘空间。
如果字符类型分析字段想要进行排序或者聚合怎么办呢,还有一个顺排索引(fielddata),这个是将词条-文档 转化成 文档-词条,存在在jvm的堆内存中。
所有字段 | 倒排序索引(硬盘) |
除了字符类型分析字段外的所有字段(默认) | 文档值(doc_value)(硬盘) |
字符类型分析字段(排序或者聚合)(默认) | 顺序索引(fielddata)(内存) |
具体的说明大家可以参考这篇博客,写的很详细 https://www.cnblogs.com/ljhdo/p/5016852.html
4.3.ES评分机制
我在该项目中一开始想到的是用es的评分机制,虽然最后放弃了,因为打分后,还要取前3000条数据,涉及到了es的评分以及排序,都是比较耗时的操作,但是这里还是简单记录了一下es的评分机制。
这里评分核心主要采用的是 TF/IDF
(1)TF
词频,词在文档中出现的次数越多,那么分数越高
(2)IDF
逆向文档频率,词在所有文档出现的频率,频率越高,说明该词越不重要,比如and,or等,分数越低,反之越高。
(3)文档长度归一长度
当词所在的文档越短,分数越高,反之越低。比如“开心”这个词在A今天我很开心B今天我非常的开心呢因为我带着我的小女友去迪斯尼溜达了,很明显A的分数高于B
具体的可以参考该博客 https://blog.csdn.net/paditang/article/details/79098830
这些都是大神呀。
4.4.ES缓存机制
上一节说到,评分这个方案放弃后,我最后采用的是filter的方式,利用了ES的缓存机制,从原来每条查询时间1~2s,直接降到了几十毫秒的速度。这里简单说明一下es的缓存机制中的filer。
Filter Cache,它的作用就是对一个查询中包含的过滤器执行结果进行缓存。比如我们常用的term,terms,range过滤器都会在满足某种条件后被缓存,bool过滤器包含的子query clause会被缓存。
GET _search { "query": { "bool": { "must": [ { "match": { "title": "Search" }}, { "match": { "content": "Elasticsearch" }} ], "filter": [ { "term": { "status": "published" }}, { "range": { "publish_date": { "gte": "2015-01-01" }}} ] } } }
上面是别人的例子,在我自己项目中,第一次查询可能呀几百毫秒,但是到了后面,如果有相同的filter,就只要几十毫秒甚至几秒(在5000W+的数据中搜索),是越来越快的感觉。
过滤器缓存是可以设置, indices.cache.filter.size ,该属性的默认值 20%。
4.5.match,match_phrase,term的区别
这三者也是工作中用到的。
match和match_phrase都会查询的字段进行分词,然后根据分词的结果去匹配,只不过后者需要匹配所有的词,而前者至少一个。比如“我喜欢她”,match只要“我”,“喜欢”,“她”,至少满足一个就好,但是match_phrase 需要三者同时满足。
term则不对字段进行分词,也就是“我喜欢她”必须整句话满足才行。
具体可以看 https://www.cnblogs.com/yjf512/p/4897294.html
4.6.MySQL 和 ElasticSearch(ES)比较
(1)模糊查询性能比较
这里Mysql中共有300W+数据,ES共500W+数据
mysql:select * from creditdefault where seg_nm like "%宝丽金%",耗时17s
es: {"query": {"match_phrase": {"seg_nm": "宝丽金"}}} ,耗时12ms
(2)统计性能对比
mysql:select count(distinct tel_nbr) from credit.white;耗时23.06s
es:727ms
{ "size": 0, "aggs": { "distinct_colors": { "cardinality": { "field": "Tel_Nbr" } } } }
待续