Sportmaster我们如何选择缓存系统。第1部分

你好!我叫Alexey Pyankov,我是Sportmaster的开发人员。在这篇文章中,我谈到了Sportmaster网站的工作是从2012年开始的,我们设法推动了哪些举措,反之亦然,我们收集了哪些佣金。

今天,我想分享一下另一个故事的想法-在网站的管理面板中为Java后端选择一个缓存系统。这个故事对我来说尤其重要-尽管故事只发生了2个月,但是这60天我们工作了12-16个小时,没有一天休息。我从没想过和想象过您可以做很多工作。

因此,我将文本分为两部分,以免全文下载。相反,第一部分将非常容易-准备,介绍以及有关什么是缓存的一些注意事项。如果您已经是一位经验丰富的开发人员,或者已经使用过缓存-从技术方面来说,本文很可能没有新内容。但是对于大三学生来说,如果他发现自己处在这样的十字路口,那么进行一次小型的评论就可以告诉他应该看哪种方式。



当新版本的Sportmaster网站投入生产时,数据以某种方式出现,说得通俗一点,不是很方便。基础是为该站点的先前版本(Bitrix)准备的表,该表必须在ETL中进行紧缩,使其具有新的外观,并从十几个系统中充实了许多不同的内容。为了使新图片或产品描述出现在网站上,您必须等到第二天-每天晚上更新一次,每天1次。

最初,从投入生产的最初几周开始就有如此多的烦恼,以至于内容管理者的不便之处微乎其微。但是,一旦一切解决,项目的开发就会继续进行-几个月后,即2015年初,我们开始积极开发管理面板。在2015年和2016年,一切进展顺利,我们将定期发布它,管理区域涵盖了数据准备的大部分,我们正在为以下事实做好准备:即将最重要和最困难的事情委托给我们的团队-产品线(完整准备和维护所有产品的数据)。但是在2017年夏季,就在产品线发布之前,该项目将处于非常困难的境地-正是由于存在缓存问题。我想在这个分为两部分的出版物的第二部分中谈论这一集。

但是在这篇文章中,我会从远处开始,就像一些想法一样-关于缓存的想法,提前在大项目之前滚动是一个好步骤。

当出现缓存任务时


缓存任务不仅会出现。我们是开发人员,我们编写软件产品,我们希望它能有需求。如果产品需求旺盛且成功,那么用户就来了。更多,更多。因此,有很多用户,然后产品变得高负载。

在开始阶段,我们不考虑优化和代码性能。最主要的是功能,可以快速推出试验和测试假设。如果负荷增加,我们就抽铁。我们将其增加两倍,三倍,五倍,使其为10倍。在这里的某个地方-财务将不再允许。用户数量会增长多少倍?它不仅是2-5-10,而且如果成功,它将是100-1000到10万次。也就是说,迟早要进行优化。

假设代码的某些部分(让我们将此部分称为函数)长时间工作不佳,并且我们希望减少执行时间。功能-它可以访问数据库,可以执行一些复杂的逻辑-主要是它需要很长时间。您可以减少多少交货时间?在限制-可以减少到零,再也没有。以及如何将运行时间减少到零?答:一般排除执行。而是,立即返回结果。你怎么知道结果呢?答案:要么计算,要么看某处。计算是很长的时间。窥视就是例如记住函数在上次使用相同参数调用时产生的结果。

也就是说,功能的实​​现对我们来说无关紧要。知道结果依赖什么参数就足够了。然后,如果参数值以可以在某些存储中用作键的对象的形式呈现,那么我们可以保存计算结果并在下一次读取它。如果这些读写结果比执行该功能快,那么我们将获得速度上的收益。利润值可以达到100、1000和10万倍(10 ^ 5更有可能是一个例外,但在基数相当滞后的情况下则很有可能)。

关键缓存要求


可能成为高速缓存系统要求的第一件事是快速的读取速度,而写入速度则稍小一些。确实如此,但是直到我们在生产中推出该系统。

让我们玩这种情况。

假设我们为当前负载提供了铁,现在我们正在逐步引入缓存。用户在增长,负载在增长-我们添加一些缓存,然后在这里和那里固定它。这已经进行了一段时间,现在几乎不调用繁重的功能-所有主要负担都落在了缓存上。在此期间,用户数量增长了N倍。

如果最初的铁供应量是2-5倍,那么在高速缓存的帮助下,我们可以每10倍提高生产率,或者在某些情况下,甚至在某些情况下,甚至是1000倍,可以提高生产率。也就是说,我们使用相同的铁加工请求增加100倍。太好了,配上姜饼!

但是现在,偶然地,系统崩溃了,缓存也崩溃了。没什么特别的-毕竟,按需选择了高速缓存“高速读写,其余的都没有关系”。

关于起始负荷,铁储备是2-5倍,并且在此期间的负荷增长了10-100倍。在缓存的帮助下,我们消除了对繁重功能的调用,因此一切顺利。而现在,没有缓存-我们的系统跌落了多少次?我们会发生什么?系统将崩溃。

即使我们的缓存没有崩溃,只是清除了一段时间,它也需要预热,这将需要一些时间。而此时-主要的负担将落在功能上。

结论:产品中的高负荷项目不仅需要高速缓存系统的读写,还需要数据安全性和抗故障能力。

选择的面粉


在带有管理面板的项目中,选择是这样的:首先,他们放了Hazelcast,因为 通过主站点的经验已经熟悉此产品。但是,这里的选择并不成功-对于我们的负载曲线,Hazelcast不仅运行缓慢,而且运行缓慢。到那时,我们已经签署了退出产品的条款。

剧透:在这种情况下,我们错过了这样的困境并出现了紧张而紧张的情况是怎么发生的-我将在第二部分中讲述-以及结果如何以及如何出去。但是,现在-我只想说这是很大的压力,并且“想一想-我以某种方式不认为,摇晃瓶子。” “摇晃瓶子”也是一个破坏者,关于这一点再进一步。

我们做了什么:

  1. 我们列出了由google和StackOverflow提示的所有系统。30多一点
  2. , . , - — , .
  3. , , , . , – , .
  4. 17- , . « », .

但这是您需要选择在预先准备的测试中“抓紧速度”的系统时的一种选择。如果尚无此类测试,并且希望选择更快?

我们将模拟这样的选择(很难想象中层+开发人员生活在真空中,并且在选择时他尚未决定首先尝试哪种产品-因此,进一步的讨论更有可能是理论家/哲学/关于初中的)。

确定要求后,我们将开始从框中选择解决方案。为什么要重新发明轮子:我们将采用现成的缓存系统。

如果您只是刚开始,并且会搜索Google,那么请在订单的前后加或减,但一般而言,指导原则是这样的。首先,您偶然发现Redis,无处不在。然后您会发现EhCache是​​最古老,最成熟的系统。然后将撰写有关Tarantool的文章-Tarantool是国内开发的解决方案,其独特之处在于解决方案。还有Ignite,因为它现在越来越流行并且得到了SberTech的支持。最后是Hazelcast,因为在企业界,它经常在大公司中闪烁。

该列表不止于此;有数十个系统。而且我们只拧一个。将所选的5个系统用于“选美比赛”并进行选择。谁将是赢家?

雷迪斯


我们阅读了他们在官方网站上写的内容。
Redis是一个开源项目。它提供了内存中的数据存储,磁盘上保存,自动分区到分区,高可用性以及从网络中断中恢复的能力。

看来一切都很好,您可以将其拧紧-他所需要的只是他所做的。但是,让我们只是为了对其他候选人感兴趣。

高速缓存


EhCache- “最广泛使用的Java缓存”(官方网站标语的翻译)。也开源。在这里,我们了解Redis不是在Java下,而是在Java之下,要与之交互,您需要一个包装器。而且EhCache会更方便。系统还承诺什么?可靠,可靠,功能齐全。好吧,她是最普通的。并缓存数TB的数据。

Redis被遗忘了,我准备选择EhCache。

但是爱国主义的精神促使我去了解什么使塔兰图尔(Tarantool)变得更好。

Tarantool


Tarantool-符合“实时数据集成平台”的称号。听起来非常困难,因此我们详细阅读了该页面,并找到了一个响亮的声明:“将100%的数据缓存在RAM中。”这应该引起问题-毕竟,数据可能比内存多得多。解密是这里暗示Tarantool不会运行序列化来将数据从内存写入磁盘。相反,当内存只是映射到具有非常好的I / O性能的文件系统时,它将使用系统的低级功能。总的来说,他们做的很棒而又酷。

让我们看一下实现:Mail.ru公司高速公路,Avito,Beeline,Megafon,Alfa-Bank,Gazprom ...

如果对Tarantool仍然有任何疑问,那么万事达卡实现的引入将使我丧命。我拿Tarantool。

但不管怎么说…

点燃


...有一个Ignite,它被宣布为“内存计算平台... PB级数据的内存速度”。这里有很多优点:分布式内存缓存,最快的键值存储和缓存,水平扩展,高可用性,严格的完整性。通常,事实证明最快的是Ignite。

实施:Sberbank,美国航空,Yahoo!日本 然后,我仍然发现Ignite不仅在Sberbank中实现,而且SberTech团队将其人员派到Ignite本身的团队中,以最终确定产品。这完全令人着迷,我已经准备好点燃Ignite。

我看第五点,这是完全无法理解的。

淡褐色


我转到Hazelcast网站,阅读它。事实证明,最快的分布式缓存解决方案是Hazelcast。他比所有其他解决方案都快几个数量级,并且总体而言,他是内存数据网格领域的领导者。在这种背景下,请采取其他措施-不要尊重自己。它还将冗余数据存储用于群集的连续操作而不会丢失数据。

一切,我准备参加Hazelcast。

比较方式


但是,如果您看一下,那么所有五个候选人都被漆成如此,以至于每个人都是最好的。如何选择?我们可以看到哪一种最受欢迎​​,可以进行比较,这样头痛就会过去。

我们找到这样的评论,选择我们的5个系统。



它们在这里进行了排序:在Redis的顶部,第二位是Hazelcast,Tarantool和Ignite越来越受欢迎,EhCache一直存在并且仍然存在。

但是,让我们看一下计算方法:网站链接,对系统的一般兴趣,工作机会-太好了!也就是说,当我的系统崩溃时,我会说:“不,它是可靠的!这里有很多工作机会……”。这样简单的比较是行不通的。

所有这些系统都不只是缓存系统。它们仍然具有很多功能,包括何时不将数据传输到客户端进行处理,而是:需要在数据上执行的代码移至服务器,在服务器上执行,然后返回结果。作为一个单独的缓存系统,它们很少被考虑。

好吧,不要放弃,我们找到了系统的直接比较。采取前两个选项-Redis和Hazelcast。我们对速度感兴趣,我们可以通过此参数对其进行比较。

Hz vs Redis


我们发现这样的比较


蓝色是Redis,红色是Hazelcast。Hazelcast无处不在,并且给出了基本原理:它是多线程的,高度优化的,每个线程都具有自己的分区,因此没有锁。Redis是单线程的,它不会从现代多核CPU中受益。Hazelcast具有异步I / O,Redis-Jedis具有阻塞套接字。最后,Hazelcast使用二进制协议,而Redis是面向文本的,这意味着效率低下。

以防万一,我们转向另一个比较来源。他会给我们看什么?

Redis与Hz


另一个比较


相反,红色是Redis。也就是说,Redis在性能上胜过Hazelcast。在第一个比较中,Hazelcast赢得了冠军,第二个冠军是Redis。在这里,他们非常精确地解释了为什么Hazelcast在之前的比较中获胜。

事实证明,第一个结果实际上是人为操纵的:Redis被放在底盒中,Hazelcast被关押在一个测试用例中。事实证明:首先,没有人能被信任;其次,当我们仍然选择一个系统时,我们仍然需要正确地配置它。这些设置包括数十个,几乎数百个参数。

摇一瓶


在我们刚刚完成的整个过程中,我可以用这样的比喻来解释:“摇晃瓶子”。也就是说,现在您不能编程,现在最主要的是能够读取stackoverflow。在我的团队中,有一个人,一个专业人员,在关键时刻就是这样工作。

他在做什么?他看到了一个坏东西,看到了堆栈跟踪,用了一些单词(这是他在程序中的专长),在Google中搜索,在答案中发现了stackoverflow。他不加思索地思考问题,在答案中选择了最类似于“做这个和那个”的句子(选择这样的答案是他的才华,因为并非总是收集到更多喜欢的答案)。 ,看起来:如果有什么变化,那就好了。如果它没有改变,我们将回滚。然后我们重复开始检查搜索。并且以这种直观的方式,他在一段时间后实现了代码的工作。他不知道为什么,他不知道自己做了什么,他无法解释。但!这种感染有效。并“扑灭大火”。现在我们了解了我们所做的。当程序运行时,它会容易得多。并大大节省时间。

这样的例子很好地解释了这种方法。

将帆船收藏在瓶子里曾经很受欢迎。同时,帆船又大又脆弱,瓶子的颈部很窄;您不能将其推入内部。如何组装呢?



有这样一种方法,非常快速而且非常有效。

船上有一堆小东西:棍棒,绳索,帆,胶水。我们把所有这些装在瓶子里。
我们双手握住瓶子,然后开始摇晃。我们在发抖。通常,您当然会得到完全的垃圾。但有时候。有时候你会得到一艘船!更确切地说,类似于船只。

我们正在向某人显示此信息:“ Serge,see !?”。确实,从远处看-就像一艘船。但是,那么你不能放手。

还有另一种方法。这些家伙正在使用更高级的工具,例如黑客。

给这样一个人一个任务,他做了一切,离开了。您看-好像完成了。过了一会儿,当有必要优化代码时,代码就从此开始了……好了,他已经设法跑得很远了。这些人以瓶子为例,将执行此操作:您会看到底部在哪里-玻璃弯曲。目前尚不清楚它是否透明。然后,“黑客”切断该船底,将飞船插入该船底,然后再次粘贴船底,好像有必要。

从解决问题的角度来看,一切似乎都是正确的。但是这里有一个船的例子:为什么一般来说这艘船,谁会需要它?它没有任何功能。通常,这类船只是送给非常高级的人的礼物,这些人将其放置在自己上方的架子上,以某种符号的形式表示。而现在,如果是这样的人,大型企业的负责人或高级官员,那面旗帜如何竖立在这样的垃圾桶上呢?如果他不知道那会更好。那么,这些船最终将如何制造并呈现给重要人物呢?

唯一不需要做任何事情的关键就是建筑物。船的船体刚好穿过脖子。而船正在瓶子外面。但这不仅是组装船只,而且是真正的珠宝工艺。特殊的杠杆被添加到组件中,以便稍后将其提起。例如,将帆折叠起来,轻轻地向里漂,然后在镊子的帮助下,将其制成非常精美的珠宝,可以肯定的是,将它们拉起并提起。结果是一件艺术品,可以清晰地展示出自己的良知和自豪感。

而且,如果我们希望该项目成功,那么团队中至少必须有一位珠宝商。任何关心产品质量并考虑到所有方面的人,即使在紧急情况下也要牺牲重要的一面,即使在压力大的时候也不牺牲任何一个。所有经过时间考验的,可持续的,成功的项目都是基于这一原则。它们具有非常精确和独特的功能,可以使用所有可用功能。在以瓶子装船的示例中,可以看出,船体穿过颈部。

返回选择缓存服务器的任务,如何应用此方法?我从所有存在的系统中提出这样的选择-不要摇晃瓶子,不要选择,而是要看原则上在选择系统时需要寻找什么。

在哪里寻找瓶颈


让我们尽量不要动摇瓶子,不要对所有依次进行的事情进行分类,而是让我们看看如果任务突然出现了什么任务,请您自己设计这样的系统。当然,我们不会组装自行车,但是我们将使用此方案来确定产品说明中要注意的地方。我们概述了这样一个方案。



如果系统是分布式的,那么我们将有几台服务器(6)。假设有四个(放置在图片中很方便,但是当然可以有很多个)。如果服务器位于不同的节点上,则意味着某些代码正在所有节点上旋转,这负责确保这些节点形成群集,并在发生中断的情况下相互连接,以相互识别。

仍然需要一个代码逻辑(2),它实际上是关于缓存的。客户端通过一些API与该代码进行交互。客户端代码(1)可以都在同一个JVM中,并且可以通过网络访问它。内部实现的逻辑是决定将哪些对象保留在缓存中,将哪些对象抛出。我们使用内存(3)来存储缓存,但是如果需要,我们也可以将部分数据保存在磁盘(4)上。

让我们看看负载将在哪个部分发生。实际上,每个箭头和每个节点都会被加载。首先,在客户端代码和api之间,如果是网络交互,则沉降会非常明显。其次,在api本身的框架内-已被复杂的逻辑覆盖,我们可以运行到CPU中。如果逻辑不再驱动存储器,那就太好了。并且与文件系统之间仍然存在交互-在通常的版本中,它被序列化/还原和写入/读取。

与集群的进一步交互。它很可能会在同一系统中,但是可以单独使用。在这里,您还需要考虑向其传输数据,数据序列化的速度以及集群之间的交互。

现在,一方面,我们可以想象当处理来自我们代码的请求时,高速缓存系统中的“齿轮将旋转”,另一方面,我们可以估计我们的代码将向该系统生成什么和多少请求。这足以或多或少地做出清醒的选择-为我们的用例选择一个系统。

Hazelcast

让我们看看这种分解如何应用于我们的列表。例如,Hazelcast。

为了从Hazelcast放置/获取数据,客户端代码访问(1)api。 Hz允许您以嵌入式方式启动服务器,在这种情况下,访问api是JVM内部的方法调用,您可以免费阅读它。

为了计算(2)中的逻辑,Hz依赖于序列化密钥的字节数组中的哈希值-也就是说,无论如何都将发生密钥序列化。这是Hz不可避免的开销。
驱逐策略实施得很好,但是对于特殊情况-您可以自己连接。您不必担心这部分。

可以连接存储器(4)。精细。嵌入的交互作用(5)可以被认为是瞬时的。集群中节点之间的数据交换(6)-是的。这有助于以速度为代价的弹性。 Near-cache的Hz功能可降低价格-从群集的其他节点接收的数据将被缓存。

在这种情况下可以采取什么措施来提高速度?

例如,为避免序列化(2)中的密钥,请在Hazelcast顶部固定另一个用于最热数据的缓存。为此,在Sportmaster中选择了咖啡因。

对于级别6的扭曲,Hz提供两种存储类型:IMap和ReplicatedMap。


值得一提的是,Hazelcast是如何进入Sportmaster技术堆栈的。

在2012年,当我们在未来站点的第一个试点项目中工作时,原来是Hazelcast成为搜索引擎发布的第一个链接。熟人是“第一次”开始的-仅仅两个小时后,当我们将Hz拧入系统时,它给我们留下了深刻的印象。而且效果很好。直到一天结束,我们添加了一些测试,我们还是很高兴。这种活力足以克服Hz随时间推移而带来的意外。现在,Sportmaster团队没有理由拒绝Hazelcast。

但是,诸如“搜索引擎中的第一个链接”和“快速组装的HelloWorld”之类的参数当然是一个例外,并且是做出选择的那一刻的特征。这些针对所选系统的测试从prod中的发行版开始,在这个阶段,选择任何系统(包括缓存)时都应注意。实际上,就我们而言,可以说我们偶然选择了Hazelcast,但事实证明我们选择了正确的。

对于生产而言,它更为重要:监视,单个节点上的处理故障,数据复制,扩展成本。也就是说,值得一提的是,仅在系统受支持时才会出现的任务-当负载比计划高出数十倍时,当我们不小心沿错误的方向填充某些内容时,当您需要推出新版本的代码,替换数据并使其不被注意时为客户。

对于所有这些要求,Hazelcast绝对适合。

未完待续


但是榛子并不是万能药。在2017年,我们仅依靠过去经验的好印象就选择了Hazelcast作为管理面板中的缓存。这在一个非常邪恶的笑话中起了关键作用,因此我们发现自己处在困难的境地中,并且“狂喜地”摆脱了60天。但是在下一部分中会对此进行更多介绍。

同时...祝新代码!

All Articles