Elastic Stack 8 快速上手
1. 创建资源
本实验支持实验资源体验、开通免费试用、个人账户资源三种实验资源方式。
在实验开始前,请您选择其中一种实验资源,单击确认开启实验。
- 如果您选择的是实验资源体验,资源创建过程需要3~5分钟(视资源不同开通时间有所差异,ACK等资源开通时间较长)。完成实验资源的创建后,在实验室页面左侧导航栏中,单击云产品资源列表,可查看本次实验资源相关信息(例如子用户名称、子用户密码、AK ID、AK Secret、资源中的项目名称等)。
说明:实验环境一旦开始创建则进入计时阶段,建议学员先基本了解实验具体的步骤、目的,真正开始做实验时再进行创建。
- 如果您选择的是开通免费试用,下方卡片会展示本实验支持的试用规格,可以选择你要试用的云产品资源进行开通。您在实验过程中,可以随时用右下角icon唤起试用卡片。
说明:试用云产品开通在您的个人账号下,并占用您的试用权益。如试用超出免费试用额度,可能会产生一定费用。
阿里云支持试用的产品列表、权益及具体规则说明请参考开发者试用中心。
2. 部署 Elasticsearch
本小节将指导您如何部署一个单节点的 Elasticsearch 集群。
出于安全原因,Elasticsearch 默认不允许使用 root 用户启动。执行以下命令,创建 elastic 用户,设置密码为 elastic123 ,并切换到该用户。
useradd -s /bin/bash -m elastic echo "elastic:elastic123" | chpasswd su - elastic
下载和解压 Elasticsearch 安装文件。
wget https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-8.2.3-linux-x86_64.tar.gz wget https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-8.2.3-linux-x86_64.tar.gz.sha512 shasum -a 512 -c elasticsearch-8.2.3-linux-x86_64.tar.gz.sha512 tar -xzf elasticsearch-8.2.3-linux-x86_64.tar.gz cd elasticsearch-8.2.3/
Elasticsearch 目录结构如下所示。
执行如下命令,启动 Elasticsearch。
./bin/elasticsearch
启动成功后,会输出以下内容,保存好以下两个内容:
elastic 用户密码。
Kibana 注册 token。
在启动过程中 Elasticsearch 需要连接公网获取 IP 地址地理位置的相关信息,由于网络原因可能导致数据下载失败而产生以下报错,该报错对 Elasticsearch 运行没有影响,用户可以忽略。
浏览器输入 https://<ESC 公网 IP>:9200 访问 Elasticsearch,注意本场景中提供了两台 ECS 实例,请填写正确的 ECS 公网 IP 地址。用户名:elastic,密码:VMtfK-YaNOptoBDnNaRY(根据上面输出填写)。用户认证通过后,会显示以下内容:
- name: 节点名,默认使用主机名作为节点名。
- cluster_name: 集群名,默认的集群名是 elasticsearch,同一个集群中的所有节点的 cluster.name 要保持一致。
- cluster_uuid: 集群的唯一标识符。
- version: 版本信息。
至此我们已经成功部署了一个单节点的 Elasticsearch 集群。
3. 部署 Kibana
本小节将指导您如何部署 Kibana 并连接到 Elasticsearch。
打开一个新的终端, 切换到 elastic 用户。
su - elastic
执行如下命令,下载和解压 Kibana 安装文件。
curl -O https://artifacts.elastic.co/downloads/kibana/kibana-8.2.3-linux-x86_64.tar.gz curl https://artifacts.elastic.co/downloads/kibana/kibana-8.2.3-linux-x86_64.tar.gz.sha512 | shasum -a 512 -c - tar -xzf kibana-8.2.3-linux-x86_64.tar.gz cd kibana-8.2.3/
Kibana 目录结构如下所示:
编辑 config/kibana.yml 配置文件,修改以下内容,允许 Kibana 服务监听本机所有 IP 地址。
server.host: "0.0.0.0"
执行如下命令,启动 Kibana。
./bin/kibana
启动成功后,会输出以下内容。
浏览器输入 http://<ESC 公网 IP>:5601/?code=321491 访问 Kibana 界面,注意本场景中提供了两台 ECS 实例,请填写正确的 ECS 公网 IP 地址。这里将 code=321491 修改为上图终端输出的内容。在 Enrollment token 输入框中填入第 2 小节启动 Elasticsearch 后输出的 Kibana 注册 token,然后点击 Configure Elastic。
说明:使用该公网IP为当前ECS服务器弹性IP,可根据实例ID在云产品资源列表中查找。
接着 Kibana 会自动完成注册设置。
Kibana 注册成功后会出现登录界面,输入 elastic 用户名密码登录 Elasticsearch。
在注册成功后,在 config/kibana.yml 配置文件中会自动添加 Elasticsearch 相关的连接信息和 SSL 证书,用户无需关注。
4. 往集群中注册新节点
本小节将指导您如何往 Elasticsearch 集群中注册新节点。
首先在第一个终端,键盘按 Ctrl + C 停止在前台运行的 Elasticsearch 进程。
4.1 修改第一个节点配置
默认情况下,Elasticsearch transport 服务的 9300 端口只在 127.0.0.1 上监听。如果要想和其他节点进行交互,我们需要更改当前节点的 config/elasticsearch.yml 文件,修改内容如下。
transport.host: 0.0.0.0
当允许 transport 服务在非环回地址监听后,Elasticsearch 将处于生产模式,在启动之前需要进行一些引导检查,我们需要修改一些系统参数确保能够通过检查。
在当前 elastic 用户下执行 exit 命令退回 root 用户,在 root 用户修改以下系统参数。
增加进程可使用的最大内存映射区域数,编辑 /etc/sysctl.conf 文件,添加以下内容。
vm.max_map_count=262144
执行以下命令立即生效:
sysctl -p
增加进程可使用的最大文件描述符数量,编辑 /etc/security/limits.conf 文件,添加以下内容。
elastic - nofile 65535
执行以下命令立即生效:
ulimit -n 65535
切换到 elastic 用户。
su - elastic
重新启动 Elasticsearch,-d 参数表示在后台运行 Elasticsearch。
cd elasticsearch-8.2.3 ./bin/elasticsearch -d
执行 tail -f logs/elasticsearch.log 命令可以查看 Elasticsearch 的启动日志。
确认 Elasticsearch 启动成功以后,执行以下命令,生成新的注册 token,用于注册新节点到 Elasticsearch 集群。
bin/elasticsearch-create-enrollment-token -s node # 返回的 token eyJ2ZXIiOiI4LjIuMyIsImFkciI6WyIxMC4yMC42MS4xNzk6OTIwMCJdLCJmZ3IiOiJiYTM3YjVmMTU4YTc4MmFiNWQ3N2M0YWI1ODFiMjc1ZjMxMTYxZDcxOWQ2YTA2NTU1NGU0MjBjYzNlZTc3YzMwIiwia2V5IjoiY1A3WGxJRUJFLU9sOTh0Qm5Deno6UXZTWEdOZ3BTYktaWXdic19pTS1fUSJ9
4.2 注册新节点
接下来配置新节点,打开一个新的终端, 执行 ssh root@<ESC 公网 IP> 命令登录到另一个节点上。
在 root 用户下修改以下系统参数。
增加进程可使用的最大内存映射区域数,编辑 /etc/sysctl.conf 文件,添加以下内容。
vm.max_map_count=262144
执行以下命令立即生效:
sysctl -p
增加进程可使用的最大文件描述符数量,编辑 /etc/security/limits.conf 文件,添加以下内容。
elastic - nofile 65535
执行以下命令立即生效:
ulimit -n 65535
执行以下命令,创建 elastic 用户,设置密码为 elastic123 ,并切换到该用户。
useradd -s /bin/bash -m elastic echo "elastic:elastic123" | chpasswd su - elastic
下载和解压 Elasticsearch 安装文件。
wget https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-8.2.3-linux-x86_64.tar.gz wget https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-8.2.3-linux-x86_64.tar.gz.sha512 shasum -a 512 -c elasticsearch-8.2.3-linux-x86_64.tar.gz.sha512 tar -xzf elasticsearch-8.2.3-linux-x86_64.tar.gz cd elasticsearch-8.2.3/
在当前节点的 config/elasticsearch.yml 文件中,添加以下内容。
transport.host: 0.0.0.0
执行以下命令,启动新节点,并往 Elasticsearch 集群进行注册,在 --enrollment-token 参数后面跟上前面生成的 token。
bin/elasticsearch --enrollment-token eyJ2ZXIiOiI4LjIuMyIsImFkciI6WyIxMC4yMC42MS4xNzk6OTIwMCJdLCJmZ3IiOiJiYTM3YjVmMTU4YTc4MmFiNWQ3N2M0YWI1ODFiMjc1ZjMxMTYxZDcxOWQ2YTA2NTU1NGU0MjBjYzNlZTc3YzMwIiwia2V5IjoiY1A3WGxJRUJFLU9sOTh0Qm5Deno6UXZTWEdOZ3BTYktaWXdic19pTS1fUSJ9
在 Kibana 的 Dev Tools 中执行以下命令,查询集群中的节点,可以看到第二个节点已经成功加入集群。默认使用主机名作为节点名。
GET _cat/nodes?v # 返回结果 ip heap.percent ram.percent cpu load_1m load_5m load_15m node.role master name 10.20.61.179 21 95 10 0.14 0.27 0.20 cdfhilmrstw * elastic-demo 10.20.61.180 6 85 2 0.74 0.28 0.09 cdfhilmrstw - node-2
在注册成功后,在 config/elasticsearch.yml 配置文件中会自动添加 Elasticsearch 相关的配置信息,用户无需关注。
节点的所需 SSL 证书也会自动拷贝到新节点上。
5. 写入文档
本小节将指导您如何将数据写入 Elasticsearch。
Elasticsearch 具有 Schemaless的能力,能够自动检测 JSON 源数据的类型并为数据中的新字段创建新的映射。正是由于这种灵活的特性,使得我们可以不用事先为索引定义 mapping(映射)就可以往索引中写入数据。
点击 Management -> Dev Tools -> Console,打开 Kibana Console 界面。在左侧面板中执行以下命令,往 books 索引中写入一条文档。Elasticsearch 会自动帮我们创建 books 索引。
# 写入单条文档 POST books/_doc { "title": "Programming Kubernetes", "isbn": "9781617297618", "pageCount": 345, "category": "Cloud Native" }
在右侧面板会返回响应结果,可以看到文档已经被成功创建了,doc id 是随机生成的 YTeEhYEBGHa5rBjoiL-Q。
如果想要在写入文档时指定 doc id,可以使用 PUT <index-name>/_doc/<doc-id>。如果指定 id 的文档不存在,则直接创建新的文档;否则先删除现有文档,再创建新的文档,同时文档版本号(_version, _seq_no)会增加。
# 设置写入的 doc id 为 1 PUT books/_doc/1 { "title": "Istio in Action", "isbn": "9781617295829", "pageCount": 480, "category": "Cloud Native" }
使用 bulk API 可以在单次请求中批量写入多条文档,这样能极大减少写入请求的网络开销。
POST books/_bulk {"index": {"_id": 2}} {"title":"Elasticsearch in Action","isbn":"9781617299858","pageCount":475,"category": "Search"} {"index": {"_id": 3}} {"title":"Relevant Search","isbn":"9781617292774","pageCount":360,"category": "Search"} {"index": {"_id": 4}} {"title":"Modern Java in Action","isbn":"9781617293566","pageCount":592,"category": "Java"}
6. 查询文档
本小节将指导您如何查询 Elasticsearch 中的数据。
使用 GET <index-name>/_doc/<doc-id> 可以获取索引中指定 doc id 的文档。执行以下命令,获取 doc id 为 1 的文档。
GET books/_doc/1
响应结果如下,除了返回文档的内容以外,还包含了一些元数据字段:
- _index: 文档所属的索引名。
- _id: 文档 id。
- _version: 文档的版本号,_version 属于单个文档,修改删除操作会在当前文档的 _version 基础上自增 1。
- _seq_no: 索引的版本号,_seq_no 属于整个索引,修改删除操作会整个索引的 _seq_no 基础上自增 1。
- _primary_term: 每当主分片发生重新分配时(例如故障转移,主分片选举),_primary_term 会自增 1。该参数主要是为了防止主分片重新分配到新节点后,原主分片所在的节点恢复后覆盖新节点上主分片写入的数据。
- _source: 文档原始的 JSON 数据。
{ "_index" : "books", // 索引名 "_id" : "1", "_version" : 2, "_seq_no" : 3, "_primary_term" : 1, "found" : true, "_source" : { // 文档内容 "title" : "Istio in Action", "isbn" : "9781617295829", "pageCount" : 480, "category" : "Cloud Native" } }
查询 books 索引,不指定任何过滤条件,默认最多返回 10 条文档。
GET books/_search
返回了当前索引中的全部 4 条文档。
- took: 搜索请求总共耗费了多少毫秒。
- timed_out: 查询是否超时。如果超时,Elasticsearch 将会返回超时前已经成功从每个分片获取的结果。
- _shards: 查询的分片计数。
- total: 需要查询的分片总数,包括未分配的分片。
- successful: 执行请求成功的分片数。
- skipped: 跳过的分片数,Elasticsearch 在查询前先会通过轻量级的检查判断分片上是否含符合条件的文档,通常发生在查询中带有 range 过滤条件的情况下。
- failed: 执行请求失败的分片数。
- hits: 返回的文档和元数据。
- total: 匹配到的文档总数。
- max_score: 返回最高的文档分数。
- hits: 文档对象数组。
{ "took" : 0, "timed_out" : false, "_shards" : { "total" : 1, "successful" : 1, "skipped" : 0, "failed" : 0 }, "hits" : { "total" : { "value" : 5, "relation" : "eq" }, "max_score" : 1.0, "hits" : [ { "_index" : "books", "_id" : "1", "_score" : 1.0, "_source" : { "title" : "Istio in Action", "isbn" : "9781617295829", "pageCount" : 480, "category" : "Cloud Native" } }, { "_index" : "books", "_id" : "2", "_score" : 1.0, "_source" : { "title" : "Elasticsearch in Action", "isbn" : "9781617299858", "pageCount" : 475, "category" : "Search" } }, { "_index" : "books", "_id" : "3", "_score" : 1.0, "_source" : { "title" : "Relevant Search", "isbn" : "9781617292774", "pageCount" : 360, "category" : "Search" } }, { "_index" : "books", "_id" : "4", "_score" : 1.0, "_source" : { "title" : "Modern Java in Action", "isbn" : "9781617293566", "pageCount" : 592, "category" : "Java" } }, { "_index" : "books", "_id" : "ZDfNjoEBGHa5rBjoYL_5", "_score" : 1.0, "_source" : { "title" : "Programming Kubernetes", "isbn" : "9781617297618", "pageCount" : 345, "category" : "Cloud Native" } } ] } }
Elasticsearch 在不设置字段映射的情况下, 默认会将字符串类型的字段设置成 text 类型,并加上一个 keyword 子字段,子字段的类型是 keyword。text 类型的字段默认使用 standard 分析器解析文本,按照 Unicode 文本分割算法对文本进行切分,去除标点符号,并将切分后的单词转换为小写,主要用于全文搜索;keyword 类型的字段使用 keyword 分析器解析文本,不对文本进行切分,保留原始的文本内容,用于精确匹配和聚合查询。
使用 match 查询 title 字段中含有 action 的文档,不区分大小写。
GET books/_search { "query": { "match": { "title": "action" } } }
这次只返回了 title 中含有 action 的 3 条文档。
{ "took" : 0, "timed_out" : false, "_shards" : { "total" : 1, "successful" : 1, "skipped" : 0, "failed" : 0 }, "hits" : { "total" : { "value" : 3, "relation" : "eq" }, "max_score" : 0.35667494, "hits" : [ { "_index" : "books", "_id" : "1", "_score" : 0.35667494, "_source" : { "title" : "Istio in Action", "isbn" : "9781617295829", "pageCount" : 480, "category" : "Cloud Native" } }, { "_index" : "books", "_id" : "2", "_score" : 0.35667494, "_source" : { "title" : "Elasticsearch in Action", "isbn" : "9781617299858", "pageCount" : 475, "category" : "Search" } }, { "_index" : "books", "_id" : "4", "_score" : 0.31387398, "_source" : { "title" : "Modern Java in Action", "isbn" : "9781617293566", "pageCount" : 592, "category" : "Java" } } ] } }
使用 term 查询精确匹配 category.keyword 字段值是 Cloud Native 的文档。上面介绍到 keyword 类型的字段保留原始文本,用于精确匹配,因此这里的查询区分大小写,如果查询 cloud native 将匹配不到文档。
GET books/_search { "query": { "term": { "category.keyword": { "value": "Cloud Native" } } } }
返回结果如下,匹配到了 2 条文档。
{ "took" : 0, "timed_out" : false, "_shards" : { "total" : 1, "successful" : 1, "skipped" : 0, "failed" : 0 }, "hits" : { "total" : { "value" : 2, "relation" : "eq" }, "max_score" : 0.87546873, "hits" : [ { "_index" : "books", "_id" : "1", "_score" : 0.87546873, "_source" : { "title" : "Istio in Action", "isbn" : "9781617295829", "pageCount" : 480, "category" : "Cloud Native" } }, { "_index" : "books", "_id" : "ZDfNjoEBGHa5rBjoYL_5", "_score" : 0.87546873, "_source" : { "title" : "Programming Kubernetes", "isbn" : "9781617297618", "pageCount" : 345, "category" : "Cloud Native" } } ] } }
如果我们想同时通过多个条件查询文档,可以使用 bool 组合查询。bool 查询接受以下 4 个参数:
- must: must 中的所有条件都要符合,计算相关性得分。
- must_not: 排除所有符合 must_not 条件的文档,不计算相关性得分。
- filter: 只过滤符合条件的文档,不计算相关性得分。
- should:
- 情况 1: bool 查询中只包含 should ,不包含 must 查询,此时文档必须满足至少一个条件,minimum_should_match 可以控制满足条件的个数或者百分比。
- 情况 2:bool 查询中同时包含 should 和 must 查询,此时文档不必满足 should 中的条件,但是如果满足条件,会增加相关性得分。
下面的查询表示同时满足 title 字段中含有 action 以及 category.keyword 字段值是 Cloud Native 两个条件的文档才算匹配。
GET books/_search { "query": { "bool": { "must": [ { "match": { "title": "action" } }, { "term": { "category.keyword": { "value": "Cloud Native" } } } ] } } }
返回结果如下,只有 1 条文档同时满足以上两个条件。
{ "took" : 1, "timed_out" : false, "_shards" : { "total" : 1, "successful" : 1, "skipped" : 0, "failed" : 0 }, "hits" : { "total" : { "value" : 1, "relation" : "eq" }, "max_score" : 1.3991625, "hits" : [ { "_index" : "books", "_id" : "1", "_score" : 1.3991625, "_source" : { "title" : "Istio in Action", "isbn" : "9781617295829", "pageCount" : 480, "category" : "Cloud Native" } } ] } }
Elasticsearch 除了文本搜索以外,还支持聚合查询,可以帮助我们对数据进行分析。例如我们可以通过以下命令查询 books 索引中每种类别书籍的数量。
GET books/_search { "size": 0, // 不返回文档,只返回聚合结果 "aggs": { // 聚合查询 "my_category_agg": { "terms": { "field": "category.keyword", "size": 10 } } } }
返回结果如下,可以看到 Cloud Native 类别的书籍有 2 个,Search 类别的书籍有 1 个, Java 类别的书籍有 1 个。
{ "took" : 1, "timed_out" : false, "_shards" : { "total" : 1, "successful" : 1, "skipped" : 0, "failed" : 0 }, "hits" : { "total" : { "value" : 5, "relation" : "eq" }, "max_score" : null, "hits" : [ ] }, "aggregations" : { "my_category_agg" : { "doc_count_error_upper_bound" : 0, "sum_other_doc_count" : 0, "buckets" : [ { "key" : "Cloud Native", "doc_count" : 2 }, { "key" : "Search", "doc_count" : 2 }, { "key" : "Java", "doc_count" : 1 } ] } } }
7. 更新文档
本小节将指导您如何更新 Elasticsearch 中的数据。
使用 PUT <index-name>/_doc/<doc-id> 这种语法的效果是覆盖数据,可以理解为先删除现有文档,再创建新的文档。如果该 doc id 的文档不存在,则创建一个新的文档。
PUT books/_doc/1 { "title": "Cloud Native Patterns", "isbn": "9781617294297", "category": "Cloud Native" }
查询 doc id 为 1 的这条文档。
GET books/_doc/1
返回结果如下,新的文档中只含有我们更新时指定 3 个字段,而原始文档中的 pageCount 字段消失了。
{ "_index" : "books", "_id" : "1", "_version" : 2, "_seq_no" : 5, "_primary_term" : 1, "found" : true, "_source" : { "title" : "Cloud Native Patterns", "isbn" : "9781617294297", "category" : "Cloud Native" } }
那么如何在不影响文档中其他字段的情况下对文档进行更新呢?可以使用 PUT <index-name>/_update/<doc-id> 语法,在请求体的 doc 参数中指定要更新的字段内容。
POST books/_update/1 { "doc": { // 指定要更新的字段内容 "pageCount": 400 } }
查询 doc id 为 1 的这条文档。
GET books/_doc/1
返回结果如下,pageCount 字段的值被设置为了 400,其他字段保持不变。
{ "_index" : "books", "_id" : "1", "_version" : 3, "_seq_no" : 6, "_primary_term" : 1, "found" : true, "_source" : { "title" : "Cloud Native Patterns", "isbn" : "9781617294297", "category" : "Cloud Native", "pageCount" : 400 } }
Elasticsearch 还支持通过脚本的方式更新字段,通过编写脚本可以实现更加复杂的处理逻辑。下面命令表示,当 pageCount 字段的值大于 200 时,将 pageCount 的值加 200,否则将 pageCount 的值设置为 99。
POST books/_update/1 { "script": { "source": """ if(ctx._source.pageCount > 200) { ctx._source.pageCount += 200; }else { ctx._source.pageCount = 99; } """ } }
返回结果如下,由于 pageCount 原本的值 400 大于 200,因此 pageCount 的值被更新为了 600(400+200)。
{ "_index" : "books", "_id" : "1", "_version" : 4, "_seq_no" : 7, "_primary_term" : 1, "found" : true, "_source" : { "title" : "Cloud Native Patterns", "isbn" : "9781617294297", "category" : "Cloud Native", "pageCount" : 600 } }
除了单条更新文档以外,我们还可以使用 Update By Query API 来根据查询条件批量更新文档。执行以下命令,为 pageCount 小于 400 的书籍添加一个新的字段 description,设置值为 quickStart。
POST books/_update_by_query { "query": { "range": { "pageCount": { "lte": 400 } } }, "script": { "source": "ctx._source.description = 'quickStart'" } }
不论是关系型数据库,还是 Elasticsearch,只要有数据更新,并发控制是永恒的话题。在 Elasticsearch 中,数据的更新基于版本进行乐观锁并发控制。在 Elasticsearch 6.7 版本之后推荐使用 _seq_no 和 _primary_term 两个参数进行并发控制。
先获取当前索引的 _seq_no 和 _primary_term 的值。
GET books/_doc/1
返回结果如下。
{ "_index" : "books", "_id" : "1", "_version" : 4, "_seq_no" : 9, "_primary_term" : 1, "found" : true, "_source" : { "pageCount" : 400, "isbn" : "9781617294297", "description" : "quickStart", "title" : "Cloud Native Patterns", "category" : "Cloud Native" } }
更新文档时指定 _seq_no 和 _primary_term。
PUT books/_doc/1?if_seq_no=9&if_primary_term=1 { "doc": { "pageCount": 500 } }
返回结果如下,提示更新成功。
{ "_index" : "books", "_id" : "1", "_version" : 5, "result" : "updated", "_shards" : { "total" : 2, "successful" : 1, "failed" : 0 }, "_seq_no" : 10, "_primary_term" : 1 }
如果指定的 _seq_no 和 _primary_term 不等于当前更新的文档中的值,那么更新将会失败。
PUT books/_doc/1?if_seq_no=9&if_primary_term=1 { "doc": { "pageCount": 500 } }
返回结果如下,提示版本冲突,当前文档 _seq_no 的值已经更新为 10 了,但是还使用老版本 _seq_no 的值 9 来更新。
{ "error" : { "root_cause" : [ { "type" : "version_conflict_engine_exception", "reason" : "[1]: version conflict, required seqNo [9], primary term [1]. current document has seqNo [10] and primary term [1]", "index_uuid" : "3bYmNTQpRyqOmcwUG1HvNg", "shard" : "0", "index" : "books" } ], "type" : "version_conflict_engine_exception", "reason" : "[1]: version conflict, required seqNo [9], primary term [1]. current document has seqNo [10] and primary term [1]", "index_uuid" : "3bYmNTQpRyqOmcwUG1HvNg", "shard" : "0", "index" : "books" }, "status" : 409 }
8. 删除文档
本小节将指导您如何删除 Elasticsearch 中的数据。
使用 DELETE <index-name>/_doc/<doc-id> 语法可以删除索引中指定 doc id 的文档。执行以下命令,删除 doc id 为 1 的文档。
DELETE books/_doc/1
除了单条更新文档以外,我们还可以使用 Delete By Query API 来根据查询条件批量删除文档。执行以下命令,删除所有 pageCount 小于等于 400 的文档。
POST books/_delete_by_query { "query": { "range": { "pageCount": { "lte": 400 } } } }
9. 索引设置
本小节将指导您如何设置 Elasticsearch 中的索引。
Elasticsearch 便捷的特性使得我们可以在不对索引做任何设置的情况下,便可以对索引进行读写操作。然而,默认设置有时候并不能满足我们的需求。
比如说,我们有一个 3 节点 Elasticsearch 集群,我们想在搜索数据时尽可能地利用多节点的优势,提高搜索的效率。那么我们可以在 settings 中通过 number_of_shards 参数修改索引的分片数。
再比如,默认情况下,数字类型的字段将被映射为 long 类型,pageCount 字段表示书籍的页数,数字并不会很大。那么我们可以在 mappings 中显式指定 pageCount 字段的类型为 integer,从而节省存储空间。
执行以下命令,创建一个新的索引 index-1,执行以下命令,设置索引分片数为 3,设置 pageCount 字段的类型为 integer。
PUT index-1 { "settings": { // 索引设置 "number_of_shards": 3 // 设置索引分片数,默认是 1 }, "mappings": { // 字段映射 "properties": { "pageCount": { "type": "integer" // 将字段类型设置为 integer,节省存储空间 } } } }
查看索引设置。
GET index-1
返回结果如下。
{ "index-1" : { "aliases" : { }, "mappings" : { "properties" : { "pageCount" : { "type" : "integer" // 字段类型为 integer } } }, "settings" : { "index" : { "routing" : { "allocation" : { "include" : { "_tier_preference" : "data_content" } } }, "number_of_shards" : "3", // 索引分片数 "provided_name" : "index-1", "creation_date" : "1655965811025", "number_of_replicas" : "1", "uuid" : "pP-M1Re2Qg2k2H6vztvkYg", "version" : { "created" : "8020399" } } } } }
10. 文本分析
本小节将指导您如何使用 Analyze API 测试分析器,以及如何自定义分析器。
在第 5 小节中,我们提到了 standard 和 keyword 两个分析器,这两个分析器属于 Elasticsearch 内置的分析器,我们可以直接拿来使用。有关 Elasticsearch 内置分析器的详情参见 Built-in analyzer reference
Elasticsearch 提供了 Analyze API 用于查看分词器是如何解析文本内容的,在为字段设置分析器之前可以使用 Analyze API 来测试分析器是否满足需求。
执行以下命令,使用默认的 standard 分析器分析文本。
POST _analyze { "analyzer": "standard", // standard 是默认使用的分析器,可以省略 "text": "Elasticsearch in Action." }
返回结果如下,文本被切分为 elasticsearch, in, action 3 个单词,去掉了末尾的句号,并且转换为小写,tokens 数组中的每个参数含义如下:
- token: 切分后的词项。
- start_offset: 词项在文本中的起始偏移。
- end_offset: 词项在文本中的结束偏移。
- type: 词项的类型。
- position: 分词的位置,第 1 个词项从 0 开始。
{ "tokens" : [ { "token" : "elasticsearch", "start_offset" : 0, "end_offset" : 13, "type" : "<ALPHANUM>", "position" : 0 }, { "token" : "in", "start_offset" : 14, "end_offset" : 16, "type" : "<ALPHANUM>", "position" : 1 }, { "token" : "action", "start_offset" : 17, "end_offset" : 23, "type" : "<ALPHANUM>", "position" : 2 } ] }
使用 whitespace 分析器分析相同的文本。
POST _analyze { "analyzer": "whitespace", "text": "Elasticsearch in Action." }
返回结果如下。可以发现 whitespace 分析器根据空格切分单词,不做小写转换,也不会去除标点符号。
{ "tokens" : [ { "token" : "Elasticsearch", "start_offset" : 0, "end_offset" : 13, "type" : "word", "position" : 0 }, { "token" : "in", "start_offset" : 14, "end_offset" : 16, "type" : "word", "position" : 1 }, { "token" : "Action.", "start_offset" : 17, "end_offset" : 24, "type" : "word", "position" : 2 } ] }
使用 keyword 分析器分析文本。
POST _analyze { "analyzer": "keyword", "text": "Elasticsearch in Action." }
返回结果如下,keyword 分析器不会对文本进行分词处理,原始文本被完整地保留为一个词项。
{ "tokens" : [ { "token" : "Elasticsearch in Action.", "start_offset" : 0, "end_offset" : 24, "type" : "word", "position" : 0 } ] }
如果我们想要对索引中已有字段的分析器进行测试,可以通过以下方式指定索引字段的分析器对文本进行分析。category.keyword 是 keyword 类型的字段,因此测试文本的时候使用的便是 keyword 分析器。
POST books/_analyze { "field": "category.keyword", "text": "Cloud Compute" }
返回结果如下。
{ "tokens" : [ { "token" : "Cloud Compute", "start_offset" : 0, "end_offset" : 13, "type" : "word", "position" : 0 } ] }
以上提到的 standard, whitespace, keyword 三种分析器都是 Elasticsearch 内置的分析器,我们也可以根据实际需求自定义分析器。
一个标准的 Analyzer(分析器)由以下 3 部分组成:
- Character filters(字符过滤器):对输入的文本字符进行第一步处理,例如去掉 HTML 标签(htlm_strip),或者将表情字符转换成英文单词(mapping)等。
- Tokenizer(分词器):按照指定的规则对文本进行分词处理,例如按照空格切分单词(whitespace)。分词器还负责标记每个单词的顺序、位置以及单词在原文本中开始和结束的偏移量。
- Token filters(分词过滤器):对切分后的单词进行处理,进而对分词结果进行归一化处理,例如字母的大小写转换(uppercase/lowercase),删除停用词(stop),标记同义词(synonym),提取词干(snowball)等等。
在分析器中,Tokenizer 是必须设置的,有且只有一个,Character filters 和 Token filters 是可选的,以数组的形式进行设置,按顺序执行。
如下所示,创建一个自定义分析器 my-analyzer,Character filters 使用 html_strip 字符过滤器剔除输入文本中 HTML 标签,tokenizer 使用 whitespace 分词器按照空格切分单词,Token filters 使用 uppercase 字符过滤器将切分后的每个单词转换为大写。最后在 mapping 中设置 content 字段使用我们自定义的分析器 my-analyzer。
PUT my-index { "settings": { "analysis": { "analyzer": { "my-analyzer": { "type": "custom", // 自定义分析器 "char_filter": [ // 字符串过滤器 "html_strip" // 去掉 HTML 标签 ], "tokenizer": "whitespace", // 分词器,按照空格切分单词 "filter": [ // 分词过滤器 "uppercase" // 将切分后的每个单词转换为大写 ] } } } }, "mappings": { "properties": { "content": { "type": "text", "analyzer": "my-analyzer" // 设置字段使用自定义分析器 } } } }
测试自定义分析器。
POST my-index/_analyze { "field": "content", "text": "<h1>Quick fox jumps</h1>" }
返回结果如下所示,输入的字符串 "<h1>Quick fox jumps" 分别经过了 Character filters(去掉 HTML 标签),Tokenizer(根据空格切分),Token filters(大写字母转换),最后将结果 ["QUICK", "FOX", "JUMPS"] 写入倒排索引中。
{ "tokens" : [ { "token" : "QUICK", "start_offset" : 4, "end_offset" : 9, "type" : "word", "position" : 0 }, { "token" : "FOX", "start_offset" : 10, "end_offset" : 13, "type" : "word", "position" : 1 }, { "token" : "JUMPS", "start_offset" : 14, "end_offset" : 19, "type" : "word", "position" : 2 } ] }
11. 新建章节
新建内容
实验链接:https://developer.aliyun.com/adc/scenario/8b6ea356c8e446d4aefe7eb014ca5cd5