# 一个有趣的项目：chdb

不记得是从哪里知道这个项目，总之是躺在我的 logseq 的 todo 列表里面了，然后几天前(三个朋前)牛刀小试了一下，结果最近瞄到的 1brc 这个项目，正好拿来一下呗

## 1brc 简介

> 工程师贡纳尔·莫林在元旦发起一个[挑战(1](https://www.morling.dev/blog/one-billion-row-challenge/)BRC),挑战从 1 月 1 日持续到 1 月 31 日。
> 
> 如果你决定接受它，你的任务看似简单： [**编**](https://www.morling.dev/blog/one-billion-row-challenge/)**写一个 Java 程序，用于从文本文件中检索温度测量值并计算每个气象站的最小、平均值和最高温度。**  
> 只有一点需要注意：文件有 **1,000,000,000 行**！（1 billion， 10亿行)。

## chdb 是什么？

> chDB 是一个由 ClickHouse 驱动的嵌入式 SQL OLAP 引擎

对于一个轻量级萌新+爱好者，这东西一出场，便成功吸引了我的注意力，但还没想好用它来做点啥

## 安装 clickhouse vs chDB

这是一个极易形成对比的地方，如果你想要安装一个完整的clickhouse，

```bash
sudo apt-get install -y apt-transport-https ca-certificates dirmngr
sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv 8919F6BD2B48D754

echo "deb https://packages.clickhouse.com/deb stable main" | sudo tee \
    /etc/apt/sources.list.d/clickhouse.list
sudo apt-get update

sudo apt-get install -y clickhouse-server clickhouse-client

sudo service clickhouse-server start
clickhouse-client # or "clickhouse-client --password" if you've set up a password.
```

呃，还是挺复杂的，当然，clickhouse 也有 local 模式：

> ClickHouse-local 模式可以让用户能够对本地文件执行快速处理，而无需部署和配置 ClickHouse 服务器。ClickHouse-local 使用与 ClickHouse Server 相同的核心，因此它支持大多数功能以及相同的格式和表引擎。

安装也比较简单：

```bash
# curl -O 'https://builds.clickhouse.com/master/macos/clickhouse' && chmod a+x ./clickhouse
# ./clickhouse -q "select version()"
24.3.1.1526
```

然而，使用chdb，只需如此简单与丝滑：

```bash
# pip install chdb
# python -m chdb "select version()"
"23.10.1.1"
```

## 应用

虽然知道把 clickhouse 做成 chdb 这种嵌入式形态很厉害，但总需要一个场景去实践，用 1brc 试水再合适不过了

环境：

* Python 3.12.0
    
* OSX 14.5
    
* 内存 32 GB 2667 MHz DDR4
    
* 处理器 2.6 GHz 六核Intel Core i7
    

### chdb 1.4.1

```python
import time
import chdb

query_sql = """
-- Change to your number of physical CPU threads for the best performance.
SET max_insert_threads = 28;
SET max_threads = 28;
SET format_csv_delimiter = ';';

SELECT
    concat('{', arrayStringConcat(groupArray(formatted_result), ', '), '}') AS final_output
FROM (
    SELECT
        format('{}={}/{}/{}', location, toDecimalString(min(temperature), 1), toDecimalString(avg(temperature), 1), toDecimalString(max(temperature), 1)) AS formatted_result
    FROM file('measurements.txt', 'CSV', 'location String, temperature Float32')
    GROUP BY location
    ORDER BY location
)
"""
start = time.time()
res = chdb.query(query_sql)
elapsed = time.time() - start
# 连续运行三次的耗时
# Query took 59.594 seconds
# Query took 39.029 seconds
# Query took 36.696 seconds
print(f'Query took {elapsed:.3f} seconds')
print(f'{res}')
```

## duckdb 1.0.0

最近 duckdb 发布了 1.0.0 这个版本，来看下表现如何

```python
import duckdb
import time
query_sql = '''
-- Load the data
CREATE OR REPLACE TABLE measurements AS
        SELECT * FROM READ_CSV('measurements.txt', header=false, columns= {'station_name':'VARCHAR','measurement':'double'}, delim=';');

-- Run calculations
WITH src AS (SELECT station_name,
                    MIN(measurement) AS min_measurement,
                    CAST(AVG(measurement) AS DECIMAL(8,1)) AS mean_measurement,
                    MAX(measurement) AS max_measurement
            FROM measurements
            GROUP BY station_name)
    SELECT '{' ||
            ARRAY_TO_STRING(LIST_SORT(LIST(station_name || '=' || CONCAT_WS('/',min_measurement, mean_measurement, max_measurement))),', ') ||
            '}' AS "1BRC"
    FROM src;
'''
start = time.time()
res = duckdb.sql(query_sql)
elapsed = time.time() - start
# 连续运行三次的耗时
# Query took 78.377 seconds
# Query took 72.713 seconds
# Query took 63.916 seconds
print(f'Query took {elapsed:.3f} seconds')
print(f'{res}')
```

单从时间上看，chdb 平均用时要更少，可以说非常强大了。

## go 版本

第一次看到 1brc 还是今年3月份在 **InfoQ** 的微信公众号上到到的，看到 Ben Hoyt 大佬孜孜不倦的优化，有点被触动到了。虽然这篇文件拖了这么久，但根据大佬最新的代码和算法，在我本地跑了一下，三次耗时如下所示：

* 5.659881206s
    
* 6.197552364s
    
* 4.799696744s
    

不是最强的，但确实非常优秀就是了。毕竟，最简单的实现耗时是：2m30.835476239s

## 总结

不考虑 SQL 优化的前提下，个人理解 chdb 与 duckdb 提供的是相对通用的数据分析能力，它们短小而精悍，提供了性能的下限；而使用编程语言，针对特定问题的不断优化，压榨，获得的性能收益是异常惊人的。作为一个算法难民，是不是应该重新捡起算法了

## 番外插曲

可能是因为太长时间没写 python 了，当我把 chdb 的 1brc 代码示例保存为 chdb.py 时，遇到以下错误:

```bash
AttributeError: partially initialized module 'chdb' has no attribute 'query' (most likely due to a circular import)
```

初，感觉很奇怪，百思不得其解，还排查了好一会，最后才发现是文件命令问题，重新学习一下 python 的包导入

> **Python解释器在导入模块时，首先会在当前目录查找文件名匹配的模块。如果找到了同名文件，它会试图从这个文件中加载模块，而不是从已安装的包中加载。因此，当你在当前目录下运行** `import chdb` **时，Python会尝试导入你自己的** [`chdb.py`](http://chdb.py) **文件，而不是你安装的** `chdb` **包，这就导致了循环导入的问题。**

还是要多写代码啊

---

参考：

* [4 秒处理 10 亿行数据！ Go 语言的 9 大代码方案，一个比一个快](https://mp.weixin.qq.com/s/iylZAKZfxLL6SYruww_8zA)
    
* [**十亿行的挑战**](https://colobu.com/2024/01/30/one-billion-row-challenge/)
    
* [chdb](https://github.com/chdb-io/chdb/blob/main/README-zh.md)
    
* [1brc](https://github.com/gunnarmorling/1brc)
    
* [go-1brc](https://github.com/benhoyt/go-1brc)zar
    
* [4 秒处理 10 亿行数据！ Go 语言的 9 大代码方案，一个比一个快](https://mp.weixin.qq.com/s/iylZAKZfxLL6SYruww_8zA)
    
* [https://seatunnel.apache.org/zh-CN/blog/2022/05/10/ClickHouse/](https://seatunnel.apache.org/zh-CN/blog/2022/05/10/ClickHouse/)
