在2018年,我写了一篇有关Clickhouse的文章,该内容在互联网上仍然很流行,甚至被翻译了几次。此后已经过去了两年多,而Clickhouse的开发速度并未放缓:仅在上个月就合并了800个PR!这没让你大吃一惊吗?查看完整的变更日志,例如2020年:https://clickhouse.tech/docs/en/whats-new/changelog/2020/每年仅对新功能的描述可能需要一个小时才能完成。
为了进行诚实的比较,ElasticSearch回购在当月合并了1076个令人jaw目结舌的PR,就功能而言,它们的速度也非常令人印象深刻!
我们正在将Clickhouse用于ApiRoad.net项目(这是一个API市场,开发人员出售其API,仍处于积极开发中)的日志存储和分析,到目前为止,我们对结果感到满意。作为一名API开发人员,我知道HTTP请求/响应周期的可观察性和分析对于维持服务质量并快速检测错误非常重要,对于纯API服务而言尤其如此。
我们还在其他项目上使用ELK(ElasticSearch,Logstash,filebeat,Kibana)堆栈,用于非常相似的目的-获取HTTP和邮件日志,以供以后通过Kibana分析和搜索。
这篇文章是关于为什么我们选择Clickhouse而不是ElasticSearch(或MySQL)作为ApiRoad.net基本数据(请求日志)存储解决方案的主要原因(重要说明:出于OLTP的目的,我们仍在此处使用MySQL)。
SQL是用于分析的理想语言。我喜欢SQL查询语言,SQL模式是无聊技术的完美示例,我建议将其用作99%项目中所有数据的真实来源:如果项目代码不完美,则可以相对轻松地对其进行改进您的数据库状态结构牢固。如果您的数据库状态是一个巨大的JSON Blob(NoSQL),并且没有人可以完全掌握此数据的结构,则这种重构通常会带来更多问题。
我看到了这种情况,尤其是在MongoDB的旧项目中,每一个新的分析报告和每一个涉及数据迁移的新重构都是一个很大的难题。开始这样的项目很有趣–您无需花时间仔细设计完整的项目架构,只需了解其进展情况即可。 –但是维护它们并不有趣!
但是,请务必注意,这条经验法则-“使用严格模式”。 -对于日志存储用例而言并不那么关键。这就是ElasticSearch如此成功的原因,它具有许多优势,而且架构灵活。
返回JSON:就JSON查询和语法而言,传统的RDBMS仍在赶上NoSQL DBMS,我们应该承认JSON是动态结构(如日志存储)非常方便的格式。
Clickhouse是一种现代引擎,是在JSON已经成为事物(不同于MySQL和Postgres)时设计和构建的,并且Clickhouse不必携带这些超级受欢迎的RDBMS的向后兼容性和严格的SQL标准,因此Clickhouse团队可以在功能和改进方面进展迅速,而且的确在快速发展。 Clickhouse的开发人员有更多机会在严格的相对架构和JSON灵活性之间达到最佳平衡,我认为他们在这里做得很好。 Clickhouse试图在分析领域与Google Big Query和其他大公司竞争,因此它在" standard"上获得了许多改进。 SQL,它的语法成为了杀手comb,在很多情况下都比传统的RDBMS更好,用于分析和各种计算目的。
在MySQL中,您可以提取JSON字段,但是复杂的JSON处理(例如将关系数据连接到JSON数据)仅在最近的版本(具有JSON_TABLE函数的版本8)中可用。在PosgreSQL中,情况甚至更糟-在PostgreSQL 12之前没有直接的JSON_TABLE替代方案!
将它与Clickhouse JSON和相关的数组功能集进行比较-它仅领先几英里。链接:
在很多情况下,在PostgreSQL中使用generate_series()时,这些功能很有用。来自ApiRoad的一个具体示例:我们需要在chart.js时间轴上映射请求数量。如果您每天进行常规的SELECT .. group,如果某些天没有任何查询,则会出现空白。而且我们不需要缺口,那里需要零,对吗?这正是在PostgreSQL中generate_series()函数有用的地方。在MySQL中,建议使用日历创建存根表并在其中进行连接...不是太优雅了吧?
关于查询语言:我对ElasticSearch Lucene语法,HTTP API以及为检索某些数据而需要编写的所有这些json结构的详细程度和方法仍然不满意。 SQL是我的首选。
从(用(选择toUInt32(dateDiff(' day&#39 ;, [START_DATE],[END_DATE])))选择diffInTimeUnits选择arrayJoin(arrayMap(x-> ;(toDate(addDays([START_DATE],x)),range(0,diffInTimeUnits + 1))))作为timePeriod)左联接(选择count(*)作为count,toDate(toStartOfDay(started_at))作为timePeriod从在a.timePeriod = b.timePeriod上记录WHERE [条件] GROUP BY到toStartOfDay(started_at))b
在这里,我们通过lambda函数生成一个虚拟表并循环,然后将其按天分组的日志表中的结果保持连接状态。
我认为arrayJoin + arrayMap +范围函数比Postgres或ElasticSearch方法的generate_series()具有更大的灵活性。还有一个WITH FILL关键字可用于更简洁的语法。
对于日志存储任务,确切的数据模式通常会在项目生命周期内演变,ElasticSearch允许您将巨大的JSON blob放入索引中,然后找出字段类型和索引部分。 Clickhouse允许使用相同的方法。您可以将数据放入JSON字段并相对快速地进行过滤,尽管在TB级上并不会很快。然后,当您看到经常需要在特定数据字段上快速执行查询时,便将物化列添加到日志表中,这些列会即时从现有JSON中提取值。这样可以对TB级的数据进行更快的查询。
我推荐来自Altinity的有关JSON与表格格式的主题的视频记录数据存储:
我相信没有直接的基准测试,至少我找不到任何基准测试,因为Clickhouse和ElasticSearch在查询语法,缓存实现及其整体性质方面都非常不同。
如果我们谈论MySQL,那么在只有1亿行日志数据的表上,任何不完善的查询,缺少索引都会使您的服务器爬行和交换,MySQL并不真正适合大型日志查询。但是,就存储而言,压缩的InnoDB表并没有那么糟糕。当然,由于其基于行的性质,与Clickhouse相比,压缩方面的情况要差得多(抱歉,这次没有支持基准的URL来支持该声明),但是在没有压缩的情况下,它仍然经常设法显着降低成本对性能的影响很大。在某些情况下,出于小型日志目的,我们使用压缩的InnoDB表。
从状态为404的日志中选择COUNT(*)为cnt,QuantileTiming(0.5)(duration)为duration_median,QuantileTiming(0.9)(duration)为duration_90th,QuantileTiming(0.99)(duration)为duration_99th
请注意QuantileTiming函数的用法以及此处如何优雅地使用currying。 Clickhouse具有通用分位数功能!但是QuantileTiming已针对与描述分布的序列进行了优化,这些序列描述了诸如加载网页时间或后端响应时间之类的分布。
还有更多。想要加权算术平均数吗?要计算线性回归吗?这很容易,只需使用专门的功能即可。
ElasticSearch在这方面比MySQL好得多,它既具有分位数又具有加权中位数,但是它仍然没有线性回归。
MySQL和Clickhouse具有多个级别的集成,这使它们易于使用,同时减少了数据重复:
我不能肯定地说动态数据库引擎和表引擎在JOIN上有多快和稳定,这绝对需要基准测试,但是这个概念非常吸引人-您在自己的MySQL表中拥有完整的最新克隆版本Clickhouse数据库,您不必处理缓存失效和重新编制索引。
关于将MySQL与Elasticsearch结合使用,我有限的经验表明,这两种技术太不同了,我的印象是他们说的是外语,并且不会一起玩,所以我通常所做的只是JSONify我需要在ElasticSearch中建立索引的所有数据,并将其发送到ElasticSearch。然后,在对MySQL数据进行一些迁移或任何其他UPDATE / REPLACE之后,我尝试找出Elasticseach端的重新索引部分。这是Logstash支持的方法的文章,用于同步MySQL和ElasticSearch。我应该说我真的不喜欢Logstash,因为它的性能中等,并且对RAM的要求也很高,而且因为Logstash是另一个可能会损坏的动态部分。对于我们在带有MySQL的简单项目中使用Elasticsearch而言,此同步和重新索引编制任务通常是一个重要的停止因素。
是否要更新或删除日志行以符合GDPR?现在,这很容易!
在撰写我的第一篇文章时,2018年Clickhouse中没有干净的方法来删除或更新数据,这是一个真正的弊端。现在,这不再是问题。 Clickhouse利用自定义SQL语法删除行:
这样做的目的很明确,对于Clickhouse(和其他列式数据库)来说,删除仍然是一项相当昂贵的操作,因此您不应该在生产中每秒都这样做。
与ElasticSearch相比,Clickhouse有缺点。首先,如果您构建用于日志存储的内部分析,那么您确实想获得最好的GUI工具。当您将其与Grafana进行比较时,Kibana现在对于此目的是很好的(至少,这种观点在Internet上非常流行,Grafana UI有时并不那么光滑)。如果您使用Clickhouse,则必须坚持使用Grafana或Redash。 (我们喜欢的元数据库也获得了Clickhouse的支持!)
但是,在我们的案例中,在ApiRoad.net项目中,我们正在构建面向客户的分析,因此无论如何我们都必须从头开始构建分析GUI(我们使用了很棒的Laravel,Inertia.js,Vue.js和用Charts.js来实现客户门户)。
我在Clickhouse中不喜欢的还有一些功能的奇怪名称,这些功能之所以出现是因为Clickhouse是为Yandex.Metrika(Google Analytics(分析)竞争对手)创建的。 visitParamHas()是用于检查JSON中是否存在密钥的函数。通用,错误的非通用名称。我应该提到的是,有一堆名字不错的新JSON函数:例如JSONHas(),其中有一个有趣的细节:据我所知,它们使用不同的JSON解析引擎,更符合标准,但速度稍慢。
ElasticSearch是一个非常强大的解决方案,但我认为它最强大的方面仍然是具有10多个节点的庞大设置,用于大型全文本搜索和构面,复杂的索引编制和分数计算-这是ElasticSearch的亮点。当我们谈论时间序列和日志存储时,我的感觉是有更好的解决方案,而Clickhouse就是其中之一。 ElasticSearch API的功能非常强大,而且在许多情况下,如果不从文档中复制粘贴确切的HTTP请求,就很难记住如何做一件确切的事情,它只是一种“企业精神”。和" Java风味的"。 Clickhouse和ElasticSearch都是占用内存的应用程序,但最小的Clickhouse生产安装所需的RAM要求为4GB,而ElasticSearch的RAM要求约为16GB。我还认为弹性团队关注的重点是他们部署的所有新的惊人的机器学习功能都变得越来越模糊,我的愚见是,尽管这些功能听起来非常现代和新潮,但是无法支持和改进这一庞大的功能集,无论您拥有多少开发人员和金钱,因此ElasticSearch都会越来越多地进入“千篇一律,无所不包的大师”的行列。对我而言。也许我错了。
Clickhouse感觉与众不同。 设置很容易。 SQL很简单。 控制台客户端很棒。 即使对于较小的设置,一切都变得如此轻巧和有意义,但是当您需要时,就可以使用数十亿兆字节数据的丰富功能,副本和分片。