正在缓存。第2部分:发布前60天

你好!我已经给您写过关于如何促进公司倡议的信。更准确地说,这是如何成功的(有时)如何成功,以及可能会出现什么困难:前瞻性回顾。自制解决方案如何比付费解决方案更酷,以及我们如何选择缓存系统。第一部分

今天,我想继续谈论该项目中心理上最紧张的时刻,前两篇文章是关于该时刻的-当时项目的结果并不太取决于团队的技术能力,而取决于他们对计算的信心和最终的意愿。

我不得不说-我认为将项目带到如此激烈的时刻- 对于 lshaya 来说,这是一个错误而不是任何英雄主义,因为它将项目扩展到了这个问题之外。
但是,我不会隐藏这种经验并愿意分享它-因为我认为:

  • 问题领域正是增长点
  • 最大的问题正好从您不期望的地方“到达”

这些要点的组合-仅使您必须分享“如何赚钱”的奇妙经历。但是,应该指出的是,Sportmaster公司也存在类似情况。也就是说,这种情况有可能在完全不同的水平上再次发生-现在负责计划和定义责任。

因此,如果您准备好了,介绍似乎就足够了-欢迎光临。



2017年六月 我们正在修改管理面板。管理面板不仅是Web界面中的一组表单和表格-输入的值还需要与我们从第三方系统获得的许多其他数据相结合。另外,以某种方式进行了改造,并最终将其发送给了消费者(主要是Sportmaster的ElasticSearch网站)。

主要困难只是转换和发送。即:

  1. 您需要以json的形式提供数据,每一个重100Kb,其中一些会弹出10MB(扫描商品到商店的可用性和标准)
  2. 有json,其结构具有任意嵌套级别的递归附件(例如,菜单项内的菜单,其中又有菜单项,等等)。
  3. 最终声明未获批准并且会不断变化(例如,当我们使用颜色模型进行工作时,模型所处理的货物将替换为一种方法)。不断地-这是每周几次,一周中每天有2次高峰。

如果前两点纯粹是技术性的,并且是由任务本身决定的,那么当然要获得第三点,您需要在组织上加以处理。但是,现实世界远非理想状态,因此我们会尽力而为。

即,他们想出了如何在服务器端快速铆钉Web表单及其对象。

团队中的一个人被任命为专业的“表单拍子”,并使用准备好的Web组件,比分析人员更正此UI图纸的速度更快地推出了ui演示。

但是,为了更改转换方案,此处出现了复杂性。

首先,我们采用通常的方式-在sql-query中进行到Oracle的转换。团队中有一名数据库专家。它一直持续到请求为2页连续sql-text的那一刻为止。我可以继续下去,但是当变更来自分析师时-客观上,最困难的事情是找到进行变更的地方。

分析师在方案中表达了规则,尽管这些方案被涂上了与代码分离的某种东西(例如visio / draw.io / gliffy),但是确实如此与ETL系统中的正方形和箭头类似(例如Pentaho Kettle,当时用于向Sportmaster网站提供数据)。现在,如果我们不是SQL查询,而是ETL模式!然后,该语句和解决方案将在拓扑上相同地表示,这意味着编辑代码所花费的时间与编辑该语句所花费的时间一样多!

但是对于ETL系统,还有另一个困难。相同的Pentaho Kettle-当您需要在ElasticSearch中创建新索引时非常有用,在该索引中写入从多个来源粘贴的所有数据(请注意:实际上,Pentaho Kettle效果不佳,因为它在转换中不使用javascript与Java类相关联,消费者可以通过Java类访问数据-因此,您可以编写某些内容,以后再不能将其变成必要的pojo对象,但这是一个单独的主题,与本文的主要内容无关。

但是,当用户在管理面板中更正了一个文档中的一个字段时该怎么办?要将这一更改传递给Sportmaster的ElasticSearch网站,请勿创建新索引来填充所有此类文件,包括更新的索引!

我希望当输入数据中的一个对象发生更改时,将相应输出文档的更新发送网站的ElasticSearch中

好的,输入文档本身,但是根据转换方案,毕竟可以通过join将其附加到其他类型的文档上!因此,您需要分析转换方案并计算哪些输出文档将受到源中数据更改的影响。

寻找盒装产品以解决此问题并没有任何结果。未找到。
当他们对发现不满意时,他们便找到了答案,但是它应该如何在内部工作,又如何做到这一点呢?

这个想法马上就出现了。

如果最终的ETL可以分解为组成部分,每个组成部分都来自有限集(例如filter,join等)具有某种类型,则可以创建与原始节点对应的相同有限集的特殊节点,但是它们的不同之处在于它们不是与数据本身一起工作,而是与数据更改一起工作?

详细介绍实施中的示例和要点,我们的解决方案-我想在另一篇文章中介绍。要处理辅助职位-这将需要严肃的沉浸感,抽象思考和依靠尚未表现出的能力。确实,从数学的角度来看,这将是非常有趣的,并且仅对那些对技术细节感兴趣的哈勃罗夫特人感兴趣
在这里,我只能说我们创建了一个数学模型,其中描述了7种类型的节点并表明该系统是完整的-也就是说,使用这7种类型的节点及其之间的连接-可以表示任何数据转换方案。该实现基于通过密钥(即通过密钥,没有附加条件)主动获取和记录数据的使用。

因此,我们的解决方案在所有入门方面都具有强项:

  1. 数据必须以json的形式提供->我们使用pojo对象(普通的java对象,如果有人找不到使用这种指定的时间),在json中很容易被覆盖
  2. 还有一个json,其结构具有任何嵌套级别的递归嵌入->再次,pojo(主要是没有循环,但是嵌套的多少级别并不重要,因为我们可以通过递归轻松地在Java中处理它)
  3. 最后的陈述在不断变化->非常好,因为我们改变转换方案的速度快于分析师(在图表中)拟定的实验愿望

在危险的时刻中,只有一个-我们自己编写从头开始的解决方案。

实际上,陷阱不久就来了。

特殊时刻N1。陷阱。“推算得很好”


组织性质的另一个惊喜是,在我们开发的同时,主主存储库正在迁移到新版本,并且此存储库提供数据的格式已更改。如果我们的系统立即与新存储一起使用,而不是与旧存储一起使用,那就太好了。但是新存储尚未准备就绪。但是,数据结构是已知的,它们可以为我们提供一个演示台,在该演示台上可以注入少量相关数据。要去吗

在产品方法中,当使用价值供应流时,所有乐观主义者都会毫无疑问地发出警告:存在一个障碍->任务无法执行。

但是,这种依赖甚至没有引起人们的怀疑。的确,我们对原型Delta处理器的成功感到欣喜若狂-三角洲上的数据处理系统(当使用转换方案对输入数据的变化进行响应而计算出输出数据的变化时,实现数学模型)。

在所有转换方案中,最重要的是一个。除了电路本身是最大,最复杂的事实之外,还严格要求根据该电路执行转换-执行全部数据的时间限制。

因此,转换应进行15分钟,而不是一秒钟。主要输入是一个包含550万条记录的表。在开发阶段,该表尚未填充。更准确地说,它填充了一个小的测试数据集,该数据集的数量为1万行。

好吧,让我们开始吧。在第一个实现中,Delta处理器在HashMap上用作键值存储(让我提醒您,我们需要大量地通过键读写对象)。当然,在生产量上,所有中间对象都无法容纳在内存中-因此,我们切换到Hazelcast,而不是HashMap。

为什么要精确使用Hazelcast-之所以如此,是因为熟悉该产品,因此被用于Sportmaster网站的后端。另外,这是一个分布式系统,而且在我们看来-如果一个朋友在性能方面做错了-我们将更多实例添加到两台机器上,问题就解决了。在极端情况下-一打车。水平缩放和所有事物。

因此,我们正在启动Delta处理器以实现有针对性的转型。它几乎立即起作用。这是可以理解的-数据仅为10,000,而不是550万,因此,我们将测得的时间乘以550,得到的结果是:大约2分钟。精细!实际上-胜利!

这只是项目工作的开始-当您需要确定体系结构,确认假设(进行验证以确认假设),垂直集成试验解决方案时。

由于测试显示出了极好的结果-也就是说,我们确认了所有假设,因此我们迅速扭转了局面-将垂直整合的“骨架”组合为一小部分功能。他们开始了主要的编码-用肉填充“骨骼”。

成功而有力的参与。直到那美丽的一天,当一个完整的一组数据上传到主存储区

在此集合上运行测试。

2分钟后没有工作。 5、10、15分钟后我也没有工作。也就是说,它们不适合必要的框架。但是,对于谁不会发生的事情,有必要对某些细节进行调整并使其适合。

但是测试一个小时后没有进行。即使在2个小时后,他仍然希望他能工作,我们将寻找可以收紧的方法。甚至在5个小时后仍然充满希望。但是,经过10个小时,当他们回家时,测试仍然无法进行-不再有希望了。

麻烦的是,第二天,当他们来到办公室时,测试仍然努力地继续进行。结果,它滚动了30个小时,没有等待,就关闭了。
灾难!

问题已足够迅速地本地化。

Hazelcast-处理少量数据时-实际上滚动了内存中的所有内容。但是,当需要将数据转储到磁盘上时,性能下降了数千倍。

如果不是为了当局和交付成品的义务,编程将是无聊和无味的职业。因此,从字面上讲,一天之后,我们收到了一套完整的数据后,我们需要向当局提交一份关于如何通过量产测试的报告。

这是一个非常严重且困难的选择:

  1. 说“原样” =放弃项目
  2. 说“我愿意” =冒险,也许不知道我们是否可以解决问题

要了解这种情况下会产生什么感觉,只有全力投入想法,实现半年的计划,创造出可以帮助同事解决大量问题的产品。

因此,放弃您钟爱的创作非常困难。
这是所有人的特征-我们热爱我们付出的努力。因此,很难听到批评-您必须自觉地做出努力以充分理解反馈。

总的来说,我们认为仍然可以使用很多不同的系统作为键值存储,如果Hazelcast不适合使用,那么肯定可以使用。也就是说,他们决定冒险。就我们的论据而言,我们可以说这不是一个“紧急截止日期”-总体而言,仍有时间留给“移动”到备份解决方案。

在与老板会面时,我们的经理表示:“测试表明该系统在批量生产时稳定运行,不会崩溃”。实际上,该系统运行稳定。60天后

释放

特殊时刻N2。不是陷阱,而是发现。“少即是多”


为了找到具有Key-Value数据仓库角色的Hazelcast替代产品,我们汇总了所有候选产品的列表-我们获得了31种产品的列表。这就是我设法在Google上搜索并从朋友那里找到的所有内容。此外,Google给出了一些绝对淫秽的选择,例如学生的学期论文。

为了更快地测试候选人,我们准备了一个小型测试,该测试在发布后的几分钟内显示了正确容量上的性能。他们并行进行工作-每个人都从列表中选出下一个系统,进行配置,运行测试,然后再选一个。
他们工作很快,每天抢购了几个系统。

在第18个系统上,很明显这是没有意义的。在我们的负载曲线下-这些系统都没有得到加强。他们有很多褶皱和帷幕,以方便使用,还有许多漂亮的水平缩放方法-但这并没有给我们带来任何收益。

我们需要_fast_将密钥保存到磁盘上的对象并快速读取密钥的系统。

如果是这样,我们将概述如何实现此算法。通常,这似乎是完全可行的-如果同时进行:a)牺牲将占用磁盘的数据量,b)对每个表中的数据量和特征大小进行近似估计。
以某种方式,为具有一定裕量(最大固定数量)的对象分配(磁盘上的)内存。然后使用索引表...依此类推...
幸运的是它没有来到这里。

救助以RocksDB的形式出现。
这是Facebook的产品,旨在快速读取并将字节数组保存到磁盘。同时,通过类似于键值存储的界面提供对文件的访问。实际上,键是字节数组,值是字节数组。经过优化,可以快速,可靠地完成此工作。所有。如果您需要更精美,更高级的产品,请自己拧上螺丝。
正是我们需要的!

RocksDB担负着键值存储的角色-使目标测试指标达到5小时的水平。距离15分钟还很远​​,但是主要的事情已经完成了。最主要的是要了解正在发生的事情,要了解对磁盘的写入要尽可能快,而不是不可能。在SSD上,在经过改进的测试中,RocksDB压缩了400Mb / s,这足以完成我们的任务。延迟-在我们的某个地方,在绑定代码中。

我们的代码中,这意味着我们可以处理它。让我们拆开它,但我们可以处理。

特殊时刻N3。支持。“理论计算”


我们有一个算法和输入。我们采用输入数据的范围,计算系统应执行的操作数量,这些操作在JVM的运行时成本中的表达方式(将值分配给变量,输入方法,创建对象,复制字节数组等),以及对JVM的调用次数RocksDB应该被保留。

根据计算,结果表明它们应该相隔2分钟(大约一开始就对HashMap进行了测试,但这只是一个巧合-从那时起算法发生了变化)。

但是,测试运行了5个小时。

而现在,在发布30天之前。

这是一个特殊的日期-现在将无法崩溃-我们将没有时间切换到备份选项。
当然,在这一天,项目经理被传唤给当局。问题是一样的-有时间,一切都还好吗?



这是描述这种情况的最佳方法-本文的扩展封面。也就是说,显示了老板在标题中呈现的那部分图片。但实际上-这样。

虽然,实际上,当然-我们一点也不搞笑。并说“一切都很棒!” -只有对自我掌握能力很强的人才有可能。
非常尊敬经理,相信并信任开发人员。

确实可用的代码-显示5小时。理论计算-显示2分钟。如何相信这一点?

但是有可能出现以下情况:模型清晰地表述,如何计数是可以理解的以及替代哪些价值也是可以理解的。也就是说,实际上执行需要花费更多时间这一事实意味着,实际上执行的并不是我们希望在那里执行的代码。

中心任务是在代码中找到“镇流器”。即,除了创建最终数据的主流之外,还执行一些动作。

赶走。单元测试,功能组成,功能分散和位置局部化,花费的执行时间不成比例。已经做了很多事情。
在此过程中,我们制定了可以认真收紧的地方。

例如,序列化。首先使用标准的java.io。但是,如果我们固定Cryo,则在我们的情况下,串行化速度提高了2.5倍,串行化数据量减少了3倍(这意味着IO减小了3倍,这只占用了主要资源)。但是,更详细地说,这是另一篇技术文章的主题。

但是关键点或“大象藏在哪里”-我将尝试在一个段落中进行描述。

特殊点4.寻求解决方案的接待处。“问题=解决方案”


当我们确实通过键获取/设置时-在计算中它以1个运算的形式进行,从而影响IO的数量等于键+对象值(当然是序列化的形式)。
但是,如果我们在其上调用get / set的对象本身是一个Map,我们也从磁盘通过get / set获取它。在这种情况下,将完成多少IO?

在我们的计算中,未考虑此功能。也就是说,它被认为是键+对象值的1个IO。然而事实上?

例如,在键值存储中,通过键1,存在一个具有Map类型的obj-1对象,其中某个obj-2对象必须存储在键2键下。在这里,我们认为该操作将需要密钥2 + obj-2的IO。但实际上,您需要考虑obj-1,对其进行处理并将其发送给IO:key-1 + obj-1。并且如果它是一个包含1000个对象的Map,那么IO消耗将是大约1000倍。如果有10,000个对象,那么...这就是他们获得“镇流器”的方式。

发现问题后,解决方案通常很明显。

在我们的案例中,这已成为嵌套Map内部操作的特殊结构。也就是说,这样的键值(用于获取/设置)一次需要两个键,应依次应用:键1,键2-即用于第一级和嵌套的一级。如何实现这样的结构-我将在另一篇技术文章中愉快地详细介绍您。
在这里,从这一集开始,我强调并推广这样的功能:非常详细的问题是一个很好的解决方案。

完成时间


在本文中,我试图说明可能出现的组织重点和陷阱。这样的陷阱“从侧面”或随着时间的推移非常清晰可见,但是当您第一次发现自己在陷阱旁边时,很容易将其插入。我希望有人会记住这样的描述,并且在适当的时候该提醒将起作用:“我之前在某处听到过类似的声音。”

而且,最重要的是-现在,一切都被告知过程,心理时刻,组织时刻。现在我们有了什么任务以及在什么条件下创建系统的想法。现在-您可以并且应该从技术角度介绍该系统-它是一种什么样的数学模型,我们去过哪些代码技巧,以及我们想到了什么创新解决方案。

在下一篇文章中有关此内容。

同时,祝您新代码愉快!

All Articles