Clickhouse: One table to rule them all!

前面几篇笔记我们讨论了存储海量行情数据的个人技术方案。它们之所以被称之为个人方案,并不是因为性能弱,而是指在这些方案中,数据都存储在本地,也只适合单机查询。

数据源很贵 – 在这个冬天,我们已经听说,某些上了规模的机构,也在让员工共享万得账号了。所以,共享网络存储,从而只需要一个数据账号,就成为合理的需求。更不必说,集中管理才可能让 IT 来进行数据维护,而分析师只需要专注于策略就好。

那些以讹传讹的解决方案

都已经 2024 年了,但说到行情数据的存储,你仍然能看到推荐 mysql 的文章。这完全是错误的。不要说 mysql,就是 postgres 来了也不行。不要说 postgres,就是 sqlserver 甚至 oracle 来了都不行。

其它不 work 的方案还包括 mongodb。mongodb 是挺能装的,但是它不适合行情数据这类时序数据的查询。

Influxdb 是最早和最出名的时序数据库。但是它的社区版本性能还是偏弱,特别是它限制了查询的并发度。此外,它的引擎是 Go 语言,这仍然要比 C 慢好几倍。

Dolphinedb 可能性能上强于 Influxdb 不少,但缺点也是社区版对性能的限制太多。Tidb 据说性能不错,但我们没有机会评测过它。

不过如果有犹如王者般的 clickhouse 社区版摆在面前,还有什么必要去评估那些青铜呢?

Why click house is so f**ing fast?

Clickhouse 是战斗民族开发的产品。它的开发者是俄国的搜索引擎 Yandex!(提到 Yandex! 时,不能漏了这个感叹号)。搜索引擎天生要处理很多查询和统计分析,所以就催生了这个性能怪兽。


Clickhouse 的优化是全方位的。在硬件级别上,它利用了 SIMD CPU 指令。Clickhouse 特别强调他们使用了 SIMD 指令来进行并行优化,当你安装 clickhouse 时,它提供了一个检测工具,让你检测 SIMD 指令优化能否开启。

在这里插入图片描述

数据结构上,Clickhouse 使用了列存储,这一点,其实像 parquet, hdf5 都是这样存储的。基于列存储,就有很多很好的压缩方案可用,一旦存盘的数据量变小,显然易见 IO 效率也会提升。

但是它基于 merge-tree 的存储引擎,使得在查询上,不仅可以利用所有的 CPU 核和磁盘、还可以利用集群的所有 CPU 核和磁盘。这使得它的查询性能可以随硬件增加线性扩展。

在这里插入图片描述

在这一块,确实用了很多大数据的技巧,比如使用了 bloomfilter 的索引。它还留了一些优化技巧给使用者,这也是我们这篇笔记将要介绍的:如何设计一个能存储上百亿条行情数据的数据库,并达到最佳性能。

实战!先存一个小目标

尽管在 clickhouse 中,我们可以把分钟线与日线存在一张表里,但是考虑到我们几乎不可能同时取两个不同周期的数据,所以,把它们分别按表存储显然更合理。所以,我们在举例时,就只以分钟线为例:

CREATE TABLE if not exists bars_1m
(`frame` DateTime64 CODEC(Delta, ZSTD),`symbol` LowCardinality(String),`open` Float32 DEFAULT -1 CODEC(Delta, ZSTD),`high` Float32 DEFAULT -1 CODEC(Delta, ZSTD),`low` Float32 DEFAULT -1 CODEC(Delta, ZSTD),`close` Float32 DEFAULT -1 CODEC(Delta, ZSTD),`volume` Float64 DEFAULT -1 ,`money` Float64 DEFAULT -1 ,`factor` Float64 DEFAULT -1 CODEC(Delta, ZSTD)
)
ENGINE = MergeTree
ORDER BY (frame, symbol)

这里我们看到使用 clickhouse 的第一个好处。它完全兼容了 sql 的核心语法。要知道在设计 zillionare 2.0 版时我们被 influxdb 折磨的不行 – 天知道他们为什么抛弃了原先对 sql 的兼容,而独出心裁地设计了一种全新的查询语言!

这意味着如果我们在团队内安装了 clickhouse – 这常常是 IT 的活,分析师也就能直接上手 – 因为做数据分析的人,你们都是懂 sql 的。

这里有一些技巧,是普通的 SQL 中没有的。

首先是 frame 字段中的 CODEC(Delta, ZSTD) 压缩。它巧妙地通过行间数据的差值,将列数据转换成为一个稀疏的数据向量 – 这样可以大大减少存储空间和读取时间。实际上,在行情数据中,大量的时间戳都是相同的,或者只有很小的 delta。比如,如果我们把 5000 多支个股的分钟数据存入一张表,那么我们常常会看到连续 5000 个相同的时间戳,这些都可以存为 0!

OHLC 中的 default 值也给得颇有讲究。如果某天某个标的停牌,那么它的 OHLC 等数据就是空值。clickhouse 允许我们存空值。但这样一来,clickhouse 必须使用另外的文件来存储空值,并在查询时再通过 join 把空值连接起来。这会花掉一些时间。所以,这里我们使用了一个不可能的值作为默认值,这样所有的数据仍然存在一起,将会加快存储和运算速度。

OHLC 数据的变化都很小,所以我们也通过 DELTA 编码压缩它们。而成交量和成交额的跳动则可能很大,启用压缩就会得不偿失。

我们能做这些优化,是因为我们知道数据的分布特性 – 就像数据分析师也必须懂得数据的分布特性一样 – clickhouse 也是这样才能做好优化。

我们在 OHLC 数据上使用了单精度,但对 factor 却使用了 64 位浮点数。这是必要的,尽管看上去它们都很小,但是,OHLC 数据的取值范围很小,不会有精度问题,而 factor 数据则不一样,它必须更准确。
在这里插入图片描述

Symbol 字段我们也使用了一种优化。通过这种编码,我们实际上在存储 Symbol 时,存储的是整数而非字符串,这样会大大提高存储效率和查询速度。这个方法,如果你熟悉 Pandas 的性能优化,就应该已经见过了 – 它就是类似于 pandas 的 categorize 优化。

最后,我们把 frame 和 symbo 设为主键。我们的大多数查询将基于这样两个字段的比较。


我们设计的表,性能究竟怎么样?

让我们先分别存入 100 万条、1000 万条和 1 亿条数据,并且分别计算插入时间和查询时间。在准备数据时,我们使用了全随机的数据,这点很重要。如果我们都使用相同的数据,那么速度会快一些。

  • 写入 1 百万条数据,用时 8.3 秒,查询结果为 200 行时,用时 0.1 秒。
  • 写入 1 千万条数据,用时 77.7 秒,查询结果为 2000 行时,用时 0.7 秒。
  • 写入 1 亿条数据,用时 16 分钟左右,查询结果为 20200 行时,用时 13.7 秒。

这个结果已经很优秀了。但还看上去并没有超出预期,对吧?作为对比,我们在 influxdb 上,返回 100 万条记录时,花费 55 秒左右,其中网络传输和客户端重新组装数据占约 30 秒),这样看来,clickhouse在这一局很可能还没有超过influxdb。另外,在 1000 万级别下,优化到极致的 mysql 也能做到0.7秒以内的查询,不过它处理不了上亿条数据。

为什么没有惊喜?我去看了一下我的测试环境:

一个只有 8CPU,8GB 的虚拟机(当然底层是磁盘阵列),并且我已经开了 4 个 vscode 窗口,这样系统只剩下0.6GB的自由内存。我们测试Influxdb时,使用的是物理机,48CPU+96GB内存,总记录是30多亿条。

改天再找机会在同样的环境下进行对比测试。不过,clickhouse 员工已经在类似的行情数据库上进行了测试:

在一台 macbook pro 上,在 2.4 亿条记录中,进行 argmax 的查询,只用了 0.9 秒!这个速度虽然不够跑高频,但已足够多数场景下使用了。

不过,clickhouse 的测试与我们的测试有很大差别:

在 clickhouse 的测试中,它返回的数据量很小;而在我们的测试中,要求查询返回了 2 万条数据。

这是另一个优化方向。把能做的事情放到 clickhouse server 端做。也就是,很多因子的计算,之前我们需要取数据回 python 端再计算的,现在如果有可能,直接让 clickhouse 来做,我们只要结果。

这是我们后续笔记要发表的内容。

量化数据本地化方案全系列发布在大富翁量化网站的这个合集下,欢迎前往一次性读完!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/411168.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Peter算法小课堂—并查集

我们先来看太戈编程467题 攀亲戚 题目描述: 最近你发现自己和古代一个皇帝长得很像:都有两个鼻子一个眼睛,你想知道这皇帝是不是你的远方亲戚,你是不是皇亲国戚。目前你能掌握的信息有m条,关于n个人:第i条…

Python二级中的进制转换:看这一篇就够了

在计算机中,不同的进制之间的转换是常见的问题,也是令很多朋友比较头疼的问题,这种题虽然在Python二级中分不多,有时甚至还不会出,但是必须要掌握它们,以备不时之需。 这里详细介绍如何在二进制、八进制、…

Unity之铰链关节和弹簧组件

《今天闪电侠他回来了,这一次他要拿回属于他的一切》 目录 📕一、铰链关节组件HingeJoint 1. 实例 2. 铰链关节的坐标属性 ​3.铰链关节的马达属性Motor 📕二、弹簧组件 📕三、杂谈 一、铰链关节组件HingeJoint 1. 实例 说…

Docker从入门到精通

系列文章目录 docker常见用法之镜像构建1 docker 系列文章目录一、镜像的分层结构二、容器的用法三、镜像的构建3.1docker commit 构建新镜像三部曲3.1.1运行容器并且修改容器3.1.2提交容器3.1.2删除docker镜像 3.2Dockerfile构建镜像 系列文章目录一、 Dockerfile写法详解1.1…

MySQL优化之SQL调优策略

首先以一张思维导图从全局上给大家分享以下几种SQL优化策略,再详细讲解 1、避免使用SELECT * 在阿里的编码规范中也强制了数据库查询不能使用SELECT *,因为SELECT *方式走的都是全表扫描,导致的结果就是查询效率非常低下,其原因为…

Redis数据结构学习笔记

图文主要参考小林Coding的图解redis数据结构 redis为什么快 除了它是内存数据库,使得所有的操作都在内存上进⾏之外,还有⼀个重要因素,它实现的数据结构,使 得我们对数据进⾏增删查改操作时,Redis 能⾼效的处理。 数…

1.机器学习-机器学习算法分类概述

机器学习-机器学习算法分类概述 个人简介机器学习算法分类:监督学习、无监督学习、强化学习一监督学习1. 监督学习分类任务举例:1.1 特征1.2 标签 二无监督学习1.关键特点2.应用示例3.常见的无监督学习算法 三强化学习1.定义2.示例场景 四机器学习开发流…

【Python3】【力扣题】389. 找不同

【力扣题】题目描述: 【Python3】代码: 1、解题思路:使用计数器分别统计字符串中的元素和出现次数,两个计数器相减,结果就是新添加的元素。 知识点:collections.Counter(...):字典子类&#x…

【白皮书下载】GPU计算在汽车中的应用

驾驶舱域控制器 (CDC) 是汽车 GPU 的传统应用领域。在这里,它可以驱动仪表板上的图形,与车辆保持高度响应和直观的用户界面,甚至为乘客提供游戏体验。随着车辆屏幕数量的增加和分辨率的提高,对汽车 GPU 在 CDC 中进行图形处理的需…

Jxls 实现动态导出功能

目录 引言前端页面后端代码excel模板导出效果 引言 在实际做项目的过程中,导出报表时需要根据每个人所关注的点不一样,所需导出的字段也不一样,这时后端就需要根据每个所选的字段去相应的报表,这就是本文要讲的动态导出报表。 前端…

S/MIME电子邮件证书申请指南

近年来,邮件安全问题日益突出,电子邮件成为诈骗、勒索软件攻击的重灾区。恶意邮件的占比屡创新高,邮件泄密事件更是比比皆是。在如此严峻的网络安全形势下,使用S/MIME电子邮件证书进行邮件收发是当今最佳的邮件安全解决方案之一。…

生产消费者模型

生产消费者模型概念 生产者消费者模式就是通过一个容器来解决生产者和消费者的强耦合问题。生产者和消费者彼此之间不直接通讯,而 通过阻塞队列来进行通讯,所以生产者生产完数据之后不用等待消费者处理,直接扔给阻塞队列,消费者不…

pygame学习(三)——支持多种类型的事件

大家好!我是码银🥰 欢迎关注🥰: CSDN:码银 公众号:码银学编程 实时事件循环 为了保证程序的持续刷新、保持打开的状态,我们会创建一个无限循环,通常使用的是while语句,w…

Apache POI 导出Excel报表

大家好我是苏麟 , 今天聊聊Apache POI . Apache POI 介绍 Apache POI 是一个处理Miscrosoft Office各种文件格式的开源项目。简单来说就是,我们可以使用 POI 在 Java 程序中对Miscrosoft Office各种文件进行读写操作。 一般情况下,POI 都是用于操作 E…

实时云渲染服务:流式传输 VR 和 AR 内容

想象一下无需专用的物理计算机,甚至无需实物连接,就能获得高质量的 AR/VR 体验是种什么样的体验? 过去,与 VR 交互需要专用的高端工作站,并且根据头显、壁挂式传感器和专用的物理空间。VR 中的复杂任务会突破传感器范…

路由器初始化配置、功能配置

实验环境 拓扑图 Ip规划表(各组使用自己的IP规划表) 部门 主机数量 网络地址 子网掩码 网关 可用ip Vlan 市场部 38 192.168.131.0 255.255.255.0 192.168.131.1 2-254 11 研发部 53 192.168.132.0 255.255.255.0 192.168.132.1 2-2…

appium之联动pycharm

前置条件: 1.java环境安装好了 2.android-sdk安装好(uiautomatorviewer 也可以把这个启动起来) 3.appium安装好 4.adb devices查看下设备是否连接 pycharm入门代码--固定写法 from appium import webdriver# 定义字典变量 desired_caps …

【机器学习300问】9、梯度下降是用来干嘛的?

当你和我一样对自己问出这个问题后,分析一下!其实我首先得知道梯度下降是什么,也就它的定义。其次我得了解它具体用在什么地方,也就是使用场景。最后才是这个问题,梯度下降有什么用?怎么用? 所以…

QT 原生布局和QML的区别

一、QML 与 Qt Quick的区别 1.1 从概念上区分 为了更精确地对两者进行说明,先看助手对 QML 的描述: QML is a user interface specification and programming language. QML 是一种用户界面规范和标记语言,允许开发人员和设计师创建高性能、流…

[go语言]输入输出

目录 知识结构 输入 1.Scan ​编辑 2.Scanf 3.Scanln 4.os.Stdin --标准输入,从键盘输入 输出 1.Print 2.Printf 3.Println 知识结构 输入 为了展示集中输入的区别,将直接进行代码演示。 三者区别的结论:Scanf格式化输入&#x…