当前位置:   article > 正文

ES 和 Clickhouse 查询能力对比,实践结果根本料不到……

clickhouse es mongodb

点击上方“芋道源码”,选择“设为星标

管她前浪,还是后浪?

能浪的浪,才是好浪!

每天 10:33 更新文章,每天掉亿点点头发...

源码精品专栏

 

来源:zhuanlan.zhihu.com/

p/353296392


Elasticsearch 是一个实时的分布式搜索分析引擎,它的底层是构建在Lucene之上的。简单来说是通过扩展Lucene的搜索能力,使其具有分布式的功能。ES通常会和其它两个开源组件logstash(日志采集)和Kibana(仪表盘)一起提供端到端的日志/搜索分析的功能,常常被简称为ELK。

Clickhouse是俄罗斯搜索巨头Yandex开发的面向列式存储的关系型数据库。ClickHouse是过去两年中OLAP领域中最热门的,并于2016年开源。

ES是最为流行的大数据日志和搜索解决方案,但是近几年来,它的江湖地位受到了一些挑战,许多公司已经开始把自己的日志解决方案从ES迁移到了Clickhouse,这里就包括:携程,快手等公司。

架构和设计的对比

ES的底层是Lucenc,主要是要解决搜索的问题。搜索是大数据领域要解决的一个常见的问题,就是在海量的数据量要如何按照条件找到需要的数据。搜索的核心技术是倒排索引和布隆过滤器。ES通过分布式技术,利用分片与副本机制,直接解决了集群下搜索性能与高可用的问题。

5fe6cad1cbc00236af8231cf9876fb9b.png

ElasticSearch是为分布式设计的,有很好的扩展性,在一个典型的分布式配置中,每一个节点(node)可以配制成不同的角色,如下图所示:

  • Client Node,负责API和数据的访问的节点,不存储/处理数据

  • Data Node,负责数据的存储和索引

  • Master Node, 管理节点,负责Cluster中的节点的协调,不存储数据。

0ee50b94769ea5e4a942ddb6609d6b2b.png

ClickHouse是基于MPP架构的分布式ROLAP(关系OLAP)分析引擎。每个节点都有同等的责任,并负责部分数据处理(不共享任何内容)。ClickHouse 是一个真正的列式数据库管理系统(DBMS)。在 ClickHouse 中,数据始终是按列存储的,包括矢量(向量或列块)执行的过程。让查询变得更快,最简单且有效的方法是减少数据扫描范围和数据传输时的大小,而列式存储和数据压缩就可以帮助实现上述两点。Clickhouse同时使用了日志合并树,稀疏索引和CPU功能(如SIMD单指令多数据)充分发挥了硬件优势,可实现高效的计算。Clickhouse 使用Zookeeper进行分布式节点之间的协调。

f9404461163898b97a364ea6f45520de.png

为了支持搜索,Clickhouse同样支持布隆过滤器。

基于 Spring Boot + MyBatis Plus + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能。

项目地址:https://github.com/YunaiV/ruoyi-vue-pro

查询对比实战

为了对比ES和Clickhouse的基本查询能力的差异,我写了一些代码(https://github.com/gangtao/esvsch)来验证。

这个测试的架构如下:

2a3cc9351a33f82544186e1ffb2750a3.png

架构主要有四个部分组成:

  • ES stack ES stack有一个单节点的Elastic的容器和一个Kibana容器组成,Elastic是被测目标之一,Kibana作为验证和辅助工具。部署代码如下:

  1. version: '3.7'
  2. services:
  3.   elasticsearch:
  4.     image: docker.elastic.co/elasticsearch/elasticsearch:7.4.0
  5.     container_name: elasticsearch
  6.     environment:
  7.       - xpack.security.enabled=false
  8.       - discovery.type=single-node
  9.     ulimits:
  10.       memlock:
  11.         soft: -1
  12.         hard: -1
  13.       nofile:
  14.         soft: 65536
  15.         hard: 65536
  16.     cap_add:
  17.       - IPC_LOCK
  18.     volumes:
  19.       - elasticsearch-data:/usr/share/elasticsearch/data
  20.     ports:
  21.       - 9200:9200
  22.       - 9300:9300
  23.     deploy:
  24.       resources:
  25.         limits:
  26.           cpus: '4'
  27.           memory: 4096M
  28.         reservations:
  29.           memory: 4096M
  30.   kibana:
  31.     container_name: kibana
  32.     image: docker.elastic.co/kibana/kibana:7.4.0
  33.     environment:
  34.       - ELASTICSEARCH_HOSTS=http://elasticsearch:9200
  35.     ports:
  36.       - 5601:5601
  37.     depends_on:
  38.       - elasticsearch
  39. volumes:
  40.   elasticsearch-data:
  41.     driver: local
  • Clickhouse stack Clickhouse stack有一个单节点的Clickhouse服务容器和一个TabixUI作为Clickhouse的客户端。部署代码如下:

  1. version: "3.7"
  2. services:
  3.   clickhouse:
  4.     container_name: clickhouse
  5.     image: yandex/clickhouse-server
  6.     volumes:
  7.       - ./data/config:/var/lib/clickhouse
  8.     ports:
  9.       - "8123:8123"
  10.       - "9000:9000"
  11.       - "9009:9009"
  12.       - "9004:9004"
  13.     ulimits:
  14.       nproc: 65535
  15.       nofile:
  16.         soft: 262144
  17.         hard: 262144
  18.     healthcheck:
  19.       test: ["CMD""wget""--spider""-q""localhost:8123/ping"]
  20.       interval: 30s
  21.       timeout: 5s
  22.       retries: 3
  23.     deploy:
  24.       resources:
  25.         limits:
  26.           cpus: '4'
  27.           memory: 4096M
  28.         reservations:
  29.           memory: 4096M
  30.   tabixui:
  31.     container_name: tabixui
  32.     image: spoonest/clickhouse-tabix-web-client
  33.     environment:
  34.       - CH_NAME=dev
  35.       - CH_HOST=127.0.0.1:8123
  36.       - CH_LOGIN=default
  37.     ports:
  38.       - "18080:80"
  39.     depends_on:
  40.       - clickhouse
  41.     deploy:
  42.       resources:
  43.         limits:
  44.           cpus: '0.1'
  45.           memory: 128M
  46.         reservations:
  47.           memory: 128M
  • 数据导入 stack 数据导入部分使用了Vector.dev开发的vector,该工具和fluentd类似,都可以实现数据管道式的灵活的数据导入。

  • 测试控制 stack 测试控制我使用了Jupyter,使用了ES和Clickhouse的Python SDK来进行查询的测试。

用Docker compose启动ES和Clickhouse的stack后,我们需要导入数据,我们利用Vector的generator功能,生成syslog,并同时导入ES和Clickhouse,在这之前,我们需要在Clickhouse上创建表。ES的索引没有固定模式,所以不需要事先创建索引。

创建表的代码如下:

  1. CREATE TABLE default.syslog(
  2.     application String,
  3.     hostname String,
  4.     message String,
  5.     mid String,
  6.     pid String,
  7.     priority Int16,
  8.     raw String,
  9.     timestamp DateTime('UTC'),
  10.     version Int16
  11. ) ENGINE = MergeTree()
  12.     PARTITION BY toYYYYMMDD(timestamp)
  13.     ORDER BY timestamp
  14.     TTL timestamp + toIntervalMonth(1);

创建好表之后,我们就可以启动vector,向两个stack写入数据了。vector的数据流水线的定义如下:

  1. [sources.in]
  2. type = "generator"
  3. format = "syslog"
  4. interval = 0.01
  5. count = 100000
  6. [transforms.clone_message]
  7. type = "add_fields"
  8. inputs = ["in"]
  9. fields.raw = "{{ message }}"
  10. [transforms.parser]
  11. # General
  12. type = "regex_parser"
  13. inputs = ["clone_message"]
  14. field = "message" # optional, default
  15. patterns = ['^<(?P<priority>\d*)>(?P<version>\d) (?P<timestamp>\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z) (?P<hostname>\w+\.\w+) (?P<application>\w+) (?P<pid>\d+) (?P<mid>ID\d+) - (?P<message>.*)$']
  16. [transforms.coercer]
  17. type = "coercer"
  18. inputs = ["parser"]
  19. types.timestamp = "timestamp"
  20. types.version = "int"
  21. types.priority = "int"
  22. [sinks.out_console]
  23. # General
  24. type = "console"
  25. inputs = ["coercer"]
  26. target = "stdout"
  27. # Encoding
  28. encoding.codec = "json"
  29. [sinks.out_clickhouse]
  30. host = "http://host.docker.internal:8123"
  31. inputs = ["coercer"]
  32. table = "syslog"
  33. type = "clickhouse"
  34. encoding.only_fields = ["application", "hostname", "message", "mid", "pid", "priority", "raw", "timestamp", "version"]
  35. encoding.timestamp_format = "unix"
  36. [sinks.out_es]
  37. # General
  38. type = "elasticsearch"
  39. inputs = ["coercer"]
  40. compression = "none"
  41. endpoint = "http://host.docker.internal:9200"
  42. index = "syslog-%F"
  43. # Encoding
  44. # Healthcheck
  45. healthcheck.enabled = true

这里简单介绍一下这个流水线:

  • http://source.in 生成syslog的模拟数据,生成10w条,生成间隔和0.01秒

  • transforms.clone_message 把原始消息复制一份,这样抽取的信息同时可以保留原始消息

  • transforms.parser 使用正则表达式,按照syslog的定义,抽取出application,hostname,message ,mid ,pid ,priority ,timestamp ,version 这几个字段

  • transforms.coercer 数据类型转化

  • sinks.out_console 把生成的数据打印到控制台,供开发调试

  • sinks.out_clickhouse 把生成的数据发送到Clickhouse

  • sinks.out_es 把生成的数据发送到ES

运行Docker命令,执行该流水线:

  1. docker run \
  2.   -v $(mkfile_path)/vector.toml:/etc/vector/vector.toml:ro \
  3.   -p 18383:8383 \
  4.   timberio/vector:nightly-alpine

数据导入后,我们针对一下的查询来做一个对比。ES使用自己的查询语言来进行查询,Clickhouse支持SQL,我简单测试了一些常见的查询,并对它们的功能和性能做一些比较。

  • 返回所有的记录

  1. > 基于微服务的思想,构建在 B2C 电商场景下的项目实战。核心技术栈,是 Spring Boot + Dubbo 。未来,会重构成 Spring Cloud Alibaba 。
  2. >
  3. > 项目地址:<https://github.com/YunaiV/onemall>
  4. # ES
  5. {
  6.   "query":{
  7.     "match_all":{}
  8.   }
  9. }
  10. # Clickhouse
  11. "SELECT * FROM syslog"
  • 匹配单个字段

  1. # ES
  2. {
  3.   "query":{
  4.     "match":{
  5.       "hostname":"for.org"
  6.     }
  7.   }
  8. }
  9. # Clickhouse
  10. "SELECT * FROM syslog WHERE hostname='for.org'"
  • 匹配多个字段

  1. # ES
  2. {
  3.   "query":{
  4.     "multi_match":{
  5.       "query":"up.com ahmadajmi",
  6.         "fields":[
  7.           "hostname",
  8.           "application"
  9.         ]
  10.     }
  11.   }
  12. }
  13. # Clickhouse、
  14. "SELECT * FROM syslog WHERE hostname='for.org' OR application='ahmadajmi'"
  • 单词查找,查找包含特定单词的字段

  1. # ES
  2. {
  3.   "query":{
  4.     "term":{
  5.       "message":"pretty"
  6.     }
  7.   }
  8. }
  9. # Clickhouse
  10. "SELECT * FROM syslog WHERE lowerUTF8(raw) LIKE '%pretty%'"
  • 范围查询, 查找版本大于2的记录

  1. # ES
  2. {
  3.   "query":{
  4.     "range":{
  5.       "version":{
  6.         "gte":2
  7.       }
  8.     }
  9.   }
  10. }
  11. # Clickhouse
  12. "SELECT * FROM syslog WHERE version >= 2"
  • 查找到存在某字段的记录 ES是文档类型的数据库,每一个文档的模式不固定,所以会存在某字段不存在的情况;而Clickhouse对应为字段为空值

  1. # ES
  2. {
  3.   "query":{
  4.     "exists":{
  5.       "field":"application"
  6.     }
  7.   }
  8. }
  9. # Clickhouse
  10. "SELECT * FROM syslog WHERE application is not NULL"
  • 正则表达式查询,查询匹配某个正则表达式的数据

  1. # ES
  2. {
  3.   "query":{
  4.     "regexp":{
  5.       "hostname":{
  6.         "value":"up.*",
  7.           "flags":"ALL",
  8.             "max_determinized_states":10000,
  9.               "rewrite":"constant_score"
  10.       }
  11.     }
  12.   }
  13. }
  14. # Clickhouse
  15. "SELECT * FROM syslog WHERE match(hostname, 'up.*')"
  • 聚合计数,统计某个字段出现的次数

  1. # ES
  2. {
  3.   "aggs":{
  4.     "version_count":{
  5.       "value_count":{
  6.         "field":"version"
  7.       }
  8.     }
  9.   }
  10. }
  11. # Clickhouse
  12. "SELECT count(version) FROM syslog"
  • 聚合不重复的值,查找所有不重复的字段的个数

  1. # ES
  2. {
  3.   "aggs":{
  4.     "my-agg-name":{
  5.       "cardinality":{
  6.         "field":"priority"
  7.       }
  8.     }
  9.   }
  10. }
  11. # Clickhouse
  12. "SELECT count(distinct(priority)) FROM syslog "

我用Python的SDK,对上述的查询在两个Stack上各跑10次,然后统计查询的性能结果。

我们画出出所有的查询的响应时间的分布:

8ea4b321437e62d323fd381debe31e6b.png

总查询时间的对比如下:

051715299fe21f164c4f7aa4e31f9b82.png

通过测试数据我们可以看出Clickhouse在大部分的查询的性能上都明显要优于Elastic。在正则查询(Regex query)和单词查询(Term query)等搜索常见的场景下,也并不逊色。

在聚合场景下,Clickhouse表现异常优秀,充分发挥了列村引擎的优势。

注意,我的测试并没有任何优化,对于Clickhouse也没有打开布隆过滤器。可见Clickhouse确实是一款非常优秀的数据库,可以用于某些搜索的场景。当然ES还支持非常丰富的查询功能,这里只有一些非常基本的查询,有些查询可能存在无法用SQL表达的情况。

总结

本文通过对于一些基本查询的测试,对比了Clickhouse 和Elasticsearch的功能和性能,测试结果表明,Clickhouse在这些基本场景表现非常优秀,性能优于ES,这也解释了为什么用很多的公司应从ES切换到Clickhouse之上。



欢迎加入我的知识星球,一起探讨架构,交流源码。加入方式,长按下方二维码噢

d0afaf0caab7debbd2e7a15748f6f9e6.png

已在知识星球更新源码解析如下:

8a7a0ac2d0caa715b6197573f14005d7.png

f59869dc2f16bd84a8e73bbeea137180.png

ddac1192643672337357c4ebb4b6b6e2.png

3aa1cd203eaa4f992e7bbec3a505cb0c.png

最近更新《芋道 SpringBoot 2.X 入门》系列,已经 101 余篇,覆盖了 MyBatis、Redis、MongoDB、ES、分库分表、读写分离、SpringMVC、Webflux、权限、WebSocket、Dubbo、RabbitMQ、RocketMQ、Kafka、性能测试等等内容。

提供近 3W 行代码的 SpringBoot 示例,以及超 4W 行代码的电商微服务项目。

获取方式:点“在看”,关注公众号并回复 666 领取,更多内容陆续奉上。

  1. 文章有帮助的话,在看,转发吧。
  2. 谢谢支持哟 (*^__^*)
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Cpp五条/article/detail/583135
推荐阅读
相关标签
  

闽ICP备14008679号