Elasticsearch 简介和实战
最近项目要用到全文搜索,需要有中文、拼音检索以及混合搜索以及数据聚合功能,使用到了 Elsaticsearch
关于 Elasticsearch
Elasticsearch 是一个基于 Lucene 的搜索服务器。它提供了一个分布式多用户能力的全文搜索引擎,Elasticsearch 是用 Java 语言开发的,基于 RESTful web 接口,其客户端在其他语言中都是可用的
Lucene 被认为是目前最先进,性能最好、功能最全的搜索引擎库,不过,Lucene 只是一个库,需要使用 java 集成到应用中,而且 Lucene 是非常复杂的,需要深入了解检索的相关知识来理解它的工作原理。一般我们会选择 Solr 和 Elasticsearch 来做搜索服务,两者的对比可以网上自行了解
Elasticsearch 基于 Lucene,提供一套简单一致的 RESTful API,隐藏 Lucene 的复杂性,使得全文检索变得更简单,不过 Elasticsearch 也不仅仅只是全文搜索引擎
- 一个分布式的实时文档存储,每个字段 可以被索引与搜索
- 一个分布式实时分析搜索引擎
- 能胜任上百个服务节点的扩展,并支持 PB 级别的结构化或者非结构化数据
回忆时光
许多年前,一个刚结婚的名叫 Shay Banon 的失业开发者,跟着他的妻子去了伦敦,他的妻子在那里学习厨师。 在寻找一个赚钱的工作的时候,为了给他的妻子做一个食谱搜索引擎,他开始使用 Lucene 的一个早期版本。
直接使用 Lucene 是很难的,因此 Shay 开始做一个抽象层,Java 开发者使用它可以很简单的给他们的程序添加搜索功能。 他发布了他的第一个开源项目 Compass。
后来 Shay 获得了一份工作,主要是高性能,分布式环境下的内存数据网格。这个对于高性能,实时,分布式搜索引擎的需求尤为突出, 他决定重写 Compass,把它变为一个独立的服务并取名 Elasticsearch。
第一个公开版本在2010年2月发布,从此以后,Elasticsearch 已经成为了 Github 上最活跃的项目之一,他拥有超过300名 contributors(目前736名 contributors )。 一家公司已经开始围绕 Elasticsearch 提供商业服务,并开发新的特性,但是,Elasticsearch 将永远开源并对所有人可用。
据说,Shay 的妻子还在等着她的食谱搜索引擎…
Elasticsearch 安装
从官网 elastic.co下载最新或合适版本的 Elasticsearch(这里是7.6.2)
解压之后进入目录下
修改配置文件config/elasticsearch.yml
vim config/elasticsearch.yml
# 集群名称,建议修改,防止误用默认集群
cluster.name: es-zou
# 配置绑定地址,配置外网可以访问(或本地地址)
network.host: 0.0.0.0
# 配置访问端口
http.port: 9200
运行命令
bin/elasticsearch
# 后台启动
bin/elasticsearch -d
运行 curl http://localhost:9200/
可以看到以下信息,启动成功
{
"name" : "DESKTOP-G11TC44",
"cluster_name" : "es-zou",
"cluster_uuid" : "yoq-PT-sTVml0H0Vrna6xQ",
"version" : {
"number" : "7.6.2",
"build_flavor" : "default",
"build_type" : "zip",
"build_hash" : "ef48eb35cf30adf4db14086e8aabd07ef6fb113f",
"build_date" : "2020-03-26T06:34:37.794943Z",
"build_snapshot" : false,
"lucene_version" : "8.4.0",
"minimum_wire_compatibility_version" : "6.8.0",
"minimum_index_compatibility_version" : "6.0.0-beta1"
},
"tagline" : "You Know, for Search"
}
在 Linux 下,network.host 设置为 0.0.0.0 启动可能遇见的问题
[1]: max file descriptors [4096] for elasticsearch process is too low, increase to at least [65536]
切换到root用户,修改配置文件 /etc/security/limits.conf
,增加配置
vi /etc/security/limits.conf
* soft nofile 65536
* hard nofile 65536
[2]: max number of threads [3818] for user [es] is too low, increase to at least [4096]
最大线程个数太低。修改配置文件 etc/security/limits.conf
,增加配置
vi /etc/security/limits.conf
* soft nproc 4096
* hard nproc 4096
[3]: max virtual memory areas vm.max_map_count [65530] is too low, increase to at least [262144]
修改 /etc/sysctl.conf
,增加配置
vi /etc/sysctl.conf
vm.max_map_count=262144
执行命令 sysctl -p
生效
[4]: system call filters failed to install; check the logs and fix your configuration or disable system call filters at your own risk
问题原因:因为 Centos6 不支持 SecComp,而ES5.2.1默认 bootstrap.system_call_filter 为 true 进行检测,所以导致检测失败,失败后直接导致ES不能启动
解决方法:在 elasticsearch.yml 中配置 bootstrap.system_call_filter 为 false,注意要在 Memory下面:
bootstrap.memory_lock: false
bootstrap.system_call_filter: false
Kibana 安装
Kibana 是官方的数据可视化工具,不过我们暂时用不到那些数据分析的功能,其中有个 Dev Tools 开发工具可以比较方便的与 Elasticsearch 服务进行交互
下载对应 Elasticsearch 版本的 Kibana 并解压(与 Elasticsearch 保持一致,这里都是 7.6.2)
编辑配置文件 config/kibana.yml
vim config/kibana.yml
# 服务地址,对外暴露地址,(服务器地址)
#server.host: "localhost"
server.host: "127.0.0.1"
# 配置 elasticsearch 地址
elasticsearch.hosts: ["http://localhost:9200"]
# 中文支持
i18n.locale: "zh-CN"
启动命令
bin/kibana
# 使用nohup命令 后台启动
nohup bin/kibana >/dev/null &
启动后,浏览器输入地址 http://localhost:5601
可以使用开发工具 Dev Tools 与 Elasticsearch 服务进行交互
基本概念
操作之前,先简单了解一波 Elasticsearch 中的数据模型和一些基本概念
全文搜索(full-text search)
全文检索是指计算机索引程序通过扫描文章中的每一个词,对每一个词建立一个索引,指明该词在文章中出现的次数和位置,当用户查询时,检索程序就根据事先建立的索引进行查找,并将查找的结果反馈给用户的检索方式。这个过程类似于通过字典中的检索字表查字的过程。全文搜索搜索引擎数据库中的数据
集群、节点(cluster & node)
一个节点(node)就是一个 Elasticsearch 实例,而一个集群(cluster)由一个或多个节点组成,它们具有相同的 cluster.name
(默认为 “elasticsearch”),节点可以通过这个集群名加入群集,可以在 elasticsearch.yml
自定义集群名称和节点名称
# Use a descriptive name for your cluster:
cluster.name: my-application
# Use a descriptive name for the node:
node.name: node-1
索引(index)
index 包含两层意思,一是名词索引,类似于关系型数据库中的一个数据库,是一个存储关系型文档的地方;二是动词,表示建立索引,存储一个文档到索引中以被检索查询
一个 Elasticsearch 集群可以包含多个 索引 ,相应的每个索引可以包含多个 类型 ,这些不同的类型存储着多个 文档 ,每个文档又有多个 属性 。我们可以与关系型数据库做个简单对比
Relational DB -> Databases -> Tables -> Rows -> Columns
Elasticsearch -> Indices -> Types -> Documents -> Fields
类型(mapping type)
之前的版本中,每个索引下可以建立多个类型,存储文档的时候需要指定 index 和 type,但是从 6.0 开始每个索引只能有一个类型,7.0 以后不建议使用,8.0 以后将完全不支持
为什么要移除映射类型
开始的时候,我们把索引(index)和类型(type)类比于SQL数据库中的 database 和 table,但是这样类比是不合适的。在SQL数据库中,表之间是相互独立的。一个表中的各列并不会影响到其它表中的同名的列。而在映射类型(mapping type)中却不是这样的。
在同一个 Elasticsearch 索引中,其中不同映射类型中的同名字段在内部是由同一个 Lucene 字段来支持的。换句话说,使用上面的例子,user 类型中的 user_name 字段与 tweet 类型中的 user_name 字段是完全一样的,并且两个 user_name 字段在两个类型中必须具有相同的映射(定义)。
这会在某些情况下导致一些混乱,比如,在同一个索引中,当你想在其中的一个类型中将 deleted 字段作为 date 类型,而在另一个类型中将其作为 boolean 字段。
在此之上需要考虑一点,如果同一个索引中存储的各个实体如果只有很少或者根本没有同样的字段,这种情况会导致稀疏数据,并且会影响到Lucene的高效压缩数据的能力
文档(document)
索引里存储的单条数据,被索引信息的基本单位
Elasticsearch 是面向文档(document oriented)的,这意味着它可以存储整个对象或文档(document),还会索引(index)每个文档的内容使之可以被搜索。在 Elasticsearch 中,你可以对文档(而非成行成列的数据)进行索引、搜索、排序、过滤。这种理解数据的方式与以往完全不同,这也是Elasticsearch能够执行复杂的全文搜索的原因之一
映射、字段(mapping & field)
Elasticsearch 是使用 JSON 作为文档序列化格式,映射就定义了文档的结构,会包含许多字段。一个映射定义了字段类型,每个字段的数据类型,以及字段被 Elasticsearch 处理的方式。映射还用于设置关联到类型上的元数据
快速入门
使用 RESTful API 通过端口 9200 可以和 Elasticsearch 进行交互,可以使用 curl
命令,也可以用 Kibana 的 Dev Tools 更加方便
这里简单创建一个索引,并添加几条数据
创建索引
用 curl 命令创建了一个 commodity 商品索引
curl -X PUT "localhost:9200/commodity?pretty"
也使用 Kibana,并配置参数和映射
PUT /commodity
{
"settings": {
"number_of_shards": 1,
"number_of_replicas": 1
},
"mappings": {
"properties": {
"commodity_id": {
"type": "long"
},
"commodity_name": {
"type": "text"
},
"picture_url": {
"type": "keyword"
},
"price": {
"type": "double"
}
}
}
}
创建了一个分片数为1,副本数为1的索引,没有指定类型,默认会创建一个 _doc
类型,有四个不同类型的字段,创建成功返回如下结果
{
"acknowledged" : true,
"shards_acknowledged" : true,
"index" : "commodity"
}
添加文档数据
PUT /commodity/_doc/1
{
"commodity_id": 1,
"commodity_name": "全面屏手机",
"picture_url": "/commodity/1",
"price": 1999
}
这里是在 commodity 索引的 _doc 类型下创建一个 id 为 1 的文档数据,创建成功
{
"_index" : "commodity",
"_type" : "_doc",
"_id" : "1",
"_version" : 2,
"result" : "updated",
"_shards" : {
"total" : 2,
"successful" : 1,
"failed" : 0
},
"_seq_no" : 1,
"_primary_term" : 1
}
查询
查询所有
POST /commodity/_search
查询名称
POST /commodity/_search
{
"query": {
"match": {
"commodity_name": "全面"
}
}
}
{
"took" : 0,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 1,
"relation" : "eq"
},
"max_score" : 0.9646722,
"hits" : [
{
"_index" : "commodity",
"_type" : "_doc",
"_id" : "1",
"_score" : 0.9646722,
"_source" : {
"commodity_id" : 1,
"commodity_name" : "全面屏手机",
"picture_url" : "/commodity/1",
"price" : 1999
}
}
]
}
}
后面再去总结 API 的详细用法