为了方便理解,这里只是类比
1、elasticsearch的index就好比mysql的database.
2、elasticsearch的type就好比mysql的table.
在es中存储数据的基本单位是索引,其中mapping types在ElasticSearch 7. X 已被完全移除,详细说明可以参考官方文档),有的更直白的来说一个索引差不多就是相当于是 mysql 里的一张表,mapping 就是这个type的表结构定义index -> type -> mapping -> document -> field。
ElasticSearch 设计的理念是分布式搜索引擎,底层基于 lucene。主要核心思想在多台机器上启动多个ES进程实例,组成了一个ES集群。
一、当执行数据插入的时候,若有重复数据的插入需要覆盖而不是追加>>>
1、elasticsearch的插入方法支持覆盖(该条数据记录后的主键(唯一索引)若有重的会覆盖已存在的,而不会追加数据或造成数据冗余)。
2、mysql可以使用插入方法replace into(覆盖:先判别该条记录是否存在,若存在则先删除再增加) 代替 insert into(纯插入)。
二、es读写数据的过程>>>
es官网:https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-index_.html#index-refresh
首先我们来了解下索引, Elasticsearch数据存储于一个或多个索引中,索引是具有类似特性的文档的集合。类比传统的关系型数据库领域来说,索引相当于SQL中的一个数据库,或者说是一个数据存储方案(schema)。索引由其名称(全小写字符)进行标识(可以根据时间),并通过引用此名称完成文档的创建、搜索、更新及删除等相关操作。
es写数据过程:
- 客户端向Node(协调节点-
coordinating node
)发送索引文档请求。 - Node对document 进行路由,根据文档ID(_id字段)计算出该文档所属的shard,然后请求路由到对应的 node(有 primary shard)。
- 实际的 node 上的
primary shard
处理请求,然后再将数据同步到replica node,
当所有的Replicas报告成功后,node向请求的Node发送成功报告 ,Node发现primary node
和所有replica node
都完成之后,就返回响应结果给客户端-报告至Client。
注:在了解索引的写操作后可知,更新、索引、删除文档都是写操作,这些操作必须在primary shard完全成功后才能拷贝至其对应的replicas上,在默认情况下主分片等待所有备份完成索引后才返回客户端。即当客户端收到执行成功后,操作已经在primary shard和所有的replica shards上执行成功了。
es读数据过程:
- 客户端发送Get请求到任意一个 Node(协调节点-
coordinating node
)。 - Node对doc id进行哈希路由,将请求转发到对应的 node(
round-robin
轮询策略轮询所有的replica shards)
,在primary shard
以及其所有 replica 中随机选择一个,可让读请求负载均衡。 - 实际的 node 返回 document 给 Node,Node返回document给客户端。
注:一个文档可以在primary shard和所有的replica shard上读取。
es搜索数据过程:
- 客户端发送请求到一个 Node(协调节点-
coordinating node
)。 - Node将搜索请求转发到所有的 shard 对应的
primary shard
或replica shard
。 - query phase:每个 shard 将自己的搜索结果(
doc id
)返回给Node,由Node进行数据的合并、排序、分页等操作,产生出最终结果。 - fetch phase:接着由Node节点根据
doc id
去各个node上拉取实际的document
数据,最终返回document给客户端。
总:写请求是写入 primary shard,然后同步给所有的 replica shard;读请求可以从 primary shard 或 replica shard 读取-采用的是随机轮询算法。
接下来,我们来了解下es的底层原理,
es写数据底层原理:
数据先是写入内存 buffer,然后每隔 1s,会将数据 refresh 到 os cache,到了 os cache 数据就能被搜索到(准实时-es 从写入到能被搜索到-中间有 1s 的延迟,其实这里也可以通过 es 的 restful api
或者 java api
,手动执行一次 refresh 操作-手动将 buffer 中的数据刷入 os cache
中,让数据立马就可以被搜索到。只要数据被输入 os cache
中,buffer 就会被清空,不需要保留 buffer ,数据在 translog 里面已经持久化一份到磁盘)。每隔 5s,将数据写入 translog 文件(若机器宕机,内存数据全没,最多会有 5s 的数据丢失),当translog 大到一定程度,或者默认每隔 30mins,会触发 commit 操作,将缓冲区的数据都 flush 到 segment file 磁盘文件中。也可以通过 es api,手动执行 flush 操作,手动将 os cache 中的数据 fsync 强刷到磁盘上去。
注:核心->refresh、flush、translog、merge,当数据写入 segment file 之后,同时就建立好了倒排索引。
那了解如何让新添加的文档对搜索可见的呢?底层的Apache Lucene工具包是如何处理?
在索引过程中,新添加的文档都是写入到段(segments)中。而每个段都是有着独立的索引结构,这就意味着查询与索引两个过程是可以并行存在的,索引过程中,系统会不定期创建新的段。Apache Lucene通过在索引目录中创建新的segments_N文件来标识新的段。段创建的过程就称为索引的提交。Lucene可以一种安全的方式实现索引的提交——我们可以确定段文件要么全部创建成功,要么失败。如果错误发生,我们可以确保索引状态的一致性。
所以对新添加文档到索引中,但是没有提交。这就是它的工作方式。然而索引数据的提交也不能保证数据是搜索可见的。Lucene工具包使用一个名为Searcher的抽象类来读取索引。索引提交操作完成后,Searcher对象需要重新打开才能加载到新创建的索引段。这整个过程称为更新。出于性能的考虑,es会将推迟开销巨大的更新操作,默认情况下,单个文档的添加并不会触发搜索器的更新,Searcher对象会每秒更新一次。在一些应用程序中,若需要更频繁的更新,可以使用es api强制更新:
curl –XGET localhost:9200/test/_refresh
也可以修改Searcher对象默认的更新时间,Searcher对象的默认更新时间可以通过使用index.refresh_interval
参数来修改,该参数无论是添加到ElasticSearch的配置文件中或者使用update settings API都可以生效:
curl -XPUT localhost:9200/test/_settings -d '{
"index" : {
"refresh_interval" : "5m"
}
}'
则使Searcher每5秒钟自动更新一次,但是在更新两个时间点之间添加到索引的数据对查询是不可见的。其中,更新操作的时间开销和内存开销都很大,更新的时间设置越长,索引的速度越快,可以通过把值设置为-1关闭,但需要在索引完成后改回原来的值。
最后,我们来看一下事务日志的配置,当默认的配置无法满足业务需求时,可以通过设置es的配置参数:
curl -XPUT localhost:9200/test/_settings -d '{
"index" : {
"translog.disable_flush" : true
}
}'
当然也可以在elasticsearch.yml中调整参数,上述就是一般用于导入大量数据到索引前执行,大大提升索引阶段的性能,数据索引完毕后再开启事务日志的自动刷新。
而实时Get操作从索引中获取数据,事先会从事务日志中查看是否有更新的版本,若存在没有刷新的文档,索引中的数据就会被忽略,返回更新版本的文档-事务日志中的文档,即近实时GET操作-提供了返回早期版本文档,包括未提交文档的功能。