文章目录
- 前言
- 数据类型种类
- ES解决什么问题
- ELK Stack
- ES是什么
- 数据格式
- 正排(正向)索引
- 倒排索引
- 创建索引
- 索引查询
- 索引删除
- 创建文档(添加数据)
- 自定义ID
- 简单查询
- 类似于主键查询
- 查询所有数据
- 修改数据
- 全量修改
- 局部修改
- 删除数据
- 条件查询
- 请求路径(不推荐)
- 请求体
- 全查询
- 分页查询
- 指定查询字段
- 查询结果排序
- 多条件组合查询
- AND关系 使用must
- OR关系 使用should
- 范围查询 使用filter
- 查询-全文检索 match
- 问题:为什么一个米查询出来了小米的结果
- 问题:这个查询条件为什么也能查询出来数据
- 查询-完全匹配 match_phrase
- 查询-高亮查询
- 查询-聚合查询
- 带原始数据
- 不需要查询出原始数据
- 求平均数据
- 映射
- 集群
- 进阶
- 索引(index)
- 文档(document)
- 字段(Field)
- 映射(mapping)
- 分片(shards)
- 副本(Replicas)
- 分配(Allocation)
- 分片和副本设置
- ES数据写入流程
- ES数据读流程
- ES数据更新流程
- ES读取/插入多个文档
- 分片原理
- 倒排索引
- 举例
- 分词器
- 词条
- 词典
- 倒排表
- 文档搜索
- 索引写入
- 文档刷新
- 文档刷写
- 文档合并
- 文档分析
- 1.字符过滤器
- 2.分词器
- 3.Token过滤器
- 内置分析器
- 标准分析器
- 简单分析器
- 空格分析器
- 语言分析器
- 分析器的使用场景
- 测试分词器
- 标准分词器
- 请求
- 响应
- 指定分词器
- 请求
- 响应
- 请求
- 响应
- 分词器扩展词汇
- 请求
- 响应
- 添加扩展词汇
- 请求
- 响应
- 自定义分词器
- 请求
- 响应
- 文档控制
- 悲观锁控制
- 乐观锁控制
- 使用示例
- kibana使用
- 下载kibana
- 解压kibana
- 添加配置kibana.yml
- 启动kibana
- 浏览器访问
- 点击页面的控制台,进入命令行
- Elasticsearch框架集成
- 集成SpringData
- 集成spark
- 集成flink
- 优化
- 硬件选择
- 分片策略
- 路由选择
- 写入速度优化
- 内存设置
- 代码操作ES示例地址
- 最后
前言
使用Java代码操作使用ES,跳到文末代码操作ES示例地址即可
数据类型种类
-
结构化
- 可以用二维表表示的数据 mysql
-
非结构化
- key value redis
-
半结构化
- 结构和内容混在一起 XML文档
- 一般来说也是要存储到redis hbase 等 非结构化中,但是不方便查询
ES解决什么问题
如何查询结构化数据和查询非结构化数据,并且准确查询
ELK Stack
ES Kibana(数据展示) Beats Logstash
ES是什么
开源的高扩展的分布式全文引擎搜索
数据格式
存储一条数据等于存储了一个文档
7版本已经删除了Type的概念
正排(正向)索引
id content
1001 my name is zhangsan
通过ID查询很快,但是通过content查询的话得模糊查询
而且查询的大小写也会影响到结果的准确性
倒排索引
keywords id
name 1001,1002
zhang 1001
通过内容找到ID,再通过ID找到内容
创建索引
创建索引等于创建数据库
PUT http://localhost:9200/{index}
{
"acknowledged": **true**,
"shards_acknowledged": **true**,
"index": "shopping"
}
索引查询
GET http://localhost:9200/{index}
查询所有
GET http://localhost:9200/_cat/indices
索引删除
DELETE http://localhost:9200/{index}
创建文档(添加数据)
POST http://localhost:9200/shopping/_doc
{
"_index": "shopping",
"_id": "JOv_l5ABLvXlzB8VWAXZ", // id是唯一的,可以认为是主键
"_version": 1,
"result": "created",
"_shards": {
"total": 2,
"successful": 1,
"failed": 0
},
"_seq_no": 0,
"_primary_term": 1
}
自定义ID
POST http://localhost:9200/shopping/_doc/1001
{
"_index": "shopping",
"_id": "1001",
"_version": 1,
"result": "created",
"_shards": {
"total": 2,
"successful": 1,
"failed": 0
},
"_seq_no": 4,
"_primary_term": 1
}
简单查询
类似于主键查询
GET http://localhost:9200/shopping/_doc/1001
查询所有数据
GET http://localhost:9200/shopping/_search
{
"took": 323, // 耗费的时间
"timed_out": false, // 是否超时
"_shards": {
"total": 1,
"successful": 1,
"skipped": 0,
"failed": 0
},
"hits": {
"total": {
"value": 5,
"relation": "eq"
},
"max_score": 1.0,
"hits": [
{
"_index": "shopping",
"_id": "JOv_l5ABLvXlzB8VWAXZ",
"_score": 1.0,
"_source": {
"title": "小米手机",
"category": "小米",
"images": "http://www.gulixueyuan.com/xm.jpg",
"price": 3999.00
}
},
{
"_index": "shopping",
"_id": "JesAmJABLvXlzB8V4AV4",
"_score": 1.0,
"_source": {
"title": "小米手机",
"category": "小米",
"images": "http://www.gulixueyuan.com/xm.jpg",
"price": 3999.00
}
},
{
"_index": "shopping",
"_id": "JusAmJABLvXlzB8V6QV8",
"_score": 1.0,
"_source": {
"title": "小米手机",
"category": "小米",
"images": "http://www.gulixueyuan.com/xm.jpg",
"price": 3999.00
}
},
{
"_index": "shopping",
"_id": "1001",
"_score": 1.0,
"_source": {
"title": "小米手机",
"category": "小米",
"images": "http://www.gulixueyuan.com/xm.jpg",
"price": 3999.00
}
},
{
"_index": "shopping",
"_id": "J-sCmJABLvXlzB8VTQUS",
"_score": 1.0,
"_source": {
"title": "小米手机",
"category": "小米",
"images": "http://www.gulixueyuan.com/xm.jpg",
"price": 3999.00
}
}
]
}
}
修改数据
全量修改
POST(PUT 因为结果是幂等性的,所以PUT也行) http://localhost:9200/shopping/_doc/1001
{
"_index": "shopping",
"_id": "1001",
"_version": 8,
"result": "updated",
"_shards": {
"total": 2,
"successful": 1,
"failed": 0
},
"_seq_no": 11,
"_primary_term": 1
}
局部修改
POST(PUT 因为结果不是幂等性的,所以PUT不行) http://localhost:9200/shopping/_doc/1001
{
"doc":{
"title":"华为手机"
}
}
{
"_index": "shopping",
"_id": "1001",
"_version": 9,
"result": "updated",
"_shards": {
"total": 2,
"successful": 1,
"failed": 0
},
"_seq_no": 12,
"_primary_term": 1
}
删除数据
DELETE http://localhost:9200/shopping/_doc/1001
{
"_index": "shopping",
"_id": "1001",
"_version": 10,
"result": "deleted",
"_shards": {
"total": 2,
"successful": 1,
"failed": 0
},
"_seq_no": 13,
"_primary_term": 1
}
条件查询
请求路径(不推荐)
GET http://localhost:9200/shopping/_search?q=category:小米
请求体
GET http://localhost:9200/shopping/_search
{
"query":{
"match":{
"category": "小米"
}
}
}
全查询
GET http://localhost:9200/shopping/_search
{
"query":{
"match_all":{
}
}
}
分页查询
GET http://localhost:9200/shopping/_search
{
"query":{
"match_all":{
}
},
"from": 0,
"size": 2
}
指定查询字段
GET http://localhost:9200/shopping/_search
{
"query":{
"match_all":{
}
},
"from": 0,
"size": 2,
"_source":["title"]
}
查询结果排序
GET http://localhost:9200/shopping/_search
{
"query":{
"match_all":{
}
},
"from": 0,
"size": 2,
"_source":["title"],
"sort":{
"price":{
"order": "desc"
}
}
}
多条件组合查询
GET http://localhost:9200/shopping/_search
AND关系 使用must
{
"query":{
"bool":{
"must": [
{
"match":{
"category":"小米"
}
},
{
"match":{
"price":1999
}
}
]
}
}
}
OR关系 使用should
{
"query":{
"bool":{
"should": [
{
"match":{
"category":"小米"
}
},
{
"match":{
"category":"华为"
}
}
]
}
}
}
范围查询 使用filter
{
"query":{
"bool":{
"should": [
{
"match":{
"category":"小米"
}
},
{
"match":{
"category":"华为"
}
}
],
"filter":{
"range":{
"price":{
"lt": 3000
}
}
}
}
}
}
查询-全文检索 match
GET http://localhost:9200/shopping/_search
{
"query":{
"match":{
"category":"米"
}
}
}
问题:为什么一个米查询出来了小米的结果
ES会对小米进行分词(拆分成了"小"和"米"),并将拆解后的数据保存到倒排索引了,所以"米"(用小也能查)对应的数据是当前这条,这样即使使用文字的一部分也能查询出来数据了,所以查询出来了,这种检索方式叫做全文检索
{
"query":{
"match":{
"category":"小华"
}
}
}
问题:这个查询条件为什么也能查询出来数据
因为先对小华进行了分词,分成了"小"和"华",底层查询的时候会进行拆解,形成一个一个的关键词,每个分词都有对应的数据,所以查询出来了
查询-完全匹配 match_phrase
这个完全匹配的意思是输入的词 匹配,(当然这个词还是会作为一个整体进行模糊查询的)
GET http://localhost:9200/shopping/_search
{
"query":{
"match_phrase":{
"category":"小华"
}
}
}
{
"query":{
"match_phrase":{
"category":"米"
}
}
}
还是会查询出category中包含"米"的数据
查询-高亮查询
GET http://localhost:9200/shopping/_search
{
"query":{
"match_phrase":{
"category":"米"
}
},
"highlight":{
"fields":{
"category":{}
}
}
}
{
"took": 61,
"timed_out": false,
"_shards": {
"total": 1,
"successful": 1,
"skipped": 0,
"failed": 0
},
"hits": {
"total": {
"value": 4,
"relation": "eq"
},
"max_score": 0.10536051,
"hits": [
{
"_index": "shopping",
"_id": "JesAmJABLvXlzB8V4AV4",
"_score": 0.10536051,
"_source": {
"title": "小米手机",
"category": "小米",
"images": "http://www.gulixueyuan.com/xm.jpg",
"price": "1999"
},
"highlight": {
"category": [
"小<em>米</em>"
]
}
},
{
"_index": "shopping",
"_id": "JOv_l5ABLvXlzB8VWAXZ",
"_score": 0.10536051,
"_source": {
"title": "小米手机",
"category": "小米",
"images": "http://www.gulixueyuan.com/xm.jpg",
"price": 3999.00
},
"highlight": {
"category": [
"小<em>米</em>"
]
}
},
{
"_index": "shopping",
"_id": "JusAmJABLvXlzB8V6QV8",
"_score": 0.10536051,
"_source": {
"title": "小米手机",
"category": "小米",
"images": "http://www.gulixueyuan.com/xm.jpg",
"price": 3999.00
},
"highlight": {
"category": [
"小<em>米</em>"
]
}
},
{
"_index": "shopping",
"_id": "J-sCmJABLvXlzB8VTQUS",
"_score": 0.10536051,
"_source": {
"title": "小米手机",
"category": "小米",
"images": "http://www.gulixueyuan.com/xm.jpg",
"price": 3999.00
},
"highlight": {
"category": [
"小<em>米</em>"
]
}
}
]
}
}
查询-聚合查询
带原始数据
GET http://localhost:9200/shopping/_search
{
"aggs":{ // 聚合操作
"price_group":{ // 名称 随意起
"terms":{ // 分组
"field":"price" // 分组字段
}
}
}
}
不需要查询出原始数据
{
"aggs":{ // 聚合操作
"price_group":{ // 名称 随意起
"terms":{ // 分组
"field":"price" // 分组字段
}
}
},
"size":0
}
求平均数据
{
"aggs":{ // 聚合操作
"price_avg":{ // 名称 随意起
"avg":{ // 分组
"field":"price" // 分组字段
}
}
},
"size":0
}
映射
数据的约束
设置映射
PUT http://localhost:9200/user/_mapping
{
"properties":{
"name":{
"type": "text", // 可以分词匹配
"index": true // 走索引
},
"sex":{
"type": "keyword", // 完全匹配
"index": true // 走索引
},
"tel":{
"type": "keyword",
"index": false // 不走索引
}
}
}
查询映射
GET http://localhost:9200/user/_mapping
创建数据验证
POST http://localhost:9200/user/_doc/1001
{
"name":"小米",
"sex":"男的",
"tel":"111"
}
查询验证
GET http://localhost:9200/user/_search
{
"query":{
"match":{
"name": "小"
}
}
}
有结果 name走了全文检索
{
"query":{
"match":{
"sex": "男"
}
}
}
无结果 sex必须完全匹配
{
"query":{
"match":{
"tel": "1111"
}
}
}
没有被索引,无法查询 tel要被索引
场景
表示该字段不会被索引,无法被查询和搜索。
但是该字段仍然会被存储在文档中。
存储元数据信息:
有些字段只是用来存储一些元数据信息,比如文章的发布时间、作者等。
这些信息通常不需要被搜索,只需要在显示文章详情时使用。将这些字段设置为index=false可以节省索引空间。
存储大字段:
有些字段可能包含大量的文本内容,比如文章的正文内容。
如果将这些大字段设置为index=true,会占用大量的索引空间,影响查询性能。
将这些大字段设置为index=false,可以只存储原始数据,不进行索引,从而节省空间和提高性能。
提高搜索性能:
有时候我们只需要搜索部分字段,而不需要搜索所有字段。
将不需要搜索的字段设置为index=false,可以减少需要搜索的字段数量,提高搜索性能。
集群
配置文件
elasticsearch.yml
# 集群名称
cluster.name: my-application
# 节点名称
node.name: node-1001
# 当前节点是否可以充当master
node.master: true
# 当前节点是否支持存储数据
node.data: true
# 服务IP
network.host: localhost
# 端口号
http.port: 1001
# 监听关口号
transport.tcp.port: 9301
# 跨域
http.cors.enabled: true
http.cors.allow-origin: "*"
第二台机器需要增加额外的参数
# 查找其他的ES机器
# 内部通讯端口
discovery.seed_hosts: ["localhost:9301"]
discovery.zen.fd.ping_timeout: 1m
discovery.zen.fd.ping_retries: 5
第三胎机器需要增加额外的参数
# 查找其他的ES机器
# 内部通讯端口
discovery.seed_hosts: ["localhost:9301", "localhost:9302"]
discovery.zen.fd.ping_timeout: 1m
discovery.zen.fd.ping_retries: 5
进阶
索引(index)
一个索引就是一个拥有积分相似特征的文档的集合,一切设计都是为了提高搜索的性能
文档(document)
保存一条数据就是保存一个文档,JSON格式的
字段(Field)
JSON的多个属性
映射(mapping)
相当于MYSQL的表结构,mapping是处理数据的方式和规则方面做一些限制,例如:字段的数据类型、默认值
分片(shards)
相当于MYSQL的分表
例如:性别表,拆分成男表和女表
-
允许水平分割/扩展内容容量
-
允许再分片上进行分布式、并行的操作,提高性能/吞吐量
副本(Replicas)
防止数据丢失,在节点失败的情况下,提供了高可用
分配(Allocation)
将分片分配给某个节点的过程,包括分配主分片或者副本。如果是副本,还包含从主分片复制数据的过程。这个过程是由master节点完成的。
分片和副本设置
PUT http://localhost:1001/users
{
"settings": {
"number_of_shards": 3,
"number_of_replicas": 1
}
}
ES数据写入流程
ES数据读流程
ES数据更新流程
ES读取/插入多个文档
批量数据查询:并行的将每条数据的读取分配到每个节点去查询
批量数据写入:并且的将每条数据的写入分配到每个节点去写入
ES只是负责接收了这一批的请求,内部还是分配到多个节点上单个去执行的
分片原理
分片是Elasticsearch最小的工作单元。但是究竟什么么是一个分片,它是如何工作的?
传统的数据库每个字段存储单个值,但这对全文检索并不够。文本字段中的每个单词需要被搜索,对数据库意味着需要单个字段有索引多值的
能力。最好的支持是一个字段多个值需求的数据结构是倒排索引。
多个分片组合起来九城了倒排索引
倒排索引
举例
1001 my name is zhangsan
形成倒排索引如下:
name 1001
zhang 1001
zhangsan 1001
通过关键词匹配到了数据行,从数据行获取到了相当于主键的1001,通过1001查询数据,效率会很高
分词器
有些可以分词,有些不能分词
keyword类型不可以分词
text类型不可以分词,具体怎么分词,依赖于分词器
词条
索引中最小存储和查询单元
词典
字典,词条的集合,B+树,Hash表
倒排表
关键词出现在什么位置,频率如何
文档搜索
倒排索引一旦被确定之后不可变,持久化到硬盘中,不修改,只补充(补充的是段,最新的索引会补充到罪行的段中)
如果文档被删除了,ES只会逻辑删除,在查询的时候查询不出来,索引不会改变
索引写入
translog,记录待操作的日志,防止宕机,宕机后恢复数据的作用
文档刷新
refresh,将内存中的segment刷新到OS cache中,用户查询Cache即可
文档刷写
flush,将segment持久化到磁盘
文档合并
segment过多的时候会触发文档合并,减少文件数量,效率提高
因为倒排索引是不可变的,所以每一次的更新和删除都会体现在新的索引段中,会产生一些.del文件这样的话,在合并的时候会将这些文件真正的删除,也能提高效率
文档分析
- 将一块文本分成适合于倒排索引的独立的词条
- 将这些词条统一化为标准格式以提高他们的"可搜索性"
- 分析器执行上面的工作。分析器实际上是将三个功能封装到了一个包里:
1.字符过滤器
首先:字符串按顺序通过每个字符过滤器,他们的任务是在分词器前整理字符串,一个字符过滤器可以用来去掉HTML,或者将**&转换成为AND**
2.分词器
其次:字符串被分词器分为单个的词条。一个简单的分词器遇到空格和标点的时候,可能会将文本拆分成词条。
3.Token过滤器
最后:词条按顺序通过每个token过滤器,这个过程可能会改变词条(例如,小写话Quick),删除词条(例如,a and the等无用词),增加词条(想jump和leap这种同义词)
内置分析器
举例
“Set the shape to semi-transparent by calling set_trans(5)”
标准分析器
标准分析器,ES默认使用的分析器,根据Unicode联盟定义的单词边界划分文本,删除绝大部分标点,最后,将词条小写
set,the,shape,to,semi,transparent,by,calling,set_trans,5
简单分析器
简单分析器在任何不是字母的地方分割文本,将词条小写
set,the,shape,to,semi,transparent,by,calling,set,trans
空格分析器
空格分析器在空格的地方划分文本
set,the,shape,to,semi,transparent,by,calling,set_trans(5)
语言分析器
可用于多种语言,根据语言的特点进行拆分,中文,英文等
set,shape,semi,transpar,call,set_tran,5
transparent、calling、set_trans已经变为词根格式
分析器的使用场景
当我们索引一个文档,它的全文被分析称词条以用来创建倒排索引。但是,当我们在全文搜索的时候,我们需要将查询字符串通过相同的分析过程,以保证我们搜索的词条格式与索引中的词条格式一致
测试分词器
标准分词器
GET http://localhost:9200/_analyze
请求
{
"analyzer": "standard",
"text": "Text to analyze"
}
响应
{
"tokens": [
{
"token": "text",
"start_offset": 0,
"end_offset": 4,
"type": "<ALPHANUM>",
"position": 0
},
{
"token": "to",
"start_offset": 5,
"end_offset": 7,
"type": "<ALPHANUM>",
"position": 1
},
{
"token": "analyze",
"start_offset": 8,
"end_offset": 15,
"type": "<ALPHANUM>",
"position": 2
}
]
}
指定分词器
请求
{
"text": "测试单词"
}
响应
{
"tokens": [
{
"token": "测",
"start_offset": 0,
"end_offset": 1,
"type": "<IDEOGRAPHIC>",
"position": 0
},
{
"token": "试",
"start_offset": 1,
"end_offset": 2,
"type": "<IDEOGRAPHIC>",
"position": 1
},
{
"token": "单",
"start_offset": 2,
"end_offset": 3,
"type": "<IDEOGRAPHIC>",
"position": 2
},
{
"token": "词",
"start_offset": 3,
"end_offset": 4,
"type": "<IDEOGRAPHIC>",
"position": 3
}
]
}
说明:分词的效果不好
指定分词器再次测试
下载地址:https://github.com/medcl/elasticsearch-analysis-ik/releases/tag/v7.8.0
作为ES软件的插件来使用
请求
{
"text": "测试单词",
"analyzer": "ik_max_word"
}
响应
{
"tokens": [
{
"token": "测试",
"start_offset": 0,
"end_offset": 2,
"type": "CN_WORD",
"position": 0
},
{
"token": "单词",
"start_offset": 2,
"end_offset": 4,
"type": "CN_WORD",
"position": 1
}
]
}
分词器扩展词汇
请求
{
"text": "弗雷尔卓德",
"analyzer": "ik_max_word"
}
响应
{
"tokens": [
{
"token": "弗",
"start_offset": 0,
"end_offset": 1,
"type": "CN_CHAR",
"position": 0
},
{
"token": "雷",
"start_offset": 1,
"end_offset": 2,
"type": "CN_CHAR",
"position": 1
},
{
"token": "尔",
"start_offset": 2,
"end_offset": 3,
"type": "CN_CHAR",
"position": 2
},
{
"token": "卓",
"start_offset": 3,
"end_offset": 4,
"type": "CN_CHAR",
"position": 3
},
{
"token": "德",
"start_offset": 4,
"end_offset": 5,
"type": "CN_CHAR",
"position": 4
}
]
}
添加扩展词汇
ES的plugins的ik文件夹,进入config目录,创建custom.dic文件,写入弗雷尔卓德,打开IKAnalyzer.cfg.xml文件,来新建的custom.dic配置一下,重启ES
IKAnalyzer.cfg.xml中添加
<entry key="ext_dict">custom.dic</entry>
配置完成之后
请求
{
"text": "弗雷尔卓德",
"analyzer": "ik_max_word"
}
响应
{
"tokens": [
{
"token": "弗雷尔卓德",
"start_offset": 0,
"end_offset": 5,
"type": "CN_WORD",
"position": 0
}
]
}
自定义分词器
PUT http://localhost:9200/my_index
{
"settings": {
"analysis": {
"char_filter": {
"&_to_and": {
"type": "mapping",
"mappings": [
"&=> and "
]
}
},
"filter": {
"my_stopwords": {
"type": "stop",
"stopwords": [
"the",
"a"
]
}
},
"analyzer": {
"my_analyzer": {
"type": "custom",
"char_filter": [
"html_strip",
"&_to_and"
],
"tokenizer": "standard",
"filter": [
"lowercase",
"my_stopwords"
]
}
}
}
}
}
测试自定义分词器
GET http://localhost:9200/my_index/_analyze
请求
{
"text": "The quick & brown fox",
"analyzer": "my_analyzer"
}
响应
{
"tokens": [
{
"token": "quick",
"start_offset": 4,
"end_offset": 9,
"type": "<ALPHANUM>",
"position": 1
},
{
"token": "and",
"start_offset": 10,
"end_offset": 11,
"type": "<ALPHANUM>",
"position": 2
},
{
"token": "brown",
"start_offset": 12,
"end_offset": 17,
"type": "<ALPHANUM>",
"position": 3
},
{
"token": "fox",
"start_offset": 18,
"end_offset": 21,
"type": "<ALPHANUM>",
"position": 4
}
]
}
文档控制
用户A局部更新数据
用户B局部更新数据
两个人同时改,会出问题
悲观锁控制
不允许并发访问
乐观锁控制
ES默认使用,如果出现冲突,可以充实更新、使用新的数据、或将相关情况报告给用户
最终根据版本号去控制是否能被修改
使用示例
创建文档
PUT http://localhost:9200/user/_create/1001
创建文档后会有三个属性
_version: 1
_seq_no: 0
_primary_term: 1
修改文档
POST http://localhost:9200/user/_update/1001
修改一次后三个属性的变化
_version: 2
_seq_no: 1
_primary_term: 1
使用乐观锁修改文档
- 使用_seq_no和_primary_term_
- POST http://localhost:9200/user/_update/1001?if_seq_no=0&if_primary_term=0
- 修改失败,版本不一致
- 修改请求参数的版本号,再次修改文档
- POST http://localhost:9200/user/_update/1001?if_seq_no=1&if_primary_term=1
- 修改成功,版本是最新的
- 修改后三个属性的变化
- _version: 3
- _seq_no: 2
- _primary_term: 1
- POST http://localhost:9200/user/_update/1001?if_seq_no=0&if_primary_term=0
- 使用version修改
- 当前三个属性
- _version: 3
- _seq_no: 2
- _primary_term: 1
- POST http://localhost:9200/user/_update/1001?version=1&version_type=external
- 修改失败,version不是最新的
- 修改请求参数的版本号,再次修改文档
- POST http://localhost:9200/user/_update/1001?version=3&version_type=external
- 修改失败,version要大于原有的version
- 修改请求参数的版本号,再次修改文档
- POST http://localhost:9200/user/_update/1001?version=4&version_type=external
- 修改成功
- 修改后三个属性的变化
- _version: 4
- _seq_no: 3
- _primary_term: 1
- 当前三个属性
kibana使用
下载kibana
https://artifacts.elastic.co/downloads/kibana/kibana-7.8.0-windows-x86_64.zip
解压kibana
添加配置kibana.yml
# 默认端口
server.port: 5601
# ES服务器的地址
elasticsearch.hosts: ["http://localhost:9200"]
# 索引名
kibana.index: ".kibana"
# 支持中文
i18n.locale: "zh-CN"
启动kibana
bin/kibana.bat
浏览器访问
http://localhost:5601
点击页面的控制台,进入命令行
请求方式 命令
JSON数据
进行ES的增删改查
Elasticsearch框架集成
见文末示例代码
集成SpringData
springdata-operator
集成spark
sparkstreaming-operator
集成flink
flink-operator
优化
硬件选择
- SSD取代机械
- 使用RAID 0
- 使用多块硬盘
- 不要使用远程挂载的存储
分片策略
合理设置分片数
一个分片底层是一个lucene索引,会消耗一定文件句柄、内存、CPU运转
-
控制每个分片占用的硬盘容量不超过ES的最大 JVM的堆空间设置(一般设置不超过32G),因此,如果索引的总容量在500G左左右,那分片大小在16个左右即可
-
考虑一下node 数量,一般一个节点有时候就是一台物理机如果分片数过多,大大超过了节点数,很可能会导致一个节点上存在多个分片,一旦该节点故障,即使保持了1个以上的副本,同样有可能会导致数据丢失,集群无法恢复。所以,一般都设置分片数数不超过节点数的3倍。
-
主分片,副本和节点最大数之间数量,我们分配的时候可以参考以下关系:
- 节点数<=主分片数*(副本数+1)
路由选择
-
当我们查询文档的时候,Elasticsearch如何知道一个文档应该存放到哪个分片中呢?它其实是通过下面这个公式来计算出来:
- shard = hash(routing) % number_of_primary_shards
- routing 默认值是文档的id,也可以采用自定义值,比如用户id。
- shard = hash(routing) % number_of_primary_shards
-
不带routing查询
- 在查询的时候因为不知道要查询的数据具体在哪个分片上,所以整个过程分为2个步骤
- 分发:请求到达协调节点后,协调节点将查询请求,分发到每个分片上。
- 聚合:协调节点搜集到每个分片上查询结果,在将查查询的结果进行排序,之后给用户返回结果。
-
带routing查询
- 查询的时候,可以直接根据routing信息定位到某个分配查询,不需要查询所有的分配,经过协调节点排序。向上面自定义的用户查询,如果routing设置为userid 的话,就可以直接查询出数据来,效率提升很多。
写入速度优化
- 加大Translog Flush
- 增加Index Refresh间隔,减少segmentMerge的次数(ES会默认后台定期进行段合并)
- 调整Bulk线程池和队列
- 优化节点间的任务分配
- 调整Lucene层的索引建立,目的是降低CPU使用和IO操作
内存设置
在jvm.option中设置内存大小
-
不要超过物理内存的50%,Lucene的设计目的是把底层OS里的数据缓存到内存中
-
堆内存的大小最好不要超过32G
-
一般采用31G设置
- -Xms 31g
- -Xmx 31g
假设一个机器有128G的内存,创建两个节点, 一个节点31G,剩下的都留给Lucene
代码操作ES示例地址
https://github.com/ChenJiahao0205/ES-Learning
模块组成:
- original-operator ES原生命令
- springdata-operator ES-Spring命令
- spark-operator ES-Spark命令(Scala语言)
- flink-operator ES-Flink命令
最后
我是通过B站【尚硅谷】的ES课程视频进行学习的,本文中借鉴了部分图片和文字描述,视频URL:https://www.bilibili.com/video/BV1hh411D7sb
感谢大家看到这里,文章如有不足,欢迎大家指出;彦祖点个赞吧彦祖点个赞吧彦祖点个赞吧,欢迎关注程序员五条!