从Redshift切换到ClickHouse



长期以来,iFunny将Redshift用作后端服务和移动应用程序中发生的事件的数据库。 选择它是因为在实施时,在成本和便利性上几乎没有其他可比的替代方案。

但是,在公开发布ClickHouse之后,一切都发生了变化。 我们研究了很长时间,比较了成本,估算了大概的体系结构,最后,今年夏天,我们决定看看它对我们有多大用处。 在本文中,您将了解Redshift帮助我们解决的问题,以及如何将该解决方案移至ClickHouse。

问题


iFunny需要类似于Yandex.Metrica的服务,但只能用于家庭消费。 我将解释原因。

外部客户端编写事件。 它可以是移动应用程序,网站或内部后端服务。 这些客户很难解释当前不提供事件接收服务,“尝试在15分钟或一个小时内发送”。 客户很多,他们想一直发送事件,根本无法等待。

与它们相反,有些内部服务和用户在这方面可以容忍:即使使用不可访问的分析服务,它们也可以正常工作。 而且大多数产品指标和A / B测试结果通常每天只观看一次,甚至更不经常观看。 因此,阅读要求非常低。 如果发生事故或更新,我们可以承受数小时甚至数天(在一个特别被忽略的情况下)无法阅读或不一致的情况。

如果我们谈论数字,那么我们每天需要处理约50亿个事件(300 GB压缩数据),同时以“热”形式存储数据三个月以用于SQL查询,而以“冷”形式存储则为两年。或更多,但几天之内我们就可以将它们变成“热门”。

基本上,数据是按时间排序的事件的集合。 大约有三百种事件,每种都有自己的属性集。 仍然有一些第三方数据应与分析数据库同步:例如,来自MongoDB或外部AppsFlyer服务的应用程序安装集合。

事实证明,我们需要大约40 TB的磁盘用于数据库,而大约250 TB的用于“冷”存储。

红移解决方案




因此,您需要从中接收事件的移动客户端和后端服务。 HTTP服务接受数据,进行最低限度的验证,将本地磁盘上的事件收集到按一分钟分组的文件中,立即对其进行压缩并将其发送到S3存储桶。 该服务的可用性取决于带有应用程序和AWS S3的服务器的可用性。 应用程序不存储状态,因此很容易进行平衡,扩展和互换。 S3是一种相对简单的文件存储服务,具有良好的信誉和可用性,因此您可以依靠它。

接下来,您需要以某种方式将数据交付给Redshift。 这里的一切都非常简单:Redshift具有内置的S3导入程序,这是推荐的加载数据的方式。 因此,每10分钟启动一次脚本,该脚本连接到Redshift,并要求它使用s3://events-bucket/main/year=2018/month=10/day=14/10_3*前缀下载数据s3://events-bucket/main/year=2018/month=10/day=14/10_3*

为了监视下载任务的状态,我们使用Apache Airflow :它允许您在出现错误的情况下重复操作并具有清晰的执行历史记录,这对于大量此类任务很重要。 如果出现问题,您可以间隔一定时间重复下载一次,或者一年前从S3下载“冷”数据。

在相同的Airflow中,以相同的方式,根据时间表,脚本工作以连接到数据库并从外部存储库执行定期下载,或以INSERT INTO ... SELECT ...的形式构建事件的聚合

Redshift的可用性保证很弱。 每周一次,最多半小时(在设置中指定了时间窗口),AWS可以停止集群更新或任何其他计划的工作。 如果一个节点发生故障,则群集也将不可用,直到还原主机为止。 通常大约需要15分钟,大约每六个月发生一次。 在当前系统中,这不是问题,它最初是为定期不可用而设计的。

在Redshift下,使用了4个ds2.8xlarge实例(36 CPU,16 TB HDD),总共为我们提供了64 TB的磁盘空间。

最后一点是备份。 可以在群集设置中指定备份计划,并且工作正常。

ClickHouse过渡动机


当然,如果没有问题,没有人会想到迁移到ClickHouse。 但是,他们是。

如果您使用MergeTree和Redshift引擎查看ClickHouse存储方案,您会发现它们的思想非常相似。 两个数据库都是柱状的,可以很好地处理大量的列,并且可以很好地压缩磁盘上的数据(在Redshift中,您可以为每个单独的列配置压缩类型)。 甚至数据也以相同的方式存储:它们通过主键排序,这使您只能读取特定的块,而不能在内存中保留单独的索引,这在处理大量数据时很重要。

一如既往,本质上的区别在于细节。

每日餐桌


在执行以下操作时,将对磁盘上的数据进行排序并实际上在Redshift中将其删除:
 VACUUM <tablename> 
在这种情况下,真空处理将处理该表中的所有数据。 如果将所有三个月的数据存储在一个表中,则此过程将花费不长的时间,并且您至少需要每天执行一次,因为会删除旧数据,并添加新数据。 我每天必须构建单独的表,然后通过视图将它们组合在一起,这不仅是旋转和支持该视图的困难,而且还会减慢查询速度。 根据要求,通过解释判断,扫描了所有表格。 而且,尽管扫描一张表所需的时间少于一秒,但扫描90张表的结果却表明,任何查询至少需要一分钟。 这不是很方便。

重复项


下一个问题是重复项。 通过网络传输数据时,有一种方法,有两种选择:丢失数据或接收重复数据。 我们不会丢失消息,因此,我们只调和了以下事实:一小部分事件将被重复。 您可以每天创建一个新表,将旧表中的数据插入其中,然后使用window函数删除具有重复ID的行,删除旧表并重命名新表,从而每天删除重复表。 由于在每日表格的上方都有一个视图,因此在重命名表格时,有必要不要忘记并将其删除。 在这种情况下,还必须监视锁,否则,在查询阻塞视图或表之一的情况下,此过程可能会拖很长时间。

监控与维护


在Redshift中,没有一个请求会花费不到几秒钟的时间。 即使您只想添加用户或查看活动请求列表,也将不得不等待几十秒钟。 当然,您可以忍受,对于这类数据库,这是可以接受的,但最终它会导致大量时间的浪费。

费用


根据我们的计算,使用完全相同的资源在AWS实例上部署ClickHouse的成本仅为一半。 当然应该是这样,因为使用Redshift,您将获得一个现成的数据库,您可以在单击AWS控制台中的几个按钮后立即连接到任何PostgreSQL客户端,AWS会为您完成其余工作。 但是值得吗? 我们已经有了基础架构,我们似乎能够执行备份,监视和配置,并且我们为大量内部服务执行了此操作。 为什么不解决ClickHouse支持?

过渡过程


首先,我们从一台计算机上安装了一个小型ClickHouse,然后开始使用内置工具定期从S3下载数据。 因此,我们能够检验有关ClickHouse速度和功能的假设。

在对一小部分数据进行了几周的测试之后,很明显,要用Clickhouse代替Redshift,必须解决一些问题:

  • 在什么类型的实例和磁盘上进行部署;
  • 使用复制?
  • 如何安装,配置和运行;
  • 如何进行监控;
  • 什么样的计划?
  • 如何从S3传送数据;
  • 如何将所有查询从标准SQL重写为非标准?

实例和磁盘的类型 。 在处理器,磁盘和内存的数量上,他们决定在Redshift的当前安装基础上进行构建。 有多种选择,包括具有本地NVMe磁盘的i3实例,但决定在r5.4xlarge停止并以每个实例8T ST1 EBS的形式存储。 据估计,这应该具有与Redshift相当的性能,而成本却只有一半。 同时,由于使用了EBS磁盘,我们几乎可以像Redshift一样通过磁盘快照获得简单的备份和恢复。

复制 。 由于我们是从Redshift中开始的,所以我们决定不使用复制。 此外,这并不强迫我们立即研究尚未在基础架构中的ZooKeeper,但是现在可以根据需要进行复制了,这一点很棒。

安装方式 这是最简单的部分。 对于Ansible来说,这是一个很小的角色,它将安装现成的RPM软件包并在每个主机上进行相同的配置。

监控方式 为了监视所有服务,Prometheus与Telegraf和Grafana一起使用,因此,他们只需将Telegraf代理通过ClickHouse放置在主机上,在Grafana中收集仪表板,即可按处理器,内存和磁盘显示当前服务器的负载。 通过Grafana插件,我们将当前对集群的活动请求,从S3导入的状态以及其他有用的东西带到了该仪表板。 事实证明,与提供AWS控制台的仪表板相比,它甚至更好,更实用(并且速度更快)。

方案 。 我们在Redshift中的主要错误之一是仅将主事件字段放在单独的列中,并添加很少用于添加的字段
在一个大列属性中。 一方面,这使我们可以灵活地在初始阶段更改字段,这是因为我们对要收集的事件,具有什么属性没有完全的完全了解,而且它们每天更改5次。 另一方面,对大量属性的请求花费了越来越多的时间。 在ClickHouse中,我们决定立即采取正确的措施,因此我们收集了所有可能的列,并为它们输入了最佳类型。 结果是一个大约有两百列的表。

下一个任务是为存储和分区选择正确的引擎。
他们没有考虑再次分区,但是做了与Redshift相同的工作-每天分区,但是现在所有分区都是一个表,
大大加快了请求的速度并简化了维护。 该存储引擎由ReplacingMergeTree使用,因为它使您可以通过执行OPTIMIZE ... FINAL来从特定分区中删除重复项。 此外,每日分区方案允许在出现错误或事故的情况下仅处理一天而不是一个月的数据,这要快得多。

将数据从s3传递到ClickHouse 。 这是最长的过程之一。 它只是无法用内置的ClickHouse工具进行加载,因为S3上的数据是JSON格式的,就像在Redshift中一样,每个字段都需要提取到自己的jsonpath中,有时我们还需要使用转换:例如,来自标准记录的消息的UUID格式为DD96C92F-3F4D-44C6-BCD3-E25EB26389E9转换为字节并放入FixedString(16)类型。

我希望有一个类似于Redshift中作为COPY命令提供的特殊服务。 他们没有找到任何准备好的东西,所以我不得不这样做。 您可以撰写有关其工作方式的单独文章,但总之,这是在ClickHouse上的每台主机上部署的HTTP服务。 您可以参考其中任何一个。 request参数指定从中获取文件的S3前缀,用于从JSON转换为一组列的jsonpath列表,以及每列的一组转换。 请求到达的服务器开始扫描S3上的文件,并将解析工作分配给其他主机。 同时,对于我们而言,重要的是将无法导入的行和错误一起添加到单独的ClickHouse表中。 这对调查事件接收服务和生成这些事件的客户端中的问题和错误有很大帮助。 通过将导入程序直接放置在数据库主机上,我们利用了这些资源,这些资源通常是空闲的,因为复杂的请求不会全天候进行。 当然,如果有更多请求,您可以随时将导入者的服务用于单独的主机。

从外部来源导入数据没有大问题。 在那些脚本中,他们只是将目标从Redshift更改为ClickHouse。

可以选择以字典的形式连接MongoDB,而不是每天进行复制。 不幸的是,它不合适,因为字典必须放在内存中,而MongoDB中大多数集合的大小都不允许这样做。 但是字典对我们也很有用:使用字典非常方便,可以连接来自MaxMind的GeoIP数据库并在查询中使用。 为此,我们使用服务提供的布局ip_trie和CSV文件。 例如,geoip_asn_blocks_ipv4词典配置如下所示:

 <dictionaries> <dictionary> <name>geoip_asn_blocks_ipv4</name> <source> <file> <path>GeoLite2-ASN-Blocks-IPv4.csv</path> <format>CSVWithNames</format> </file> <\/source> <lifetime>300</lifetime> <layout> <ip_trie /> </layout> <structure> <key> <attribute> <name>prefix</name> <type>String</type> </attribute> </key> <attribute> <name>autonomous_system_number</name> <type>UInt32</type> <null_value>0</null_value> </attribute> <attribute> <name>autonomous_system_organization</name> <type>String</type> <null_value>?</null_value> </attribute> </structure> </dictionary> </dictionaries> 

将此配置放在/etc/clickhouse-server/geoip_asn_blocks_ipv4_dictionary.xml就足够了,之后您可以查询字典以通过IP地址获取提供者的名称:

 SELECT dictGetString('geoip_asn_blocks_ipv4', 'autonomous_system_organization', tuple(IPv4StringToNum('192.168.1.1'))); 

更改数据模式 。 如上所述,我们决定不使用复制,因为现在我们可以承受因事故或计划中的工作而无法访问的情况,并且数据的副本已经在s3上,并且可以在合理的时间内将其传输到ClickHouse。 如果没有复制,则它们不会扩展ZooKeeper,并且ZooKeeper的缺失也会导致无法在DDL查询中使用ON CLUSTER表达式。 这个问题是通过一个小型python脚本解决的,该脚本连接到每个ClickHouse主机(到目前为止只有8个),并执行指定的SQL查询。

ClickHouse中不完整的SQL支持 。 将请求从Redshift语法转换为ClickHouse语法的过程与导入程序的开发并行进行,并且主要由一组分析人员处理。 奇怪的是,但问题甚至不在JOIN中,而是在窗口函数中。 要了解如何通过数组和lambda函数完成操作,花了几天时间。 很好的是,有关ClickHouse的报告通常会涵盖此问题,其中有大量的报告,例如events.yandex.ru/lib/talks/5420 。 此时,数据已经在两个位置一次写入:在Redshift和新的ClickHouse中,因此,当我们传输请求时,我们比较了结果。 比较速度是有问题的,因为我们删除了一个大的属性列,并且大多数查询只开始使用必需的列,这当然会显着增加,但是那些没有属性列参与的查询以相同的方式工作,或者稍微快一些。

结果,我们得到了以下方案:



结果


最重要的是,我们获得了以下好处:

  • 一张桌子代替90张桌子
  • 服务请求以毫秒为单位执行
  • 成本减半
  • 轻松删除重复事件

我们还准备解决以下缺点:

  • 万一发生意外,您将必须自行修理机群
  • 现在需要在每个主机上分别进行架构更改
  • 更新到新版本必须自己做

由于数据方案已发生重大变化,因此我们无法直接比较请求的速度。 许多查询之所以变得更快,仅仅是因为它们从磁盘读取的数据更少。 以一种好方法,必须在Redshift中进行这样的更改,但是决定将其与向ClickHouse的迁移结合起来。

所有的迁移以及准备工作大约花费了三个月的时间。 她从7月初走到9月底,并要求两个人参加。 9月27日,我们关闭了Redshift,此后我们仅在ClickHouse上工作。 事实证明,已经超过两个月了。 术语很短,但是到目前为止,从未发生过数据丢失或严重错误,因此整个群集都将恢复正常运行。 我们前面的人正在等待新版本的更新!

Source: https://habr.com/ru/post/zh-CN433346/


All Articles