MySQL数据写入ES可以通过多种方式实现,以下是一些常见的方法:
使用Logstash:
Logstash是一个开源的数据收集引擎,支持多种输入和输出插件,包括MySQL和Elasticsearch。
通过配置Logstash的input插件为MySQL,filter插件进行数据处理,output插件为Elasticsearch,可以实现MySQL数据到ES的同步。
优点:配置简单,支持多种数据源和输出目标。
缺点:可能存在性能不高、可靠性差等问题。
使用Elasticsearch JDBC插件:
Elasticsearch JDBC插件允许通过JDBC连接MySQL数据库,并将数据导入到ES中。
需要配置JDBC连接器和相应的数据映射规则。
优点:可以实现实时同步和定期同步。
缺点:需要一定的配置和管理工作。
使用Canal:
Canal是阿里巴巴开源的一个用于MySQL数据库增量数据同步的工具。
通过解析MySQL的binlog来获取增量数据,并将数据发送到指定的位置,包括ES。
优点:性能高、可靠性好、支持多种过滤规则。
缺点:配置相对复杂,需要额外的消息队列等。
使用自定义脚本(如Python、Java等):
通过编写自定义脚本来同步MySQL数据到ES。
优点:具有灵活性和可定制性。
缺点:需要编写代码来实现数据同步,且需要进行配置和管理。
使用数据库中间件(如MyCat):
MyCat等数据库中间件可以在MySQL数据库和应用程序之间提供负载均衡、故障切换、读写分离等功能。
通过配置中间件与ES的连接,可以实现MySQL数据到ES的同步。
优点:配置简单,支持读写分离等。
缺点:性能相对较低,需要额外的中间件等。
使用流处理框架(如Apache Flink):
流处理框架如Apache Flink可以通过连接MySQL和ES,实时处理并同步数据。
Flink提供了强大的流处理能力和状态管理,适合处理大规模实时数据流。
优点:实时性高,处理能力强。
缺点:需要一定的配置和编程知识。
选择哪种方法取决于具体的需求、系统架构和性能要求。在实际应用中,可以根据实际情况灵活选择和组合使用这些方法。
简介
可伸缩、有弹力的查询,简称es。es是一个开源的高扩展的分布式全文检索引擎,它可以近乎实时的存储、检索数据,可以扩展到上百台服务器,处理PB级别的数据。
es也使用Java开发并使用Lucene作为其核心来实现所有索引和搜索的功能,但是它的目的是通过简单的RESTful API来隐藏Lucene的复杂性,从而让全文搜索变得简单。
发展历程
核心组件和附加工具
ELK三大核心组件:ElasticSearch、Logstash、Kibana。
ELK Stack:开源的日志管理平台,除了以上的三个核心组件,还包含了一些其他组件和附加工具。
(组件可以理解为必不可少,附加工具通常为可选的,并不是必须安装和使用的),其他的附加工具如下:
Beats:一组轻量级数据收集器,包括FileBeat(文件)、Metricbeat(系统指标)、Packetbeat(网络流量)、Winlogbeat(系统事件日志)等。
APM:是一个应用程序性能监控工具,用于监视和分析应用程序性能和用户体验。它可以追踪应用程序中的事务和请求,并生成实时的性能指标和错误日志。
Graph:图形分析工具,用于发现和分析数据中的关系和模式。
Machine Learning:机器学习工具,用于在大规模数据集上训练和应用机器学习模型。
Marvel:可用于监视和管理Elasticsearch集群,帮助用户更好地了解集群健康状况、性能指标和资源使用情况等。
Shield:保护Elasticsearch集群的安全性,提供对集群数据的身份验证、授权和加密保护。
Watcher:可用于监视Elasticsearch中的事件,并在满足特定条件时自动触发警报或操作。
X-Pack并不是一个单一的附加工具,而是一个集成了多个模块的扩展工具集。ES7.0+后,无需安装,已集成到ES核心代码中。
数据采集模块:X-Pack Beats,提供了一系列轻量级数据收集器,负责收集各种类型的数据并将其发送到Logstash或Elasticsearch。
安全模块:X-Pack Security,负责增强Elasticsearch的安全性,实现身份验证、授权和审计等功能。
监控模块:X-Pack Monitoring,提供了实时的集群和节点监控、性能指标和警报、日志记录和分析等功能。
报告模块:X-Pack Reporting,提供了定制化的报告和可视化工具,如图表、仪表盘等。
机器学习模块:X-Pack Machine Learning,提供了基于机器学习的异常检测、预测和分类等功能。
图形探索模块:X-Pack Graph,提供了基于图形的数据探索和分析功能。
应用性能管理模块:X-Pack APM,提供了对应用程序性能的实时监测和分析功能。
ELK Stack架构图
架构一
架构二
架构三
架构一:这是最简单的ELK架构方式,优点是搭建简单、易于上手,缺点是Logstash消耗资源较大,运行占用CPU和内存高,且没有消息队列,数据存在丢失隐患。
Logstash分布于各个节点上搜集相关日志、数据,并经过分析、过滤后发送给远端服务器上的Elasticsearch进行存储。
架构二:各个节点上的Logstash Agent先将数据/日志传递给Kafka(或者Redis),并将队列中消息或数据间接传递给Logstash,Logstash过滤、分析后将数据传递
给Elasticsearch存储。
架构三:将收集端logstash替换为beats,更灵活,消耗资源更少,扩展性更强。同时可配置Logstash 和Elasticsearch 集群用于支持大集群系统的运维日志数据监控
和查询。
相关术语
Elasticsearch是面向文档(document oriented)的数据库,这意味着它可以存储整个对象或文档(document)。然而它不仅是存储,还会索引(index)每个文档的内容使之可以被搜索。在Elasticsearch中,你可以对文档(而非成行成列的数据)进行索引、搜索、排序、过滤。Elasticsearch与传统关系型数
据库比较如下:
Relational DB ‐> Databases ‐> Tables ‐> Rows ‐> Columns
Elasticsearch ‐> Indices ‐> Types ‐> Documents ‐> Fields
index索引
是将相关数据组合在一起以便查询的一种结构。它类似于关系数据库中的表,但它更加灵活,不需要预先定义数据结构
document文档
是 ElasticSearch 存储数据的最小单位,它类似于关系数据库中的行。
type类型
document类型,7.0之后一个索引只能定义一个类型,且类型默认为 _doc,文档都有一个唯一的 _id 属性,用于标识该文档
field类型,每个字段都有一个类型(type),它用于定义该字段如何存储和解析。类型可以是基本类型,如字符串、数字、日期等。
text:用于存储文本数据,会被分词器分解成单词后存储。
keyword:用于存储不需要分词的关键字,比如标签、分类等。
date:用于存储日期和时间。
integer、long、short、byte:用于存储整数。
float、double:用于存储浮点数。
boolean:用于存储布尔值
field字段
是文档中的一个属性或者一列,它类似于关系数据库中的列,每个字段包含了一个或多个数据值。
mapping映射
可以理解为指定字段类型、索引选项和其他属性的过程。
cluster集群
Elasticsearch集群会有多个Elasticsearch节点,在众多节点种会有一个Master Node,它主要负责索引元数据、负责切换主分片和副本分片等工作。当一个节点挂了,会选举一个新的主节点。
shard分片
replica副本
主分片的拷贝。副本分片存储在不同的节点上,当主分片不可用时,副本分片会被提升为新的主分片。
是将索引分成多个部分存储的机制。一个索引创建时默认分成5个主分片和1个副本(5副本分片),postman默认创建一个分片和一个副本。
为何需要分片?
如果一个index的数据量太大,只有一个分片,那只会在一个节点上存储,随着数据量的增长,一个分片就不能满足。
如何实现高可用?
每个节点都会有主分片和副本分片,主分片负责创建索引、删除索引、分配分片、追踪节点状态等,副本分片会复制主分片的数据,主分片和副本分片都可以进行读取。
如何防止数据丢失?
每个节点上的主分片和副本分片都不相同,如果某个节点挂了,会把对应副本分片提拔为主分片,防止数据丢失。
如何修改分片和副本?
索引一旦创建分片和副本禁止修改分片和副本,如果您需要更改分片数量,使用以下步骤重新索引数据:
创建一个新的索引,并指定所需的分片数量和副本数量。
从旧索引中检索所有文档,并将它们重新索引到新索引中。
停止索引文档到旧索引中。
更新您的应用程序,使其开始使用新索引。
在确认新索引工作正常后,您可以删除旧索引。
为何不能修改分片?
因为分片的数量是在索引创建的时候就确定的,修改分片数量涉及到数据的重分布,需要重新建立索引并将数据重新分配到新的分片上,这个过程非常耗时,并且会导致索引不可用。
总体来说,一个Elasticsearch集群可以由多个节点组成,每个节点可以存储一个或多个索引,每个索引又可以被划分为多个分片,每个分片又可以有零个或多个副本分片。
这种分布式架构可以实现水平扩展、提高系统的可用性和并行处理。
倒排索引VS正向索引
正向索引 (forward index) 以文档的ID为关键字,表中记录文档中每个字的位置信息,查找时扫描表中每个文档中字的信息直到找出所有包含查询关键字的文档。
倒排索引 ,一般也被称为反向索引(inverted index)。带有倒排索引的文件我们称为倒排索引文件,简称倒排文件(inverted file)。
假设有文档一(id为doc_1)和文档二(id为doc_2),假设有文档一(id为doc_1)和文档二(id为doc_2),文档二:my car is BMW
正向索引如下:
倒排索引如下:
分词
把文本转换成一系列单次的过程,ES中是通过分词器来实现的,text默认使用的是Standard Analyzer。
例如:「中华人民共和国国歌」会被拆分为「中华人民共和国、中华人民、中华、华人、人民共和国、人民、人、民、共和国、共和、和、国国、国歌」
Standard Analyzer - 默认分词器,按词切分,小写处理
Simple Analyzer - 按照非字母切分(符号被过滤),小写处理
Stop Analyzer - 小写处理,停用词过滤(the ,a,is)
Whitespace Analyzer - 按照空格切分,不转小写
Keyword Analyzer - 不分词,直接将输入当做输出
Pattern Analyzer - 正则表达式,默认 \W+
Language - 提供了 30 多种常见语言的分词器
Customer Analyzer - 自定义分词器
安装使用
Query DSL
API:Document APIs | Elasticsearch Guide [8.15] | Elastic
es提供了一个完整的基于 JSON 的查询,提供了强大的检索数据方式,这种检索方式称之为Query DSL,类似数据库种的Sql。
语法、属性、查询类型介绍:
语法
# GET /索引名/_doc/_search { json格式请求体数据 }
# GET /索引名/_search { json格式请求体数据 }
属性
Settings(设置):索引的设置,例如分片数、副本数等。
Mappings(映射):索引中的字段映射。
properties是Mappings的子属性,用于定义每个字段的类型、分析器等。
sort 指定返回结果的排序规则。
size:指定返回结果的数量。
Aliases(别名):索引的别名。
Index lifecycle management(索引生命周期管理):管理索引的生命周期,例如创建、删除等。
Templates(模板):创建索引时使用的模板。
查询类型
_doc 查询:该查询是 Elasticsearch 默认的查询方式,它可以用来根据文档的唯一 ID 值来查询对应的文档。
_search查询:
match 查询:该查询可以用来查询包含指定关键词的文档。
match_all 查询:该查询可以用来查询所有文档。
term查询:精确匹配某个词项的查询。
bool查询:将多个查询组合起来,支持and、or和not操作。
range查询:查询某个字段值在一定范围内的文档,支持大于、小于、等于等操作符。
wildcard查询:支持通配符匹配的查询,类似于SQL中的“like”操作符。
prefix查询:查询某个字段以特定前缀开头的文档。
match_phrase_prefix:
should 关键字:在 should 关键字里的内容只要满足其中一项就可以执行,作用相当于 SQL 语句中的 OR 。
must 关键字:在 must 中的内容表明都是必须执行的内容,在 must 中可以创建多条语句,多条语句需同时满足条件才能执行,作用相当于 SQL 语句中的 AND
_count 查询:用于查询匹配某个查询条件的文档数量,但不返回实际文档的内容。
_update 操作:用于更新符合某个查询条件的文档的部分或全部内容。
_delete 操作:用于删除符合某个查询条件的文档。
_bulk 操作:用于在一个请求中执行多个操作,例如批量索引、批量更新和批量删除等,提高效率
其他
当使用 POST 请求向索引中添加文档时,如果不指定文档的 ID,则 Elasticsearch 会自动生成一个唯一的 ID。
PUT 请求的语义是在指定的位置(即文档 ID)上更新或创建文档。因此,如果在执行 PUT 请求时不指定文档 ID, 会返回错误信息。
索引创建:
PUT /user
create:
{
"aliases": {
"user1": {},
"user2": {
"filter": {
"term": {
"gender": "male"
}
}
}
},
"settings": {
"number_of_shards": 5,
"number_of_replicas": 1
},
"mappings": {
"properties": {
"name": {
"type": "text",
"analyzer": "standard"
},
"age": {
"type": "integer"
},
"gender": {
"type": "keyword"
},
"address": {
"type": "text"
}
}
}
}
result:
{
"acknowledged": true,//表示 Elasticsearch 节点已经成功响应了这个 API 请求,并执行了请求的操作。
"shards_acknowledged": true,//表示在响应的过程中,所有分片都已经成功执行了相关操作。
"index": "role"
}
索引修改
1) 添加索引别名
POST /_aliases
{
"actions": [
{
"add": {
"index": "user",
"alias": "test_alias"
}
}
]
}
result:
{
"acknowledged": true
}
2) 修改索引别名
POST /_aliases
{
"actions" : [
{
"remove" : {
"index" : "user",
"alias" : "user2"
}
},
{
"add" : {
"index" : "user",
"alias" : "user3",
"filter": {
"term": {
"gender": "female"
}
}
}
}
]
}
result:
{
"acknowledged": true
}
3) 删除如上
4) 复制索引
POST _reindex
{
"source": {
"index": "user"
},
"dest": {
"index": "new_user",
"alias": "user"
}
}
索引删除:
DELETE /user
result:
{
"acknowledged": true,//表示 Elasticsearch 节点已经成功响应了这个 API 请求,并执行了请求的操作。
}
索引打开、关闭
POST /my_index/_close 关闭的索引不能进行读写操作
POST /my_index/_open 关闭的索引可以打开
索引添加/修改文档:
PUT /user/_doc/1(如不使用id:1,将默认生成一个字符串id)
1) create/update:
{
"name": "张三",
"age": 26,
"gender": "male",
"address": "北京 海淀"
}
{
"name": "李四",
"age": 20,
"gender": "female",
"address": "山东 济南"
}
{
"name": "张三四",
"age": 22,
"gender": "male",
"address": "山西 太原"
}
result:
{
"_index": "user",
"_type": "_doc",
"_id": "1",
"_version": 1,//每次操作,会自动更新
"result": "created",//修改时result:update
"_shards": {
"total": 2,
"successful": 1,
"failed": 0
},
"_seq_no": 0,
"_primary_term": 1
}
查看、添加、修改、删除文档:
1) 查看文档
GET /user/_doc/1
GET /user/_doc/_search
2) 更新文档部分信息
修改文档的全部内容_doc/id,覆盖原有文档信息
PUT /user/_update/id
{
"script": {
"source": "ctx._source.name = '张三'"
}
}
POST /user/_update_by_query
{
"query": {
"match": {
"name": "张三"
}
},
"script": {
"source": "ctx._source.name = '张四'"
}
}
4) 删除文档
DELETE /user/_doc/1
DELETE /user/_delete_by_query
{
"query": {
"term": {
"gender": "male"
}
}
}
索引文档查询
1) term查询:精确匹配某个词项的查询,使用于keyword类型
GET /user/_search
{
"query":{
"term":{
"gender":"male"
}
}
}
3) match查询:匹配某个字段包含特定词条的文档
GET /user/_search
{
"query": {
"match": {
"name": "张"
}
}
}
4) range查询:查询某个字段值在一定范围内的文档,支持大于、小于、等于等操作符。例如:
GET /user/_search
{
"query": {
"range": {
"age": {
"gte": 23
}
}
}
}
5) bool查询:将多个查询组合起来,支持and、or和not操作。例如:
GET /user/_search
{
"query": {
"bool": {
"must": [
{
"match": {
"name": "三"
}
},
{
"match": {
"gender": "male"
}
},
{
"range": {
"age": {
"gte": 22
}
}
}
]
}
}
}
6) wildcard查询:支持通配符匹配的查询,类似于SQL中的“like”操作符。例如:
GET /user/_search
{
"query": {
"wildcard": {
"name": "张"
}
}
}
7) prefix查询:查询某个字段以特定前缀开头的文档。例如:
GET /user/_search
{
"query": {
"prefix": {
"name": "张"
}
}
}
8) GET /my_index 表示查询整个索引的所有文档,返回文档结构信息
9) GET /my_index/_search 表示使用搜索API进行查询,返回文档数据信息
10) GET /_cat/indices 表示查询所有的索引信息
批量操作:
//postman 请求体结尾一定要加个换行
1) 添加索引
PUT /test1,test2/_bulk
{"create":{"_index":"test1","_id":"1"}}
{"name":"test1-1"}
{"create":{"_index":"test2","_id":"2"}}
{"name":"test2-1"}
2) 修改索引
PUT /_alias
{
"user" : {
"aliases" : {
"user1" : { }
}
},
"role" : {
"aliases" : {
"role1" : { }
}
}
}
3) 删除索引
DELETE /test1,test2
4) 添加文档
PUT /test1/_bulk
{ "index" : { "_index" : "test1", "_id" : "4" } }
{ "name" : "document1" }
{ "index" : { "_index" : "test1", "_id" : "5" } }
{ "name" : "document2" }
{ "index" : { "_index" : "test1", "_id" : "6" } }
{ "name" : "document3" }
5) 修改文档
PUT /test1/_bulk
{"update":{"_id":"4"}}
{"doc":{"name":"new_name1"}}
{"update":{"_id":"5"}}
{"doc":{"name":"new_name2"}}
{"update":{"_id":"6"}}
{"doc":{"name":"new_name3"}}
6) 删除文档
DELETE /test1/_bulk
{ "delete": { "_id": "4" }}
{ "delete": { "_id": "5" }}
{ "delete": { "_id": "6" }}
查看分词:
GET /_analyze
{
"analyzer":"standard",
"text":"张三"
}
分词结果
{
"tokens": [
{
"token": "张",
"start_offset": 0,
"end_offset": 1,
"type": "<IDEOGRAPHIC>",
"position": 0
},
{
"token": "三",
"start_offset": 1,
"end_offset": 2,
"type": "<IDEOGRAPHIC>",
"position": 1
}
]
}
一、es 的 refresh 策略设置
ES的索引数据是写入到磁盘上的。但这个过程是分阶段实现的,因为IO的操作是比较费时的。
首先写到内存中,此时是不可搜索的;默认经过 1s 之后会被写入 lucene 的底层文件 segment 中 ,此时才可以搜索到,也就是refresh 之后才会写入磁盘。
在 index ,Update , Delete , Bulk 等作中,可以设置 refresh 的值。取值如下:
那么选择哪种刷新方式呢?
wait_for 和 true 对比,前者每次会积累一定的工作量再去刷新;true 是低效的,因为每次实时刷新会产生很小的 segment,随后这些零碎的小段会被合并到效率更高的大 segment 中。也就是说使用 true 的代价在于,在 index 阶段会创建这些小的 segment,在搜索的时候也是搜索这些小的 segment,在合并的时候去将小的 segment 合并到大的 segment 中。
如果 index.refresh_interval: -1 ,将会禁用刷新,那带上了 refresh=wait_for 参数的请求实际上刷新的时间是未知的。如果 index.refresh_interval 的值设置的比默认值( 1s )更小,比如 200 ms,那即使带上了 refresh=wait_for 参数,请求仍将很快刷新,并且仍然会产生一些低效的segment。
refresh=wait_for 只会影响到当前需要强制刷新的请求,refresh=true 却会影响正在处理的其他请求。所以如果想尽可能小的缩小影响范围时,应该用 refresh=wait_for。
不要在多个请求中对每一条数据都设置 refresh=wait_for , 用bulk 去批量更新,然后在单个的请求中设置 refresh=wait_for 会好一些
二、elasticsearch的Nested嵌套类型
1.概述
我们在使用Elasticsearch做搜索引擎的时候有可能会遇到跨domain查询的场景,比如做一个学生课程管理系统,搜一个学生的名字,想知道该学生的选课情况,这时候我们就可以使用嵌套类型来解决这类问题。
2.对象类型
ES支持对象类型的存储,我们可以把一个对象数组存到某个document的字段内,比如一个课程作为一个document,那么这个课程可以建立一个students字段,存储该课程下的学生object数组。
在ES中,新建一个如下的class_test索引,其中student作为一个object数组类型。
PUT /class_test
{
"mappings":{
"class_test": {
"properties": {
"id": {
"type": "keyword"
},
"name": {
"analyzer": "ik_max_word",
"type": "text"
},
"type":{
"type":"keyword"
},
"student":{
"properties": {
"name":{
"analyzer": "ik_max_word",
"type": "text"
},
"id":{
"type":"keyword"
}
}
}
}
}
},
"settings":{
"index": {
"refresh_interval": "1s",
"number_of_shards": 5,
"max_result_window": "10000000",
"mapper": {
"dynamic": "false"
},
"number_of_replicas": 0
}
}
}
往class_test放入一些数据,现在索引里面一共有两条数据
{
"took" : 1,
"timed_out" : false,
"_shards" : {
"total" : 5,
"successful" : 5,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : 2,
"max_score" : 1.0,
"hits" : [
{
"_index" : "class_test",
"_type" : "class_test",
"_id" : "ijfJ5GoBJeNZPNCWykLR",
"_score" : 1.0,
"_source" : {
"id" : "1",
"name" : "数学课",
"student" : [
{
"id" : "1",
"name" : "张三"
},
{
"id" : "2",
"name" : "李四"
}
]
}
},
{
"_index" : "class_test",
"_type" : "class_test",
"_id" : "Q9NxGGsBa-TqHCWqAaM4",
"_score" : 1.0,
"_source" : {
"id" : "2",
"name" : "语文",
"student" : [
{
"id" : "3",
"name" : "杰克"
},
{
"id" : "4",
"name" : "玛丽"
}
]
}
}
]
}
}
接下来,我们可以使用查询语句对索引进行查询。当我们查询id为1的学生参见的课程的时候,可以查到数学课。
GET /class_test/class_test/_search
{
"query": {
"bool": {
"must": [
{
"match": {
"student.id": "1"
}
}
]
}
}
}
返回结果:
{
"took" : 0,
"timed_out" : false,
"_shards" : {
"total" : 5,
"successful" : 5,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : 1,
"max_score" : 0.2876821,
"hits" : [
{
"_index" : "class_test",
"_type" : "class_test",
"_id" : "ijfJ5GoBJeNZPNCWykLR",
"_score" : 0.2876821,
"_source" : {
"id" : "1",
"name" : "数学课",
"student" : [
{
"id" : "1",
"name" : "张三"
},
{
"id" : "2",
"name" : "李四"
}
]
}
}
]
}
}
当我们查名字叫张三的学生参加的课程的时候,也能查到数学课。
GET /class_test/class_test/_search
{
"query": {
"bool": {
"must": [
{
"match": {
"student.name": "张三"
}
}
]
}
}
}
返回结果:
{
"took" : 4,
"timed_out" : false,
"_shards" : {
"total" : 5,
"successful" : 5,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : 1,
"max_score" : 0.5753642,
"hits" : [
{
"_index" : "class_test",
"_type" : "class_test",
"_id" : "ijfJ5GoBJeNZPNCWykLR",
"_score" : 0.5753642,
"_source" : {
"id" : "1",
"name" : "数学课",
"student" : [
{
"id" : "1",
"name" : "张三"
},
{
"id" : "2",
"name" : "李四"
}
]
}
}
]
}
}
但是当我们查询id为1并且名字叫李四的学生参加的课程时
GET /class_test/class_test/_search
{
"query": {
"bool": {
"must": [
{
"match": {
"student.name": "李四"
}
},
{
"match": {
"student.id": "1"
}
}
]
}
}
}
返回结果:
{
"took" : 6,
"timed_out" : false,
"_shards" : {
"total" : 5,
"successful" : 5,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : 1,
"max_score" : 0.8630463,
"hits" : [
{
"_index" : "class_test",
"_type" : "class_test",
"_id" : "ijfJ5GoBJeNZPNCWykLR",
"_score" : 0.8630463,
"_source" : {
"id" : "1",
"name" : "数学课",
"student" : [
{
"id" : "1",
"name" : "张三"
},
{
"id" : "2",
"name" : "李四"
}
]
}
}
]
}
}
我们发现,出来的结果也是数学课,这就有点奇怪,因为并没有一个id为1并且名字是李四的学生,那就不应该有这节课。这是怎么回事?原来在es内部,object数组类型会被打平,简单来说我们输入的对象数组,实际存储的类型是
"student.id":[1,2], "student.name":[张三,李四]
所以倒排索引的建立,也是按照这种打平的逻辑。这个时候我们可以借助Elasticsearch内的嵌套类型来解决问题。
3.Nested类型
和2中类似的,我们需要建一个测试索引,名字为class,不同的是student有了type字段,为 "type":"nested"。
PUT /class
{
"mappings":{
"class": {
"properties": {
"id": {
"type": "keyword"
},
"name": {
"analyzer": "ik_max_word",
"type": "text"
},
"type":{
"type":"keyword"
},
"student":{
"type":"nested",
"properties": {
"name":{
"analyzer": "ik_max_word",
"type": "text"
},
"id":{
"type":"keyword"
}
}
}
}
}
},
"settings":{
"index": {
"refresh_interval": "1s",
"number_of_shards": 5,
"max_result_window": "10000000",
"mapper": {
"dynamic": "false"
},
"number_of_replicas": 0
}
}
}
我们导入相同的数据,然后用搜索id为1并且名字为李四的学生的课程,这个时候我们看到搜索结果为空:
GET /class/class/_search
{
"query": {
"bool": {
"must": [
{
"nested": {
"path": "student",
"query": {
"bool": {"must": [
{
"match": {
"student.name": "李四"
}
},
{
"match": {
"student.id": "1"
}
}
]}
}
}
}
]
}
}
}
返回结果:
{
"took" : 3,
"timed_out" : false,
"_shards" : {
"total" : 5,
"successful" : 5,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : 0,
"max_score" : null,
"hits" : [ ]
}
}
4.对应学校课程产品的索引建立:
{
"settings": {
"index": {
"number_of_shards": "3",
"number_of_replicas": "1"
}
},
"mappings": {
"_doc": {
"properties": {
"id": {
"type": "long"
},
"course_code": {
"type": "keyword"
},
"school_id": {
"type": "integer"
},
"school_name": {
"type": "keyword"
},
"standard_name": {
"type": "keyword"
},
"course_name": {
"type": "keyword"
},
"dept_code": {
"type": "keyword"
},
"dept_name": {
"type": "keyword"
},
"project_code": {
"type": "keyword"
},
"project_name": {
"type": "keyword"
},
"management_project_code": {
"type": "keyword"
},
"management_project_name": {
"type": "keyword"
},
"template_code": {
"type": "keyword"
},
"template_name": {
"type": "keyword"
},
"product_system_code": {
"type": "keyword"
},
"product_system_name": {
"type": "keyword"
},
"product_level": {
"type": "keyword"
},
"category_code": {
"type": "keyword"
},
"category_name": {
"type": "keyword"
},
"category_all_name": {
"type": "keyword"
},
"system_type": {
"type": "integer"
},
"status": {
"type": "integer"
},
"classbook_version": {
"type": "keyword"
},
"class_teaching_content": {
"type": "keyword"
},
"period": {
"type": "integer"
},
"make_point_type": {
"type": "integer"
},
"ext_template_code": {
"type": "keyword"
},
"ext_template_name": {
"type": "keyword"
},
"is_exam_month": {
"type": "integer"
},
"is_exam_date": {
"type": "integer"
},
"course_intro": {
"type": "keyword"
},
"student_level": {
"type": "keyword"
},
"teaching_objective": {
"type": "keyword"
},
"teaching_program": {
"type": "keyword"
},
"teaching_arrangement": {
"type": "keyword"
},
"course_feature": {
"type": "keyword"
},
"class_discipline": {
"type": "keyword"
},
"min_class_minutes": {
"type": "integer"
},
"max_class_minutes": {
"type": "integer"
},
"other": {
"type": "keyword"
},
"enabled": {
"type": "integer"
},
"course_properties": {
"include_in_parent": true,
"type": "nested",
"properties": {
"property_code": {
"type": "keyword"
},
"property_text": {
"type": "keyword"
},
"value_code": {
"type": "keyword"
},
"value_text": {
"type": "keyword"
},
"is_default": {
"type": "integer"
}
}
},
"inner_info": {
"type": "object",
"enabled": true,
"properties": {
"quarter_list": {
"type": "keyword"
},
"subject_list": {
"type": "keyword"
},
"grade_list": {
"type": "keyword"
}
}
},
"outward_info": {
"type": "object",
"enabled": true,
"properties": {
"grade_outward_name": {
"type": "keyword"
},
"quarter_outward_name": {
"type": "keyword"
},
"subject_outward_name": {
"type": "keyword"
},
"product_level_outward_name": {
"type": "keyword"
}
}
},
"specification_list": {
"include_in_parent": true,
"type": "nested",
"properties": {
"version_name": {
"type": "keyword"
},
"teaching_method_code": {
"type": "keyword"
},
"teaching_method_name": {
"type": "keyword"
},
"continue_type": {
"type": "integer"
},
"class_hour": {
"type": "double"
},
"class_time": {
"type": "integer"
},
"class_hour_sum": {
"type": "double"
},
"class_capacity_code": {
"type": "keyword"
},
"unit_price": {
"type": "double"
},
"price_sum": {
"type": "double"
}
}
},
"submitter_name": {
"type": "keyword"
},
"submit_time": {
"type": "date",
"format": "yyyy-MM-dd HH:mm:ss"
},
"approver_name": {
"type": "keyword"
},
"approver_email": {
"type": "keyword"
},
"approve_time": {
"type": "date",
"format": "yyyy-MM-dd HH:mm:ss"
},
"is_sign_deleted": {
"type": "integer"
},
"is_deleted": {
"type": "integer"
},
"create_time": {
"type": "date",
"format": "yyyy-MM-dd HH:mm:ss"
},
"update_time": {
"type": "date",
"format": "yyyy-MM-dd HH:mm:ss"
},
"latest_update_time": {
"type": "date",
"format": "yyyy-MM-dd HH:mm:ss"
}
}
}
}
}
在使用nested查询的时候,比如当我们需要查询年级=九年级,并且科目=英语的学校课程产品时:
当我们使用同一个QueryBuilder,会变成同一路径下,条件的叠加;
错误示例:
boolQuery.must(QueryBuilders.nestedQuery("course_properties", QueryBuilders.boolQuery().must(QueryBuilders.termQuery("course_properties.property_code", "80")).must(QueryBuilders.termsQuery("course_properties.value_text", req.getGradeNames())).must(QueryBuilders.termQuery("course_properties.property_code", "42")).must(QueryBuilders.termsQuery("course_properties.value_text", req.getSubjectNames())), ScoreMode.None));
这里创建一个布尔查询(boolQuery),该查询必须(must)满足一个嵌套查询的条件。
该嵌套查询是在"course_properties"这个嵌套对象中进行,它包含了一个布尔查询,这个布尔查询必须满足四个条件:
"course_properties.property_code"字段的值必须是"80"(使用termQuery进行精确匹配)
"course_properties.value_text"字段的值必须在req.getGradeNames()返回的列表中(使用termsQuery进行多值匹配)
"course_properties.property_code"字段的值必须是"42"(使用termQuery进行精确匹配)
"course_properties.value_text"字段的值必须在req.getSubjectNames()返回的列表中(使用termsQuery进行多值匹配)
这段代码的问题在于有一个逻辑错误。因为它在同一个布尔查询中对"course_properties.property_code"字段进行了两次must匹配,一次是匹配"80",另一次是匹配"42",这两者是互斥的,
所以这个查询无法返回任何结果。需要将这两组查询条件分别放在两个不同的嵌套查询中。
属性是年级,属性是科目,属性值是九年级,属性值是英语,这样查询出来的记录当然是空
vs
使用不同的QueryBuilder:
if (!CollectionUtils.isEmpty(req.getGradeNames())) {
boolQuery.must(QueryBuilders.nestedQuery("course_properties", QueryBuilders.boolQuery().must(QueryBuilders.termQuery("course_properties.property_code", "80")).must(QueryBuilders.termsQuery("course_properties.value_text", req.getGradeNames())), ScoreMode.None));
}
if (!CollectionUtils.isEmpty(req.getSubjectNames())) {
boolQuery.must(QueryBuilders.nestedQuery("course_properties", QueryBuilders.boolQuery().must(QueryBuilders.termQuery("course_properties.property_code", "42")).must(QueryBuilders.termsQuery("course_properties.value_text", req.getSubjectNames())), ScoreMode.None));
}
这里创建了一个布尔查询(boolQuery),它必须满足两个嵌套查询(nestedQuery)的条件。
第一个嵌套查询在"course_properties"这个嵌套对象中进行,它包含了一个布尔查询,这个布尔查询必须满足两个条件:
"course_properties.property_code"字段的值必须是"80"(使用termQuery进行精确匹配)
"course_properties.value_text"字段的值必须在req.getGradeNames()返回的列表中(使用termsQuery进行多值匹配)
第二个嵌套查询也在"course_properties"这个嵌套对象中进行,它包含了一个布尔查询,这个布尔查询必须满足两个条件:
"course_properties.property_code"字段的值必须是"42"(使用termQuery进行精确匹配)
"course_properties.value_text"字段的值必须在req.getSubjectNames()返回的列表中(使用termsQuery进行多值匹配)
这段代码查找的是"course_properties.property_code"为"80"且"course_properties.value_text"在指定年级名称列表中,和"course_properties.property_code"为"42"且"course_properties.value_text"在指定科目名称列表中的所有"course_properties"嵌套对象的交集。
通过这种方式查询【属性是年级,属性值是九年级】的记录和【属性是科目,属性值是英语】的记录的交集
三、elasticsearch动态映射
1.映射的增删改查
方式一:Elasticsearch可以根据数据中的新字段来创建新的映射;
方式二:当然,在正式数据写入之前我们可以自己定义Mapping,等数据写入时,会按照定义的Mapping进行映射。如果后续数据有其他字段时,Elasticsearch会自动进行处理。
curl -XPUT 'http://localhost:9200/logstash-2016.01.01/_mapping' -d '
{
"mappings" : {
"syslog" : {
"properties" : {
"@timestamp" : {
"type" : "date"
},
"message" : {
"type" : "string"
},
"pid" : {
"type" : "long"
}
}
}
}
}
'
在这里需要注意一下,我们已经存在的索引是不可以更改它的映射的,配置打开时,对于存在的索引,只有新字段出现时,Elasticsearch才会自动进行处理。
2.映射的删除
Delete
虽然写入数据时Elasticsearch会自动的添加映射进行处理,但是删除数据并不会删除数据的映射:
#curl -XDELETE 'http://localhost:9200/logstash-2016.01.01/syslog',虽然删除了syslog下面的全部数据,但是syslog的映射还在;
删除映射的命令:
#curl -XDELETE 'http://localhost:9200/logstash-2016.01.01/_mapping'
删除索引的话映射也会删除
#curl -XDELETE 'http://localhost:9200/logstash-2016.01.01'
3.使用过程中遇到过的异常
在api中查询elasticsearch,报错如下
Elasticsearch exception [type=search_phase_execution_exception, reason=all shardsElasticsearcfailed]
caurse:Elasticsearch exception [type=illegal_argument_exception, reason=Fielddata is disabled on text field
Set fielddata=true on [update_time] in order to load fielddata in memory by uninverting the inverted
原因:
Elasticsearch5.x版本中对Text类型进行聚合时提示illegal_argument_exception
于是检查了索引的类型:发现都是text类型,而我在建索引的时候,分明没有建text类型的字段,后来发现,我创建了索引,没有指定别名(+_es),于是在往索引里面写数据的时候,是往索引别名里面写数据,于是就根据数据自动创建了索引,类型都是默认text类型,而我的查询是有根据几个字段进行聚合的操作的,自然就会报出如上错误。
四、Text vs keyword
随着ElasticSearch 5.0的到来, 同时也迎来了该版本的重大特性之一: 移除了string类型. 这个变动的根本原因是string类型会给我们带来很多困惑: 因为ElasticSearch对字符串拥有两种完全不同的搜索方式. 你可以按照整个文本进行匹配, 即关键词搜索(keyword search), 也可以按单个字符匹配, 即全文搜索(full-text search). 对ElasticSearch稍有了解的人都知道, 前者的字符串被称为not-analyzed字符, 而后者被称作analyzed字符串.
事实上, 同一种类型用于应对两种不同的使用场景是会让人崩溃的, 因为有些选项只对其一的场景设置有效.例如position_increment_gap对not-analyzed字符就不会起作用, 而像ignore_above对于analyzed字符串就很难区分它到底是对整个字符串的值有效还是对单独的每个分词有效(在这种场景, ignore_above确实只对整个字符串值有效)。
为了避免上述尴尬, string字段被拆分成两种新的数据类型: text用于全文搜索的, 而keyword用于关键词搜索.
1.新的默认类型
做了这个类型分解之后, 我们对string字段的默认dynamic mappings 也做了改变. 在以前刚接接触ElasticSearch时, 如果需要对某个字段的所有取值做聚合, 你不得不对这些数据重做索引. 假如你正在处理的文档中包含一个city字段. 对这个字段做聚合的话会分别给出new和york的总数, 而非我们通常期望的New York的总数.让人沮丧的是为了达到我们希望的结果, 我们必须对这个字段重新进行索引.
为了不让事情变得这么糟糕, ElasticSearch决定从Logstash中借取思路: 字符串将默认被同时映射成text和keyword类型. 例如对下面的文档进行索引后:
{
"foo": "bar"
}
ElasticSearch将会为你创建下面的动态映射(dynamic mappings):
{
"foo": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
}
}
当然, 基于这个映射你即可以在foo字段上进行全文搜索, 也可以通过foo.keyword字段实现关键词搜索及数据聚合.
禁用这个特性也很方便: 你只需要在定义mapping时显式声明字符串字段的类型或者使用一个动态模板(dynamic template)来匹配你所有的字符串字段即可. 例如通过下面的dynamic template就可以恢复到在ElasticSearch 2.x中使用的dynamic template的效果:
{
"match_mapping_type": "string",
"mapping": {
"type": "text"
}
}
elasticsearch中keyword类型和text类型的区别
1、text类型:
①默认会被分词;
②不能用来过滤、排序和聚合;
③可以被分词后建立索引,被检索;
2、keyword类型:
①默认不会分词;
②可以用来过滤、排序和聚合;
2.Elasticsearch 的聚合功能(aggregations)
Elasticsearch 有一个功能叫聚合(aggregations):
"answer": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
执行:
"aggs": {
"answer": {
"terms": {
"field": "answer"
}
},
报错:
"type": "illegal_argument_exception",
"reason": "Fielddata is disabled on text fields by default. Set fielddata=true on [answer] in order to load fielddata in memory by uninverting the inverted index. Note that this can however use significant memory."
原因:
这个问题是查询时候一个比较常见的问题。
如果你的索引中,该字段不是数值型(integer、double、long)或者keyword,而是text类型的,在使用该字段进行查询、排序、聚合时候,就会出现Fielddata is disabled on text fields by default.
解决办法:
1.设置sub-field如下所示:
"aggs": {
"answer": {
"terms": {
"field": "answer.keyword"
}
},
或者
2:索引字段类型还是text,但是加mapping中fielddata=true。这种不推荐,因为这样加载时候,占用的内存。
搜了一下应该是5.x后对排序,聚合这些操作用单独的数据结构(fielddata)缓存到内存里了,需要单独开启,简单来说就是在聚合前执行如下操作:
PUT my_index/_mapping/my_type
{
"properties": {
"my_field": {
"type": "text",
"fielddata": true
}
}
}
再执行上面的命令,返回结果如下:
{
"took": 65,
"timed_out": false,
"_shards": {
"total": 5,
"successful": 5,
"skipped": 0,
"failed": 0
},
"hits": {
"total": 3,
"max_score": 1.0,
"hits": [{
"_index": "megacorp",
"_type": "employee",
"_id": "2",
"_score": 1.0,
"_source": {
"first_name": "Jane",
"last_name": "Smith",
"age": 32,
"about": "I like to collect rock albums",
"interests": ["music"]
}
}, {
"_index": "megacorp",
"_type": "employee",
"_id": "1",
"_score": 1.0,
"_source": {
"first_name": "John",
"last_name": "Smith",
"age": 25,
"about": "I love to go rock climbing",
"interests": ["sports", "music"]
}
}, {
"_index": "megacorp",
"_type": "employee",
"_id": "3",
"_score": 1.0,
"_source": {
"first_name": "Douglas",
"last_name": "Fir",
"age": 35,
"about": "I like to build cabinets",
"interests": ["forestry"]
}
}]
},
"aggregations": {
"all_interests": {
"doc_count_error_upper_bound": 0,
"sum_other_doc_count": 0,
"buckets": [{
"key": "music",
"doc_count": 2
}, {
"key": "forestry",
"doc_count": 1
}, {
"key": "sports",
"doc_count": 1
}]
}
}
}
五、一些基本操作
1、创建一个索引
curl -XPUT -u elastic:elastic "172.22.29.126:9200/jiaowu_product_class_capacity?pretty" -H 'Content-Type: application/json' -d "@jiaowu_product_class_capacity.txt"
2、删除一个索引
curl -XDELETE -u elastic:elastic "172.22.29.126:9200/jiaowu_product_class_capacity_es?pretty"
3、查看一个索引
curl -u elastic:elastic -XGET 'http://172.22.29.126:9200/jiaowu_d_product_system_level_es/_search?pretty'
4、给索引创建别名
curl -XPOST -u elastic:elastic "172.22.29.126:9200/_aliases" -H 'Content-Type: application/json' -d '{"actions":[{"add":{"index":"jiaowu_product_class_capacity","alias":"jiaowu_product_class_capacity_es"}}]}'
curl -XPUT -u es-jwcourseproduct-wr:CHna1NCi19 "172.22.31.203:9212/_aliases" -H 'Content-Type:application/json' -d "@alias.txt"
别名:
{
"actions": [
{
"add": {
"index": "jiaowu_product_class_capacity",
"alias": "jiaowu_product_class_capacity_es"
}
}
]
}
5、给索引添加一个字段
curl -XPUT -u elastic:elastic "172.22.29.126:9200/jiaowu_d_product_system_level/_doc/_mapping" -H 'Content-Type:application/json' -d "@jiaowu_product_system_level_add_id.txt"
{
"_doc": {
"properties": {
"product_system_id": {
"type": "integer"
},
"product_level_id": {
"type": "integer"
}
}
}
}
6、删除索引下的所有数据
curl -XPOST -u elastic:elastic '172.22.29.126:9200/jiaowu_d_product_system_level/_doc/_delete_by_query?refresh&slices=5&pretty' -H 'Content-Type: application/json' -d'{"query": {"match_all": {}}}'
7、查看索引的创建mapping
curl -XGET -u elastic:elastic "172.22.29.126:9200/jiaowu_product_base_info/_mapping?pretty"
8、根据条件查询索引中的数据
curl -XPOST -u jiaowu-user:MY6rr3yvrc "172.22.79.15:9203/jiaowu_schoolcourse_es/_search" -H 'Content-Type:application/json' -d '{"query": {"bool": {"must": [{"term": {"course_code": "422982"}}, {"term": {"school_id": "3"}}],"must_not": [],"should"[]}},"size": 100}'
es里给索引创建别名的好处
在Elasticsearch(ES)中给索引创建别名(Aliases)具有多个显著的好处,这些好处主要体现在索引管理、查询优化、系统维护和升级等方面。以下是详细的好处归纳:
1. 简化索引管理
-
抽象索引名称:别名允许使用用户定义的名称来抽象底层的索引名称,这样应用程序就可以通过一致且有意义的名称来引用索引,而不必直接暴露或依赖底层的索引名称。
-
无缝切换:在需要替换或更新索引时,可以简单地更新别名指向新的索引,而无需修改应用程序中的索引名称。这大大简化了索引的替换和更新过程,减少了出错的可能性。
2. 查询优化
-
查询路由:通过别名,可以实现查询路由,将查询定向到特定的索引或索引组。这有助于优化查询性能,特别是在处理大型数据集时。
-
数据分区:别名可以与具有共同特征的索引子集关联起来,如时间范围或类别,从而缩小搜索空间并提高搜索性能。
3. 系统维护和升级
-
平滑升级:在索引升级或迁移过程中,使用别名可以使过程更加平滑。通过在升级期间将新索引添加到别名,并逐步将查询和写入流量切换到新索引,可以实现无缝升级,减少或消除对业务的影响。
-
蓝绿部署:在蓝绿部署策略中,别名特别有用。通过为索引的不同版本分配别名,可以轻松实现无缝切换流量从旧版本到新版本,同时确保在部署过程中零停机时间。
4. 灵活性和可扩展性
-
动态添加或移除索引:别名允许动态地添加或移除与别名关联的索引,这为索引的扩展和收缩提供了极大的灵活性。
-
支持复杂查询场景:别名可以与过滤器或路由值关联,为搜索操作提供额外的灵活性。通过在别名中定义过滤器,可以对符合特定条件的文档子集执行搜索或聚合。
5. 数据组织
-
索引分组:别名可以将多个索引组合成一个逻辑组,便于管理和查询。例如,可以将过去几个月的日志索引组合成一个别名,以便统一查询。
总结
综上所述,Elasticsearch中给索引创建别名的好处包括简化索引管理、优化查询性能、支持平滑的系统维护和升级、提供灵活性和可扩展性,以及改进数据组织。这些好处使得别名成为Elasticsearch中一个不可或缺的功能,对于提高系统的可维护性、性能和可用性具有重要意义。
mysql数据表是怎么在es中抽取对应的列来构建的
MySQL数据表在Elasticsearch(ES)中的抽取和构建过程,通常涉及几个关键步骤,包括数据同步、索引创建、数据映射和查询优化。以下是一个概括性的流程说明:
1. 数据同步
数据同步是将MySQL中的数据导入到Elasticsearch的基础。有多种方法可以实现这一过程,包括但不限于以下几种:
- Logstash:Logstash是一个开源的服务器端数据处理管道,可以实时地从MySQL等数据源中抽取数据,并转换数据格式,然后将其推送到Elasticsearch中。Logstash通过配置文件来定义数据源、数据过滤器和输出目标。
- Kettle(Pentaho Data Integration):Kettle是一个强大的ETL(Extract, Transform, Load)工具,可以用来从MySQL中抽取数据,经过转换后加载到Elasticsearch中。Kettle提供了直观的图形界面,方便用户定义数据抽取、转换和加载的流程。
- 自定义脚本:如果数据量不大或需要更灵活的数据处理逻辑,可以使用Python、Java等编程语言编写自定义脚本来实现数据同步。这些脚本可以定期运行,从MySQL查询数据,并更新到Elasticsearch中。
2. 索引创建
在Elasticsearch中,索引是存储数据的逻辑命名空间,相当于关系数据库中的数据库或表。在抽取MySQL数据之前,需要在Elasticsearch中创建相应的索引,并定义索引的结构(如字段类型、分词器等)。索引的创建可以通过Elasticsearch的REST API来完成,也可以通过Kibana等管理工具进行图形化操作。
3. 数据映射
数据映射是将MySQL中的字段与Elasticsearch中的索引字段进行对应的过程。在抽取数据时,需要确保MySQL中的每个字段都能正确地映射到Elasticsearch索引的相应字段上。这通常需要在数据同步工具或脚本中进行配置。
- 字段类型匹配:确保MySQL中的数据类型与Elasticsearch中的字段类型兼容。例如,MySQL中的INT类型可以映射到Elasticsearch的integer或long字段。
- 文本处理:对于文本字段,需要考虑是否需要进行分词处理。Elasticsearch提供了强大的文本分析能力,可以通过定义分词器来优化文本搜索。
- 索引优化:根据业务需求,可以对某些字段进行索引优化,以提高查询性能。例如,对于经常用于过滤或排序的字段,可以设置为索引字段。
4. 查询优化
在数据抽取和索引创建完成后,还需要对Elasticsearch中的查询进行优化,以确保能够快速、准确地检索到所需的数据。查询优化包括但不限于以下几个方面:
- 使用合适的查询类型:根据查询需求选择合适的查询类型,如match查询、term查询等。
- 优化查询语句:避免在查询语句中使用复杂的子查询和大量的JOIN操作,以减少查询时间。
- 缓存频繁查询:对于频繁执行的查询,可以使用Elasticsearch的缓存机制来提高查询效率。
总结
MySQL数据表在Elasticsearch中的抽取和构建过程是一个涉及数据同步、索引创建、数据映射和查询优化的复杂过程。通过选择合适的同步工具、定义合理的索引结构、进行准确的数据映射和优化查询语句,可以实现MySQL数据在Elasticsearch中的高效存储和检索。
es实现快速大数据量检索的底层原理
Elasticsearch(ES)实现快速大数据量检索的底层原理主要依赖于其分布式架构、分片与副本机制、以及底层Lucene库构建的倒排索引技术。以下是详细解析:
1. 分布式架构
- 节点与集群:ES由多个节点(Node)组成,这些节点可以组成一个集群(Cluster)。每个节点都可以处理读写请求,并且数据在集群中的节点之间进行分布和复制,以实现高可用性和扩展性。
- 分布式处理:通过启动多个ES实例形成集群,ES可以将一个索引(Index)分成多个分片(Shard),每个分片存放部分数据。这种分布式存储方式可以并行处理查询请求,显著提高性能。
2. 分片与副本机制
- 分片:ES将索引划分为多个分片,每个分片可以独立存储和处理数据。这种机制使得ES能够水平扩展,处理更大量的数据。
- 副本:每个分片可以有多个副本(Replica),用于提供数据冗余、故障恢复和读取负载均衡。写请求会打到主分片(Primary Shard)上,主分片会将数据同步给副本分片。如果主分片不可用,其中一个副本分片会重新成为主分片,保证系统的高可用性。
3. 倒排索引
- Lucene库:ES建立在Lucene库之上,Lucene构建倒排索引来加速搜索过程。倒排索引将文档中的单词映射到包含这些单词的文档列表,从而实现快速查找和检索。
- 索引过程:
- 文档被提交到ES后,首先通过分词器进行处理,文档内容被转换成一系列关键词的集合。
- ES根据这些关键词建立文档-词语矩阵,并构建倒排索引。倒排索引包括Term Dictionary(词项字典)和Posting List(词项列表)。
- Term Dictionary存储所有唯一的词项及其相关信息(如词项在文档中的位置、频率等),而Posting List存储了包含该词项的所有文档的列表。
- 检索过程:
- 当用户发起查询时,ES会根据查询条件在倒排索引中查找匹配的词项。
- 遍历匹配的词项对应的Posting List,找出包含这些词项的文档。
- 对这些文档进行合并、排序等操作,最终将结果返回给用户。
4. 性能优化
- 缓存机制:ES的搜索引擎严重依赖于底层的filesystem cache。通过给filesystem cache更多的内存,尽量让内存可以容纳所有的索引数据文件,可以显著提高搜索性能。
- 冷热数据分离:将冷热数据放到不同的节点中,可以进一步优化查询性能。
- 数据预热:提前访问热点数据,将热点数据加载到内存中,也可以提高查询效率。
综上所述,ES通过分布式架构、分片与副本机制以及底层Lucene库构建的倒排索引技术,实现了对大数据量的快速检索。这些机制共同协作,确保了ES在处理大规模数据集时的高性能和高可用性。