ElasticSearch基础知识及应用实例
一、简介
ES是基于Lucene构建的开源、分布式、RESTful接口全文搜索引擎。同时它还是一个分布式文档数据库,其中每个字段均是被索引的数据且可被搜索,便于扩展,能在短时间内搜索和分析大量数据。
Lucene是一个Java全文搜索引擎;仅是一个框架,提供代码库和API,并不是完整的应用程序。咱们之前讲的处理分词,构建倒排索引,等等,都是这个叫lucene的做的。那么能不能说这个lucene就是搜索引擎呢?
还不能。lucene只是一个提供全文搜索功能类库的核心工具包,而真正使用它还需要一个完善的服务框架搭建起来的应用。好比lucene是类似于jdk,而搜索引擎软件就是tomcat 的。
使用的存储数据的结构是:倒排索引;
常用匹配使用的工具:kibana 可以在客户端操作es结构、参数、数据等等;
二、应用场景
搜索,根据用户输入的关键词进行匹配,从已有的数据库中摘录出相关的记录反馈给用户。
常见的全网搜索引擎,像百度、谷歌这样的。但是除此以外,搜索技术在垂直领域也有广泛的使用,比如淘宝、京东搜索商品,万芳、知网搜索期刊,csdn中搜索问题贴。也都是基于海量数据的搜索。
日志存储,用于K8S嵌入ES做日志存储,用于页面日志内容检索;ELK方案等;
大数据分析存储、应用系统性能分析、安全指标监控等;
三、ES的基本概念
1. elasticsearch的基本概念
cluster |
整个elasticsearch 默认就是集群状态,整个集群是一份完整、互备的数据。 |
node |
集群中的一个节点,一般只一个进程就是一个node |
shard |
分片,即使是一个节点中的数据也会通过hash算法,分成多个片存放,默认是5片。 |
index |
相当于rdbms的database, 对于用户来说是一个逻辑数据库,虽然物理上会被分多个shard存放,也可能存放在多个node中。 |
type |
类似于rdbms的table,但是与其说像table,其实更像面向对象中的class , 同一Json的格式的数据集合。当前使用的ES中建议一个index中只设一个type. |
document |
类似于rdbms的 row、面向对象里的object |
field |
相当于字段、属性 |
2. elasticsearch restful api (DSL)使用kibana进行操作
数据在Java中的格式是一个一个的对象,但是存储到es之中后,能将其之前的一个对象看成是一个JSON结构的数据,或者是一个嵌套的JSON结构。
例如:
#在Java中的结构,存储到es后的结构是JSON //电影实例 public class Movie { String id; String name; Double dazhongScore; List<Actor> actorList; } //演员实例 public class Actor{ String id; String name; } #存储到ES后的结构如下: { "id":"1", "name":"red sea", "dazhongScore":"8.5", "actorList":[ {"id":"1","name":"zhangyi"}, {"id":"2","name":"haiqing"}, {"id":"2","name":"zhanghanyu"} ] }
2.1 对数据的操作,基本的命令(执行命令的窗口在kibana的页面中,的Dev Tools下的Console中运行)
1) 查看es中有哪些索引 GET /_cat/indices?v #注意 es中会默认存在一个名为.kibana的索引 2)删除一个索引 DELETE /index名称 例如:DELETE /movie_index 3)新增document数据,新增一条数据,命令格式:PUT /index/type/document_id 换行 {数据的JSON结构} 例如: PUT /movie_index/movie/1 { "id":1, "name":"red sea", "dazhongScore":8.5, "actorList":[ {"id":1,"name":"zhang yi"}, {"id":2,"name":"hai qing"}, {"id":3,"name":"zhang han yu"} ] } 及时执行上面这个新建的命令之前没有创建index和type,也能创建数据成功,因为es会根据插入数据的内容 自动创建对应index和type的mapping文件格式,自动解析数据中的字段内容;所以,在日常的使用中如果数据的 字段数量很多,可以只指定几个特别用来检索字段的mapping类型和分词方式等,其他的字段可以不指定类型,让 es自行去创建对应的完整的mapping格式。 4)直接通过es存储数据的id进行查找。格式:GET /index_name/type_name/document_id 例如:GET/movie_index/movie/1 5)修改—整体替换,和新增没有区别,都是指定id进行的操作,如下: PUT /movie_index/movie/1 { "id":1, "name":"red sea ddd", "dazhongScore":9.5, "actorList":[ {"id":1,"name":"zhang yi 1"}, {"id":2,"name":"hai qing 1"}, {"id":3,"name":"zhang han yu 1"} ] } 6) 修改—某个字段, 格式:POST movie_index/movie/1/_update 换行 {json数据} POST movie_index/movie/1/_update { "doc": { "dazhongScore":"7.0" } } 7)删除一个document数据, 格式: DELETE index_name/type_name/document_id 例如:DELETE movie_index/movie/3 8) 搜索type全部数据和结果讲解, 格式:GET index_name/type_name/_search 当前es在 没有指定查询多少条数据的时候,那es默认在kibana中展示10条数据。如果需要指定查询数量需要设置参数。 from和size两个参数。 例如:GET movie_index/movie/_search 结果字段含义: { "took": 2, //耗费时间 毫秒 "timed_out": false, //是否超时 "_shards": { "total": 5, //发送给全部5个分片 "successful": 5, "skipped": 0, "failed": 0 }, "hits": { "total": 3, //命中3条数据 "max_score": 1, //最大评分 "hits": [ // 结果 { "_index": "movie_index", "_type": "movie", "_id": 2, "_score": 1, "_source": { "id": "2", "name": "operation meigong river", "doubanScore": 8.0, "actorList": [ { "id": "1", "name": "zhang han yu" } ] } } 9)按条件查询全部: GET movie_index/movie/_search { "query":{ "match_all": {} } } 10)按分词查询,分词匹配: GET movie_index/movie/_search { "query":{ "match": {"name":"red"} } } 11)按分词子属性查询 GET movie_index/movie/_search { "query":{ "match": {"actorList.name":"zhang"} } } 12)短语匹配 match phrase,按短语查询,不再利用分词技术,直接用短语在原始数据中匹配 GET movie_index/movie/_search { "query":{ "match_phrase": {"name":"red sea"} } } 13) fuzzy查询(模糊查询), 校正匹配分词,当一个单词都无法准确匹配,es通过一种算法对非常接近的单词也给与一定的评分, 能够查询出来,但是消耗更多的性能。 GET movie_index/movie/_search { "query":{ "fuzzy": {"name":"rad"} } } 14)过滤--查询前过滤 GET movie_index/movie/_search { "query":{ "bool":{ "filter":[ {"term": { "actorList.id": "1" }}, {"term": { "actorList.id": "3" }} ], "must":{"match":{"name":"red"}} } } } 15) 过滤--按范围过滤 GET movie_index/movie/_search { "query": { "bool": { "filter": { "range": { "dazhongScore": {"gte": 8} } } } } } 关于范围操作符: gt 大于;lt 小于;gte 大于等于;lte 小于等于 16) 排序 GET movie_index/movie/_search { "query":{ "match": {"name":"red sea"} } , "sort": [ { "dazhongScore": { "order": "desc" } } ] } 17) 分页查询, es使用默认分页查询的时候,默认一次获取数据10000条,如果超过10000那就会在代码中报错, 需要设置对应的查询数量,设置参数max_result_window, 执行命令: GET /movie_index/_settings ——查看参数 PUT /movie_index/_settings ——设置参数 {"max_result_window":1000000} GET movie_index/movie/_search { "query": { "match_all": {} }, "from": 1, "size": 1 } 18)指定查询的字段,查询展示字段限制,只展示自己想要的字段 GET movie_index/movie/_search { "query": { "match_all": {} }, "_source": ["name", "dazhongScore"] } 19) 高亮显示 GET movie_index/movie/_search { "query":{ "match": {"name":"red sea"} }, "highlight": { "fields": {"name":{} } } } 20) 聚合查询 GET movie_index/movie/_search { "aggs": { "groupby_actor": { "terms": { "field": "actorList.name.keyword" } } } } 其中的aggs代表的统计,terms表示精确匹配多个字段;groupby_actor表示按actor进行分组; 21)每个演员参演电影的平均分是多少,并按评分排序 GET movie_index/movie/_search { "aggs": { "groupby_actor_id": { "terms": { "field": "actorList.name.keyword" , "order": { "avg_score": "desc" } }, "aggs": { "avg_score":{ "avg": { "field": "dazhongScore" } } } } } } 22)关于mapping文件的信息 之前说type可以理解为table,那每个字段的数据类型是如何定义的呢,查看看mapping命令: GET index_name/type_name/_mapping 实际上每个type中的字段是什么数据类型,由mapping定义。 但是如果没有设定mapping系统会自动,根据一条数据的格式来推断出应该的数据格式。 true/false → boolean 1020 → long 20.1 → double “2018-02-01” → date “hello world” → text +keyword 默认只有text会进行分词,keyword是不会分词的字符串。 mapping除了自动定义,还可以手动定义,但是只能对新加的、没有数据的字段进行定义。一旦有了数据就无法再做修改了。 注意:虽然每个Field的数据放在不同的type下,但是同一个名字的Field在一个index下只能有一种mapping定义。
2.2 使用Java语言进行dsl组装
#引入的jar包依赖 <dependency> <groupId>org.elasticsearch</groupId> <artifactId>elasticsearch</artifactId> <version>6.3.2</version> </dependency> <dependency> <groupId>org.elasticsearch.client</groupId> <artifactId>elasticsearch-rest-high-level-client</artifactId> <version>6.3.2</version> </dependency>
使用的是QueryBuilders类进行操作,这个是基础的builder。日常的开发使用中有:
bool语句使用:BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
must语句使用:List<QueryBuilder> mustBuilder = boolQueryBuilder.must();
must_not语句使用:List<QueryBuilder> mustNotBuilder = boolQueryBuilder.mustNot();
should语句使用:List<QueryBuilder> should = boolQueryBuilderName.should();
filter语句使用: List<QueryBuilder> filter = boolQueryBuilder.filter();
funcation语句使用:FunctionScoreQueryBuilder;
排序sort语句使用:SearchSourceBuilder;
聚合函数的调用使用:SearchSourceBuilder的aggregation()方法进行组装等等;
2.3 es的倒排索引
es索引底层存储的数据结构是倒排索引的方式进行索引存储的,是Lucene提供的技术实现。
存储数据的结构:
倒排表以字或词为关键字进行索引,表中关键字所对应的记录表项记录了出现这个字或词的所有文档,一个表项就是一个字表段,它记录该文档的ID和字符在该文档中出现的位置情况。
由于每个字或词对应的文档数量在动态变化,所以倒排表的建立和维护都较为复杂,但是在查询的时候由于可以一次得到查询关键字所对应的所有文档,所以效率高于正排表。在全文检索中,检索的快速响应是一个最为关键的性能,而索引建立由于在后台进行,尽管效率相对低一些,但不会影响整个搜索引擎的效率。