ElasticSearch基础入门
一:什么是ElasticSearch
1:ELK Stack
The Elastic Stack, 包括 Elasticsearch、Kibana、Beats 和 Logstash(也称为 ELK Stack)。
能够安全可靠地获取任何来源、任何格式的数据,然后实时地对数据进行搜索、分析和可视化。
Elaticsearch,简称为 ES,ES 是一个开源的高扩展的分布式全文搜索引擎,是整个 Elastic Stack 技术栈的核心。
它可以近乎实时的存储、检索数据;本身扩展性很好,可以扩展到上百台服务器,处理 PB 级别的数据。
ES可以进行如下的工作:
- 分布式实时文件存储,并将每一个字段都编入索引,使其可以被搜索。
- 实时分析的分布式搜索引擎。
- 可以扩展到上百台服务器,处理PB级别的结构化或非结构化数据。
Elasticsearch是面向文档型数据库,一条数据在这里就是一个文档,用JSON作为文档序列化的格式
{
"name" : "John",
"sex" : "Male",
"age" : 25,
"birthDate": "1990/05/01",
"about" : "I love to go rock climbing",
"interests": [ "sports", "music" ]
}
ES和传统的关系型数据库相关术语对比如下:(重点)
关系型数据库 | ES |
---|---|
数据库(Database) | 索引(index) |
表(table) | 类型(Type)[es6.0.0废弃] |
行(row) | 文档(document) |
列(column) | 字段(field) |
表结构(schema) | 映射(mapping) |
索引 | 反向索引 |
SQL | 查询DSL |
Select * from table | Get http://… |
update table set… | Put http://… |
delete | Delete http://… |
🎉 往Elasticsearch里插入一条记录,其实就是直接PUT一个json的对象,这个对象有多个fields
// PUT /megacorp/employee/1 // put + url
// 这一个json构成一个文档(document)
{
"name" : "John", // field
"sex" : "Male", // field
"age" : 25, // field
"about" : "I love to go rock climbing", // field
"interests": [ "sports", "music" ] // field
} // 插入数据的内容
2:全文搜索引擎
Google,百度类的网站搜索,它们都是根据网页中的关键字生成索引,我们在搜索的时候输入关键字,它们会将该关键字即索引匹配到的所有网页返回;还有常见的项目中应用日志的搜索等等。
对于这些非结构化的数据文本,关系型数据库搜索不是能很好的支持。
一般传统数据库,全文检索都实现的很鸡肋,因为一般也没人用数据库存文本字段。进行全文检索需要扫描整个表,如果数据量大的话即使对 SQL 的语法优化,也收效甚微。建立了索引,但是维护起来也很麻烦,对于 insert 和 update 操作都会重新构建索引。
基于以上原因可以分析得出,在一些生产环境中,使用常规的搜索方式,性能是非常差的:
- 搜索的数据对象是大量的非结构化的文本数据。
- 文件记录量达到数十万或数百万个甚至更多。
- 支持大量基于交互式文本的查询。
- 需求非常灵活的全文搜索查询。
- 对高度相关的搜索结果的有特殊需求,但是没有可用的关系数据库可以满足。
- 对不同记录类型、非文本数据操作或安全事务处理的需求相对较少的情况。为了解决结构化数据搜索和非结构化数据搜索性能问题,我们就需要专业,健壮,强大的全文搜索引擎。
这里说到的全文搜索引擎指的是目前广泛应用的主流搜索引擎。它的工作原理是计算机索引程序通过扫描文章中的每一个词,对每一个词建立一个索引,指明该词在文章中出现的次数和位置,当用户查询时,检索程序就根据事先建立的索引进行查找,并将查找的结果反馈给用户的检索方式。这个过程类似于通过字典中的检索字表查字的过程。
3:ES or Sola
Elasticsearch 和 Solr 都是开源搜索引擎,那么我们在使用时该如何选择呢?
- Google 搜索趋势结果表明,与 Solr 相比,Elasticsearch 具有很大的吸引力,但这并不意味着 Apache Solr 已经死亡。
- 虽然有些人可能不这么认为,但 Solr 仍然是最受欢迎的搜索引擎之一,拥有强大的社区和开源支持。
- 与 Solr 相比,Elasticsearch 易于安装且非常轻巧。此外,你可以在几分钟内安装并运行Elasticsearch。
- 但是,如果 Elasticsearch 管理不当,这种易于部署和使用可能会成为一个问题。
- 基于 JSON 的配置很简单,但如果要为文件中的每个配置指定注释,那么它不适合您。
- 总的来说,如果你的应用使用的是 JSON,那么 Elasticsearch 是一个更好的选择。
- 否则,请使用 Solr,因为它的 schema.xml 和 solrconfig.xml 都有很好的文档记录。
- Solr 拥有更大,更成熟的用户,开发者和贡献者社区。
- ES 虽拥有的规模较小但活跃的 用户社区以及不断增长的贡献者社区。
- Solr 贡献者和提交者来自许多不同的组织,而 Elasticsearch 提交者来自单个公司。
- Solr 更成熟,但 ES 增长迅速,更稳定。
- Solr 是一个非常有据可查的产品,具有清晰的示例和 API 用例场景。
- Elasticsearch 的文档组织良好,但它缺乏好的示例和清晰的配置说明。
那么,到底是 Solr 还是 Elasticsearch?
有时很难找到明确的答案。无论您选择 Solr 还是 Elasticsearch,首先需要了解正确的用例和未来需求。总结他们的每个属性。
- 由于易于使用,Elasticsearch 在新开发者中更受欢迎。一个下载和一个命令就可以启动一切。
- 如果除了搜索文本之外还需要它来处理分析查询,Elasticsearch 是更好的选择
- 如果需要分布式索引,则需要选择 Elasticsearch。对于需要良好可伸缩性和以及性能分布式环境,Elasticsearch 是更好的选择。
- Elasticsearch 在开源日志管理用例中占据主导地位,许多组织在 Elasticsearch 中索引它们的日志以使其可搜索。
- 如果你喜欢监控和指标,那么请使用 Elasticsearch,因为相对于 Solr,Elasticsearch 暴露了更多的关键指标
二:ES下载安装
1:windows安装操作
1.1:下载对应的压缩包
1.2:全部解压
- elasticsearch-7.6.1:es运行环境
- ik:IK分词器,一会要装到es运行环境的运行插件中,为的是更好的支持中文
- head-master:可视化界面【会被kibana】代替
- kibana:ELK的K,可视化ES,并能进行其他配置工作
🎉 es目录结构如下:
修改elasticsearch配置文件:config/elasticsearch.yml
,增加以下两句命令:
# 允许跨域访问设置
http.cors.enabled: true
http.cors.allow-origin: "*"
1.3:IK分词器
将IK分词器中的解压压缩包,重命名为ik,并放入到es运行环境的plugins下
1.4:运行es
进入bin目录下,启动elasticsearch.bat
启动要求是有java运行环境,并且观察日志,可以看到IK分词器的插件被加载了
9300 端口为 Elasticsearch 集群间组件的通信端口,9200 端口为浏览器访问的 http协议 RESTful 端口
访问localhost:9200,如果输入下面的json,说明启动成功
1.5:可视化 - head-master【可选】
使用head-master前提是有node的运行环境,如果使用要现有nodejs
双击nodejs安装程序,一路next即可,可以换安装路径
将grunt安装为全局命令 ,Grunt是基于Node.js的项目构建工具,在cmd控制台使用管理员权限运行,执行如下命令
npm install -g grunt -cli
修改 elasticsearch-head-master\Gruntfile.js
文件,在97行处添加如下内容
进入elasticsearch-head-master 根目录启动cmd,在命令提示符下输入命令
npm install
grunt server
访问localhost:9100,如果看到下面的界面,说明成功
1.6:可视化 - 使用Kibana
先进入 config/kibana.yml
中将界面变成中文zh-CN
直接在bin中启动kibana的bat批处理脚本即可
在这里会卡一下,不要急
输入 localhost:5601
,看到下面的界面,说明启动成功
1.7:windows es集群搭建
es自成集群,能够自己在内部进行集群的容错,主从节点替换等工作,这里以三个es为例,演示es集群
复制多分es包作为节点,核心工作就是修改各个集群节点的elasticsearch.yml
配置文件
各个节点的配置信息如下:
#节点1的配置信息:集群名称,节点之间要保持一致, 保证唯一
cluster.name: my-elasticsearch
#节点名称,必须不一样
node.name: node-01
node.master: true
node.data: true
#必须为本机的ip地址
network.host: 127.0.0.1
#服务端口号,在同一机器下必须不一样,http 端口
http.port: 9200
#tcp 监听端口,集群间通信端口号,在同一机器下必须不一样
transport.tcp.port: 9300
#集群内的可以被选为主节点的节点列表
cluster.initial_master_nodes: ["node-1001", "node-1002","node-1003"]
#跨域配置
#action.destructive_requires_name: true
http.cors.enabled: true
http.cors.allow-origin: "*"
#节点2的配置信息:
#集群名称,保证唯一
cluster.name: my-elasticsearch
#节点名称,必须不一样
node.name: node-02
node.master: true
node.data: true
#必须为本机的ip地址
network.host: 127.0.0.1
#服务端口号,在同一机器下必须不一样
http.port: 9201
#集群间通信端口号,在同一机器下必须不一样
transport.tcp.port: 9301
#集群内的可以被选为主节点的节点列表
cluster.initial_master_nodes: ["node-1001", "node-1002","node-1003"]
#跨域配置
#action.destructive_requires_name: true
http.cors.enabled: true
http.cors.allow-origin: "*"
#节点3的配置信息:
#集群名称,保证唯一
cluster.name: my-elasticsearch
#节点名称,必须不一样
node.name: node-03
node.master: true
node.data: true
#必须为本机的ip地址
network.host: 127.0.0.1
#服务端口号,在同一机器下必须不一样
http.port: 9202
#集群间通信端口号,在同一机器下必须不一样
transport.tcp.port: 9302
#集群内的可以被选为主节点的节点列表
cluster.initial_master_nodes: ["node-1001", "node-1002","node-1003"]
#跨域配置
#action.destructive_requires_name: true
http.cors.enabled: true
http.cors.allow-origin: "*"
启动前先删除每个节点中的 data 目录中所有内容(如果存在)
进入 bin 目录,分别双击执行 bin/elasticsearch.bat
,启动节点服务器,启动后,会自动加入指定名称的集群
谁先启动就会成为 master
2:Linux下安装操作
2.1:下载并解压
下载linux对应的安装包
解压当前的文件夹
tar -zxvf elasticsearch-7.6.1-linux-x86_64.tar.gz
因为elasticsearch 不能使用root用户启动,需要用普通用户启动,这里创建一个普通用户
adduser aitpm # 创建一个普通用户
passwd aitpm # 指定密码
为这个用户赋予es这个目录的权限
chown -R aitpm:aitpm elasticsearch-7.9.3
2.2:配置修改
修改elasticsearch.yml
文件,修改一些核心配置
2.3:es和jdk强依赖问题
如果启动es,会报错,因为ES文件夹里自己携带了 JDK ,但是如果我们的 Linux 下安装了 JDK ,ES 就不会用自己自带的 JDK ,反而会使用我们 Linux 安装的 JDK
这个时候如果两个jdk的版本不一致,就会造成jdk不能正常运行
需要把配置修改下,指定JAVA的环境是es中自带的JDK环境
vim bin/elasticsearch-env
2.4:解决内存不足问题
由于elasticsearch 默认分配 jvm空间大小为2g,如果服务器内存不大就会报错,所以我们需要修改 jvm空间
如果Linux服务器本来配置就很高,可以不用修改。
vim config/jvm.options
解决vm.max_map_count [65530] is too low问题
vim /etc/sysctl.conf
sysctl -p # 刷新配置
2.5:启动配置
bin> ./elasticsearch -d
# 查看进程
ps -ef | grep elasticsearch
2.6:安装kibana
上传压缩包之后解压Kibana
tar -xvf kibana-7.9.3-linux-x86_64.tar.gz
修改配置文件,修改端口号和es地址,默认是5601
vim kibana-7.9.3-linux-x86_64/config/kibana.yml
server.port: 8508 # 默认是5601,可以不改
server.host: 192.168.111.129 # 本机ip
elasticsearch.host: ["http://192.168.111.129:9200"] # es集群的端口和地址
kibana.inex: ".kibana" # kibana索引
i18n.locate: "zh-CN" # 中文
如果报错[BABEL] Note: The code generator has deoptimised the styling of .js as it exceeds the max of 500KB.
需要再项目的根目录下添加文件并设置内容
vi .babelrc
# 下面是添加的内容
{"compact": false}
启动kibana:
./bin/kibana
2.7:设置登录信息【选】
2.8:es集群搭建
需要修改两个节点上的配置文件elasticsearch.yml
节点1 配置
cluster.name: codesheep # 集群名称
node.name: sheep1 # 节点名
network.host: 192.168.31.8 # 绑定的节点1地址
network.bind_host: 0.0.0.0 # 此项不设置本机可能访问不了
discovery.zen.ping.unicast.hosts: ["192.168.31.8","192.168.31.9"] # hosts列表
discovery.zen.minimum_master_nodes: 1
# 如下配置是为了解决 Elasticsearch可视化工具 dejavu的跨域问题!若不用可视化工具则可省略之
http.port: 9200
http.cors.allow-origin: "http://192.168.199.76:1358"
http.cors.enabled: true
http.cors.allow-headers : X-Requested-With,X-Auth-Token,Content-Type,Content-Length,Authorization
http.cors.allow-credentials: true
节点2 配置
cluster.name: codesheep # 集群名称
node.name: sheep1 # 节点名
network.host: 192.168.31.9 # 绑定的节点2地址
network.bind_host: 0.0.0.0 # 此项不设置本机可能访问不了
discovery.zen.ping.unicast.hosts: ["192.168.31.8","192.168.31.9"] # hosts列表
discovery.zen.minimum_master_nodes: 1
# 如下配置是为了解决 Elasticsearch可视化工具 dejavu的跨域问题!若不用可视化工具则可省略之
http.port: 9200
http.cors.allow-origin: "http://192.168.199.76:1358"
http.cors.enabled: true
http.cors.allow-headers : X-Requested-With,X-Auth-Token,Content-Type,Content-Length,Authorization
http.cors.allow-credentials: true
3:docker安装
使用docker用于快速构建部署logstash, es和kibana
# 1:构建docker网络,可以使得容器之间互联互通
docker network create es-network
# 用docker运行es容器
docker run
--name elasticsearch # 指定容器名称
-v=/home/elasticsearch.yml:/usr/share/elasticsearch/config/elasticsearch.yml # yml映射,使得容器内部可以使用外面刚刚写好的yaml
--network=es-network # 声明使用的docker网络,保证这几个容器之间可以通过es-network网桥互联互通
-p 9200:9200
-p 9300:9300 # 端口映射,9200是es的端口,9300是可视化端口
-e "discovery.type=single-node" # 单点模式启动
-d # 后台运行
elasticsearch:8.14.1 # 使用的镜像及其tag,如果本机没有,将会通过docker pull 拉取
# 用docker运行logstash容器
docker run
--name Logstash # 指定容器名称
-v=/home/Logstash.conf:/bitnami/logstash/pipeline/Logstash.conf # conf映射
--network=es-network # 声明使用的docker网络,保证这几个容器之间可以通过es-network网桥互联互通
-p 5044:5044 # 端口映射,声明本机的5044端口映射到容器的5044端口
-d # 后台运行
bitnami/logstash:8.14.1 # 使用的镜像及其tag,如果本机没有,将会通过docker pull 拉取
# 用docker运行kibana容器
docker run
--name kibana # 指定容器名称
--network=es-network # 声明使用的docker网络,保证这几个容器之间可以通过es-network网桥互联互通
-e ELASTICSEARCH_URL=http://elasticsearch:9200 # 指定要可视化的es的地址
-p 5601:5601 # 指定端口映射
-d # 后台运行
kibana:8.14.1 # 使用的镜像及其tag,如果本机没有,将会通过docker pull 拉取
三:相关名词解释
1:header下的内容
2:索引(index)
一个索引就是一个拥有几分相似特征的文档的集合。-> MySQL的dataBase
比如说,你可以有一个客户数据的索引,另一个产品目录的索引,还有一个订单数据的索引。
一个索引由一个名字来标识(必须全部是小写字母),并且当我们要对这个索引中的文档进行检索、搜索、更新和删除的时候,都要使用到这个名字。
在一个集群中,可以定义任意多的索引。
能搜索的数据必须有索引,这样的好处是可以提高查询速度,比如:新华字典前面的目录就是索引的意思,目录可以提高查询速度。
Elasticsearch 索引的精髓:一切设计都是为了提高搜索的性能
3:类型(type)
在一个索引中,你可以定义一种或多种类型。
一个类型是你的索引的一个逻辑上的分类/分区,其语义完全由你来定。
通常,会为具有一组共同字段的文档定义一个类型。不同的版本,类型发生了不同的变化
版本 | Type |
---|---|
5.x | 支持多种 type |
6.x | 支持多种 type |
7.x | 默认不再支持自定义索引类型(默认类型为:_doc) |
4:文档(Document)
一个文档是一个可被检索的基础信息单元,也就是一条数据。
比如:你可以拥有某一个客户的文档,某一个产品的一个文档,当然,也可以拥有某个订单的一个文档。
文档以 JSON格式来表示,而 JSON 是一个到处存在的互联网数据交互格式。
在一个 index/type 里面,你可以存储任意多的文档。
5:字段(Field)
相当于是数据表的字段,对文档数据根据不同属性进行的分类标识
6:映射(mapping)
mapping 是处理数据的方式和规则方面做一些限制
如:某个字段的数据类型、默认值、分析器、是否被检索等等。这些都是映射里面可以设置的
其它就是处理 ES 里面数据的一些使用规则设置也叫做映射
按着最优规则处理数据对性能提高很大,因此才需要建立映射,并且需要思考如何建立映射才能对性能更好
7:分片(shard)
一个索引可以存储超出单个节点硬件限制的大量数据。
比如,一个具有 10 亿文档数据的索引占据 1TB 的磁盘空间,而任一节点都可能没有这样大的磁盘空间。
或者单个节点处理搜索请求,响应太慢。
为了解决这个问题,Elasticsearch 提供了将索引划分成多份的能力,每一份就称之为分片。
当你创建一个索引的时候,你可以指定你想要的分片的数量。
每个分片本身也是一个功能完善并且独立的“索引”,这个“索引”可以被放置到集群中的任何节点上。
分片很重要,主要有两方面的原因:
- 允许你水平分割/扩展你的内容容量
- 允许你在分片之上进行分布式的、并行的操作,进而提高性能/吞吐量
至于一个分片怎样分布,它的文档怎样聚合和搜索请求,是完全由 Elasticsearch 管理的,对于作为用户来说,无需过分关心。
如同完整的大数据,分割成多个小数据,分布到不同的地方,小数据就是分片,所有的分片合起来就是完整的数据
被混淆的概念是:一个 Lucene 索引,我们在 Elasticsearch 称作分片。一个 Elasticsearch 索引是分片的集合。
当 Elasticsearch 在索引中搜索的时候,他发送查询到每一个属于索引的分片(Lucene 索引),然后合并每个分片的结果到一个全局的结果集
8:副本(replicas)
在一个网络/云的环境里,失败随时都可能发生,在某个分片/节点不知怎么的就处于离线状态,或者由于任何原因消失了
这种情况下,有一个故障转移机制是非常有用并且是强烈推荐的。
为此目的,Elasticsearch 允许你创建分片的一份或多份拷贝,这些拷贝叫做复制分片(副本)
。
复制分片之所以重要,有两个主要原因:
- 在分片/节点失败的情况下,提供了高可用性。
- 扩展你的搜索量/吞吐量,因为搜索可以在所有的副本上并行运行
总之,每个索引可以被分成多个分片。一个索引也可以被复制 0 次(意思是没有复制)或多次。
一旦复制了,每个索引就有了主分片(作为复制源的原来的分片)和复制分片(主分片的拷贝)之别。
分片和复制的数量可以在索引创建的时候指定。在索引创建之后,你可以在任何时候动态地改变复制的数量
,但是你事后不能改变分片的数量
。
默认情况下,Elasticsearch 中的每个索引被分片 1 个主分片和 1 个复制
这意味着,如果你的集群中至少有两个节点,你的索引将会有 1 个主分片和另外 1 个复制分片(1 个完全拷贝)
这样的话每个索引总共就有 2 个分片,我们需要根据索引需要确定分片个数,副本就是分片的备份(分身),和数据库的主从复制类似
如果你的es是单节点启动的,那么将无法使用复制分片。
四:ES索引详述
可以类比一下mysql数据库的创建
Elasticsearch最关键的就是提供强大的索引能力 -> 一切设计都是为了提高搜索的性能
在进行插入数据的同时,还默默的为这些内容进行倒排索引,这就是为什么ES最为核心的功能是搜索
传统索引B-Tree
-
二叉树的查找的效率是LogN,同时插入新的节点不用移动全部的节点,所以用树形结构存储索引,能够同时兼备插入和查询的性能,因此在这个基础上,再结合磁盘的读取特性,传统的关系型数据库采用了B-Tree或者B+Tree进行存储
-
为了提高效率,减少磁盘的寻道次数,将多个值作为一个数组通过连续的区间进行存放,一次巡道读取多个数据,同时减低了树的高度
1:倒排索引
term-index -> Term Dictionary -> Posting List
假设现在有如下的数据:
ID | Name | Age | Sex |
---|---|---|---|
1 | Kate | 24 | Female |
2 | John | 24 | Male |
3 | Bill | 29 | Male |
ID是Elasticsearch自建的文档id,那么Elasticsearch建立的索引如下
Name
Term | Posting List |
---|---|
Kate | 1 |
John | 2 |
Bill | 3 |
Age
Term | Posting List |
---|---|
24 | [1,2] |
29 | 3 |
Sex
Term | Posting List |
---|---|
Female | 1 |
Male | [2,3] |
-
Posting list就是一个int的数组,存储了所有符合某个term的文档id。
-
term Dictionary:为了能快速找到某个term,将所有的term排个序,二分法查找term,logN的查找效率,就像通过字典查找一样
-
term index:就像字典里的索引页一样,再结合FST的压缩技术,可以使term index缓存到内存中。大大减少了查询时间
2:索引的创建
2.1:自动创建索引
如下的语句会动态创建一个customer的index
PUT /customer/_doc/1
{
"name": "John Doe"
}
而这个index实际上已经自动创建了它里面的字段(name)的类型
{
"mappings": {
"_doc": { // 表名,es6之后废弃,都是_doc,在后续版本中国将被直接移除
// ================ 下面是这个表的属性声明 ================
"properties": {
"name": { // ==> name字段
"type": "text", // ==> name字段的类型是text
"fields": { // ==> name字段的属性
"keyword": { // keyword属性
"type": "keyword", // 类型是keyword
// ignore_above 最大值是32766,但是要根据场景来设置,比如说中文最大值应该是设定在10922。
"ignore_above": 256 // 当字段文本的长度大于256时,不做倒排索引。
}
}
}
}
}
}
}
2.2:手动创建索引
可以通过在 config/elasticsearch.yml
的每个节点下添加下面的配置来禁止自动创建索引
action.auto_create_index: false
2.2.1:索引的格式
PUT /my_index
{
// 用来设置分片,副本,分析器等配置信息
"settings": { ... any settings ... },
// 字段映射,类型【就是这个数据库有哪些字段】
"mappings": {
"properties": { ... any properties ... }
}
}
2.2.2:创建索引
// 创建一个user 索引 test-index-users, 其中包含三个属性:name,age, remarks;
// 存储在一个分片一个副本上
PUT /test-index-users
{
"settings": {
"number_of_shards": 1, // 分片数目
"number_of_replicas": 1 // 副本数目
},
// 这个就类似于表的结构
"mappings": {
"properties": {
// name字段
"name": {
"type": "text", // text类型
"fields": { // 属性信息配置
"keyword": {
"type": "keyword", // keyword属性
"ignore_above": 256 // 当字段文本的长度大于256时,不做倒排索引。
}
}
},
// age字段
"age": {
"type": "long" // long类型
},
// remarks字段
"remarks": {
"type": "text" // text类型
}
}
}
}
插入测试数据
进入Kibana之后,点击小扳手,编写输入
向新建的索引【数据库】中插入一条数据
使用查询语句查询一下是否插入成功
测试下不匹配的数据类型
2.2.3:更改索引
可以修改通过PUT修改索引的分片副本个数
PUT /test-index-users/_settings
{
"settings": {
"number_of_replicas": 0 // 将副本的个数修改成为0
}
}
2.2.4:查看索引
# 查看索引结构 - mapping - 类似于表结构
GET /test-index-users/_mapping
2.2.5:打开和关闭索引
关闭索引
# 一旦索引被关闭,那么这个索引只能显示元数据信息,不能够进行读写操作。
# 强行的插入数据将会报错
POST /test-index-users/_close
打开索引
# 打开索引
POST /test-index-users/_open
2.2.6:删除索引
# 删除索引
DELETE /test-index-users
3:再谈索引配置
3.1:分析器的使用analysis
分析器是一种用于分析数据或者按照用户想要的方式处理数据的工具
对于字符串类型的字段,Elasticsearch 允许用户自定义分析器分析器是一种用于分析数据或者按照用户想要的方式处理数据的工具
对于字符串类型的字段,Elasticsearch 允许用户自定义分析器。
下面是分词器的常见操作:
3.1.1: 规范化
#normalization
GET _analyze
{
"text":"Mr Ma is an excellent teacher",
"analyzer": "pattern"
}
可以看到,normalization 的主要作用就是将一句话中的所有单词都切分成单个单词,并且将所有单词都变成小写形式。
其中 pattrern 是我们指定使用的分词器,对输入的语句进行分词。比如,我们还可以将分词器换成 english
3.1.2:字符过滤器
字符过滤器可以过滤掉用户输入的一些无用字符。首先创建一个自定义的字符串过滤器:
PUT my_index
{
"settings": {
"analysis": {
"char_filter": {
"my_char_filter":{ # 声明my_char_filter是一个字符过滤器
"type":"html_strip" # 将html内容过滤
}
},
# 定义过滤器,名称是my_analyzer
"analyzer": {
"my_analyzer":{
"tokenizer":"keyword", # 使用的分词器是 keyword
"char_filter":"my_char_filter" # 使用的字符过滤器是上面的my_char_filter
}
}
}
}
}
我们也可以选择不干掉哪些标签:
"char_filter": {
"my_char_filter":{
"type":"html_strip",
"escaped_tags":["a"] # <a>不会被干掉
}
},
字符过滤器也可以使用映射方式将一些特殊字符映射成另一种字符
"char_filter": {
"my_char_filter":{
"type":"mapping",
"mappings":[
"去你妈 => *",
"滚 => *",
"你妈 => *",
"傻逼 => *"
]
}
},
可以利用正则匹配的方式对一些输入进行映射
"char_filter": {
"my_char_filter":{
"type":"pattern_replace",
"pattern":"(\\d{3})\\d{4}(\\d{4})", // 正则条件
"replacement":"$1****$2" // 满足上面那个正则的,将会替换成为下面这个,$1和$2是第一分组(\\d{3})和第二分组(\\d{4})
}
},
3.1.3:IK分词器
在 ik 分词的帮助下可以分解成了若干个含有意义的词语。
GET custom_analysis/_analyze
{
"analyzer": "ik_max_word",
"text":["男士西装 加肥加大 夏天可穿 弹性修身"]
}
3.1.4:热更新
热词更新就是指将新出现的词汇加入到我们的 ik 分词库中,这就需要我们对 ik 分词的词库进行拓展。
首先进入到 ik 分词的 config 文件夹下,创建一个文件夹 custom 作为自定义词库的存放位置。
在其中增加两个文件 extend_words 和 extend_words2:
向 extend_words 和 extend_words2 中写入自定义的单词
之后将这个自定义词库配置到 ik 的 config 文件中
接下来重新启动启动当前的集群。这样配置的热词就可以被IK分词器识别,并且不会将他们拆分
3.1.5:自定义分析器
虽然 Elasticsearch 带有一些现成的分析器,然而在分析器上 Elasticsearch 真正的强大之处在于,你可以通过在一个适合你的特定数据的设置之中组合字符过滤器、分词器、词汇单元过滤器来创建自定义的分析器。
一个分析器就是在一个包里面组合了三种函数的一个包装器,三种函数按照顺序被执行:
字符过滤器
字符过滤器用来整理一个尚未被分词的字符串。例如,如果我们的文本是 HTML 格式的,它会包含像
或者
这样的 HTML 标签,这些标签是我们不想检索的。我们可以使用 HTML “清除字符过滤器”来移除掉所有的 HTML 标签
并且像把 Á 转换为相对应的 Unicode 字符 Á 这样,转换 HTML 实体。一个分析器可能有 0 个或者多个字符过滤器。
分词器
一个分析器必须有一个唯一的分词器。分词器把字符串分解成单个词条或者词汇单元。
“标准分析器”里使用的是把一个字符串根据单词边界分解成单个词条,并且移除掉大部分的标点符号,然而还有其他不同行为的分词器存在。
例如,“关键词分词器”完整地输出接收到的同样的字符串,并不做任何分词。
“空格分词 器”只根据空格分割文本。
“正则分词器”根据匹配正则表达式来分割文本。
词单元过滤器
经过分词,作为结果的“词单元流”会按照指定的顺序通过指定的词单元过滤器。
“词单元过滤器”可以修改、添加或者移除词单元。
我们已经提到过 lowercase 和 stop 词过滤器 ,但是在 Elasticsearch 里面还有很多可供选择的词单元过滤器。
“词干过滤器”把单词遏制为词干。
“ascii_folding 过滤器”移除变音符,把一个像 “très” 这样的词转换为 “tres”。
“ngram 和 edge_ngram 词单元过滤器”可以产生适合用于部分匹配或者自动补全的词单元。
自定义分词器
{
"settings": {
"analysis": { // 开启自定义分析器
"char_filter": { // 自定义分词器名
"&_to_and": { // 自定义名字,尽量和分析内容有关
"type": "mapping", // 转换类型
"mappings": [ "&=> and "] // 将 & 转换为 and
}
},
"filter": { // 自定义分词器名
"my_stopwords": { // 自定义名字,尽量和分析内容有关
"type": "stop", // 删除类型
"stopwords": [ "the", "a" ] // 把 the 和 a 删除
}
},
"analyzer": { // 实现已经做好的自定义分词器
"my_analyzer": { // 自定义名字
"type": "custom", // 代表 自定义 类型
"char_filter": [ "html_strip", "&_to_and" ], // 上面做好的自定义分词器添加进来
"tokenizer": "standard", // 隶属于标准分词器
"filter": [ "lowercase", "my_stopwords" ] // 上面做好的自定义分词器添加进来
}
}
}
}
}
// 测试自定义分词器
GET custom_analysis/_analyze
{
"analyzer": "my_analyzer",
"text":["what is, asdf&sdx in ssd. at wed | fer for qweas !"]
}
3.2:相似度模型配置similarity
Elasticsearch 允许为索引模式映射文件中的不同字段指定不同的相似度得分计算模型
其用法例析如下:
这样,ES就会为这个hobby字段使用这个相似度模型计算响应的相应得分
3.3:信息格式的配置postings_format
es支持为每个字段指定信息格式,以满足通过改变字段被索引的方式来提高性能的条件。es中的信息格式有如下几个:
default
:默认信息格式,其提供了实时的对存储字段和词向量的压缩pulsing
:将重复值较少字段的信息列表 编码为词条矩阵,可加快 该字段的查询速度direct
:该格式在读过程中将词条加载到未经压缩而存在内存的矩阵中,该格式可以提升常用字段的性能,但损耗内存memory
:该格式将所有的数据写到磁盘,然后需要FST来读取词条和信息列表到内存中bloom_default
:默认信息格式的扩展,增加了写入磁盘的功能。以便快速检查给定的值是否存在bloom_pulsing
:pulsing
格式的扩展,也加入bloom filter
的支持
信息格式字段( postings_format
)可以在 任何一个字段上 进行设置,配置信息格式的示例如下:
3.4:文档值及其格式的配置doc_values_format
文档值这个字段属性作用在于:其允许将给定字段的值被写入一个更高内存效率的结构,以便进行更加高效的排序和搜索
我们通常可以将该属性加在需要进行排序的字段上,这样可以提效。
其配置方式是 通过属性 doc_values_format
进行,有三种常用的 doc_values_format
属性值:
default
:默认格式,其使用少量的内存但性能也不错disk
:将数据存入磁盘,几乎无需内存memory
:将数据存入内存
假如我们想对年龄字段进行排序,那么给该字段设置文档值格式的属性是可以提升效率的。
五:ES数据类型
ElasticSearch 的数据类型定义了字段如何被存储、索引和查询。
选择正确的数据类型对搜索性能、存储效率和功能实现至关重要。
1:核心类型
1.1:text
用于需要全文搜索的文本内容(如电子邮件正文、产品描述、日志消息)
在索引前会被分析器 (analyzer) 处理(分词、转小写、移除停用词等),生成倒排索引。查询时也会被分析
不适合精确匹配、排序或聚合(除非使用 fielddata
或 keyword
子字段)
通常会定义一个 keyword
类型的子字段(如 my_field.keyword
)用于精确值操作(排序、聚合、脚本、精确匹配)
// 这个就类似于表的结构
"mappings": {
"properties": {
// name字段
"name": {
"type": "text", // text类型
"fields": { // 属性信息配置
"keyword": { // 创建一个keyword的子字段,用于精确值操作
"type": "keyword", // keyword属性
"ignore_above": 256 // 当字段文本的长度大于256时,不做倒排索引。
}
}
},
// remarks字段
"remarks": {
"type": "text" // text类型
}
}
}
1.2:keyword
用于精确值字符串(如状态码、标签、姓名、ID、枚举值)。也常用于排序、聚合和过滤
keyword不会被分析器分词。整个字符串作为一个单一的 term 被索引。
适合精确匹配 (term
查询)、范围查询 (range
)、排序、聚合。区分大小写
存在两个变种:constant_keyword
(所有文档该字段值相同), wildcard
(优化了通配符和正则查询性能)。
1.3:数值类型
存储各种精度的数值,根据数值范围和精度需求选择最合适的类型,节省存储空间和提高效率。
类型 | 精度 |
---|---|
byte | 1字节 - 8位有符号整数 (-128 到 127) |
short | 2字节 - 16位有符号整数 (-32768 到 32767) |
integer | 4字节 - 32位有符号整数 (-2³¹ 到 2³¹-1) |
long | 8字节 - 64位有符号整数 (-2⁶³ 到 2⁶³-1) |
float | 4字节 - 32位单精度浮点数 |
double | 8字节 - 64位双精度浮点数 |
支持范围查询 (range
)、排序、聚合。
在 ES 内部主要被索引用于范围查询,精确匹配效率不如 keyword
。
1.4:date
存储日期和/或时间
可以接受多种格式(如 "2020-04-05"
, "2020-04-05T12:34:56.789Z"
, 毫秒时间戳 1586086896789
)。
需要指定格式或使用默认格式。
在ES内部 存储为 UTC 时间戳(毫秒精度)
支持强大的日期范围查询、排序、聚合(如按日/月/年聚合)。可以指定时区
1.5:boolean
存储 true
/false
值
索引时可接受:
true
,false
,"true"
,"false"
(字符串),"on"
,"off"
,"yes"
,"no"
,"0"
,"1"
,0
,1
。
但在 _source
中会统一为 true
/false
1.6:binary
存储 Base64 编码的二进制数据(如图片、加密内容、序列化对象)
不会被索引,默认不存储(但可通过 store: true
显式存储)。只能通过 _source
字段或显式存储来检索
仅用于存储,无法直接搜索其内容。
{
"mappings": {
"properties": {
// Text 类型 (带 keyword 子字段)
"product_description": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
// Keyword 类型
"status_code": {
"type": "keyword"
},
// 数值类型
"population": {
"type": "long" // 长整数
},
"temperature": {
"type": "float" // 单精度浮点
},
// Date 类型
"order_date": {
"type": "date",
"format": "yyyy-MM-dd HH:mm:ss||epoch_millis"
},
// Boolean 类型
"in_stock": {
"type": "boolean"
},
// Binary 类型
"encrypted_data": {
"type": "binary"
}
}
}
}
2:复杂类型
2.1:object
适用于单个对象嵌套。表示一个内嵌的 JSON 对象, 默认情况下,内嵌对象的字段会被“扁平化”处理。
{
"user": {
"first": "John",
"last": "Smith"
}
}
// 上述json,在内部会被索引为:
user.first = "John"
user.last = "Smith"
对于对象数组,如果数组元素之间需要保持独立性(避免跨对象字段匹配),需要使用 nested
类型
2.2:nested
表示一个对象数组,并且需要数组中的每个对象保持其字段的独立性,避免跨对象匹配。
ES 将数组中的每个对象作为独立的隐藏文档进行索引。
查询时需要使用专门的 nested
查询和 nested
聚合
nested的存在解决了 object
类型处理数组时导致的“跨对象匹配”问题。
例如,搜索 {"user.first": "John", "user.last": "White"}
:
- 在
object
类型中可能会匹配到first=John
和last=White
来自数组不同元素的对象 - 而
nested
类型要求匹配发生在同一个内嵌对象内
因为每个嵌套对象都是独立索引的,所以创建、查询和更新 nested
对象比 object
类型开销更大
2.3:flattened
将整个 JSON 对象(或数组)视为一个字段来处理,将其所有叶节点值映射为 keyword
类型的值进行索引
对象内部的结构信息丢失,所有值都被视为独立的关键字。默认限制为 10000 个唯一键
- 处理未知或大量动态字段(避免
mapping explosion
映射爆炸)。 - 不需要查询内嵌对象内部结构,只需要搜索对象中是否存在某些值。
- 日志数据中处理包含大量不同键的标签或元数据部分。
不支持范围查询、高亮、精确短语匹配等功能(因为值都被当作 keyword
处理且结构丢失)
{
"mappings": {
"properties": {
// Object 类型
"manufacturer": {
"type": "object",
"properties": {
"name": {"type": "keyword"},
"address": {"type": "text"}
}
},
// Nested 类型
"reviews": {
"type": "nested", // 保持数组元素的独立性
"properties": {
"user": {"type": "keyword"},
"rating": {"type": "integer"}
}
},
// Flattened 类型
"metadata": {
"type": "flattened" // 处理动态字段
}
}
}
}
3:特殊类型
3.1:geo_point
存储单个地理坐标点(经纬度)
支持多种格式:{"lat": 40.73, "lon": -74.1}
, "40.73, -74.1"
, "drm3btev3e86"
(Geohash)
支持按距离 (geo_distance
)、按边界框 (geo_bounding_box
)、按多边形 (geo_polygon
) 过滤
支持按距离排序,按地理位置聚合 (geohash grid)
3.2:geo_shape
存储复杂的地理形状(点、线、多边形、多多边形等)
使用 GeoJSON 或 Well-Known Text (WKT) 格式
支持空间关系查询(如形状相交、包含、不相交等)
3.3:ip
存储 IPv4 或 IPv6 地址。
支持 CIDR 表示法的范围查询 (ip_range
), 精确匹配等
3.4:completion
提供自动补全 (auto-complete) 功能
构建内存中的有限状态转换器 (FST) 结构以实现快速前缀查找
使用专门的 completion
查询器 (suggesters
)。
3.5:token_count
将一个字符串字段(通常是 text
)分词后的词项数量存储为一个整数
需要指定 analyzer
和来源的 string
字段
用于按词项数量进行过滤或聚合(如查找标题长度超过 10 个词的文档)
3.6:join
在同一个索引内建立文档之间的父子关系 (Parent-Child Relationship) 或嵌套集 (Nested Set)
定义关系类型 (如 question
和 answer
)。父文档和子文档存储在同一个分片上(路由到同一个分片)
使用专门的 has_parent
, has_child
, parent_id
查询。
相比 nested
类型,join
类型允许子文档独立更新,但联接查询 (has_child
, has_parent
) 通常比同等 nested
查询性能开销更大,尤其是在高基数父-子关系上
3.7:其他不常用的
类型 | 说明 |
---|---|
rank_feature/rank_features | 存储用于在查询时提升文档相关性排名的数值特征 |
dense_vector | 存储密集向量(浮点数数组),用于表示文本、图像等的嵌入向量 |
sparse_vector | 存储稀疏向量 |
search_as_you_type | 优化字段以实现按需搜索体验,类似即时搜索 |
histogram | 预聚合的数值直方图数据 |
constant_keyword | 所有文档中该字段的值都相同的关键字字段 |
alias | 定义现有字段的别名 |
version | 专门用于软件版本号,支持符合语义版本规范的比较和排序 |
{
"mappings": {
"properties": {
// Geo_point 类型
"location": {
"type": "geo_point" // 经纬度坐标
},
// Geo_shape 类型
"service_area": {
"type": "geo_shape" // 地理多边形
},
// IP 类型
"client_ip": {
"type": "ip" // IPv4/IPv6地址
},
// Completion 类型
"suggest": {
"type": "completion" // 自动补全
},
// Token_count 类型
"title_length": {
"type": "token_count",
"analyzer": "standard",
"store": true
},
// Join 类型 (父子文档)
"product_relation": {
"type": "join",
"relations": {
"product": "variant" // 父类型:子类型
}
},
// Rank_feature 类型
"popularity_score": {
"type": "rank_feature" // 相关性评分特征
},
// Dense_vector 类型
"image_embedding": {
"type": "dense_vector",
"dims": 512, // 向量维度
"index": true,
"similarity": "cosine"
},
// Search_as_you_type 类型
"instant_search": {
"type": "search_as_you_type"
},
// Histogram 类型
"price_distribution": {
"type": "histogram" // 预聚合直方图
},
// Constant_keyword 类型
"data_source": {
"type": "constant_keyword",
"value": "web_crawler" // 所有文档固定值
},
// Alias 类型
"product_name": {
"type": "alias", // 字段别名
"path": "name.original"
}
}
}
}
六:ES文档元数据
每个被索引的文档都附带一组元数据字段,提供关于文档本身的信息。这些字段通常以下划线 (_) 开头。
1:_index
文档所属的索引名称。
标识文档的位置。在查询、更新、删除文档时都需要指定索引(或索引模式)。
2:_id
文档在索引内的唯一标识符。
- 可以在索引文档时显式指定 (
PUT my_index/_doc/1
)。 - 如果未指定,Elasticsearch 会自动生成一个唯一的 ID (
PUT my_index/_doc
不提供 ID)。
是引用特定文档的关键。在 GET
、UPDATE
、DELETE
操作中必须使用。
3:_source
一个 JSON 对象,包含在索引时传递的原始文档主体。
默认情况下,_source
字段会被存储。但可以关闭 ("enabled": false
) 以节省存储空间(代价是无法通过 _source
获取原始文档、无法使用更新 API、无法使用高亮等功能)。
- 检索文档时,默认返回的字段就是
_source
的内容。 Reindex
和Update
API 依赖它。- 部分搜索特性(如高亮
highlighting
、字段提取fields
)需要它。 - 调试和查看原始数据。
4:_version
文档的版本号。
- 每次文档变更(创建、更新、删除)时,
_version
都会递增。 - 主要用于乐观并发控制 (Optimistic Concurrency Control, OCC)。在更新或删除文档时,可以通过指定
if_seq_no
和if_primary_term
(更现代) 或version
(旧方式) 来确保你修改的是你上次读取的版本,防止覆盖其他人的更改。
注意:_version
是文档级别的,不保证跨文档操作的顺序。
_seq_no
和 _primary_term
:
含义:这两个字段共同构成了一种比 _version
更严格的并发控制机制。
_seq_no
(Sequence Number): 单调递增的序列号,分配给索引中发生的每一次操作(索引、更新、删除文档)。它保证了单个分片内所有操作的全局顺序。_primary_term
(Primary Term): 每当分片的主分片发生重新分配(例如节点故障、重启)时递增。它标识了当前拥有该分片写入权的主分片的“任期”。
{
"_index": "products", // 索引名称
"_id": "p123", // 文档ID
"_version": 3, // 文档版本
"_seq_no": 356,
"_primary_term": 1,
"_source": { // 原始文档内容
"name": "Wireless Headphones",
"price": 129.99,
"tags": ["electronics", "audio"]
}
}