为什么 ClickHouse 字典性能这么低?

Why is ClickHouse dictionary performance so low?

我在 PostgreSql 数据库中有一个带有产品名称的 table。总行数约为 30M。我在 ClickHouse 中有价格的历史记录。我想将名称与价格结合起来。 DDL 创建字典:

CREATE DICTIONARY products_dict
(
    product_id String,
    name String
)
PRIMARY KEY product_id
SOURCE(POSTGRESQL(
    ...
    query 'SELECT product_id, name FROM products'
))
LAYOUT(COMPLEX_KEY_HASHED())
LIFETIME(3600);

然后我有字典:

database:                    wdm
name:                        products_dict
uuid:                        1464ba09-990c-4e69-9464-ba09990c0e69
status:                      LOADED
origin:                      1464ba09-990c-4e69-9464-ba09990c0e69
type:                        ComplexKeyHashed
key.names:                   ['product_id']
key.types:                   ['String']
attribute.names:             ['name']
attribute.types:             ['String']
bytes_allocated:             4831830312
query_count:                 57912282
hit_rate:                    1
found_rate:                  1
element_count:               28956140
load_factor:                 0.4314801096916199
source:                      PostgreSQL: ...
lifetime_min:                0
lifetime_max:                3600
loading_start_time:          2022-01-17 03:53:21
last_successful_update_time: 2022-01-17 03:54:46
loading_duration:            84.79
last_exception:              
comment:                     

此外,我还有 table 这本词典:

-- auto-generated definition
create table products_dict
(
    product_id String,
    name       String
)
    engine = Dictionary;

当我查询这本词典时,大约需要 3 秒。

  1. 一个 id 与 WHERE IN
SELECT name FROM products_dict WHERE  product_id IN ('97646221')
1 row retrieved starting from 1 in 2 s 891 ms (execution: 2 s 841 ms, fetching: 50 ms)
  1. 501个产品无条件排序
SELECT t.*
     FROM products_dict t
     LIMIT 501
500 rows retrieved starting from 1 in 2 s 616 ms (execution: 2 s 601 ms, fetching: 15 ms)
  1. 加入
SELECT ppd.*, p.name
              FROM
                  (
                      SELECT
                          product_id,
                          price
                      FROM product_prices_daily
                      WHERE
                            product_id IN ('97646221','97646318','976464823','97647223','976472425','976474961','976476908')
                            AND day between '2022-01-13' and '2022-01-14'
                  ) as ppd
          LEFT JOIN products_dict as p ON p.product_id = ppd.product_id
4 rows retrieved starting from 1 in 6 s 984 ms (execution: 6 s 959 ms, fetching: 25 ms)

DBMS:ClickHouse(版本 21.12.3.32) 客户端:DataGrip 2021.3.2 服务器:128GB RAM,数十核,3TB SSD,无任何负载。 product_id 从 160 亿个 MergeTree table 中读取大约需要 100 毫秒。 我用 engine=dictionary 测试了手动创建的 table 并得到了相同的结果。 我不能使用平面布局,因为 product_id 是字符串。

Clickhouse-client 的另一个测试:

ch01 :) SELECT name FROM products_dict WHERE  product_id IN ('97646239');

SELECT name
FROM products_dict
WHERE product_id IN ('97646239')

Query id: d4f467c9-be0e-4619-841b-a76251d3e714

┌─name──┐
│ ...│
└───────┘

1 rows in set. Elapsed: 2.859 sec. Processed 28.96 million rows, 2.30 GB (10.13 million rows/s., 803.25 MB/s.)

怎么了?

尚未实现此类优化。

最初假设词典只能与 dictGet 函数一起使用。

Table 表示法是后来引入的。

内部字典是哈希表的集合——如果您的字典有 50 个属性,那么它将是 50 个哈希表。如果您通过键查找,这些哈希表非常快,但如果您需要查找下一个元素,则非常慢。

现在查询 SELECT name FROM products_dict WHERE product_id IN ('97646239') 以非常直接的方式执行,尽管它可以在后台转换为 dictGet