全文检索
什么是全文检索
全文检索是一种通过对文本内容进行全面索引和搜索的技术。
设想一个全文检索的场景,比如搜索Elasticsearch原理:
总结: 根据提供的信息,返回与之关键字相关的所有内容
思考:用传统关系型数据库实现有什么问题?
select * from t_blog where content like "%elasticsearch原理%"
这种需要遍历所有的记录进行匹配,如果数据量比较庞大的话,不但查询效率低,而且搜索结果不符合我们搜索时的期望。
全文检索的原理
- 首先需要对文本数据进行处理,比如:分词,过滤停用词等。
- 对分好的词建立倒排索引,索引会记录每个单词在文档中的 位置信息以及其他相关的元数据,如词频、权重等。
- 当用户发起搜索请求时,搜索引擎会根据用户提供的关键词 或短语,在建立好的索引中查找匹配的文档
什么是倒排索引?
索引一般分为正排索引与倒排索引两种。
mysql库索引,就是正排索引,通过表字段依次匹配。
倒排索引是根据单词或短语建立的索引结构,需要对文档进行分词处理,然后记录每个单词在哪些文档中出现,以及出现的位置信息,这样能够快速定位文档,提高搜索效率。
我们在创建文章的时候,建立一个关键词与文章的对应关系表,也可以称之为倒排索引。如下图所示:
关键词 | 文章id | 是否命中 |
---|---|---|
elasticsearch | 1,2 | ✔️ |
原理 | 1,2,3,4 | ✔️ |
java | 3 | |
设计模式 | 2 |
ElasticSearch介绍
ElasticSearch(简称ES)是一个开源的分布式搜索和数据分析引擎,是用Java开发并且是当前最流行的开源的企业级搜索引擎,能够达到近实时搜索,它专门设计用于处理大规模的文本数据和实现高性能的全文检索。
以下是一些 Elasticsearch 的特点和优势:
分布式架构:Elasticsearch 是一个分布式系统,可以轻松地 水平扩展处理大规模的数据集和高并发的查询请求。
全文检索功能:Elasticsearch 提供了强大的全文检索功能, 包括分词、词项查询、模糊匹配、多字段搜索等,并支持 丰富的查询语法和过滤器。
多语言支持:Elasticsearch 支持多种语言的分词器和语言 处理器,可以很好地处理不同语言的文本数据。
高性能:Elasticsearch 使用倒排索引和缓存等技术,具有 快速的搜索速度和高效的查询性能。
实时性:Elasticsearch 支持实时索引和搜索,可以几乎实时地将文档添加到索引中,并立即可见。
易用性:Elasticsearch 提供了简单易用的 RESTful API,方便进行索引管理、查询操作和数据分析。
搜索引擎排名:
参考网站:https://db-engines.com/en/ranking/search+engine
ElasticSearch应用场景
只要用到搜索的场景,ES几乎都可以是最好的选择。 除了搜索之外,还被广泛运用在大数据分析,日志分析,
指标监控,信息安全等多个领域,可以帮助你对海量数据 进行探索,监控等
技术选型对比
Elasticsearch | Solr | MongoDB | MySQL | |
---|---|---|---|---|
DB类型 | 搜索引擎 | 搜索引擎 | 文档数据库 | 关系型数据库 |
基于何种框架开发 | Lucene | Lucene | ||
基于何种开发语言 | Java | Java | C++ | C、C++ |
数据结构 | FST、Hash等 | B+ Trees | ||
数据格式 | Json | Json/XML/CSV | Json | Row |
分布式支持 | 原生支持 | 支持 | 原生支持 | 不支持 |
数据分区方案 | 分片 | 分片 | 分片 | 分库分表 |
业务系统类型 | OLAP | OLAP | OLTP | OLTP |
事务支持 | 不支持 | 不支持 | 多文档ACID事务 | 支持 |
数据量级 | PB级 | TB级~PB级 | PB级 | 单库3000万 |
一致性策略 | 最终一致性 | 最终一致性 | 最终一致性即时一致性 | 即时一致性 |
擅长领域 | 海量数据全文检索 | 大数据聚合分析 | 大数据全文检索 | 海量数据CRUD |
劣势 | 不支持事务写入实时性低 | 海量数据的性能不如ES随着数据量的不断增大,稳定性低于ES | 弱事务支持不支持join查询 | 大数据全文搜索性能低 |
查询性能 | ★★★★★ | ★★★★ | ★★★★★ | ★★★ |
写入性能 | ★★ | ★★ | ★★★★ | ★★★ |
总结:Mysql存储数据量比较少,需要考虑分库分表,MongoDB更适合需要高效读写和大规模数据存储的应用,而Elasticsearch则更适合需要进行复杂搜索和分析的应用场景
ElasticSearch环境搭建
安装ElasticSearch
安装文档
https://www.elastic.co/guide/en/elasticsearch/reference/8.14/targz.html
下载地址
https://www.elastic.co/cn/downloads/past-releases#elasticsearch
windows安装ElasticSearch
下载ElasticSearch
windows
https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-8.14.2-windows-x86_64.zip
ElasticSearch文件目录结构
目录 | 描述 |
---|---|
bin | 脚本文件,包括启动elasticsearch,安装插件,运行统计数据等 |
config | 配置文件目录,如elasticsearch配置、角色配置、jvm配置等。 |
jdk | 7.x 以后特有,自带的 java 环境,8.x版本自带jdk 17 |
data | 默认的数据存放目录,包含节点、分片、索引、文档的所有数据,生产环境需要修改。 |
lib | elasticsearch依赖的Java类库 |
logs | 默认的日志文件存储路径,生产环境需要修改。 |
modules | 包含所有的Elasticsearch模块,如Cluster、Discovery、Indices等。 |
plugins | 已安装插件目录 |
配置JDK环境
- ES比较耗内存,建议虚拟机4G或以上内存,jvm1g以上的内存分配
- 运行Elasticsearch,需安装并配置JDK。 各个版本对Java的依赖地址:
https://www.elastic.co/cn/support/matrix#matrix_jvm- 7.0开始,内置了Java环境。ES的JDK环境变量生效的优先级配置顺序 ES_JAVA_HOME>JAVA_HOME>ES_HOME
- ES_JAVA_HOME:这个环境变量用于指定Elasticsearch使用的Java运行时环境的路径。在启动Elasticsearch时,它会检查ES_JAVA_HOME环境变量并使用其中的Java路径。
- ES_HOME:这个环境变量指定Elasticsearch的安装路径。它用于定位Elasticsearch的配置文件、插件和其他相关资源。设置ES_HOME环境变量可以让您在命令行中更方便地访问Elasticsearch的目录结构和文件。
温馨提示:
- 初学者建议直接安装windows版本elasticSearch。
- 在windows下,需要设置ES_JAVA_HOME和ES_HOME环境变量。
- 安装包解压后就可以进入bin目录,直接运行elasticsearch.bat。
- 浏览器中就可以访问:http://localhost:9200/ 了
centos7安装ElasticSearch
下载ElasticSearch
linux
https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-8.14.3-linux-x86_64.tar.gz
解压
tar -zxvf elasticsearch-8.14.3-linux-x86_64.tar.gz
注意:es默认不能用root用户启动,建议先为elasticsearch创建用户。
- 为elaticsearch创建用户并赋予相应权限
- adduser es
- passwd es
- chown -R es:es elasticsearch-17.3
切换es用户
su es
配置JDK环境
- linux 进入用户主目录,比如/home/es目录下,设置用户级别的环境变量
- vim .bash_profile
- 设置ES_JAVA_HOME和ES_HOME的路径
- export ES_JAVA_HOME=/home/es/elasticsearch-8.14.3/jdk
- export ES_HOME=/home/es/elasticsearch-8.14.3
- #执行以下命令使配置生效
- source .bash_profile
修改配置
单机模式启动配置
vim elasticsearch.yml
#开启远程访问
network.host: 0.0.0.0
#单节点模式
discovery.type: single-node
默认为true,启用节点上ES的XPACK安全功能,相当于总开关
xpack.security.enabled: false
配置好以后直接./bin/elasticsearch就可以运行了。
集群模式启动配置
vim elasticsearch.yml
#开启远程访问
network.host: 0.0.0.0
默认为true,启用节点上ES的XPACK安全功能,相当于总开关
xpack.security.enabled: false
修改jvm参数配置
vim jvm.options
#修改参数
-Xms1g
-Xmx1g
配置的建议
1. Xms和Xms设置成—样
2. Xmx不要超过机器内存的50%
3. 不要超过30GB
配置好以后直接./bin/elasticsearch就可以运行了。
启动方式
#非root用户启动
窗口启动
bin/elasticsearch
后台启动
./bin/elasticsearch -d
启动ES服务常见错误解决方案
- max file descriptors [4096] for elasticsearch process is too low, increase to at least [65536]
ES因为需要大量的创建索引文件,需要大量的打开系统的文件,所以我们需要解除linux系统当中打开
文件最大数目的限制,不然ES启动就会抛错。
#切换到root用户
vim /etc/security/limits.conf
末尾添加如下配置:
* soft nofile 65536
* hard nofile 65536
* soft nproc 4096
* hard nproc 4096
- max number of threads [1024] for user [es] is too low, increase to at least [4096]
无法创建本地线程问题,用户最大可创建线程数太小
vim /etc/security/limits.d/20-nproc.conf
改为如下配置:
* soft nproc 4096
- max virtual memory areas vm.max_map_count [65530] is too low, increase to at least [262144]
最大虚拟内存太小,调大系统的虚拟内存
vim /etc/sysctl.conf
追加以下内容:
vm.max_map_count=262144
保存退出之后执行如下命令:
sysctl -p
- the default discovery settings are unsuitable for production use; at least one of
[discovery.seed_hosts, discovery.seed_providers, cluster.initial_master_nodes] must be configured
缺少默认配置,至少需要配置
discovery.seed_hosts
discovery.seed_providers
cluster.initial_master_nodes
中的一个参数.
discovery.seed_hosts: 集群主机列表
discovery.seed_providers: 基于配置文件配置集群主机列表
cluster.initial_master_nodes: 启动时初始化的参与选主的node,生产环境必填
vim config/elasticsearch.yml
#添加配置
discovery.seed_hosts: ["127.0.0.1"]
cluster.initial_master_nodes: ["node-1"]
#或者指定配置单节点(集群单节点)
discovery.type: single-node
客户端Kibana安装
Kibana是一个开源分析和可视化平台,就是为了与Elasticsearch协同工作
参考文档:https://www.elastic.co/guide/en/kibana/8.14/get-started.html
下载地址
Windows:https://artifacts.elastic.co/downloads/kibana/kibana-8.14.3-windows-x86_64.zip
Linux:https://artifacts.elastic.co/downloads/kibana/kibana-8.14.3-linux-x86_64.tar.gz
运行kibana
windows
运行Kibana
直接执行kibana.bat
Linux
注意:kibana也需要非root用户启动
bin/kibana
后台启动
nohup bin/kibana &
查询kibana进程
netstat -tunlp | grep 5601
访问Kibana
http://localhost:5601/app/dev_tools#/console
注意:kibana 使用ps -ef|grep kibana是查不到进程的,因为其实运行在node里面。但是我们也不能关闭所有node里面的软件,所以我们需要查询kibana监听端口5601的进程,所以需要如下操作:
[es@ ~]$ netstat -tunlp|grep 5601
(Not all processes could be identified, non-owned process info
will not be shown, you would have to be root to see it all.)
tcp 0 0 192.168.1.100:5601 0.0.0.0:* LISTEN 89671/./bin/../node
[es@ ~]$ kill -9 89671
Elasticsearch安装分词插件
在线安装analysis-icu分词插件
查看已安装插件
bin/elasticsearch-plugin list
安装插件
bin/elasticsearch-plugin install analysis-icu
删除插件
bin/elasticsearch-plugin remove analysis-icu
注意:安装和删除完插件后,需要重启ES服务才能生效。
离线安装ik中文分词插件
下载地址:https://release.infinilabs.com/analysis-ik/stable/
解压,然后手动上传到elasticsearch的plugins目
录,然后重启ES实例就可以了。
注意:es版本需要与ik一致
不一致怎么办
我的ES版本是8.14.3
由于ik没有8.14.3这个版本我使用的8.14.2版本
导致启动报版本不匹配的错误
所以我们需要修改他下面的
plugin-descriptor.properties
这个文件
把所有的8.14.2改为8.14.3然后就可以运行了
测试分词效果
以上介绍2中分词器安装,es还默认支持Standard Analyzer 分词器 这种分词器对中文不太友好,单个字切个,以下是各个分词器效果展示:
- 默认分词器
- analysis-icu分词器
- ik_smart为最少,粗粒度切分
- ik_max_word为最细粒度划分
附上测试实例信息
# 默认分词器 Standard Analyzer
POST _analyze
{
"analyzer":"standard",
"text":"今天天气很好,很适合出去玩"
}
# analysis-icu 分词器
POST _analyze
{
"analyzer":"icu_analyzer",
"text":"今天天气很好,很适合出去玩"
}
# ik ik_smart为最少切分
POST _analyze
{
"analyzer":"ik_smart",
"text":"今天天气很好,很适合出去玩"
}
# ik ik_max_word为最细粒度划分
POST _analyze
{
"analyzer":"ik_max_word",
"text":"今天天气很好,很适合出去玩"
}
在ES中支持中文分词器,推荐使用的就是 IK分词器
ElasticSearch核心概念
节点:Node
一个节点就是一个Elasticsearch的实例,可以理解为一个 ES 的进程。
角色:Roles
ES的角色分类:
- 主节点(active master):一般指活跃的主节点,一个集群中只能有一个,主要作用是对集群的管理。
- 候选节点(master-eligible):当主节点发生故障时,参与选举,也就是主节点的替代节点。
- 数据节点(data node):数据节点保存包含已编入索引的文档的分片。数据节点处理数据相关操作,如CRUD、搜索和聚合。这些操作是 I/O 密集型、内存密集型和 CPU 密集型的。监控这些资源并在它们过载时添加更多数据节点非常重要。
- 预处理节点(ingest node):预处理节点有点类似于消息管道,常用于一些数据写入之前的预处理操作。
注意:一般用于集群中,node.roles属性进行角色分配,如果 node.roles 为缺省配置,那么当前节点具备所有角色。
索引:Index
索引在 ES 中所表述的含义和 MySQL 中的索引完全不同,在 MySQL 中索引指的是加速数据查询的一种特殊的数据结构。
在 ES 中,索引表述的含义可以想象成 MySQL 中的表,注意这里只是类比去理解,索引并不等于表哈。
es 8.15.3版本索引结构如下
索引的组成部分:
- alias:索引别名
- settings:索引设置,常见设置如分片和副本的数量等。
- mapping:映射,定义了索引中包含哪些字段,以及字段的类型、长度、分词器等
类型:Type(ES 7.x 之后版本已删除此概念)
从ES 7.x版本开始类型已经被弃用,一个索引只能包含一个类型,在 8.14.2 中,_doc是路径的永久部分,表示端点名称而不是文档类型
索引 API :
- PUT {index}/_doc/{id}于显式ID
- POST {index}/_doc 自动生成的 ID
文档:Document
文档是ES中的最小数据单元。
它是一个具有结构化JSON格式的记录。文档可以被索引并进行搜索、更新和删除操作。
文档元数据,所有字段均以下划线开头,为系统字段,用于标注文档的相关信息:
- _index:文档所属的索引名
_type:文档所属的类型名- _id:文档唯一id
- _source: 文档的原始Json数据
- _version: 文档的版本号,修改删除操作_version都会自增1
- _seq_no: 和_version一样,一旦数据发生更改,数据也一直是累计的。Shard级别严格递增,保证后写入
的Doc的_seq_no大于先写入的Doc的_seq_no。- _primary_term: _primary_term主要是用来恢复数据时处理当多个文档的_seq_no一样时的冲突,避免Primary
Shard上的写入被覆盖。每当Primary Shard发生重新分配时,比如重启,Primary选举等,_primary_term会递
增1。
ElasticSearch索引操作
参考文档:https://www.elastic.co/guide/en/elasticsearch/reference/8.14/index.html
创建索引
格式: PUT /索引名称
索引命名规范:
以小写英文字母命名索引
不要使用驼峰命名法则
如出现多个单词的索引名称,以全小写 + 下划线分隔的方式:如test_index。
ES 索引创建成功之后,以下属性将不可修改
索引名称
主分片数量
字段类型
#创建索引
PUT /es_db
默认索引信息如下:
{
"es_db": {
"aliases": {},
"mappings": {},
"settings": {
"index": {
"routing": {
"allocation": {
"include": {
"_tier_preference": "data_content"
}
}
},
"number_of_shards": "1", #主分片数
"provided_name": "es_db",
"creation_date": "1721033661027",
"number_of_replicas": "1", #分片副本数
"uuid": "5plzCOkBRZSoIFUjVCTNjg",
"version": {
"created": "8505000"
}
}
}
}
}
设置 Settings
创建索引的时候可以指定 settings
PUT <index_name>
{
"settings": {}
}
举例:
比如创建索引时可以设置Settings中分片数和副本数
#创建索引es_db,指定其主分片数量为 3,每个主分片的副本数量为 2
PUT /es_db
{
"settings" : {
"number_of_shards" : 3,
"number_of_replicas" : 2
}
}
再比如创建索引时可以指定Settings中IK分词器作为默认分词器
PUT /es_db
{
"settings" : {
"index" : {
"analysis.analyzer.default.type": "ik_max_word"
}
}
}
静态索引设置
index.number_of_shards:索引的主分片的个数,默认为 1,此设置只能在创建索引时设置
动态索引设置
可以使用 _setting API 在实时修改的配置项。
index.number_of_replicas:每个主分片的副本数。默认为 1,允许配置为 0。
index.refresh_interval:数据写入磁盘需要时间,这个是设置刷新操作频率的,默认为1s. 可以设置 -1 为禁用刷新。
index.max_result_window:from + size 搜索此索引 的最大值(返回条数),默认为 10000。
使用 _setting 只能修改允许动态修改的配置项
#修改索引配置,把每个主分片的副本数量修改为 1
PUT /es_db/_settings
{
"index" : {
"number_of_replicas" : 1
}
}
设置 Mapping
ES 中的 mapping 有点类似与关系数据库中表结构的概念,在 MySQL 中,表结构里包含了字段名称,字段的类型还有索引信息等。在 Mapping 里也包含了一些属性,比如字段名称、类型、字段使用的分词器、是否评分、是否创建索引等属性,并且在 ES 中一个字段可以有多个类型。ES中Mapping可以分为动态映射和静态映射。
#查看完整的索引 mapping
GET /<index_name>/_mappings
#查看索引中指定字段的 mapping
GET /<index_name>/_mappings/field/<field_name
mapping 的使用禁忌
ES 没有隐式类型转换
ES 不支持类型修改
生产环境尽可能的避免使用 动态映射(dynamic mapping)
动态映射
在关系数据库中,需要事先创建数据库,然后在该数据库下创建数据表,并创建表字段、类型、长度、主键等,最后才能基于表插入数据。
而Elasticsearch中不需要定义Mapping映射,在文档写入Elasticsearch时,会根据文档字段自动识别类型,这种机制称之为动态映射。
示例
#创建文档(ES根据数据类型, 会自动创建映射)
PUT /user/_doc/1
{
"name":"mm",
"age":32,
"address":"河北"
}
#获取文档映射
GET /user/_mapping
返回信息
{
"user": {
"mappings": {
"properties": {
"address": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"age": {
"type": "long"
},
"name": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
}
}
}
}
}
静态映射
静态映射也叫做显式映射,即:在索引文档写入之前,人为创建索引并且指定索引中每个字段类型、分词器等参数。
实例
# 创建索引并且指定索引中每个字段类型、分词器、分片与分片备份等参数
PUT /user
{
"settings": {
"number_of_shards": "3",
"number_of_replicas": "2",
"index" : {
"analysis.analyzer.default.type": "ik_max_word"
}
},
"mappings": {
"properties": {
"name": {
"type": "keyword"
},
"age": {
"type": "long"
},
"address": {
"type": "text"
}
}
}
}
返回信息
{
"user": {
"aliases": {},
"mappings": {
"properties": {
"address": {
"type": "text"
},
"age": {
"type": "long"
},
"name": {
"type": "keyword"
}
}
},
"settings": {
"index": {
"routing": {
"allocation": {
"include": {
"_tier_preference": "data_content"
}
}
},
"number_of_shards": "3",
"provided_name": "user",
"creation_date": "1721041794614",
"analysis": {
"analyzer": {
"default": {
"type": "ik_max_word"
}
}
},
"number_of_replicas": "2",
"uuid": "fU9JVWEOTweggyi2Hkkmqg",
"version": {
"created": "8505000"
}
}
}
}
}
常用Mapping参数配置
参数名称 | 释义 |
---|---|
analyzer | 指定分析器,只有 text 类型字段支持。 |
copy_to | 该参数允许将多个字段的值复制到组字段中,然后可以将其作为单个字段进行查询 |
dynamic | 是否可以动态添加新字段,支持以下四个选项:true:(默认)允许动态映射。false:忽略新字段。这些字段不会被索引或搜索,但仍会出现在_source返回的命中字段中。这些字段不会添加到映射中,必须显式添加新字段。runtime:新字段作为运行时字段添加到索引中,这些字段没有索引,是_source在查询时加载的。strict:如果检测到新字段,则会抛出异常并拒绝文档。必须将新字段显式添加到映射中。 |
doc_values | 为了提升排序和聚合效率,默认true,如果确定不需要对字段进行排序或聚合,也不需要通过脚本访问字段值,则可以禁用doc值以节省磁盘空间(不支持 text 和 annotated_text) |
eager_global_ordinals | 用于聚合的字段上,优化聚合性能。 |
enabled | 这是一个index和doc_value的总开关,如果enabled设置为false,则这个字段将会仅存在于_source中,其对应的index和doc_value都不会被创建。这意味着,该字段将不可以被搜索、排序或者聚合,但可以通过_source获取其原始值。 |
fielddata | 与doc_values功能类似,doc_values在磁盘操作,这个在内存操作,查询时内存数据结构,在首次用当前字段聚合、排序或者在脚本中使用时,需要字段为fielddata数据结构,并且创建倒排索引保存到堆中fields 给 field 创建多字段,用于不同目的(全文检索或者聚合分析排序) |
format | 用于格式化代码,如 “data”:{ “type”: “data”, “format”: “yyyy-MM-ddHH:mm:ss”} |
index | 是否对创建对当前字段创建倒排索引,默认true,如果不创建索引,该字段不会通过索引被搜索到,但是仍然会在 source 元数据中展示。 |
norms | 是否禁用评分(在filter和聚合字段上应该禁用) |
null_value | 为 null 值设置默认值 |
search_analyzer | 设置单独的查询时分析器 |
示例:
- analyzer用法
#创建索引,给address字段指定分析器
PUT /user
{
"mappings": {
"properties": {
"address": {
"type": "text",
"analyzer": "ik_max_word"
}
}
}
}
#获取mapping信息
GET /user/_mapping
#返回信息
{
"user": {
"mappings": {
"properties": {
"address": {
"type": "text",
"analyzer": "ik_max_word"
}
}
}
}
}
- copy_to用法
# 创建索引,copy_to可以把两个字段合并到all_text中
PUT /user
{
"mappings": {
"properties": {
"address": {
"type": "text",
"copy_to": "all_text",
"analyzer": "ik_max_word"
},
"name": {
"type": "keyword",
"copy_to": "all_text"
},
"all_text":{
"type": "text"
}
}
}
}
# 添加测试数据
put /user/_doc/1
{
"name": "fox",
"address": "邯郸"
}
#查询
GET /user/_search
{
"query": {
"match": {
"all_text": "fox邯郸"
}
}
}
#返回信息
{
"took": 9,
"timed_out": false,
"_shards": {
"total": 1,
"successful": 1,
"skipped": 0,
"failed": 0
},
"hits": {
"total": {
"value": 1,
"relation": "eq"
},
"max_score": 0.8630463,
"hits": [
{
"_index": "user",
"_id": "1",
"_score": 0.8630463,
"_source": {
"name": "fox",
"address": "邯郸"
}
}
]
}
}
查询索引
格式: GET /索引名称
#查询索引
GET /es_db
#es_db是否存在
HEAD /es_db
删除索引
格式: DELETE /索引名称
DELETE /es_db
使用ReIndex重建索引
具体方法:
- 如果要推倒现有的映射, 你得重新建立一个静态索引
- 然后把之前索引里的数据导入到新的索引里
- 删除原创建的索引
- 为新索引起个别名, 为原索引名
通过这几个步骤可以实现了索引的平滑过渡,并且是零停机
使用ReIndex重建索引
# 1. 重新建立一个静态索引
PUT /user2
{
"mappings": {
"properties": {
"name": {
"type": "text"
},
"address": {
"type": "text",
"analyzer": "ik_max_word"
}
}
}
}
# 2. 把之前索引里的数据导入到新的索引里
POST _reindex
{
"source": {
"index": "user"
},
"dest": {
"index": "user2"
}
}
# 3. 删除原创建的索引
DELETE /user
# 4. 为新索引起个别名, 为原索引名
PUT /user2/_alias/user
GET /user
ElasticSearch文档操作
示例数据
PUT /es_db
{
"settings": {
"index": {
"analysis.analyzer.default.type": "ik_max_word"
}
}
}
PUT /es_db/_doc/1
{
"name": "张三",
"sex": 1,
"age": 25,
"address": "广州天河公园",
"remark": "java developer"
}
PUT /es_db/_doc/2
{
"name": "李四",
"sex": 1,
"age": 28,
"address": "广州荔湾大厦",
"remark": "java assistant"
}
PUT /es_db/_doc/3
{
"name": "王五",
"sex": 0,
"age": 26,
"address": "广州白云山公园",
"remark": "php developer"
}
PUT /es_db/_doc/4
{
"name": "赵六",
"sex": 0,
"age": 22,
"address": "长沙橘子洲",
"remark": "python assistant"
}
PUT /es_db/_doc/5
{
"name": "张龙",
"sex": 0,
"age": 19,
"address": "长沙麓谷企业广场",
"remark": "java architect assistant"
}
PUT /es_db/_doc/6
{
"name": "赵虎",
"sex": 1,
"age": 32,
"address": "长沙麓谷兴工国际产业园",
"remark": "java architect"
}
索引文档
- 格式: [PUT | POST] /索引名称/[_doc | _create ]/id
# 创建文档,指定id
# 如果id不存在,创建新的文档,否则先删除现有文档,再创建新的文档,版本会增加
PUT /es_db/_doc/1
{
"name": "张三",
"sex": 1,
"age": 25,
"address": "广州天河公园",
"remark": "java developer"
}
#创建文档,ES生成id
POST /es_db/_doc
{
"name": "张三",
"sex": 1,
"age": 25,
"address": "广州天河公园",
"remark": "java developer"
}
#_create创建文档,指定id,如果id存在会失败
POST /es_db/_create/13
{
"name": "张三",
"sex": 1,
"age": 25,
"address": "广州天河公园",
"remark": "java developer"
}
注意:POST和PUT都能起到创建/更新的作用,PUT需要对一个具体的资源进行操作也就是要确定id才能进行更新/创建,而POST是可以针对整个资源集合进行操作的,如果不写id就由ES生成一个唯一id进行创建新文档,如果填了id那就针对这个id的文档进行创建/更新。
create: 如果ID已经存在,会失败
查询文档
- 根据id查询文档,格式: GET /索引名称/_doc/id
GET /es_db/_doc/1
- 条件查询 _search,格式: /索引名称/_search
查询前10条文档 , 默认from为0,size为10
GET /es_db/_search
ES Search API提供了两种条件查询搜索方式:
- REST风格的请求URI,直接将参数带过去
- 封装到request body中,这种方式可以定义更加易读的JSON格式
URI Query
#通过URI搜索,使用“q”指定查询字符串,“qery string syntax” KV键值对
#条件查询, 如要查询age等于28岁的 _searc?q=*:***
GET /es_db/_search?q=age:28
#范围查询, 如要查询age在25至26岁之间的_search?q=***[** TO **] 注意: TO必须为大写
GET /es_db/_search?q=age[25 TO 26]
#查询年龄小于等于28岁的 :<=
GET /es_db/_search?q=age:<=28
#查询年龄大于28前的 :>
GET /es_db/_doc/_search?q=age:>28
#分页查询 from=*&size=*
GET /es_db/_search?q=age[25 TO26]&from=0&size=1
#对查询结果只输出某些字段 _source=字,字段
GET /es_db/_search?_source=nam,age
#对查询结果排序 sort=字段:desc/asc
GET /es_db/_search?sort=age:desc
DSL Query
- DSL(Domain Specific Language领域专用语言)查询是使用Elasticsearch的查询语言来构建查询的方式。
# match 匹配查询,会对查询文本分词后匹配
GET /es_db/_search
{
"query": {
"match": {
"address": "广州白云"
}
}
}
# term 词项查询,属于精确查询,不会对文本分词
# 思考:能否查到文档?
GET /es_db/_search
{
"query": {
"term": {
"address": "广州白云"
}
}
}
修改文档
- 全量更新,整个json都会替换格式: [PUT | POST] /索引名称/_doc/id
如果文档存在,现有文档会被删除,新的文档会被索引
# 全量更新,替换整个json
PUT /es_db/_doc/1/
{
"name": "张三",
"sex": 1,
"age": 25
}
#查询文档
GET /es_db/_doc/1
- 使用_update部分更新,格式: POST /索引名称/_update/id
update不会删除原来的文档,而是实现真正的数据更新
# 部分更新:在原有文档上更新
# Update -文档必须已经存在,更新只会对相应字段做增量修改
POST /es_db/_update/1
{
"doc": {
"age": 28
}
}
#查询文档
GET /es_db/_doc/1
- 使用 _update_by_query 更新文档
通过查询条件来批量更新文档
POST /es_db/_update_by_query
{
"query": {
"match": {
"address": "广州"
}
},
"script": {
"source": "ctx._source.age = 666"
}
}
#查询文档
GET /es_db/_doc/1
并发场景下修改文档
- _seq_no和_primary_term是对_version的优化,7.X版本以后的ES默认使用这种方式控制版本,所以当在高并发环境下使用乐观锁机制修改文档时,要带上当前文档的_seq_no和_primary_term进行更新:
POST /es_db/_doc/2?if_seq_no=37&if_primary_term=1
{
"name": "李四xxx"
}
如果版本号不对,会抛出版本冲突异常,如下:
{
"error": {
"root_cause": [
{
"type": "version_conflict_engine_exception",
"reason": "[2]: version conflict, required seqNo [37], primary term [1]. current document has seqNo [38] and primary term [1]",
"index_uuid": "qDj0iJ3DQXyDRuDQwEtZqg",
"shard": "0",
"index": "es_db"
}
],
"type": "version_conflict_engine_exception",
"reason": "[2]: version conflict, required seqNo [37], primary term [1]. current document has seqNo [38] and primary term [1]",
"index_uuid": "qDj0iJ3DQXyDRuDQwEtZqg",
"shard": "0",
"index": "es_db"
},
"status": 409
}
删除文档
- 格式: DELETE /索引名称/_doc/id
DELETE /es_db/_doc/1
ElasticSearch文档批量操作
批量操作可以减少网络连接所产生的开销,提升性能
- 支持在一次API调用中,对不同的索引进行操作
- 可以在URI中指定Index,也可以在请求的Payload中进行
- 操作中单条操作失败,并不会影响其他操作
- 返回结果包括了每一条操作执行的结果
批量写入
批量对文档进行写操作是通过_bulk的API来实现的
- 请求方式:POST
- 请求地址:_bulk
- 请求参数:通过_bulk操作文档,一般至少有两行参数(或偶数行参数)
- 第一行参数为指定操作的类型及操作的对象(index,type和id)
- 第二行参数才是操作的数据
参数类似于:
{"actionName":{"_index":"indexName", "_type":"typeName","_id":"id"}}
{"field1":"value1", "field2":"value2"}
actionName:表示操作类型,主要有create,index,delete和update
批量创建文档create
#批量创建, 7.x版本需要指定 "_type":"_doc"才行
POST _bulk
{"create":{"_index":"article","_id":5}}
{"id":3,"title":"河北","content":"河北666","tags":["java", "面向对象"],"create_time":1554015482530}
{"create":{"_index":"article","_id":6}}
{"id":4,"title":"邯郸","content":"邯郸NB","tags":["java", "面向对象"],"create_time":1554015482530}
#查询
GET /article/_mget
{
"ids":["5","6"]
}
普通创建与全量替换index
POST _bulk
{"index":{"_index":"article", "_id":7}}
{"id":3,"title":"图灵徐庶老师","content":"图灵学院徐庶老师666","tags":["java", "面向对象"],"create_time":1554015482530}
{"index":{"_index":"article", "_id":8}}
{"id":4,"title":"图灵诸葛老师","content":"图灵学院诸葛老师NB","tags":["java", "面向对象"],"create_time":1554015482530}
#查询
GET /article/_mget
{
"ids":["7","8"]
}
- 如果原文档不存在,则是创建
- 如果原文档存在,则是替换(全量修改原文档)
批量删除delete
POST _bulk
{"delete":{"_index":"article","_id":7}}
{"delete":{"_index":"article","_id":8}}
批量修改update
POST _bulk
{"update":{"_index":"article","_id":3}}
{"doc":{"title":"6666666666666"}}
{"update":{"_index":"article","_id":4}}
{"doc":{"create_time":1554018421008}}
组合使用
POST _bulk
{"create":{"_index":"article","_id":7}}
{"id":3,"title":"邯郸666","content":"6666666666","tags":["java","面向对象"],"create_time":1554015482530}
{"delete":{"_index":"article","_id":7}}
{"update":{"_index":"article","_id":4}}
{"doc":{"create_time":1554018421008}}
批量读取
es的批量查询可以使用mget和msearch两种。其中mget是需要我们知道它的id,可以指定不同的index,也可以指定返回值source。msearch可以通过字段查询来进行一个批量的查找。
- _mget
#可以通过ID批量获取不同index和type的数据
GET _mget
{
"docs": [
{
"_index": "es_db",
"_id": 2
},
{
"_index": "article",
"_id": 4
}
]
}
#可以通过ID批量获取es_db的数据
GET /es_db/_mget
{
"docs": [
{
"_id": 2
},
{
"_id": 4
}
]
}
#简化后
GET /es_db/_mget
{
"ids": [
"2",
"4"
]
}
- _msearch
GET /_msearch
{"index":"es_db"}
{"query":{"match_all":{}},"from":0,"size":2}
{"index":"article"}
{"query":{"match_all":{}}}
#index也可以在url中指定
GET /es_db/_msearch
{}
{"query":{"match_all":{}},"from":0,"size":2}
{"index":"article"}
{"query":{"match_all":{}}}
在_msearch中,请求格式和bulk类似。
查询一条数据需要两个对象,第一个设置index。
第二个设置查询语句,语法和search相同。
如果只是查询一个index,我们可以在url中带上index,这样,如果查该index可以直接用空对象表示。