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

做网批那个网站好电商店铺装修

做网批那个网站好,电商店铺装修,王占山战斗英雄简历,wordpress注册不发邮件近似聚合 如果所有的数据都在一台机器上#xff0c;那么生活会容易许多。 CS201 课上教的经典算法就足够应付这些问题。如果所有的数据都在一台机器上#xff0c;那么也就不需要像 Elasticsearch 这样的分布式软件了。不过一旦我们开始分布式存储数据#xff0c;就需要小心…近似聚合 如果所有的数据都在一台机器上那么生活会容易许多。 CS201 课上教的经典算法就足够应付这些问题。如果所有的数据都在一台机器上那么也就不需要像 Elasticsearch 这样的分布式软件了。不过一旦我们开始分布式存储数据就需要小心地选择算法。 有些算法可以分布执行到目前为止讨论过的所有聚合都是单次请求获得精确结果的。这些类型的算法通常被认为是 高度并行的 因为它们无须任何额外代价就能在多台机器上并行执行。比如当计算 max 度量时以下的算法就非常简单 把请求广播到所有分片。查看每个文档的 price 字段。如果 price current_max 将 current_max 替换成 price 。返回所有分片的最大 price 并传给协调节点。找到从所有分片返回的最大 price 。这是最终的最大值。 这个算法可以随着机器数的线性增长而横向扩展无须任何协调操作机器之间不需要讨论中间结果而且内存消耗很小一个整型就能代表最大值。 不幸的是不是所有的算法都像获取最大值这样简单。更加复杂的操作则需要在算法的性能和内存使用上做出权衡。对于这个问题我们有个三角因子模型大数据、精确性和实时性。 我们需要选择其中两项 精确 实时 数据可以存入单台机器的内存之中我们可以随心所欲使用任何想用的算法。结果会 100% 精确响应会相对快速。 大数据 精确 传统的 Hadoop。可以处理 PB 级的数据并且为我们提供精确的答案但它可能需要几周的时间才能为我们提供这个答案。 大数据 实时 近似算法为我们提供准确但不精确的结果。 Elasticsearch 目前支持两种近似算法 cardinality 和 percentiles 。 它们会提供准确但不是 100% 精确的结果。 以牺牲一点小小的估算错误为代价这些算法可以为我们换来高速的执行效率和极小的内存消耗。 对于 大多数 应用领域能够 实时 返回高度准确的结果要比 100% 精确结果重要得多。乍一看这可能是天方夜谭。有人会叫 “我们需要精确的答案” 。但仔细考虑 0.5% 误差所带来的影响 99% 的网站延时都在 132ms 以下。0.5% 的误差对以上延时的影响在正负 0.66ms 。近似计算的结果会在毫秒内返回而“完全正确”的结果就可能需要几秒甚至无法返回。 只要简单的查看网站的延时情况难道我们会在意近似结果是 132.66ms 而不是 132ms 吗当然不是所有的领域都能容忍这种近似结果但对于绝大多数来说是没有问题的。接受近似结果更多的是一种 文化观念上 的壁垒而不是商业或技术上的需要。 统计去重后的数量 Elasticsearch 提供的首个近似聚合是 cardinality 注基数度量。 它提供一个字段的基数即该字段的 distinct 或者 unique 值的数目。 你可能会对 SQL 形式比较熟悉 SELECT COUNT(DISTINCT color) FROM cars去重是一个很常见的操作可以回答很多基本的业务问题 网站独立访客是多少卖了多少种汽车每月有多少独立用户购买了商品 我们可以用 cardinality 度量确定经销商销售汽车颜色的数量 GET /cars/_search {size: 0,aggs: {distinct_colors: {cardinality: {field: color.keyword}}} }返回的结果表明已经售卖了三种不同颜色的汽车 aggregations: {distinct_colors: {value: 3}可以让我们的例子变得更有用每月有多少颜色的车被售出为了得到这个度量我们只需要将一个 cardinality 度量嵌入一个 date_histogram GET /cars/_search {size: 0,aggs: {months: {date_histogram: {field: sold,calendar_interval: month},aggs: {distinct_colors: {cardinality: {field: color.keyword}}}}} }学会权衡 正如我们本章开头提到的 cardinality 度量是一个近似算法。 它是基于 HyperLogLog HLL算法的。 HLL 会先对我们的输入作哈希运算然后根据哈希运算的结果中的 bits 做概率估算从而得到基数。 我们不需要理解技术细节如果确实感兴趣可以阅读这篇论文 但我们最好应该关注一下这个算法的 特性 可配置的精度用来控制内存的使用更精确 更多内存。小的数据集精度是非常高的。我们可以通过配置参数来设置去重需要的固定内存使用量。无论数千还是数十亿的唯一值内存使用量只与你配置的精确度相关。 要配置精度我们必须指定 precision_threshold 参数的值。 这个阈值定义了在何种基数水平下我们希望得到一个近乎精确的结果。参考以下示例 GET /cars/_search {size: 0,aggs: {distinct_colors: {cardinality: {field: color.keyword,precision_threshold: 100}}} }precision_threshold 接受 0–40,000 之间的数字更大的值还是会被当作 40,000 来处理。 示例会确保当字段唯一值在 100 以内时会得到非常准确的结果。尽管算法是无法保证这点的但如果基数在阈值以下几乎总是 100% 正确的。高于阈值的基数会开始节省内存而牺牲准确度同时也会对度量结果带入误差。 对于指定的阈值HLL 的数据结构会大概使用 precision_threshold * 8 字节的内存所以就必须在牺牲内存和获得额外的准确度间做平衡。 在实际应用中 100 的阈值可以在唯一值为百万的情况下仍然将误差维持 5% 以内。 速度优化 如果想要获得唯一值的数目 通常 需要查询整个数据集合或几乎所有数据。 所有基于所有数据的操作都必须迅速原因是显然的。 HyperLogLog 的速度已经很快了它只是简单的对数据做哈希以及一些位操作。 但如果速度对我们至关重要可以做进一步的优化。 因为 HLL 只需要字段内容的哈希值我们可以在索引时就预先计算好。 就能在查询时跳过哈希计算然后将哈希值从 fielddata 直接加载出来。 预先计算哈希值只对内容很长或者基数很高的字段有用计算这些字段的哈希值的消耗在查询时是无法忽略的。 尽管数值字段的哈希计算是非常快速的存储它们的原始值通常需要同样或更少的内存空间。这对低基数的字符串字段同样适用Elasticsearch 的内部优化能够保证每个唯一值只计算一次哈希。 基本上说预先计算并不能保证所有的字段都更快它只对那些具有高基数和/或者内容很长的字符串字段有作用。需要记住的是预计算只是简单的将查询消耗的时间提前转移到索引时并非没有任何代价区别在于你可以选择在 什么时候 做这件事要么在索引时要么在查询时。 要想这么做我们需要为数据增加一个新的多值字段。我们先删除索引再增加一个包括哈希值字段的映射然后重新索引 PUT /cars/ {mappings: {properties: {color: {type: text,fields: {hash: {type: murmur3}}}}} } POST /cars/_bulk { index: {}} { price : 10000, color : red, make : honda, sold : 2014-10-28 } { index: {}} { price : 20000, color : red, make : honda, sold : 2014-11-05 } { index: {}} { price : 30000, color : green, make : ford, sold : 2014-05-18 } { index: {}} { price : 15000, color : blue, make : toyota, sold : 2014-07-02 } { index: {}} { price : 12000, color : green, make : toyota, sold : 2014-08-19 } { index: {}} { price : 20000, color : red, make : honda, sold : 2014-11-05 } { index: {}} { price : 80000, color : red, make : bmw, sold : 2014-01-01 } { index: {}} { price : 25000, color : blue, make : ford, sold : 2014-02-12 }“type”: “murmur3”这个子字段的类型是 Murmur3 哈希类型。Murmur3 哈希类型可以用于快速查找和比较哈希值。 现在当我们执行聚合时我们使用 color.hash 字段而不是 color 字段 GET /cars/_search {size: 0,aggs: {distinct_colors: {cardinality: {field: color.hash}}} }注意我们指定的是哈希过的多值字段而不是原始字段。 现在 cardinality 度量会读取 “color.hash” 里的值预先计算的哈希值取代动态计算原始值的哈希。 单个文档节省的时间是非常少的但是如果你聚合一亿数据每个字段多花费 10 纳秒的时间那么在每次查询时都会额外增加 1 秒如果我们要在非常大量的数据里面使用 cardinality 我们可以权衡使用预计算的意义是否需要提前计算 hash从而在查询时获得更好的性能做一些性能测试来检验预计算哈希是否适用于你的应用场景。 百分位计算 Elasticsearch 提供的另外一个近似度量就是 percentiles 百分位数度量。 百分位数展现某以具体百分比下观察到的数值。例如第95个百分位上的数值是高于 95% 的数据总和。 百分位数通常用来找出异常。在统计学的正态分布下第 0.13 和 第 99.87 的百分位数代表与均值距离三倍标准差的值。任何处于三倍标准差之外的数据通常被认为是不寻常的因为它与平均值相差太大。 更具体的说假设我们正运行一个庞大的网站一个很重要的工作是保证用户请求能得到快速响应因此我们就需要监控网站的延时来判断响应是否能保证良好的用户体验。 在此场景下一个常用的度量方法就是平均响应延时。 但这并不是一个好的选择尽管很常用因为平均数通常会隐藏那些异常值 中位数有着同样的问题。 我们可以尝试最大值但这个度量会轻而易举的被单个异常值破坏。 在图 Figure 40, “Average request latency over time” 查看问题。如果我们依靠如平均值或中位数这样的简单度量就会得到像这样一幅图 Figure 40, “Average request latency over time” 。 一切正常。 图上有轻微的波动但没有什么值得关注的。 但如果我们加载 99 百分位数时这个值代表最慢的 1% 的延时我们看到了完全不同的一幅画面如图 Figure 41, “Average request latency with 99th percentile over time” 。 令人吃惊在上午九点半时均值只有 75ms。如果作为一个系统管理员我们都不会看他第二眼。 一切正常但 99 百分位告诉我们有 1% 的用户碰到的延时超过 850ms这是另外一幅场景。 在上午4点48时也有一个小波动这甚至无法从平均值和中位数曲线上观察到。 这只是百分位的一个应用场景百分位还可以被用来快速用肉眼观察数据的分布检查是否有数据倾斜或双峰甚至更多。 百分位度量 让我加载一个新的数据集汽车的数据不太适用于百分位。我们要索引一系列网站延时数据然后运行一些百分位操作进行查看 POST /website/_bulk { index: {}} { latency : 100, zone : US, timestamp : 2014-10-28 } { index: {}} { latency : 80, zone : US, timestamp : 2014-10-29 } { index: {}} { latency : 99, zone : US, timestamp : 2014-10-29 } { index: {}} { latency : 102, zone : US, timestamp : 2014-10-28 } { index: {}} { latency : 75, zone : US, timestamp : 2014-10-28 } { index: {}} { latency : 82, zone : US, timestamp : 2014-10-29 } { index: {}} { latency : 100, zone : EU, timestamp : 2014-10-28 } { index: {}} { latency : 280, zone : EU, timestamp : 2014-10-29 } { index: {}} { latency : 155, zone : EU, timestamp : 2014-10-29 } { index: {}} { latency : 623, zone : EU, timestamp : 2014-10-28 } { index: {}} { latency : 380, zone : EU, timestamp : 2014-10-28 } { index: {}} { latency : 319, zone : EU, timestamp : 2014-10-29 }数据有三个值延时、数据中心的区域以及时间戳。让我们对数据全集进行 百分位 操作以获得数据分布情况的直观感受 GET /website/_search {size: 0,aggs: {load_times: {percentiles: {field: latency}},avg_load_time: {avg: {field: latency}}} }percentiles 度量被应用到 latency 延时字段。为了比较我们对相同字段使用 avg 度量。 默认情况下percentiles 度量会返回一组预定义的百分位数值 [1, 5, 25, 50, 75, 95, 99] 。它们表示了人们感兴趣的常用百分位数值极端的百分位数在范围的两边其他的一些处于中部。在返回的响应中我们可以看到最小延时在 75ms 左右而最大延时差不多有 600ms。与之形成对比的是平均延时在 200ms 左右 信息并不是很多 aggregations: {load_times: {values: {1.0: 75.55,5.0: 77.75,25.0: 94.75,50.0: 101,75.0: 289.75,95.0: 489.3499999999998,99.0: 596.2700000000002}},avg_load_time: {value: 199.58333333333334}}所以显然延时的分布很广让我们看看它们是否与数据中心的地理区域有关 GET /website/_search {size: 0,aggs: {zones: {terms: {field: zone.keyword},aggs: {load_times: {percentiles: {field: latency,percents: [50,95,99]}},load_avg: {avg: {field: latency}}}}} }首先根据区域我们将延时分到不同的桶中。再计算每个区域的百分位数值。percents 参数接受了我们想返回的一组百分位数因为我们只对长的延时感兴趣。 在响应结果中我们发现欧洲区域EU要比美国区域US慢很多在美国区域US50 百分位与 99 百分位十分接近它们都接近均值。 与之形成对比的是欧洲区域EU在 50 和 99 百分位有较大区分。现在显然可以发现是欧洲区域EU拉低了延时的统计信息我们知道欧洲区域的 50% 延时都在 300ms。 aggregations: {zones: {doc_count_error_upper_bound: 0,sum_other_doc_count: 0,buckets: [{key: EU,doc_count: 6,load_times: {values: {50.0: 299.5,95.0: 562.25,99.0: 610.85}},load_avg: {value: 309.5}},{key: US,doc_count: 6,load_times: {values: {50.0: 90.5,95.0: 101.5,99.0: 101.9}},load_avg: {value: 89.66666666666667}}]}}百分位等级 这里有另外一个紧密相关的度量叫 percentile_ranks 。 percentiles 度量告诉我们落在某个百分比以下的所有文档的最小值。例如如果 50 百分位是 119ms那么有 50% 的文档数值都不超过 119ms。 percentile_ranks 告诉我们某个具体值属于哪个百分位。119ms 的 percentile_ranks 是在 50 百分位。 这基本是个双向关系例如 50 百分位是 119ms。119ms 百分位等级是 50 百分位。 所以假设我们网站必须维持的服务等级协议SLA是响应时间低于 210ms。然后开个玩笑我们老板警告我们如果响应时间超过 800ms 会把我开除。可以理解的是我们希望知道有多少百分比的请求可以满足 SLA 的要求并期望至少在 800ms 以下。 为了做到这点我们可以应用 percentile_ranks 度量而不是 percentiles 度量 GET /website/_search {size: 0,aggs: {zones: {terms: {field: zone.keyword},aggs: {load_times: {percentile_ranks: {field: latency,values: [210,800]}}}}} }percentile_ranks 度量接受一组我们希望分级的数值。 在聚合运行后我们能得到两个值 aggregations: {zones: {doc_count_error_upper_bound: 0,sum_other_doc_count: 0,buckets: [{key: EU,doc_count: 6,load_times: {values: {210.0: 31.944444444444443,800.0: 100}}},{key: US,doc_count: 6,load_times: {values: {210.0: 100,800.0: 100}}}]}}这告诉我们三点重要的信息 在欧洲EU210ms 的百分位等级是 31.94% 。在美国US210ms 的百分位等级是 100% 。在欧洲EU和美国US800ms 的百分位等级是 100% 。 通俗的说在欧洲区域EU只有 32% 的响应时间满足服务等级协议SLA而美国区域US始终满足服务等级协议的。但幸运的是两个区域所有响应时间都在 800ms 以下所以我们还不会被炒鱿鱼至少目前不会。 percentile_ranks 度量提供了与 percentiles 相同的信息但它以不同方式呈现如果我们对某个具体数值更关心使用它会更方便。 学会权衡 和基数一样计算百分位需要一个近似算法。 朴素的 实现会维护一个所有值的有序列表 但当我们有几十亿数据分布在几十个节点时这几乎是不可能的。 取而代之的是 percentiles 使用一个 TDigest 算法由 Ted Dunning 在 Computing Extremely Accurate Quantiles Using T-Digests 里面提出的。 与 HyperLogLog 一样不需要理解完整的技术细节但有必要了解算法的特性 百分位的准确度与百分位的 极端程度 相关也就是说 1 或 99 的百分位要比 50 百分位要准确。这只是数据结构内部机制的一种特性但这是一个好的特性因为多数人只关心极端的百分位。对于数值集合较小的情况百分位非常准确。如果数据集足够小百分位可能 100% 精确。随着桶里数值的增长算法会开始对百分位进行估算。它能有效在准确度和内存节省之间做出权衡。 不准确的程度比较难以总结因为它依赖于 聚合时数据的分布以及数据量的大小。 与 cardinality 类似我们可以通过修改参数 compression 来控制内存与准确度之间的比值。 TDigest 算法用节点近似计算百分比节点越多准确度越高同时内存消耗也越大这都与数据量成正比。 compression 参数限制节点的最大数目为 20 * compression 。 因此通过增加压缩比值可以以消耗更多内存为代价提高百分位数准确性。更大的压缩比值会使算法运行更慢因为底层的树形数据结构的存储也会增长也导致操作的代价更高。默认的压缩比值是 100 。 一个节点大约使用 32 字节的内存所以在最坏的情况下例如大量数据有序存入默认设置会生成一个大小约为 64KB 的 TDigest。 在实际应用中数据会更随机所以 TDigest 使用的内存会更少。 通过聚合发现异常指标 significant_terms SigTerms聚合 与其他聚合都不相同。 目前为止我们看到的所有聚合在本质上都是简单的数学计算。将不同这些构造块相互组合在一起我们可以创建复杂的聚合以及数据报表。 significant_terms 有着不同的工作机制。对有些人来说它甚至看起来有点像机器学习。 significant_terms 聚合可以在你数据集中找到一些 异常 的指标。 如何解释这些 不常见 的行为 这些异常的数据指标通常比我们预估出现的频次要更频繁这些统计上的异常指标通常象征着数据里的某些有趣信息。 例如假设我们负责检测和跟踪信用卡欺诈客户打电话过来抱怨他们信用卡出现异常交易它们的帐户已经被盗用。这些交易信息只是更严重问题的症状。在最近的某些地区一些商家有意的盗取客户的信用卡信息或者它们自己的信息无意中也被盗取。 我们的任务是找到 危害的共同点 如果我们有 100 个客户抱怨交易异常他们很有可能都属于同一个商户而这家商户有可能就是罪魁祸首。 当然这里面还有一些特例。例如很多客户在它们近期交易历史记录中会有很大的商户如亚马逊我们可以将亚马逊排除在外然而在最近一些有问题的信用卡的商家里面也有亚马逊。 这是一个 普通的共同 商户的例子。每个人都共享这个商户无论有没有遭受危害。我们对它并不感兴趣。 相反我们有一些很小商户比如街角的一家药店它们属于 普通但不寻常 的情况只有一两个客户有交易记录。我们同样可以将这些商户排除因为所有受到危害的信用卡都没有与这些商户发生过交易我们可以肯定它们不是安全漏洞的责任方。 我们真正想要的是 不普通的共同 商户。所有受到危害的信用卡都与它们发生过交易但是在未受危害的背景噪声下它们并不明显。这些商户属于统计异常它们比应该出现的频率要高。这些不普通的共同商户很有可能就是需要调查的。 significant_terms 聚合就是做这些事情。它分析统计你的数据并通过对比正常数据找到可能有异常频次的指标。 暴露的异常指标 代表什么 依赖的你数据。对于信用卡数据我们可能会想找出信用卡欺诈。 对于电商数据我们可能会想找出未被识别的人口信息从而进行更高效的市场推广。 如果我们正在分析日志我们可能会发现一个服务器会抛出比它本应抛出的更多异常。 significant_terms 的应用还远不止这些。 significant_terms 演示 因为 significant_terms 聚合 是通过分析统计信息来工作的 需要为数据设置一个阀值让它们更有效。也就是说无法通过只索引少量示例数据来展示它。 正因如此我们准备了一个大约 8000 个文档的数据集并将它的快照保存在一个公共演示库中。可以通过以下步骤在集群中还原这些数据 在 elasticsearch.yml 配置文件中增加以下配置 以便将演示库加入到白名单中 repositories.url.allowed_urls: [http://download.elastic.co/*]重启 Elasticsearch。运行以下快照命令。更多使用快照的信息参见 备份集群Backing Up Your Cluster。 PUT /_snapshot/sigterms {type: url,settings: {url: http://download.elastic.co/definitiveguide/sigterms_demo/} }GET /_snapshot/sigterms/_all POST /_snapshot/sigterms/snapshot/_restore GET /mlmovies,mlratings/_recovery 注册一个新的只读地址库并指向演示快照。可选检查库内关于快照的详细信息。开始还原过程。会在集群中创建两个索引 mlmovies 和 mlratings 。可选使用 Recovery API 监控还原过程。 在本演示中会看看 MovieLens 里面用户对电影的评分。在 MovieLens 里用户可以推荐电影并评分这样其他用户也可以找到新的电影。 为了演示会基于输入的电影采用 significant_terms 对电影进行推荐。 让我们看看示例中的数据感受一下要处理的内容。本数据集有两个索引 mlmovies 和 mlratings 。首先查看 mlmovies GET mlmovies/_search {took: 4,timed_out: false,_shards: {...},hits: {total: 10681,max_score: 1,hits: [{_index: mlmovies,_type: mlmovie,_id: 2,_score: 1,_source: {offset: 2,bytes: 34,title: Jumanji (1995)}},....执行一个不带查询条件的搜索以便能看到一组随机演示文档。 mlmovies 里的每个文档表示一个电影数据有两个重要字段电影ID _id 和电影名 title 。可以忽略 offset 和 bytes 。它们是从原始 CSV 文件抽取数据的过程中产生的中间属性。数据集中有 10681 部影片。 现在来看看 mlratings GET mlratings/_search{took: 3,timed_out: false,_shards: {...},hits: {total: 69796,max_score: 1,hits: [{_index: mlratings,_type: mlrating,_id: 00IC-2jDQFiQkpD6vhbFYA,_score: 1,_source: {offset: 1,bytes: 108,movie: [122,185,231,292,316,329,355,356,362,364,370,377,420,466,480,520,539,586,588,589,594,616],user: 1}},...这里可以看到每个用户的推荐信息。每个文档表示一个用户用 ID 字段 user 来表示 movie 字段维护一个用户观看和推荐过的影片列表。 基于流行程度推荐Recommending Based on Popularity 可以采取的首个策略就是基于流行程度向用户推荐影片。 对于某部影片找到所有推荐过它的用户然后将他们的推荐进行聚合并获得推荐中最流行的五部。 我们可以很容易的通过一个 terms 聚合 以及一些过滤来表示它看看 Talladega Nights塔拉迪加之夜 这部影片它是 Will Ferrell 主演的一部关于全国运动汽车竞赛NASCAR racing的喜剧。 在理想情况下我们的推荐应该找到类似风格的喜剧很有可能也是 Will Ferrell 主演的。 GET mlmovies/_search {query: {match: {title: Talladega Nights}} }...hits: [{_index: mlmovies,_type: mlmovie,_id: 46970, _score: 3.658795,_source: {offset: 9575,bytes: 74,title: Talladega Nights: The Ballad of Ricky Bobby (2006)}},...Talladega Nights 的 ID 是 46970 。 有了 ID可以过滤评分再应用 terms 聚合从喜欢 Talladega Nights 的用户中找到最流行的影片 GET mlratings/_search {size : 0, query: {filtered: {filter: {term: {movie: 46970 }}}},aggs: {most_popular: {terms: {field: movie, size: 6}}} }这次查询 mlratings 将结果内容 大小设置 为 0 因为我们只对聚合的结果感兴趣。对影片 Talladega Nights 的 ID 使用过滤器。最后使用 terms 桶找到最流行的影片。 在 mlratings 索引下搜索然后对影片 Talladega Nights 的 ID 使用过滤器。由于聚合是针对查询范围进行操作的它可以有效的过滤聚合结果从而得到那些只推荐 Talladega Nights 的用户。 最后执行 terms 聚合得到最流行的影片。 请求排名最前的六个结果因为 Talladega Nights 本身很有可能就是其中一个结果并不想重复推荐它。 返回结果就像这样 { ...aggregations: {most_popular: {buckets: [{key: 46970,key_as_string: 46970,doc_count: 271},{key: 2571,key_as_string: 2571,doc_count: 197},{key: 318,key_as_string: 318,doc_count: 196},{key: 296,key_as_string: 296,doc_count: 183},{key: 2959,key_as_string: 2959,doc_count: 183},{key: 260,key_as_string: 260,doc_count: 90}]}} ...通过一个简单的过滤查询将得到的结果转换成原始影片名 GET mlmovies/_search {query: {filtered: {filter: {ids: {values: [2571,318,296,2959,260]}}}} }最后得到以下列表 Matrix, The黑客帝国Shawshank Redemption肖申克的救赎Pulp Fiction低俗小说Fight Club搏击俱乐部Star Wars Episode IV: A New Hope星球大战 IV曙光乍现 好吧这肯定不是一个好的列表我喜欢所有这些影片。但问题是几乎 每个人 都喜欢它们。这些影片本来就受大众欢迎也就是说它们出现在每个人的推荐中都会受欢迎。 这其实是一个流行影片的推荐列表而不是和影片 Talladega Nights 相关的推荐。 可以通过再次运行聚合轻松验证而不需要对影片 Talladega Nights 进行过滤。会提供最流行影片的前五名列表 GET mlratings/_search {size : 0,aggs: {most_popular: {terms: {field: movie,size: 5}}} }返回列表非常相似 Shawshank Redemption肖申克的救赎Silence of the Lambs, The沉默的羔羊Pulp Fiction低俗小说Forrest Gump阿甘正传Star Wars Episode IV: A New Hope星球大战 IV曙光乍现 显然只是检查最流行的影片是不能足以创建一个良好而又具鉴别能力的推荐系统。 基于统计的推荐Recommending Based on Statistics 现在场景已经设定好使用 significant_terms 。 significant_terms 会分析喜欢影片 Talladega Nights 的用户组 前端 用户组并且确定最流行的电影。 然后为每个用户 后端 用户构造一个流行影片列表最后将两者进行比较。 统计异常就是与统计背景相比在前景特征组中过度展现的那些影片。理论上讲它应该是一组喜剧因为喜欢 Will Ferrell 喜剧的人给这些影片的评分会比一般人高。 让我们试一下 GET mlratings/_search {size : 0,query: {filtered: {filter: {term: {movie: 46970}}}},aggs: {most_sig: {significant_terms: { field: movie,size: 6}}} }设置几乎一模一样只是用 significant_terms 替代了 terms 。 正如所见查询也几乎是一样的。过滤出喜欢影片 Talladega Nights 的用户他们组成了前景特征用户组。默认情况下 significant_terms 会使用整个索引里的数据作为统计背景所以不需要特别的处理。 与 terms 类似结果返回了一组桶不过有更多的元数据信息 ...aggregations: {most_sig: {doc_count: 271, buckets: [{key: 46970,key_as_string: 46970,doc_count: 271,score: 256.549815498155,bg_count: 271},{key: 52245, key_as_string: 52245,doc_count: 59, score: 17.66462367106966,bg_count: 185 },{key: 8641,key_as_string: 8641,doc_count: 107,score: 13.884387742677438,bg_count: 762},{key: 58156,key_as_string: 58156,doc_count: 17,score: 9.746428133759462,bg_count: 28},{key: 52973,key_as_string: 52973,doc_count: 95,score: 9.65770100311672,bg_count: 857},{key: 35836,key_as_string: 35836,doc_count: 128,score: 9.199001116457955,bg_count: 1610}]...顶层 doc_count 展现了前景特征组里文档的数量。每个桶里面列出了聚合的键值例如影片的ID。桶内文档的数量 doc_count 。背景文档的数量表示该值在整个统计背景里出现的频度。 可以看到获得的第一个桶是 Talladega Nights 。它可以在所有 271 个文档中找到这并不意外。让我们看下一个桶键值 52245 。 这个 ID 对应影片 Blades of Glory荣誉之刃 它是一部关于男子学习滑冰的喜剧也是由 Will Ferrell 主演。可以看到喜欢 Talladega Nights 的用户对它的推荐是 59 次。 这也意味着 21% 的前景特征用户组推荐了影片 Blades of Glory 59 / 271 0.2177 。 形成对比的是 Blades of Glory 在整个数据集合中仅被推荐了 185 次 只占 0.26% 185 / 69796 0.00265 。因此 Blades of Glory 是一个统计异常它在喜欢 Talladega Nights 的用户中是显著的共性注uncommonly common。这样就找到了一个好的推荐 如果看完整的列表它们都是好的喜剧推荐其中很多也是由 Will Ferrell 主演 Blades of Glory荣誉之刃Anchorman: The Legend of Ron Burgundy王牌播音员Semi-Pro半职业选手Knocked Up一夜大肚40-Year-Old Virgin, The四十岁的老处男 这只是 significant_terms 它强大的一个示例一旦开始使用 significant_terms 可能碰到这样的情况我们不想要最流行的而想要显著的共性注uncommonly common。这个简单的聚合可以揭示出一些数据里出人意料的复杂趋势。 Doc Values and Fielddata Doc Values 聚合使用一个叫 doc values 的数据结构在 Doc Values 介绍 里简单介绍。 Doc values 可以使聚合更快、更高效并且内存友好所以理解它的工作方式十分有益。 Doc values 的存在是因为倒排索引只对某些操作是高效的。 倒排索引的优势 在于查找包含某个项的文档而对于从另外一个方向的相反操作并不高效即确定哪些项是否存在单个文档里聚合需要这种次级的访问模式。 对于以下倒排索引 Term Doc_1 Doc_2 Doc_3 ------------------------------------ brown | X | X | dog | X | | X dogs | | X | X fox | X | | X foxes | | X | in | | X | jumped | X | | X lazy | X | X | leap | | X | over | X | X | X quick | X | X | X summer | | X | the | X | | X ------------------------------------如果我们想要获得所有包含 brown 的文档的词的完整列表我们会创建如下查询 GET /my_index/_search {query : {match : {body : brown}},aggs : {popular_terms: {terms : {field : body}}} }查询部分简单又高效。倒排索引是根据项来排序的所以我们首先在词项列表中找到 brown 然后扫描所有列找到包含 brown 的文档。我们可以快速看到 Doc_1 和 Doc_2 包含 brown 这个 token。 然后对于聚合部分我们需要找到 Doc_1 和 Doc_2 里所有唯一的词项。 用倒排索引做这件事情代价很高 我们会迭代索引里的每个词项并收集 Doc_1 和 Doc_2 列里面 token。这很慢而且难以扩展随着词项和文档的数量增加执行时间也会增加。 Doc values 通过转置两者间的关系来解决这个问题。倒排索引将词项映射到包含它们的文档doc values 将文档映射到它们包含的词项 Doc Terms ----------------------------------------------------------------- Doc_1 | brown, dog, fox, jumped, lazy, over, quick, the Doc_2 | brown, dogs, foxes, in, lazy, leap, over, quick, summer Doc_3 | dog, dogs, fox, jumped, over, quick, the -----------------------------------------------------------------当数据被转置之后想要收集到 Doc_1 和 Doc_2 的唯一 token 会非常容易。获得每个文档行获取所有的词项然后求两个集合的并集。 因此搜索和聚合是相互紧密缠绕的。搜索使用倒排索引查找文档聚合操作收集和聚合 doc values 里的数据。 深入理解 Doc Values 在上一节一开头我们就说 Doc Values 是 “快速、高效并且内存友好” 。 这个口号听不起来不错不过话说回来 Doc Values 到底是如何工作的呢 Doc Values 是在索引时与 倒排索引 同时生成。也就是说 Doc Values 和 倒排索引 一样基于 Segement 生成并且是不可变的。同时 Doc Values 和 倒排索引 一样序列化到磁盘这样对性能和扩展性有很大帮助。 Doc Values 通过序列化把数据结构持久化到磁盘我们可以充分利用操作系统的内存而不是 JVM 的 Heap 。 当 working set 远小于系统的可用内存系统会自动将 Doc Values 驻留在内存中使得其读写十分快速不过当其远大于可用内存时系统会根据需要从磁盘读取 Doc Values然后选择性放到分页缓存中。很显然这样性能会比在内存中差很多但是它的大小就不再局限于服务器的内存了。如果是使用 JVM 的 Heap 来实现那么只能是因为 OutOfMemory 导致程序崩溃了。 列式存储的压缩 从广义来说Doc Values 本质上是一个序列化的 列式存储 。 正如我们上一节所讨论的列式存储 适用于聚合、排序、脚本等操作。 而且这种存储方式也非常便于压缩特别是数字类型。这样可以减少磁盘空间并且提高访问速度。现代 CPU 的处理速度要比磁盘快几个数量级尽管即将到来的 NVMe 驱动器正在迅速缩小差距。所以我们必须减少直接存磁盘读取数据的大小尽管需要额外消耗 CPU 运算用来进行解压。 要了解它如何压缩数据的来看一组数字类型的 Doc Values Doc Terms ----------------------------------------------------------------- Doc_1 | 100 Doc_2 | 1000 Doc_3 | 1500 Doc_4 | 1200 Doc_5 | 300 Doc_6 | 1900 Doc_7 | 4200 -----------------------------------------------------------------按列布局意味着我们有一个连续的数据块 [100,1000,1500,1200,300,1900,4200] 。因为我们已经知道他们都是数字而不是像文档或行中看到的异构集合所以我们可以使用统一的偏移来将他们紧紧排列。 而且针对这样的数字有很多种压缩技巧。你会注意到这里每个数字都是 100 的倍数Doc Values 会检测一个段里面的所有数值并使用一个 最大公约数 方便做进一步的数据压缩。 如果我们保存 100 作为此段的除数我们可以对每个数字都除以 100然后得到 [1,10,15,12,3,19,42] 。现在这些数字变小了只需要很少的位就可以存储下也减少了磁盘存放的大小。 Doc Values 在压缩过程中使用如下技巧。它会按依次检测以下压缩模式: 如果所有的数值各不相同或缺失设置一个标记并记录这些值如果这些值小于 256将使用一个简单的编码表如果这些值大于 256检测是否存在一个最大公约数如果没有存在最大公约数从最小的数值开始统一计算偏移量进行编码 你会发现这些压缩模式不是传统的通用的压缩方式比如 DEFLATE 或是 LZ4。 因为列式存储的结构是严格且良好定义的我们可以通过使用专门的模式来达到比通用压缩算法如 LZ4 更高的压缩效果。 禁用 Doc Values Doc Values 默认对所有字段启用除了 analyzed strings。也就是说所有的数字、地理坐标、日期、IP 和不分析 not_analyzed 字符类型都会默认开启。 analyzed strings 暂时还不能使用 Doc Values。文本经过分析流程生成很多 Token使得 Doc Values 不能高效运行。我们将在 聚合与分析 讨论如何使用分析字符类型来做聚合。 因为 Doc Values 默认启用你可以选择对你数据集里面的大多数字段进行聚合和排序操作。但是如果你知道你永远也不会对某些字段进行聚合、排序或是使用脚本操作 尽管这并不常见但是你可以通过禁用特定字段的 Doc Values 。这样不仅节省磁盘空间也许会提升索引的速度。 要禁用 Doc Values 在字段的映射mapping设置 doc_values: false 即可。例如这里我们创建了一个新的索引字段 “session_id” 禁用了 Doc Values PUT my_index {mappings: {properties: {session_id: {type: text}}} }通过设置 “type”: “text” 这个字段将不能被用于聚合、排序以及脚本操作 反过来也是可以进行配置的让一个字段可以被聚合通过禁用倒排索引使它不能被正常搜索例如 PUT my_index {mappings: {properties: {session_id: {type: keyword}}} }索引被禁用了这让该字段不能被查询/搜索 聚合与分析 有些聚合比如 terms 桶 操作字符串字段。字符串字段可能是 analyzed 或者 not_analyzed 那么问题来了分析是怎么影响聚合的呢 答案是影响“很多”有两个原因分析影响聚合中使用的 tokens 并且 doc values 不能使用于 分析字符串。 让我们解决第一个问题分析 tokens 的产生如何影响聚合。首先索引一些代表美国各个州的文档 POST /agg_analysis/_bulk { index: {}} { state : New York } { index: {}} { state : New Jersey } { index: {}} { state : New Mexico } { index: {}} { state : New York } { index: {}} { state : New York }我们希望创建一个数据集里各个州的唯一列表并且计数。 简单让我们使用 terms 桶 GET /agg_analysis/_search {size: 0,aggs: {states: {terms: {field: state.keyword}}} }得到结果 aggregations: {states: {doc_count_error_upper_bound: 0,sum_other_doc_count: 0,buckets: [{key: New York,doc_count: 3},{key: New Jersey,doc_count: 1},{key: New Mexico,doc_count: 1}]}}高基数内存的影响High-Cardinality Memory Implications 避免分析字段的另外一个原因就是高基数字段在加载到 fielddata 时会消耗大量内存。 分析的过程会经常尽管不总是这样生成大量的 token这些 token 大多都是唯一的。 这会增加字段的整体基数并且带来更大的内存压力。 有些类型的分析对于内存来说 极度 不友好想想 n-gram 的分析过程 New York 会被 n-gram 分析成以下 token ne ew w y yo or rk可以想象 n-gram 的过程是如何生成大量唯一 token 的特别是在分析成段文本的时候。当这些数据加载到内存中会轻而易举的将我们堆空间消耗殆尽。 因此在聚合字符串字段之前请评估情况 这是一个 not_analyzed 字段吗如果是可以通过 doc values 节省内存 。 否则这是一个 analyzed 字段它将使用 fielddata 并加载到内存中。这个字段因为 ngrams 有一个非常大的基数如果是这对于内存来说极度不友好。 限制内存使用 一旦分析字符串被加载到 fielddata 他们会一直在那里直到被驱逐或者节点崩溃。由于这个原因留意内存的使用情况了解它是如何以及何时加载的怎样限制对集群的影响是很重要的。 Fielddata 是 延迟 加载。如果你从来没有聚合一个分析字符串就不会加载 fielddata 到内存中。此外fielddata 是基于字段加载的 这意味着只有很活跃地使用字段才会增加 fielddata 的负担。 然而这里有一个令人惊讶的地方。假设你的查询是高度选择性和只返回命中的 100 个结果。大多数人认为 fielddata 只加载 100 个文档。 实际情况是fielddata 会加载索引中针对该特定字段的 所有的 文档而不管查询的特异性。逻辑是这样如果查询会访问文档 X、Y 和 Z那很有可能会在下一个查询中访问其他文档。 与 doc values 不同fielddata 结构不会在索引时创建。相反它是在查询运行时动态填充。这可能是一个比较复杂的操作可能需要一些时间。 将所有的信息一次加载再将其维持在内存中的方式要比反复只加载一个 fielddata 的部分代价要低。 JVM 堆 是有限资源的应该被合理利用。 限制 fielddata 对堆使用的影响有多套机制这些限制方式非常重要因为堆栈的乱用会导致节点不稳定感谢缓慢的垃圾回收机制甚至导致节点宕机通常伴随 OutOfMemory 异常。 选择堆大小Choosing a Heap Size 在设置 Elasticsearch 堆大小时需要通过 $ES_HEAP_SIZE 环境变量应用两个规则 不要超过可用 RAM 的 50% Lucene 能很好利用文件系统的缓存它是通过系统内核管理的。如果没有足够的文件系统缓存空间性能会受到影响。 此外专用于堆的内存越多意味着其他所有使用 doc values 的字段内存越少。 不要超过 32 GB 如果堆大小小于 32 GBJVM 可以利用指针压缩这可以大大降低内存的使用每个指针 4 字节而不是 8 字节。 更详细和更完整的堆大小讨论请参阅 堆内存:大小和交换 Fielddata的大小 indices.fielddata.cache.size 控制为 fielddata 分配的堆空间大小。 当你发起一个查询分析字符串的聚合将会被加载到 fielddata如果这些字符串之前没有被加载过。如果结果中 fielddata 大小超过了指定 大小 其他的值将会被回收从而获得空间。 默认情况下设置都是 unbounded Elasticsearch 永远都不会从 fielddata 中回收数据。 这个默认设置是刻意选择的fielddata 不是临时缓存。它是驻留内存里的数据结构必须可以快速执行访问而且构建它的代价十分高昂。如果每个请求都重载数据性能会十分糟糕。 一个有界的大小会强制数据结构回收数据。我们会看何时应该设置这个值但请首先阅读以下警告 这个设置是一个安全卫士而非内存不足的解决方案。 如果没有足够空间可以将 fielddata 保留在内存中Elasticsearch 就会时刻从磁盘重载数据并回收其他数据以获得更多空间。内存的回收机制会导致重度磁盘I/O并且在内存中生成很多垃圾这些垃圾必须在晚些时候被回收掉。 设想我们正在对日志进行索引每天使用一个新的索引。通常我们只对过去一两天的数据感兴趣尽管我们会保留老的索引但我们很少需要查询它们。不过如果采用默认设置旧索引的 fielddata 永远不会从缓存中回收 fieldata 会保持增长直到 fielddata 发生断熔请参阅 断路器这样我们就无法载入更多的 fielddata。 这个时候我们被困在了死胡同。但我们仍然可以访问旧索引中的 fielddata也无法加载任何新的值。相反我们应该回收旧的数据并为新值获得更多空间。 为了防止发生这样的事情可以通过在 config/elasticsearch.yml 文件中增加配置为 fielddata 设置一个上限 indices.fielddata.cache.size: 20% 可以设置堆大小的百分比也可以是某个值例如 5gb 。 有了这个设置最久未使用LRU的 fielddata 会被回收为新数据腾出空间。 监控 fielddataMonitoring fielddata 无论是仔细监控 fielddata 的内存使用情况 还是看有无数据被回收都十分重要。高的回收数可以预示严重的资源问题以及性能不佳的原因。 Fielddata 的使用可以被监控 按索引使用 indices-stats API GET /_stats/fielddata?fields*按节点使用 nodes-stats API GET /_nodes/stats/indices/fielddata?fields*按索引节点 GET /_nodes/stats/indices/fielddata?levelindicesfields*使用设置 ?fields* 可以将内存使用分配到每个字段。 断路器 机敏的读者可能已经发现 fielddata 大小设置的一个问题。fielddata 大小是在数据加载 之后 检查的。 如果一个查询试图加载比可用内存更多的信息到 fielddata 中会发生什么答案很丑陋我们会碰到 OutOfMemoryException 。 Elasticsearch 包括一个 fielddata 断熔器 这个设计就是为了处理上述情况。 断熔器通过内部检查字段的类型、基数、大小等等来估算一个查询需要的内存。它然后检查要求加载的 fielddata 是否会导致 fielddata 的总量超过堆的配置比例。 如果估算查询的大小超出限制就会 触发 断路器查询会被中止并返回异常。这都发生在数据加载 之前 也就意味着不会引起 OutOfMemoryException 。 可用的断路器Available Circuit Breakers Elasticsearch 有一系列的断路器它们都能保证内存不会超出限制 indices.breaker.fielddata.limit fielddata 断路器默认设置堆的 60% 作为 fielddata 大小的上限。 indices.breaker.request.limit request 断路器估算需要完成其他请求部分的结构大小例如创建一个聚合桶默认限制是堆内存的 40%。 indices.breaker.total.limit total 揉合 request 和 fielddata 断路器保证两者组合起来不会使用超过堆内存的 70%。 断路器的限制可以在文件 config/elasticsearch.yml 中指定可以动态更新一个正在运行的集群 PUT /_cluster/settings {persistent : {indices.breaker.fielddata.limit : 40% } }这个限制是按对内存的百分比设置的。 Fielddata 的过滤 设想我们正在运行一个网站允许用户收听他们喜欢的歌曲。 为了让他们可以更容易的管理自己的音乐库用户可以为歌曲设置任何他们喜欢的标签这样我们就会有很多歌曲被附上 rock摇滚 、 hiphop嘻哈 和 electronica电音 但也会有些歌曲被附上 my_16th_birthday_favorite_anthem 这样的标签。 现在设想我们想要为用户展示每首歌曲最受欢迎的三个标签很有可能 rock 这样的标签会排在三个中的最前面而 my_16th_birthday_favorite_anthem 则不太可能得到评级。 尽管如此为了计算最受欢迎的标签我们必须强制将这些一次性使用的项加载到内存中。 感谢 fielddata 过滤我们可以控制这种状况。我们 知道 自己只对最流行的项感兴趣所以我们可以简单地避免加载那些不太有意思的长尾项 预加载 fielddata Elasticsearch 加载内存 fielddata 的默认行为是 延迟 加载 。 当 Elasticsearch 第一次查询某个字段时它将会完整加载这个字段所有 Segment 中的倒排索引到内存中以便于以后的查询能够获取更好的性能。 对于小索引段来说这个过程的需要的时间可以忽略。但如果我们有一些 5 GB 的索引段并希望加载 10 GB 的 fielddata 到内存中这个过程可能会要数十秒。 已经习惯亚秒响应的用户很难会接受停顿数秒卡着没反应的网站。 有三种方式可以解决这个延时高峰 预加载 fielddata预加载全局序号缓存预热 所有的变化都基于同一概念预加载 fielddata 这样在用户进行搜索时就不会碰到延迟高峰。 预加载 fielddataEagerly Loading Fielddata 第一个工具称为 预加载 与默认的 延迟加载相对。随着新分段的创建通过刷新、写入或合并等方式 启动字段预加载可以使那些对搜索不可见的分段里的 fielddata 提前 加载。 这就意味着首次命中分段的查询不需要促发 fielddata 的加载因为 fielddata 已经被载入到内存。避免了用户遇到搜索卡顿的情形。 预加载是按字段启用的所以我们可以控制具体哪个字段可以预先加载 PUT /music {mappings: {properties: {tags: {type: text,fielddata: true}}} } GET /_cat/fielddata?v设置 “fielddata”: true 可以告诉 Elasticsearch 预先将此字段的内容载入内存中。Elasticsearch只允许fielddata的值为布尔类型的true或false而不接受其他值。 全局序号Global Ordinals 略 索引预热器Index Warmers 移除了不再需要 优化聚合查询 “elasticsearch 里面桶的叫法和 SQL 里面分组的概念是类似的一个桶就类似 SQL 里面的一个 group多级嵌套的 aggregation 类似 SQL 里面的多字段分组group by field1,field2, …​…注意这里仅仅是概念类似底层的实现原理是不一样的。 译者注” terms 桶基于我们的数据动态构建桶它并不知道到底生成了多少桶。 大多数时候对单个字段的聚合查询还是非常快的 但是当需要同时聚合多个字段时就可能会产生大量的分组最终结果就是占用 es 大量内存从而导致 OOM 的情况发生。 假设我们现在有一些关于电影的数据集每条数据里面会有一个数组类型的字段存储表演该电影的所有演员的名字。 {actors : [Fred Jones,Mary Jane,Elizabeth Worthing] }如果我们想要查询出演影片最多的十个演员以及与他们合作最多的演员使用聚合是非常简单的 {aggs : {actors : {terms : {field : actors,size : 10},aggs : {costars : {terms : {field : actors,size : 5}}}}} }这会返回前十位出演最多的演员以及与他们合作最多的五位演员。这看起来是一个简单的聚合查询最终只返回 50 条数据 但是 这个看上去简单的查询可以轻而易举地消耗大量内存我们可以通过在内存中构建一个树来查看这个 terms 聚合。 actors 聚合会构建树的第一层每个演员都有一个桶。然后内套在第一层的每个节点之下 costar 聚合会构建第二层每个联合出演一个桶请参见 Figure 42, “Build full depth tree” 所示。这意味着每部影片会生成 n2 个桶 深度优先与广度优先Depth-First Versus Breadth-First Elasticsearch 允许我们改变聚合的 集合模式 就是为了应对这种状况。 我们之前展示的策略叫做 深度优先 它是默认设置 先构建完整的树然后修剪无用节点。 深度优先 的方式对于大多数聚合都能正常工作但对于如我们演员和联合演员这样例子的情形就不太适用。 为了应对这些特殊的应用场景我们应该使用另一种集合策略叫做 广度优先 。这种策略的工作方式有些不同它先执行第一层聚合 再 继续下一层聚合之前会先做修剪。 图 Figure 45, “Build first level” 和图 Figure 47, “Prune first level” 对这个过程进行了阐述。 在我们的示例中 actors 聚合会首先执行在这个时候我们的树只有一层但我们已经知道了前 10 位的演员这就没有必要保留其他的演员信息因为它们无论如何都不会出现在前十位中。 因为我们已经知道了前十名演员我们可以安全的修剪其他节点。修剪后下一层是基于 它的 执行模式读入的重复执行这个过程直到聚合完成如图 Figure 48, “Populate full depth for remaining nodes” 所示。 这种场景下广度优先可以大幅度节省内存。 {aggs : {actors : {terms : {field : actors,size : 10,collect_mode : breadth_first },aggs : {costars : {terms : {field : actors,size : 5}}}}} }按聚合来开启 breadth_first 。 广度优先仅仅适用于每个组的聚合数量远远小于当前总组数的情况下因为广度优先会在内存中缓存裁剪后的仅仅需要缓存的每个组的所有数据以便于它的子聚合分组查询可以复用上级聚合的数据。 广度优先的内存使用情况与裁剪后的缓存分组数据量是成线性的。对于很多聚合来说每个桶内的文档数量是相当大的。 想象一种按月分组的直方图总组数肯定是固定的因为每年只有12个月这个时候每个月下的数据量可能非常大。这使广度优先不是一个好的选择这也是为什么深度优先作为默认策略的原因。 针对上面演员的例子如果数据量越大那么默认的使用深度优先的聚合模式生成的总分组数就会非常多但是预估二级的聚合字段分组后的数据量相比总的分组数会小很多所以这种情况下使用广度优先的模式能大大节省内存从而通过优化聚合模式来大大提高了在某些特定场景下聚合查询的成功率。 总结 本小节涵盖了许多基本理论以及很多深入的技术问题。聚合给 Elasticsearch 带来了难以言喻的强大能力和灵活性。桶与度量的嵌套能力基数与百分位数的快速估算能力定位信息中统计异常的能力 所有的这些都在近乎实时的情况下操作的而且全文搜索是并行的它们改变了很多企业的游戏规则。 聚合是这样一种功能特性一旦我们开始使用它我们就能找到很多其他的可用场景。实时报表与分析对于很多组织来说都是核心功能无论是应用于商业智能还是服务器日志。 Elasticsearch 默认给 大多数 字段启用 doc values所以在一些搜索场景大大的节省了内存使用量但是需要注意的是只有不分词的 string 类型的字段才能使用这种特性。 内存的管理形式可以有多种形式这取决于我们特定的应用场景 在规划时组织好数据使聚合运行在 not_analyzed 字符串而不是 analyzed 字符串这样可以有效的利用 doc values 。 在测试时验证分析链不会在之后的聚合计算中创建高基数字段。 在搜索时合理利用近似聚合和数据过滤。 在节点层设置硬内存大小以及动态的断熔限制。 在应用层通过监控集群内存的使用情况和 Full GC 的发生频率来调整是否需要给集群资源添加更多的机器节点 大多数实施会应用到以上一种或几种方法。确切的组合方式与我们特定的系统环境高度相关。 无论采取何种方式对于现有的选择进行评估并同时创建短期和长期计划都十分重要。先决定当前内存的使用情况和需要做的事情如果有通过评估数据增长速度来决定未来半年或者一年的集群的规划使用何种方式来扩展。 最好在建立集群之前就计划好这些内容而不是在我们集群堆内存使用 90% 的时候再临时抱佛脚。
http://www.zqtcl.cn/news/846497/

相关文章:

  • 学校网站源码小游戏网站审核怎么做
  • 西乡网站建设政务网站开发协议
  • 美食网站开发环境北京app网站建设
  • 郑州网站建设推广渠道重庆网站建设公司下载
  • 宜宾营销型网站建设网站建设需要什么资质
  • 重庆建网站有哪些学跨境电商要多少钱
  • 上海建设钢结构工程网站深圳电器公司排名
  • 淄博网站建设找淄深网江苏省建设斤网站
  • 免费行情软件app网站红色西安做网站印象网络
  • 宁波网站建设小程序开发聊城wap网站建设
  • 陇南网站网站建设泰安网站的建设
  • 哪个网站有介绍拿到家做的手工活建设银行网站怎么修改手机号码吗
  • 网站地图怎么用淘宝客推广网站建设
  • 外贸零售网站建设购物网站支付功能怎么做
  • 淘宝客如何做自己的网站西宁工程建设招聘信息网站
  • 天津都有哪些制作网站郑州官网首页
  • 个人网站开发模式海南省建设公司官网
  • edu网站开发做爰视频在线观看免费网站
  • 安防公司网站模板网站建设模板下载
  • 贵阳网站建设方案维护一 建设茶叶网站前的市场分析
  • 山东东营建设网官方网站百度电脑版
  • 做网站前途如何海尔网站建设推广
  • 投资公司网站建设万网域名安装wordpress
  • 高端网站建设企业官网建设wordpress相似推荐
  • php网站开发师招聘wordpress怎么换头像
  • 门禁考勤网站建设广西建设
  • 互助盘网站怎么做的织梦免费企业网站
  • 做羊毛毡的网站电子商务网站建设品牌
  • 用vue做商城网站常用的js教做发型的网站
  • 江西省寻乌县建设局网站广州网站建设一般多少钱