当前位置: 首页 > news >正文

移动网站制作价格大连博硕网站建设

移动网站制作价格,大连博硕网站建设,黑龙江建设网ca数字证书如何注销,dw中网站统计总访问量怎么做r很多大数据组件在快速原型时期都是Java实现#xff0c;后来因为GC不可控、内存或者向量化等等各种各样的问题换到了C#xff0c;比如zookeeper-nuraft(https://www.yuque.com/treblez/qksu6c/hu1fuu71hgwanq8o?singleDoc# 《olap/clickhouse keeper 一致性协调服务》)后来因为GC不可控、内存或者向量化等等各种各样的问题换到了C比如zookeeper-nuraft(https://www.yuque.com/treblez/qksu6c/hu1fuu71hgwanq8o?singleDoc# 《olap/clickhouse keeper 一致性协调服务》)kafka-redpanda(https://www.yuque.com/treblez/qksu6c/ugig8y358fyyg5lp?singleDoc# 《Clickhouse blob阅读笔记一》)之类的。 但是nuraft和redpanda估计大部分人都没听说过因为绝大多数情况下zk和kafka这种也就足够了。数据量越大Java和C的性能差别也就越小。 Elasticsearch这里也是一样它也有C的替代品manticoresearch可以看看官方的测试结果(https://github.com/manticoresoftware/manticoresearch)提升还是挺大的。 同样大多数情况下选型还得是Elasticsearch因为这东西生态好省去了大量踩坑的成本最关键的是性能也足够了百万数据几十毫秒延迟换C意义也不大。所以我们今天不看manticoresearch还是学一下Elasticsearch。 ES接近三百万行代码那肯定不能直接看本文主要还是结合《Elasticsearch源码解析与优化实战》梳理下它在分布式、数据结构、搜索方面用到的一些核心的东西。ES其实是对lucene的分布式改造在上面加了一些共识、主备、选举、高可用之类的东西。 顺带说一下它和CK的场景是不同的ES主要面对非结构化数据查询CK面对的是OLAP场景。 如何解决深度分页问题 scoll 使用的是快照没法保证实时fromsize 最差的实现内存占用和时间都很差search_after 通过记录自增id来查询减少了内存占用 lucene如何实现倒排索引的 索引 es由_index、_type和_id唯一标识一个文档。 _index索引指向一个或多个物理分片的逻辑命名空间_type类型用于区分同一个集合中的不同细分在不同的细分中数据的整体模式是相同或相似的不适合完全不同类型的数据。多个_type可以在相同的索引中存在只要它们的字段不冲突即可对于整个索引映射在本质上被“扁平化”成一个单一的、全局的模式。_id文档标记符由系统自动生成或使用者提供。 ES的结构是怎样的index、node、shard、segment、field、term的关系 一个ES Index(索引)包含很多shard(分片)一个shard是一个Lucene索引Lucene索引由很多分段组成每一个分段都是一个倒排索引。Lucene索引可以独立执行建立索引和搜索的任务。ES每次“refresh”都会生成一个新的分段其中包含若干文档的数据。在每个分段内部文档的不同字段被单独建立索引。每个字段的值由若干词Term组成Term是原文本内容经过分词器处理和语言处理后的最终结果例如去除标点符号和转换为词根。 ES通过文件页缓存的flush机制可以看https://www.yuque.com/treblez/qksu6c/yxl59pkvczqot9us?singleDoc# 《ptmalloc从内存虚拟化说起》实现了近实时查询。每秒产生一个新分段新段先写入文件系统缓存但稍后再执行flush刷盘操作写操作很快会执行完一旦写成功就可以像其他文件一样被打开和读取了。 近实时查询其实就是最终一致最终一致要保证没有易失性ES通过事务日志做到了这一点。 段合并 ES段合并 在ES中每秒清空一次写缓冲将这些数据写入文件这个过程称为refresh每次refresh会创建一个新的Lucene 段。但是分段数量太多会带来较大的麻烦**每个段都会消耗文件句柄、内存。每个搜索请求都需要轮流检查每个段查询完再对结果进行合并所以段越多搜索也就越慢。**因此需要通过一定的策略将这些较小的段合并为大的段常用的方案是选择大小相似的分段进行合并。在合并过程中标记为删除的数据不会写入新分段当合并过程结束旧的分段数据被删除标记删除的数据才从磁盘删除。 HBase、Cassandra等系统都有类似的分段机制写过程中先在内存缓冲一批数据不时地将这些数据写入文件作为一个分段分段具有不变性再通过一些策略合并分段。分段合并过程中新段的产生需要一定的磁盘空间我们要保证系统有足够的剩余可用空间。Cassandra系统在段合并过程中的一个问题就是**当持续地向一个表中写入数据如果段文件大小没有上限当巨大的段达到磁盘空间的一半时剩余空间不足以进行新的段合并过程。**如果段文件设置一定上限不再合并则对表中部分数据无法实现真正的物理删除。ES存在同样的问题。 LSM-Tree段合并 我们都知道LSM-Tree脱胎于BigTableLevelDBRocksDBHBaseCassandra等都是基于LSM结构。这里既然提到了HBase、Cassandra那就不得不看下LSM-Tree的段合并了。 我们回顾下LSM-Tree的读写过程 写入时数据会被添加到内存中的平衡树数据结构(例如红黑树)。这个内存树被称为内存表(memtable)。 当内存表大于某个阈值(通常为几兆字节)时将其作为SSTable文件写入磁盘。这可以 高效地完成因为树已经维护了按键排序的键值对。新的SSTable文件成为数据库的最新 部分。当SSTable被写入磁盘时写入可以继续到一个新的内存表实例。读取时首先尝试在内存表中找到关键字然后在最近的磁盘段中然后在 下一个较旧的段中找到该关键字。 下面来自https://zhuanlan.zhihu.com/p/462850000 sst 是不可修改的数据的更新和删除都是以写入新记录的形式呈现。数据在文件中是按 key 有序组织的利于高效地查询和后续合并。可以参考数据密集型应用系统设计第三章 随着数据的不断写入和更新sst 的数量会不断增加进而会出现两个问题 sst 中可能存在修改前的老数据和已经删除的数据这些无用数据会占用存储空间造成资源浪费由于 sst 越来越多数据分散在多个文件读取时可能会访问多个文件导致读性能下降。对于上述问题需要一些机制去解决这种机制就称为 compactioncompaction 的目的是将多个 sst 合并成一个在合并的同时将无用的数据清理掉合并成的新文件也是按 key 排序的。可以看到通过 compaction很好地解决上述问题。 LSM-Tree的常用合并算法有什么 memtable sstable **Size-Tiered Compaction Strategy (STCS) ** memtable 逐步刷入到磁盘 sst刚开始 sst 都是小文件随着小文件越来越多当数据量达到一定阈值时STCS 策略会将这些小文件 compaction 成一个中等大小的新文件。同样的道理当中等文件数量达到一定阈值这些文件将被 compaction 成大文件这种方式不断递归会持续生成越来越大的文件。总的来说STCS 就是将 sst 按大小分类相似大小的 sst 分在同一类然后将多个同类的 sst 合并到下一个类别。通过这种方式可以有效减少 sst 的数量。 由于 STCS 策略比较简单同一份数据在 compaction 期间拷贝的次数相对较少即写入放大相对小很多基于 LSM-Tree 的系统将其作为默认的 compaction 策略如 Lucene、Cassandra、Scylla 等。STCS 逻辑简单、写入放大低但是它也有很大的缺陷 – 空间放大。其实也存在较大的读放大 为什么会产生空间放大呢compaction 的过程中参与 compaction 的 sst 不能立马删除直到新生成的 sst 写入完毕这里其实还有一个原因如果老的 sst 有读操作由于文件还被引用也是不能立即删除的。因此在 compaction 的过程中磁盘上新老文件共存产生临时空间放大。即使这种空间放大是临时的但是对于系统来说不得不使用比实际数据量更大的磁盘空间以保证 compaction 正常执行这产生的代价很昂贵。 这也就是上面一节提到的那个问题。 **Leveled Compaction Strategy (LCS) ** sst 的大小可控默认每个 sst 的大小一致STCS 在经过多次合并后层级越深产生的 sst 文件就越大最终会形成超大文件LCS 在合并时会保证除 Level 0L0之外的其他 Level 有序且无覆盖除 L0 外每层文件的总大小呈指数增长假如 L1 最多 10 个则 L2 为 100 个L3 为 1000 个… 首先是内存中的 memtable 刷到 L0当 L0 中的文件数达到一定阈值后会将 L0 的所有文件及与 L1 有覆盖的文件做合并然后生成新文件如果文件大小超过阈值会切成多个到 L1L1 中的文件时全局有序的不会出现重叠的情况 当 L1 的文件数量达到阈值时会选取 L1 中的一个 sst 与 L2 中的多个文件做合并假设 L1 有 10 个文件那么一个文件便占 L1 数据量的 1/10假设每层包含的 key 范围相同那么 L1 中的一个文件理论上会覆盖 L2 层的 10 个文件因此会选取 L1 中的一个文件与 L2 中的 10 个文件一起 compaction将生成的新文件放到 L2 LCS 不会有超大文件而且在层与层之间合并时大体上只选取 11 个 sst 进行 compaction而这 11 个文件的大小只占整个系统的小部分因此临时空间放大很小。 LCS 最好的情况是最后一层数据已经被填满。假设最后一层为 L3一共有 1000 个 sst。那么 L1 和 L2 一共最多 110 个文件。由于每个 sst 基本大小相同因此几乎 90% 的数据在 L3。我们直到每层内的数据不会重复因此最多L1 和 L2 的数据包含在 L3 中重复数据导致的空间放大为 1/0.91.11 相比STCS 8 倍的空间放大好得多。然后也存在比较差的情况即当最后一层没有填满时假设 L3 的文件个数为 100 个L2 中的文件数也是 100 个最坏情况下如果 L2 和 L3 的数据相同则会产生 2 倍的空间放大。但即使是这样相对 STCS 还是好得多。 LCS存在写放大问题写放大指的是IO写带宽的放大 对于 STCS 来说写入放大和层高强相关而每层的数据量又是呈指数增长的即第一层最多 400MB第二层 1600MB第三层6400MB一次类推很明显层高和数据量的关系为对数关系而写入放大和层高一致因此写入放大随数据量增长为 O(logN)N 为数据量大小。 但是对于 LCS 来说情况会更糟糕我们来分析下原因假设需要将 L1 层的数据量 X compaction 到下一层STCS 的写如放大为 1。而 LCS 需要将这 X 的数据量和 L1 层 10倍 X 数据量一共 11 * X 一起 compaction写入放大为 11是 STCS 的 11 倍。 如果大部分都是写新数据建议stcs 如果更新频繁那么选lcs 比较奇怪的是Lucene这种读取频繁的场景 为什么选择STCS作为默认策略读放大应该是个重要的因素才对。 ES集群 节点和模块 节点分类 主节点负责管理集群变更数据节点负责保存数据/执行CRUD预处理节点进行数据转换/预处理协调节点处理客户端请求的节点部落节点在多个集群之间充当客户端 集群的健康状态 Green所有的主副分片都正常运行Yellow所有的主分片都正常运行但是不是所有的副分片都正常运行Red有主分片没有正常运行 模块 cluster负责管理集群状态、配置进行节点分片的决策、分片迁移等allocation封装了分片分配相关的功能和策略discovery进行集群节点的发现和主节点的选举gateway进行对master广播的集群状态的存储并负责集群重启时的恢复indices全局索引配置管理索引数据恢复http异步的json over http访问es的api解决C10k问题transport集群节点之间的相互通信engine封装了对Lucene的操作及translog的调用它是对一个分片读写操作的最终提供者。 集群启动流程 选主 为什么使用主从模式 这个问题我在https://www.yuque.com/treblez/qksu6c/ahgvn94c2nh1y34w中也提过除主从Leader/Follower模式外另一种选择是分布式哈希表DHT可以支持每小时数千个节点的离开和加入其可以在不了解底层网络拓扑的异构网络中工作查询响应时间大约为4到10跳中转次数例如Cassandra就使用这种方案。但是在相对稳定的对等网络中主从模式会更好。Redis集群的哈希槽也是类似的无主分布式哈希方案。 ES的典型场景中的另一个简化是集群中没有那么多节点。通常节点的数量远远小于单个节点能够维护的连接数并且网络环境不必经常处理节点的加入和离开。这就是为什么主从模式更适合ES。 现在在普遍的上云的环境下对等网络更多所以无主的方案用的就很少了。 ES的选主流程是什么样的脑裂的解决方案是什么样的 ES的选主算法是基于Bully算法的改进主要思路是对节点ID排序取ID值最大的节点作为Master每个节点都运行这个流程。 **1参选人数需要过半达到 quorum多数后就选出了临时的主。**为什么是临时的每个节点运行排序取最大值的算法结果不一定相同。举个例子集群有5台主机节点ID分别是1、2、3、4、5。当产生网络分区或节点启动速度差异较大时节点1看到的节点列表是1、2、3、4选出4节点2看到的节点列表是2、3、4、5选出5。结果就不一致了由此产生下面的第二条限制。 **2得票数需过半。某节点被选为主节点必须判断加入它的节点数过半才确认Master身份。**解决第一个问题。 **3当探测到节点离开事件时必须判断当前节点数是否过半。如果达不到quorum则放弃Master身份重新加入集群。**如果不这么做则设想以下情况假设5台机器组成的集群产生网络分区2台一组3台一组产生分区前Master位于2台中的一个此时3台一组的节点会重新并成功选取Master产生双主俗称脑裂。 这里我们很容易想到paxos或者raft为什么它们不采用这种选主的方式呢又或者说为什么es采用如此简单原始的bully算法呢 https://stackoverflow.com/questions/27558708/whats-the-benefit-of-advanced-master-election-algorithms-over-bully-algorithm bully算法的缺点在于 通信成本高 最坏可能需要n^2次通信节点不均等排名低的节点可能永远无法被选为主节点节点之间需要准确的进程排名 选举集群元信息 被选出的 Master 和集群元信息的新旧程度没有关系。因此它的第一个任务是选举元信息让各节点把各自存储的元信息发过来根据版本号确定最新的元信息然后把这个信息广播下去这样集群的所有节点都有了最新的元信息。 集群元信息的选举包括两个级别集群级和索引级。回顾一下一个索引包含多个shard不包含哪个shard存于哪个节点这种信息。这种信息以节点磁盘存储的为准需要上报。为什么呢因为读写流程是不经过Master的Master 不知道各 shard 副本直接的数据差异。HDFS 也有类似的机制block 信息依赖于DataNode的上报。 为了集群一致性参与选举的元信息数量需要过半Master发布集群状态成功的规则也是等待发布成功的节点数过半。在选举过程中不接受新节点的加入请求。集群元信息选举完毕后Master发布首次集群状态然后开始选举shard级元信息。 allocation过程 选举shard级元信息构建内容路由表是在allocation模块完成的。在初始阶段所有的shard都处于UNASSIGNED未分配状态。ES中通过分配过程决定哪个分片位于哪个节点重构内容路由表。此时首先要做的是分配主分片。 主分片负责处理所有的读和写请求因此它们负担了最大的负载。副本分片主要用于复制数据以提供高可用性和容错性它们不直接处理请求但可以在主分片不可用时接管请求。 ES根据集群级别元信息中记录的最新主分片的列表来记录主分片从上一个过程汇总的 shard 信息中选择一个副本作为副分片。 index recovery 预写日志的恢复流程是什么 之前我在https://www.yuque.com/treblez/qksu6c/ahgvn94c2nh1y34w#pxq1W中解读过Redis的主从复值的问题没有日志保证主从一致。ES这里就给出了一种解决方案translogrecovery。recovery一个是可以解决主分片中存在没刷盘的内存数据的问题另一个是可以解决主从不一致的问题。 这里注意translog会受到commit操作的影响清空translog所以这里额外引入快照机制解决这个问题。 副分片的allocation和主分片的recovery是独立的过程副分片的recovery需要等主分片的recovery结束之后才开始。 主分片recovery 由于每次写操作都会记录事务日志translog事务日志中记录了哪种操作以及相关的数据。因此将最后一次提交Lucene 的一次提交就是一次 fsync 刷盘的过程之后的 translog中进行重放建立Lucene索引如此完成主分片的recovery。 副分片的recovery phase1在主分片所在节点获取translog保留锁从获取保留锁开始会保留translog不受其刷盘清空的影响。然后调用Lucene接口把shard做快照这是已经刷磁盘中的分片数据。把这些shard数据复制到副本节点。在phase1完毕前会向副分片节点发送告知对方启动engine在phase2开始之前副分片就可以正常处理写请求了。phase2对translog做快照这个快照里包含从phase1开始到执行translog快照期间的新增索引。将这些translog发送到副分片所在节点进行重放。 当一个索引的主分片分配成功后到此分片的写操作就是允许的。当一个索引所有的主分片都分配成功后该索引变为Yellow。当全部索引的主分片都分配成功后整个集群变为Yellow。当一个索引全部分片分配成功后该索引变为 Green。当全部索引的索引分片分配成功后整个集群变为Green。 索引数据恢复是最漫长的过程。当shard总量达到十万级的时候6.x之前的版本集群从Red变为Green的时间可能需要小时级。ES 6.x中的副本允许从本地translog恢复是一次重大的改进避免了从主分片所在节点拉取全量数据为恢复过程节约了大量时间。 数据模型 这里主要是讲数据节点的架构和处理流程。 PacificA算法 概念 Replica Group一个互为副本的数据集合称为副本组。其中只有一个副本是主数据Primary其他为从数据Secondary。Configuration配置信息中描述了一个副本组都有哪些副本Primary是谁以及它们位于哪个节点。Configuration Version配置信息的版本号每次发生变更时递增。Serial Number代表每个写操作的顺序每次写操作时递增简称SN。每个主副本维护自己的递增SN。Prepared List写操作的准备序列。存储来自外部请求的列表将请求按照 SN 排序向列表中插入的序列号必须大于列表中最大的SN。每个副本上有自己的Prepared List。Committed List写操作的提交序列 设计假设 节点可以失效对消息延迟的上限不做假设。消息可以丢失、乱序但不能被篡改即不存在拜占庭问题。网络分区可以发生系统时钟可以不同步但漂移是有限度的。 整个系统框架主要由两部分组成存储管理和配置管理。 存储管理负责数据的读取和更新使用多副本方式保证数据的可靠性和可用性配置管理对配置信息进行管理维护所有配置信息的一致性。 数据写入流程 1写请求进入主副本节点节点为该操作分配SN使用该SN创建UpdateRequest结构。然后将该UpdateRequest插入自己的prepare list。 2主副本节点将携带 SN 的 UpdateRequest 发往从副本节点从节点收到后同样插入prepare list完成后给主副本节点回复一个ACK。 3一旦主副本节点收到所有从副本节点的响应确定该数据已经被正确写入所有的从副本节点此时认为可以提交了将此UpdateRequest放入committed list,committed list向前移动。 4主副本节点回复客户端更新成功完成。对每一个Prepare消息主副本节点向从副本节点发送一个commit通知告诉它们自己的committed point位置从副本节点收到通知后根据指示移动committed point到相同的位置。 错误检测 分布式系统经常存在网络分区、节点离线等异常。全局的配置管理器维护权威配置信息但其他各节点上的配置信息不一定同步我们必须处理旧的主副本和新的主副本同时存在的情况—旧的主副本可能没有意识到重新分配了一个新的主副本从而违反了强一致性。PacificA使用了租约lease机制来解决这个问题。主副本定期向其他从副本获取租约。 这个过程中可能产生两种情况 如果主副本节点在一定时间内lease period未收到从副本节点的租约回复则主副本节点认为从副本节点异常向配置管理器汇报将该异常从副本从副本组中移除同时它也将自己降级不再作为主副本节点。如果从副本节点在一定时间内grace period未收到主副本节点的租约请求则认为主副本异常向配置管理器汇报将主副本从副本组中移除同时将自己提升为新的主。如果存在多个从副本则哪个从副本先执行成功哪个从副本就被提升为新主。 假设没有时钟漂移只要grace period≥lease period则租约机制就可以保证主副本会比任意从副本先感知到租约失效。同时任何一个从副本只有在它租约失效时才会争取去当新的主副本因此保证了新主副本产生之前旧的主分片已降级不会产生两个主副本。 其他系统也经常将租约机制作为故障检测手段如GFS、Bigtable。 写入模型 每个索引操作首先会使用routing参数解析到副本组通常基于文档ID。一旦确定副本组就会内部转发该操作到分片组的主分片中。主分片负责验证操作和转发它到其他副分片。ES维护一个可以接收该操作的分片的副本列表。这个列表叫作同步副本列表in-sync copies并由Master节点维护。正如它的名字这个“好”分片副本列表中的分片都会保证已成功处理所有的索引和删除操作并给用户返回ACK。主分片负责维护不变性各个副本保持一致因此必须复制这些操作到这个列表中的每个副本。 写入过程遵循以下基本流程 1请求到达协调节点协调节点先验证操作如果有错就拒绝该操作。然后根据当前集群状态请求被路由到主分片所在节点。 2该操作在主分片上本地执行例如索引、更新或删除文档。这也会验证字段的内容如果未通过就拒绝操作例如字段串的长度超出Lucene定义的长度。 3操作成功执行后转发该操作到当前in-sync 副本组的所有副分片。如果有多个副分片则会并行转发。4一旦所有的副分片成功执行操作并回复主分片主分片会把请求执行成功的信息返回给协调节点协调节点返回给客户端。 写故障处理 对于主分片自身错误的情况它所在的节点会发送一个消息到Master节点。这个索引操作会等待默认为最多一分钟Master节点提升一个副分片为主分片。这个操作会被转发给新的主分片。注意Master同样会监控节点的健康并且可能会主动降级主分片。这通常发生在主分片所在的节点离线的时候。 在主分片上执行的操作成功后该主分片必须处理在副分片上潜在发生的错误。错误发生的原因可能是在副分片上执行操作时发生的错误也可能是因为网络阻塞导致主分片无法转发操作到副分片或者副分片无法返回结果给主分片。这些错误都会导致相同的结果in-sync replica set中的一个分片丢失一个即将要向用户确认的操作。为了避免出现不一致主分片会发送一条消息到Master节点要求它把有问题的分片从in-sync replica set中移除。一旦Master确认移除了该分片主分片就会确认这次操作。注意Master也会指导另一个节点建立一个新的分片副本以便把系统恢复成健康状态。 在转发请求到副分片时主分片会使用副分片来验证它是否仍是一个活跃的主分片。如果主分片因为网络原因或很长时间的 GC被隔离则在它意识到被降级之前可能会继续处理传入的索引操作。来自陈旧的主分片的操作将会被副分片拒绝。当它接收来自副分片的拒绝其请求的响应时它将会访问一下主节点然后就会知道自己已被替换。最后将操作路由到新的主分片。 主分片首先在本地进行索引然后转发请求由于主分片已经写成功因此在并行的读请求中有可能在写请求返回成功之前就可以读取更新的内容。 读取模型 基本流程如下 1把读请求转发到相关分片。注意因为大多数搜索都会发送到一个或多个索引通常需要从多个分片中读取每个分片都保存这些数据的一部分。 2从副本组中选择一个相关分片的活跃副本。它可以是主分片或副分片。默认情况下 ES会简单地循环遍历这些分片。 3发送分片级的读请求到被选中的副本。 4合并结果并给客户端返回响应。注意针对通过ID查找的get请求会跳过这个步骤因为只有一个相关的分片。 Allocation ID 、Sequence ID和_version Allocation ID用于标记分片副本的陈旧情况用来决策主分片分配。 SequenceID、SequenceNums、全局检查点、本地检查点用来保障脑裂时主分片唯一跟Raft中Term的概念相似。 具体就不详细介绍了可以看https://weread.qq.com/web/reader/f9c32dc07184876ef9cdeb6ka1d32a6022aa1d0c6e83eb4? 每个文档都有一个版本号_version当文档被修改时版本号递增。ES 使用这个_version来确保变更以正确顺序执行。如果旧版本的文档在新版本之后到达则它可以被简单地忽略。例如索引recovery阶段就利用了这个特性。 版本号由主分片生成在将请求转发给副本片时将携带此版本号。 版本号的另一个作用是实现乐观锁如同其他数据库的乐观锁一样。我们在写请求中指定文档的版本号如果文档的当前版本与请求中指定的版本号不同则请求会失败。 写流程 ES中写入单个文档的请求称为Index请求批量写入的请求称为Bulk请求。写单个和多个文档使用相同的处理逻辑请求被统一封装为BulkRequest。 可用的操作如下 · INDEX向索引中“put”一个文档的操作称为“索引”一个文档。此处“索引”为动词。 · CREATE:put 请求可以通过 op_type 参数设置操作类型为 create在这种操作下如果文档已存在则请求将失败。 · UPDATE默认情况下“put”一个文档时如果文档已存在则更新它。 · DELETE删除文档。 基本的写流程如下 ES的写流程是怎么样的 1客户端向NODE1协调节点发送写请求。 2NODE1使用文档ID来确定文档属于分片0通过集群状态中的内容路由表信息获知分片0的主分片位于NODE3因此请求被转发到NODE3上。 3NODE3上的主分片执行写操作。如果写入成功则它将请求并行转发到NODE1和NODE2的副分片上等待返回结果。当所有的副分片都报告成功NODE3将向协调节点报告成功协调节点再向客户端报告成功。 写一致性的默认策略是quorum仲裁一致即多数的分片其中分片副本可以是主分片或副分片在写入操作时处于可用状态。 写入的底层原理是什么 1数据先写入 memory buffer然后定时默认每隔1s将 memory buffer 中的数据写入一个新的 segment 文件中并进入 Filesystem cache同时清空 memory buffer这个过程就叫做 refreshES 的近实时性数据存在 memory buffer 时是搜索不到的只有数据被 refresh 到 Filesystem cache 之后才能被搜索到(分两步猜测是为了减轻pdflush压力)而 refresh 是每秒一次 所以称 es 是近实时的可以通过手动调用 es 的 api 触发一次 refresh 操作让数据马上可以被搜索到 2由于 memory Buffer 和 Filesystem Cache 都是基于内存假设服务器宕机那么数据就会丢失所以 ES 通过 translog 日志文件来保证数据的可靠性在数据写入 memory buffer 的同时将数据写入 translog 日志文件中在机器宕机重启时es 会自动读取 translog 日志文件中的数据恢复到 memory buffer 和 Filesystem cache 中去 3flush 操作不断重复上面的步骤translog 会变得越来越大当 translog 文件默认每30分钟或者阈值超过 512M 时就会触发 commit 操作即 flush操作。 异常处理 1如果请求在协调节点的路由阶段失败则会等待集群状态更新拿到更新后进行重试如果再次失败则仍旧等集群状态更新直到超时1分钟为止。超时后仍失败则进行整体请求失败处理。 2在主分片写入过程中写入是阻塞的。只有写入成功才会发起写副本请求。如果主shard写失败则整个请求被认为处理失败。如果有部分副本写失败则整个请求被认为处理成功。 3无论主分片还是副分片当写一个doc失败时集群不会重试而是关闭本地shard然后向Master汇报删除是以shard为单位的。 数据特性 · 数据可靠性通过分片副本和事务日志机制保障数据安全。 · 服务可用性在可用性和一致性的取舍方面默认情况下 ES 更倾向于可用性只要主分片可用即可执行写入操作。 · 一致性笔者认为是弱一致性。只要主分片写成功数据就可能被读取。因此读取操作在主分片和副分片上可能会得到不同结果。 · 原子性索引的读写、别名更新是原子操作不会出现中间状态。但bulk不是原子操作不能用来实现事务。 · 扩展性主副分片都可以承担读请求分担系统负载。 · 写入较慢副分片写入过程需要重新生成索引不能单纯复制数据浪费计算能力影响入库速度。 · 磁盘管理能力较差对坏盘检查和容忍性比HDFS差不少。例如在配置多磁盘路径的情况下有一块坏盘就无法启动节点。 读流程 get 1客户端向NODE1发送读请求。 2NODE1使用文档ID来确定文档属于分片0通过集群状态中的内容路由表信息获知分片0有三个副本数据位于所有的三个节点中此时它可以将请求发送到任意节点这里它将请求转发到NODE2。 3NODE2将文档返回给 NODE1,NODE1将文档返回给客户端。NODE1作为协调节点会将客户端请求轮询发送到集群的所有副本来实现负载均衡。 search 索引的建立流程 ES中的数据可以分为两类精确值和全文。 · 精确值比如日期和用户id、IP地址等。 · 全文指文本内容比如一条日志或者邮件的内容。 这两种类型的数据在查询时是不同的对精确值的比较是二进制的查询要么匹配要么不匹配全文内容的查询无法给出“有”还是“没有”的结果它只能找到结果是“看起来像”你要查询的东西因此把查询结果按相似度排序评分越高相似度越大。 建立索引和执行搜索的过程如下所示 全文数据 如果是全文数据则对文本内容进行分析这项工作在 ES 中由分析器实现。 分析器实现如下功能 · 字符过滤器。主要是对字符串进行预处理例如去掉HTML将转换成and等。 · 分词器Tokenizer。将字符串分割为单个词条例如根据空格和标点符号分割输出的词条称为词元Token。 · Token过滤器。根据停止词Stop word删除词元例如and、the等无用词或者根据同义词表增加词条例如jump和leap。 · 语言处理。对上一步得到的Token做一些和语言相关的处理例如转为小写以及将单词转换为词根的形式。语言处理组件输出的结果称为词Term。 分析完毕后将分析器输出的词Term传递给索引组件生成倒排和正排索引再存储到文件系统中。 执行搜索 执行搜索 搜索调用Lucene完成如果是全文检索则 · 对检索字段使用建立索引时相同的分析器进行分析产生Token列表 · 根据查询语句的语法规则转换成一棵语法树 · 查找符合语法树的文档 · 对匹配到的文档列表进行相关性评分评分策略一般使用TF/IDF · 根据评分结果进行排序。 分布式搜索过程 搜索过程分为query和fetch两步 cluster视角 1客户端发送search请求到NODE 3。 2Node 3将查询请求转发到索引的每个主分片或副分片中。 3每个分片在本地执行查询并使用本地的Term/Document Frequency信息进行打分添加结果到大小为from size的本地有序优先队列中。 4每个分片返回各自优先队列中所有文档的ID和排序值给协调节点协调节点合并这些值到自己的优先队列中产生一个全局排序后的列表。 ES搜索的过程是怎样的 1. Query 在初始查询阶段时查询会广播到索引中每一个分片拷贝主分片或者副本分片。 每个分片在本地执行搜索并构建一个匹配文档的大小为 from size 的优先队列。 每个分片返回各自优先队列中所有文档的 ID 和排序值 给协调节点它合并这些值到自己的优先队列中来产生一个全局排序后的结果列表。 2. Fetch 接下来就是 取回阶段协调节点辨别出哪些文档需要被取回并向相关的分片提交多个 GET 请求。每个分片加载并 丰富 文档如果有需要的话接着返回文档给协调节点。一旦所有的文档都被取回了协调节点返回结果给客户端。 总结 · 聚合是在ES中实现的而非Lucene。 · Query和Fetch请求之间是无状态的除非是scroll方式。 · 分页搜索不会单独“cache”,cache和分页没有关系。 · 每次分页的请求都是一次重新搜索的过程而不是从第一次搜索的结果中获取。看上去不太符合常规的做法事实上互联网的搜索引擎都是重新执行了搜索过程人们基本只看前几页很少深度分页重新执行一次搜索很快如果缓存第一次搜索结果等待翻页命中则这种缓存的代价较大意义却不大因此不如重新执行一次搜索。 · 搜索需要遍历分片所有的Lucene分段因此合并Lucene分段对搜索性能有好处。 更新和删除的流程 ES更新和删除的过程是怎么样的 删除和更新也都是写操作但是Elasticsearch中的文档是不可变的因此不能被删除或者改动以展示其变更 磁盘上的每个段都有一个相应的.del文件。当删除请求发送后文档并没有真的被删除而是在.del文件中被标记为删除。该文档依然能匹配查询但是会在结果中被过滤掉。当段合并时在.del文件中被标记为删除的文档将不会被写入新段。 在新的文档被创建时Elasticsearch会为该文档指定一个版本号当执行更新时旧版本的文档在.del文件中被标记为删除新版本的文档被索引到一个新段。旧版本的文档依然能匹配查询但是会在结果中被过滤掉。 一致性分析 在高并发场景下ES如何保证读写一致的 1对于更新操作可以通过版本号使用乐观并发控制以确保新版本不会被旧版本覆盖 每个文档都有一个_version 版本号这个版本号在文档被改变时加一。Elasticsearch使用这个 _version 保证所有修改都被正确排序。当一个旧版本出现在新版本之后它会被简单的忽略。 利用_version的这一优点确保数据不会因为修改冲突而丢失。比如指定文档的version来做更改。如果那个版本号不是现在的我们的请求就失败了。 2对于写操作一致性级别支持 quorum/one/all默认为 quorum即只有当大多数分片可用时才允许写操作。但即使大多数可用也可能存在因为网络等原因导致写入副本失败这样该副本被认为故障分片将会在一个不同的节点上重建。 one要求我们这个写操作只要有一个primary shard是active活跃可用的就可以执行all要求我们这个写操作必须所有的primary shard和replica shard都是活跃的才可以执行这个写操作quorum默认的值要求所有的shard中必须是大部分的shard都是活跃的可用的才可以执行这个写操作 3对于读操作可以设置 replication 为 sync(默认)这使得操作在主分片和副本分片都完成后才会返回如果设置replication 为 async 时也可以通过设置搜索请求参数_preference 为 primary 来查询主分片确保文档是最新版本。 可以看到ES采用了和Cassandra以及Dynamo一样的仲裁一致读写方案参考DDIA这种方案能够保证线性一致的读写但是不能保证比较和更新的线性一致性。 线程池 我们跳过对于索引恢复、gateway、allocation、snapshot、cluster、transport几个模块的分析来看线程池。这几个模块的流程比较符合直觉跟平常的分布式系统也没什么区别。篇幅原因就不在这里展开了。 一般说来线程池的大小可以参考如下设置其中N为CPU的个数 · 对于CPU密集型任务线程池大小设置为N1 · 对于I/O密集型任务线程池大小设置为2N1。 对于计算密集型任务线程池的线程数量一般不应该超过N1。如果线程数量太多则会导致更高的线程间上下文切换的代价。加1是为了当计算线程出现偶尔的故障或者偶尔的I/O、发送数据、写日志等情况时这个额外的线程可以保证CPU时钟周期不被浪费。 I/O密集型任务的线程数可以稍大一些因为I/O密集型任务大部分时间阻塞在I/O过程适当增加线程数可以增加并发处理能力。而上下文切换的代价相对来说已经不那么敏感。但是线程数量不一定设置为2N1具体需要看I/O等待时间有多长。等待时间越长需要越多的线程等待时间越少需要越少的线程。因此也可以参考下面的公式 最佳线程数线程等待时间线程CPU时间/线程CPU时间×CPU数 nginx: 呵呵一群菜鸡 nginx这种出神入化的水平一般库确实达不到属于是超出三界外不在五行中了。 线程池的分类 fixed fixed线程池拥有固定数量的线程来处理请求当线程空闲时不会销毁当所有线程都繁忙时请求被添加到队列中。 size参数用来控制线程的数量。 queue_size参数用来控制线程池相关的任务队列大小。设置为 -1表示无限制。当请求到达时如果队列已满则请求将被拒绝。 scaling scaling线程池的线程数量是动态的介于core和max参数之间变化。线程池的最小线程数为配置的core大小随着请求的增加当core数量的线程全都繁忙时线程数逐渐增大到max数量。max是线程池可拥有的线程数上限。当线程空闲时线程数从max大小逐渐降低到core大小。 direct 这种线程池对用户并不可见当某个任务不需要在独立的线程执行又想被线程池管理时于是诞生了这种特殊类型的线程池在调用者线程中执行任务。 fixed_auto_queue_size 与fixed类型的线程池相似该线程池的线程数量为固定值但是队列类型不一样。其队列大小根据利特尔法则Littles Law自动调整大小。该法则的详细信息可以参考https://en.wikipedia.org/wiki/Little%27s_law。 这个线程池加入了target_response_time用来指示任务的平均响应时间值设置如果任务经常高于这个时间则线程池队列将被调小以便拒绝任务。 线程池模块分析 generic 用于通用的操作例如节点发现线程池类型为scaling。 index 用于index/delete操作线程池类型为fixed大小为处理器的数量队列大小为200允许设置的最大线程数为1处理器数量。 search 用于count/search/suggest操作。线程池类型为fixed大小为int处理器数量3/21队列大小为1000。 get 用于get操作。线程池类型为fixed大小为处理器的数量队列大小为1000。 bulk 用于bulk操作线程池类型为fixed大小为处理器的数量队列大小为200该线程池允许设置的最大线程数为1处理器数量。 snapshot 用于snaphost/restore操作。线程池类型为scaling线程保持存活时间为5min最大线程数为min5, 处理器数量/2。 warmer 用于segment warm-up操作。线程池类型为scaling线程保持存活时间为5min最大线程数为min5, 处理器数量/2。 refresh 用于 refresh 操作。线程池类型为 scaling线程空闲保持存活时间为5min最大线程数为min10, 处理器数量/2。 listener 主要用于Java客户端线程监听器被设置为true时执行动作。线程池类型为scaling最大线程数为min10, 处理器数量/2。 same 在调用者线程执行不转移到新的线程池。 management 管理工作的线程池例如Node info、Node tats、List tasks等。flush用于索引数据的flush操作。 其实juc把线程池定的很清楚了fixed pool(固定线程数的普通任务)、single pool单线程、cached pool线程数动态扩缩适合于短时间内的大量异步任务、fork join pool多队列任务窃取适合cpu密集场景根据场景选就可以了。C就没这么好用的内部库。 shrink 索引分片数量一般在模板中统一定义在数据规模比较大的索引中索引分片数一般也大一些在笔者的集群中设置为24。同时按天生成新的索引使用别名关联。但是并非每天的索引数据量都很大小数据量的索引同样有较大的分片数。在ES 中主节点管理分片是很大的工作量降低集群整体分片数量可以减少recovery时间减小集群状态的大小。因此可以使用Shrink API缩小索引分片数。当索引缩小完成后源索引可以删除。 Shrink API是ES 5.0之后提供的新功能其可以缩小主分片数量。但其并不对源索引直接进行缩小操作而是使用与源索引相同的配置创建一个新索引仅降低分片数。由于添加新文档时使用对分片数量取余获取目的分片的关系新索引的主分片数必须是源索引主分片数的因数。例如8个分片可以缩小到4、2、1个分片。如果源索引的分片数为素数则目标索引的分片数只能为1。 过程 引用官方手册对Shrink工作过程的描述 · 以相同配置创建目标索引但是降低主分片数量。所有副本都迁移到同一个节点。创建硬链接时源文件和目标文件必须在同一台主机。 · 从源索引的Lucene分段创建硬链接到目的索引。如果系统不支持硬链接那么索引的所有分段都将复制到新索引将会花费大量时间。 · 对目标索引执行恢复操作就像一个关闭的索引重新打开时一样。 为什么要使用硬链接而不是软链接 Linux的文件系统由两部分组成实际上任何文件系统的基本概念都相似:inode和block。 block用于存储用户数据inode用于记录元数据系统通过inode定位唯一的文件。 · 硬链接文件有相同的inode和block。 · 软链接文件有独立的inode和block,block内容为目的文件路径名。 那么为什么一定要硬链接过去呢从本质上来说我们需要保证Shrink之后源索引和目的索引是完全独立的读写和删除都不应该互相影响。如果软链接过去删除源索引则目的索引的数据也会被删除硬链接则不会。满足下面条件时操作系统才真正删除文件 文件被打开的fd数量为0且硬链接数量为0。 使用硬链接删除源索引只是将文件的硬链接数量减1删除源索引和目的索引中的任何一个都不影响另一个正常读写。 由于使用了硬链接也因为硬链接的特性带来一些限制不能交叉文件系统或分区进行硬链接的创建因为不同分区和文件系统有自己的inode。 优化写入速度 综合来说提升写入速度从以下几方面入手 · 加大translog flush间隔目的是降低iops、writeblock。 · 加大index refresh间隔除了降低I/O更重要的是降低了segment merge频率。 · 调整bulk请求。 · 优化磁盘间的任务均匀情况将shard尽量均匀分布到物理主机的各个磁盘。 · 优化节点间的任务分布将任务尽量均匀地发到各节点。 · 优化Lucene层建立索引的过程目的是降低CPU占用率及I/O例如禁用_all字段。 优化搜索速度 cache预留空间使用更快的硬件合理建模文档预索引数据合理字段映射避免使用脚本日期搜索 增添模糊范围对只读索引进行force-merge每天建立新的索引而不是只写一个索引预热cache限制分片数减少协调节点的压力使用自适应副本选择提升响应速度思路来自Cassandrahttps://www.usenix.org/conference/nsdi15/technical-sessions/presentation/suresh
http://www.dnsts.com.cn/news/185527.html

相关文章:

  • 在自己电脑上建网站网站空间要多少钱
  • 重庆公司网站开发app企业签名
  • 网站开发和网络工程师h5设计软件
  • 高埗镇仿做网站苏州公司注册查询
  • 织梦做企业网站网站模板怎么样
  • 网站建设与管理专业学什么十大免费推广平台
  • 北京市电力建设公司网站深圳网页设计兴田德润i优惠吗
  • 建站大师wordpress4.94主题上传不显示
  • 成都网站排名生客seo怎么样订单详情页面设计
  • 怎么自己做论坛网站吗永州网页制作
  • 做六个网站静态页多少钱WordPress中英文旅游模板
  • 建设网站都需要哪些资料网站建设工具哪家好
  • 创建网站的网站五屏网站建设平台
  • 龙岗网站设计资讯制作网站方法
  • 吴川市建设工程公司网站seo投放是什么意思
  • 网站加入购物车的代码wordpress 文章间距
  • 微信开发 网站备案吗旅游网页设计图片素材
  • 青岛建韩国网站的公司天猫商城网官网
  • 做网站的公司介绍网站怎么获得流量
  • 站酷网设计素材临时网站怎么做
  • 大型车产品网站建设凯里做网站的公司
  • 利用博客做网站排名做网站推广排名
  • 陕西住房和建设部网站网页模板psd素材
  • 网站做app的重要性惠州模板做网站
  • 舟山建设工程信息网站一个虚拟空间做两个网站
  • 用织梦做网站有什么公司会要公众号 创意名字
  • 网站建设方案书封面成都网站制作哪家专业
  • 多语言外贸企业网站源码iis能建设网站吗
  • 网站开发连接形式怎么做业务网站
  • 北京pk10盘制作网站建设邢台学校网站建设