草庐IT

ES数据存储和集群路由原理

xianghan收藏册 2025-02-18 原文

ES数据存储

1、存储流程

为了将数据添加到Elasticsearch,我们需要索引(index)——一个存储关联数据的地方。实际上,索引   只是一个用来指向一个或多个分片(shards)逻辑命名空间(logical namespace)”.一个分片(shard)是一个最小级别工作单元(worker unit)”,它只是保存了索引中所有数据的一部分。

当一个写请求发送到 es 后,es 将数据写入 memory buffer 中,并添加事务日志( translog )。如果每次一条数据写入内存后立即写到硬盘文件上,由于写入的数据肯定是离散的,因此写入硬盘的操  作也就是随机写入了。硬盘随机写入的效率相当低,会严重降低es的性能。

因此 es 在设计时在 memory buffer 和硬盘间加入了 Linux 的页面高速缓存( File system cache 来提高 es 的写效率。

当写请求发送到 es 后,es 将数据暂时写入 memory buffer 中,此时写入的数据还不能被查询到。默认设置下,es 1秒钟将 memory buffer 中的数据 refresh Linux File system cache ,并清memory buffer ,此时写入的数据就可以被查询到了。

File system cache 依然是内存数据,一旦断电,则 File system cache 中的数据全部丢失。默认设置下,es 30分钟调用 fsync File system cache 中的数据 flush 到硬盘。因此需要通过translog 来保证即使因为断电 File system cache 数据丢失,es 重启后也能通过日志回放找回丢失的数据。

translog 默认设置下,每一个 index delete update bulk 请求都会直接 fsync 写入硬盘。为了保证 translog 不丢失数据,在每一次请求之后执行 fsync 确实会带来一些性能问题。对于一些允许丢失几秒钟数据的场景下,可以通过设置 index.translog.durability index.translog.sync_interval 参数让 translog 每隔一段时间才调用 fsync 将事务日志数据写入硬盘。

2、动态更新索引

以在线动态服务的层面看,要做到实时更新条件下数据的可用和可靠,就需要在倒排索引的基础上,再  做一系列更高级的处理。

其实总结一下 Lucene 的处理办法,很简单,就是一句话:新收到的数据写到新的索引文件里

Lucene 把每次生成的倒排索引,叫做一个段(segment)。然后另外使用一个 commit  文件,记录索引内所有的 segment。而生成 segment 的数据来源,则是内存中的 buffer。也就是说,动态更新过程如下:

    1. 当前索引有 3 segment 可用。索引状态如图 2-1

 2-1

    1. 新接收的数据进入内存 buffer索引状态如图 2-2

2-2

    1. 内存 buffer 刷到磁盘,生成一个新的 segmentcommit 文件同步更新。索引状态如图 2-3

图 2-3

3、利用磁盘缓存实现的准实时检索

既然涉及到磁盘,那么一个不可避免的问题就来了:磁盘太慢了!对我们要求实时性很高的服务来说,  这种处理还不够。所以,在第 3 步的处理中,还有一个中间状态:

  1. 内存 buffer 生成一个新的 segment,刷到文件系统缓存中,Lucene 即可检索这个新 segment索引状态如图 2-4

图 2-4

  1. 文件系统缓存真正同步到磁盘上,commit 文件更新。达到图 2-3 中的状态

​​​这一步刷到文件系统缓存的步骤,在 Elasticsearch 中,是默认设置为 1 秒间隔的,对于大多数应用来

说,几乎就相当于是实时可搜索了。Elasticsearch 也提供了单独的

间隔还不满意的,可以主动调用该接口来保证搜索可见。

接口,用户如果对 1 秒

注:5.0 中还提供了一个新的请求参数: ?refresh=wait_for ,可以在写入数据后不强制刷新但一直等到刷新才返回。

不过对于 Elastic Stack 的日志场景来说,恰恰相反,我们并不需要如此高的实时性,而是需要更快的写入性能。所以,一般来说,我们反而会通过/_settings接口或者定制 template 的方式,加大参数:

# curl -XPOST http://127.0.0.1:9200/logstash-2015.06.21/_settings -d'

{ "refresh_interval": "10s" }

如果是导入历史数据的场合,那甚至可以先完全关闭掉:

# curl -XPUT http://127.0.0.1:9200/logstash-2015.05.01 -d'
{
"settings" : { "refresh_interval": "-1"
}
}'

在导入完成以后,修改回来或者手动调用一次即可:

# curl -XPOST http://127.0.0.1:9200/logstash-2015.05.01/_refresh

4translog 提供的磁盘同步控制

既然 refresh 只是写到文件系统缓存,那么第 4 步写到实际磁盘又是有什么来控制的?如果这期间发生主机错误、硬件故障等异常情况,数据会不会丢失?

这里,其实有另一个机制来控制。 Elasticsearch 在把数据写入到内存 buffer  的同时,其实还另外记录 了一个 translog 日志。也就是说,第 2 步并不是图 2-2 的状态,而是像图 2-5 这样:

2-5

在第 3 和第 4 步, refresh 发生的时候, translog 日志文件依然保持原样,如图 2-6

2-6

也就是说,如果在这期间发生异常,Elasticsearch 会从 commit 位置开始,恢复整个 translog 文件中的记录,保证数据一致性。

等到真正把 segment 刷到磁盘,且 commit 文件进行更新的时候, translog 文件才清空。这一步,叫做 flush。同样,Elasticsearch 也提供了   /_flush             接口

对于 flush 操作,Elasticsearch 默认设置为:每 30 分钟主动进行一次 flush,或者当 translog 文件大小大于 512MB (老版本是 200MB)时,主动进行一次 flush。这两个行为,可以分别通过

index.translog.flush_threshold_period

index.translog.flush_threshold_size

参数修 改。

如果对这两种控制方式都不满意,Elasticsearch 还可以通过

index.translog.flush_threshold_ops

参数,控制每收到多少条数据后 flush 一次。

5translog 的一致性

索引数据的一致性通过 translog 保证。那么 translog 文件自己呢?

默认情况下,Elasticsearch 5 秒,或每次请求操作结束前,会强制刷新 translog 日志到磁盘上。

后者是 Elasticsearch 2.0 新加入的特性。为了保证不丢数据,每次 indexbulkdeleteupdate 成的时候,一定触发刷新 translog 到磁盘上,才给请求返回 200 OK。这个改变在提高数据安全性的同时当然也降低了一点性能。

如果你不在意这点可能性,还是希望性能优先,可以在 index template 里设置如下参数:

{

"index.translog.durability": "async"

}

五、集群路由

1、 文档路由

1document路由到shard分片上,就叫做文档路由 ,如何路由? 2)路由算法

算法公式:shard = hash(routing)%number_of_primary_shards

例子:

一个索引index ,3primary shard : p0,p1,p2

增删改查 一个document文档时候,都会传递一个参数 routing number, 默认就是document文档 _id, (也可以手动指定)

Routing = _id,假设: _id = 1

算法:

Hash(1) = 21 % 3 = 0 表示 请求被 路由到 p0分片上面。

  1. 自定义路由请求:

PUT /index/item/id?routing = _id (默认)

PUT /index/item/id?routing = user_id(自定义路由)--- 指定把某些值固定路由到某个分片上面。

  1. primary shard不可变原因

即使加服务器也不能改变主分片的数量

2、增删改原理

  1. 客户端选择一个node发送请求过去,这个node就是coordinating node(协调节点)
  2. coordinating node,对document进行路由,将请求转发给对应的node(有primary shard
  3. 实际的node上的primary shard处理请求,然后将数据同步到replica node
  4. coordinating node,如果发现primary node和所有replica node都搞定之后,就返回响应结果给客户端

所有文章都是以专栏系列编写,建议系统性学习,更容易成为架构师!

博主每天早晚坚持写博客给与读者价值提升,为了让更多人受益,请多多关照,如果觉得文章质量有帮助到你,请关注我的博客,收藏此文,持续提升,奥利给!

另外我不打算靠运营方式拿到博客专家的认证,纯纯的科技与狠活来征服读者,就看读者的感恩之心了,祝你好运连连。

有关ES数据存储和集群路由原理的更多相关文章

  1. ruby-on-rails - Rails 3 中的多个路由文件 - 2

    Rails2.3可以选择随时使用RouteSet#add_configuration_file添加更多路由。是否可以在Rails3项目中做同样的事情? 最佳答案 在config/application.rb中:config.paths.config.routes在Rails3.2(也可能是Rails3.1)中,使用:config.paths["config/routes"] 关于ruby-on-rails-Rails3中的多个路由文件,我们在StackOverflow上找到一个类似的问题

  2. ruby - 解析 RDFa、微数据等的最佳方式是什么,使用统一的模式/词汇(例如 schema.org)存储和显示信息 - 2

    我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i

  3. ruby - Ruby 有 `Pair` 数据类型吗? - 2

    有时我需要处理键/值数据。我不喜欢使用数组,因为它们在大小上没有限制(很容易不小心添加超过2个项目,而且您最终需要稍后验证大小)。此外,0和1的索引变成了魔数(MagicNumber),并且在传达含义方面做得很差(“当我说0时,我的意思是head...”)。散列也不合适,因为可能会不小心添加额外的条目。我写了下面的类来解决这个问题:classPairattr_accessor:head,:taildefinitialize(h,t)@head,@tail=h,tendend它工作得很好并且解决了问题,但我很想知道:Ruby标准库是否已经带有这样一个类? 最佳

  4. ruby - rails 3 redirect_to 将参数传递给命名路由 - 2

    我没有找到太多关于如何执行此操作的信息,尽管有很多关于如何使用像这样的redirect_to将参数传递给重定向的建议:action=>'something',:controller=>'something'在我的应用程序中,我在路由文件中有以下内容match'profile'=>'User#show'我的表演Action是这样的defshow@user=User.find(params[:user])@title=@user.first_nameend重定向发生在同一个用户Controller中,就像这样defregister@title="Registration"@user=Use

  5. ruby-on-rails - Rails 3,嵌套资源,没有路由匹配 [PUT] - 2

    我真的为这个而疯狂。我一直在搜索答案并尝试我找到的所有内容,包括相关问题和stackoverflow上的答案,但仍然无法正常工作。我正在使用嵌套资源,但无法使表单正常工作。我总是遇到错误,例如没有路线匹配[PUT]"/galleries/1/photos"表格在这里:/galleries/1/photos/1/edit路线.rbresources:galleriesdoresources:photosendresources:galleriesresources:photos照片Controller.rbdefnew@gallery=Gallery.find(params[:galle

  6. ruby - 我如何添加二进制数据来遏制 POST - 2

    我正在尝试使用Curbgem执行以下POST以解析云curl-XPOST\-H"X-Parse-Application-Id:PARSE_APP_ID"\-H"X-Parse-REST-API-Key:PARSE_API_KEY"\-H"Content-Type:image/jpeg"\--data-binary'@myPicture.jpg'\https://api.parse.com/1/files/pic.jpg用这个:curl=Curl::Easy.new("https://api.parse.com/1/files/lion.jpg")curl.multipart_form_

  7. 世界前沿3D开发引擎HOOPS全面讲解——集3D数据读取、3D图形渲染、3D数据发布于一体的全新3D应用开发工具 - 2

    无论您是想搭建桌面端、WEB端或者移动端APP应用,HOOPSPlatform组件都可以为您提供弹性的3D集成架构,同时,由工业领域3D技术专家组成的HOOPS技术团队也能为您提供技术支持服务。如果您的客户期望有一种在多个平台(桌面/WEB/APP,而且某些客户端是“瘦”客户端)快速、方便地将数据接入到3D应用系统的解决方案,并且当访问数据时,在各个平台上的性能和用户体验保持一致,HOOPSPlatform将帮助您完成。利用HOOPSPlatform,您可以开发在任何环境下的3D基础应用架构。HOOPSPlatform可以帮您打造3D创新型产品,HOOPSSDK包含的技术有:快速且准确的CAD

  8. ruby - Rack:如何将 URL 存储为变量? - 2

    我正在编写一个简单的静态Rack应用程序。查看下面的config.ru代码:useRack::Static,:urls=>["/elements","/img","/pages","/users","/css","/js"],:root=>"archive"map'/'dorunProc.new{|env|[200,{'Content-Type'=>'text/html','Cache-Control'=>'public,max-age=6400'},File.open('archive/splash.html',File::RDONLY)]}endmap'/pages/search.

  9. FOHEART H1数据手套驱动Optitrack光学动捕双手运动(Unity3D) - 2

    本教程将在Unity3D中混合Optitrack与数据手套的数据流,在人体运动的基础上,添加双手手指部分的运动。双手手背的角度仍由Optitrack提供,数据手套提供双手手指的角度。 01  客户端软件分别安装MotiveBody与MotionVenus并校准人体与数据手套。MotiveBodyMotionVenus数据手套使用、校准流程参照:https://gitee.com/foheart_1/foheart-h1-data-summary.git02  数据转发打开MotiveBody软件的Streaming,开始向Unity3D广播数据;MotionVenus中设置->选项选择Unit

  10. 使用canal同步MySQL数据到ES - 2

    文章目录一、概述简介原理模块二、配置Mysql使用版本环境要求1.操作系统2.mysql要求三、配置canal-server离线下载在线下载上传解压修改配置单机配置集群配置分库分表配置1.修改全局配置2.实例配置垂直分库水平分库3.修改group-instance.xml4.启动监听四、配置canal-adapter1修改启动配置2配置映射文件3启动ES数据同步查询所有订阅同步数据同步开关启动4.验证五、配置canal-admin一、概述简介canal是Alibaba旗下的一款开源项目,Java开发。基于数据库增量日志解析,提供增量数据订阅&消费。Git地址:https://github.co

随机推荐