博客 / 工程

ClickHouse 24.4 版本发布

author avatar
ClickHouse 团队
2024年5月5日 - 19 分钟阅读

又一个月过去了,这意味着又到了发布新版本的时候!

ClickHouse 24.3 版本包含 13 个新功能 🎁 16 项性能优化 🛷 65 个 bug 修复 🐛

新贡献者

与往常一样,我们特别欢迎 24.4 版本的所有新贡献者!ClickHouse 的受欢迎程度,很大程度上归功于社区的贡献。看到社区的壮大总是令人感到荣幸。

以下是新贡献者的名字

Alexey Katsman, Anita Hammer, Arnaud Rocher, Chandre Van Der Westhuizen, Eduard Karacharov, Eliot Hautefeuille, Igor Markelov, Ilya Andreev, Jhonso7393, Joseph Redfern, Josh Rodriguez, Kirill, KrJin, Maciej Bak, Murat Khairulin, Paweł Kudzia, Tristan, dilet6298, loselarry

提示:如果您好奇我们是如何生成此列表的… 点击这里

您还可以查看演示文稿的幻灯片

递归 CTE

由 Maksim Kita 贡献

SQL:1999 引入了用于分层查询的递归公用表表达式 (CTE),使 SQL 成为图灵完备的编程语言。

到目前为止,ClickHouse 通过使用分层字典来支持分层查询。随着我们新的查询分析和优化基础设施,现在默认启用,我们终于具备了引入期待已久的强大功能(如递归 CTE)的一切条件。

ClickHouse 递归 CTE 具有标准的 SQL:1999 语法,并且通过了所有针对递归 CTE 的 PostgreSQL 测试。此外,ClickHouse 现在比 PostgreSQL 更好地支持递归 CTE。在 CTE 的 UNION ALL 子句的底部,可以指定多个(任意复杂的)查询,CTE 基表可以被多次引用等等。

递归 CTE 可以优雅而简单地解决分层问题。例如,它们可以轻松回答分层数据模型(例如树和图)的可达性问题。

具体而言,递归 CTE 可以计算关系的传递闭包。以伦敦地铁的地铁连接作为二元关系示例,您可以想象所有直接连接的地铁站的集合:Oxford Circus -> Bond Street, Bond Street -> Marble Arch, Marble Arch -> Lancaster Gate,等等。这些连接的传递闭包包括这些车站之间所有可能的连接,例如 Oxford Circus -> Lancaster Gate, Oxford Circus -> Marble Arch,等等。

为了演示这一点,我们使用了一个改编自数据集的版本,该数据集模拟了所有伦敦地铁连接,其中每个条目代表两个直接连接的车站。然后,我们可以使用递归 CTE 来回答这样的问题

当从Oxford Circus 站中央线出发时,我们最多可以到达哪些车站,且最多停靠五站?

我们用中央线车站地图的截图来可视化这一点

unnamed2.png

我们创建一个 ClickHouse 表来存储伦敦地铁连接数据集

CREATE OR REPLACE TABLE Connections (
    Station_1 String,
    Station_2 String,
    Line String,
    PRIMARY KEY(Line, Station_1, Station_2)
);

细心的读者会注意到,我们在上面的 DDL 语句中没有指定表引擎(自 ClickHouse 24.3 起这是可能的),并且在列定义中使用了 PRIMARY KEY 语法(自 ClickHouse 23.7 起这是可能的)。这样,不仅递归 CTE,而且我们的 ClickHouse 表 DDL 语法也符合 SQL 标准。

通过利用url 表函数自动模式推断,我们将数据集直接加载到我们的表中

INSERT INTO Connections
SELECT * FROM url('https://datasets-documentation.s3.eu-west-3.amazonaws.com/london_underground/london_connections.csv')

这就是加载的数据的样子

SELECT
    *
FROM Connections
WHERE Line = 'Central Line'
ORDER BY Station_1, Station_2
LIMIT 10;

    ┌─Station_1──────┬─Station_2────────┬─Line─────────┐
 1. │ Bank           │ Liverpool Street │ Central Line │
 2. │ Bank           │ St. Paul's       │ Central Line │
 3. │ Barkingside    │ Fairlop          │ Central Line │
 4. │ Barkingside    │ Newbury Park     │ Central Line │
 5. │ Bethnal Green  │ Liverpool Street │ Central Line │
 6. │ Bethnal Green  │ Mile End         │ Central Line │
 7. │ Bond Street    │ Marble Arch      │ Central Line │
 8. │ Bond Street    │ Oxford Circus    │ Central Line │
 9. │ Buckhurst Hill │ Loughton         │ Central Line │
10. │ Buckhurst Hill │ Woodford         │ Central Line │
    └────────────────┴──────────────────┴──────────────┘

现在,我们使用递归 CTE 来回答上述问题

WITH RECURSIVE Reachable_Stations AS
(
    SELECT Station_1, Station_2, Line, 1 AS stops
    FROM Connections
    WHERE Line = 'Central Line'
      AND Station_1 = 'Oxford Circus'
    UNION ALL
    SELECT rs.Station_1, c.Station_2, c.Line, rs.stops + 1 AS stops
    FROM Reachable_Stations AS rs, Connections AS c
    WHERE rs.Line = c.Line
      AND rs.Station_2 = c.Station_1
      AND rs.stops < 5
)
SELECT DISTINCT (Station_1, Station_2, stops) AS connections
FROM Reachable_Stations
ORDER BY stops ASC;

这是结果

    ┌─connections────────────────────────────────┐
 1. │ ('Oxford Circus','Bond Street',1)          │
 2. │ ('Oxford Circus','Tottenham Court Road',1) │
 3. │ ('Oxford Circus','Marble Arch',2)          │
 4. │ ('Oxford Circus','Oxford Circus',2)        │
 5. │ ('Oxford Circus','Holborn',2)              │
 6. │ ('Oxford Circus','Bond Street',3)          │
 7. │ ('Oxford Circus','Lancaster Gate',3)       │
 8. │ ('Oxford Circus','Tottenham Court Road',3) │
 9. │ ('Oxford Circus','Chancery Lane',3)        │
10. │ ('Oxford Circus','Marble Arch',4)          │
11. │ ('Oxford Circus','Oxford Circus',4)        │
12. │ ('Oxford Circus','Queensway',4)            │
13. │ ('Oxford Circus','Holborn',4)              │
14. │ ('Oxford Circus','St. Paul\'s',4)          │
15. │ ('Oxford Circus','Bond Street',5)          │
16. │ ('Oxford Circus','Lancaster Gate',5)       │
17. │ ('Oxford Circus','Tottenham Court Road',5) │
18. │ ('Oxford Circus','Notting Hill Gate',5)    │
19. │ ('Oxford Circus','Chancery Lane',5)        │
20. │ ('Oxford Circus','Bank',5)                 │
    └────────────────────────────────────────────┘

递归 CTE 具有简单的基于迭代的执行逻辑,其行为类似于递归的自连接-...-自连接,一旦找不到新的连接伙伴或满足中止条件,自连接就会停止。为此,我们上面的 CTE 首先执行 UNION ALL 子句的顶部,查询我们的 Connections 表,以获取所有直接连接到 中央线Oxford Circus 站的车站。这将返回一个绑定到 Reachable_Stations 标识符的表,如下所示

 Initial Reachable_Stations table content
 ┌─Station_1─────┬─Station_2────────────┐
 │ Oxford Circus │ Bond Street          │
 │ Oxford Circus │ Tottenham Court Road │
 └───────────────┴──────────────────────┘

从现在开始,只有 CTE 的 UNION ALL 子句的底部部分将被执行(递归地)

Reachable_StationsConnections 表连接,以在 Connections 表中查找以下连接伙伴,这些连接伙伴的 Station_1 值与 Reachable_StationsStation_2 值匹配

Connections table join partners

┌─Station_1────────────┬─Station_2─────┐
│ Bond Street          │ Marble Arch   │
│ Bond Street          │ Oxford Circus │
│ Tottenham Court Road │ Holborn       │
│ Tottenham Court Road │ Oxford Circus │
└──────────────────────┴───────────────┘

通过 UNION ALL 子句,这些连接伙伴被添加到 Reachable_Stations 表中(其中 Station_1 列被 Oxford Circus 替换),我们的递归 CTE 的第一次迭代完成。在下一次迭代中,通过执行 CTE 的 UNION ALL 子句的底部部分,Reachable_Stations 再次与 Connections 表连接,以识别(并添加到 Reachable_StationsConnections 表中所有新的连接伙伴,这些连接伙伴的 Station_1 值与 Reachable_StationsStation_2 值匹配。这些迭代持续进行,直到找不到新的连接伙伴或满足停止条件。在上面的查询中,我们使用 stops 计数器来中止 CTE 的迭代循环,当我们从起始站到达指定的允许停靠站数时。

请注意,结果将 Oxford Circus 列为从 Oxford Circus 可到达的车站,停靠 2 站和 4 站。这在理论上是正确的,但不是很实用,并且是由于我们的查询不考虑任何方向或循环而引起的。我们将其作为读者的一个有趣的练习。

作为一个奖励问题,我们对从 中央线Oxford Circus 站到达 Stratford 站需要多少站感兴趣。我们再次使用 中央线 地图来可视化这一点

 (5).png

为此,我们只需要修改递归 CTE 的中止条件(一旦将以 Stratford 作为目标站点的连接伙伴添加到 CTE 表中,就停止 CTE 的连接迭代)

WITH RECURSIVE Reachable_Stations AS
(
    SELECT Station_1, Station_2, Line, 1 AS stops
    FROM Connections
    WHERE Line = 'Central Line'
      AND Station_1 = 'Oxford Circus'
  UNION ALL
    SELECT rs.Station_1 c.Station_2, c.Line, rs.stops + 1 AS stops
    FROM Reachable_Stations AS rs, Connections AS c
    WHERE rs.Line = c.Line
      AND rs.Station_2 = c.Station_1
      AND 'Stratford' NOT IN (SELECT Station_2 FROM Reachable_Stations)
)
SELECT max(stops) as stops
FROM Reachable_Stations;

结果显示需要 9 站,这与上面的 中央线 地图计划相符

   ┌─stops─┐
1. │     9 │
   └───────┘

递归 CTE 可以轻松回答有关此数据集的更有趣的问题。例如,可以使用数据集原始版本中的相对连接时间来发现从 Oxford Circus 站到 Heathrow Airport 站的最快(以及跨地铁线路)连接。请继续关注后续文章中对此的解决方案。

QUALIFY

由 Maksim Kita 贡献

此版本中添加的另一个功能是 QUALIFY 子句,它允许我们根据窗口函数的值进行过滤。

我们将借助 Window Functions - Rankings 示例来了解如何使用它。该数据集包含假设的足球运动员及其薪水。我们可以像这样将其导入到 ClickHouse 中

CREATE TABLE salaries ORDER BY team AS
FROM url('https://raw.githubusercontent.com/ClickHouse/examples/main/LearnClickHouseWithMark/WindowFunctions-Aggregation/data/salaries.csv')
SELECT * EXCEPT (weeklySalary), weeklySalary AS salary
SETTINGS schema_inference_make_columns_nullable=0;

让我们快速看一下 salaries 表中的数据

SELECT * FROM salaries LIMIT 5;
   ┌─team──────────────┬─player───────────┬─position─┬─salary─┐
1. │ Aaronbury Seekers │ David Morton     │ D        │  63014 │
2. │ Aaronbury Seekers │ Edwin Houston    │ D        │  51751 │
3. │ Aaronbury Seekers │ Stephen Griffith │ M        │ 181825 │
4. │ Aaronbury Seekers │ Douglas Clay     │ F        │  73436 │
5. │ Aaronbury Seekers │ Joel Mendoza     │ D        │ 257848 │
   └───────────────────┴──────────────────┴──────────┴────────┘

接下来,让我们计算每个球员按位置的薪资排名。也就是说,他们相对于在同一位置踢球的人的薪酬是多少?

SELECT player, team, position AS pos, salary,
       rank() OVER (PARTITION BY position ORDER BY salary DESC) AS posRank
FROM salaries
ORDER BY salary DESC
LIMIT 5
   ┌─player──────────┬─team────────────────────┬─pos─┬─salary─┬─posRank─┐
1. │ Robert Griffin  │ North Pamela Trojans    │ GK  │ 399999 │       1 │
2. │ Scott Chavez    │ Maryhaven Generals      │ M   │ 399998 │       1 │
3. │ Dan Conner      │ Michaelborough Rogues   │ M   │ 399998 │       1 │
4. │ Nathan Thompson │ Jimmyville Legionnaires │ D   │ 399998 │       1 │
5. │ Benjamin Cline  │ Stephaniemouth Trojans  │ D   │ 399998 │       1 │
   └─────────────────┴─────────────────────────┴─────┴────────┴─────────┘

假设我们想过滤 posRank 以仅返回每个位置薪酬最高的前 3 名球员。我们可能会尝试添加一个 WHERE 子句来执行此操作

SELECT player, team, position AS pos, salary,
       rank() OVER (PARTITION BY position ORDER BY salary DESC) AS posRank
FROM salaries
WHERE posRank <= 3
ORDER BY salary DESC
LIMIT 5
Received exception:
Code: 184. DB::Exception: Window function rank() OVER (PARTITION BY position ORDER BY salary DESC) AS posRank is found in WHERE in query. (ILLEGAL_AGGREGATION)

我们不能这样做,因为 WHERE 子句在窗口函数被评估之前运行。在 24.4 版本之前,我们可以通过引入 CTE 来解决这个问题

WITH salaryRankings AS
    (
        SELECT player, 
               if(
                 length(team) <=25, 
                 team, 
                 concat(substring(team, 5), 1, '...')
               ) AS team, 
               position AS pos, salary,
               rank() OVER (
                 PARTITION BY position 
                 ORDER BY salary DESC
               ) AS posRank
        FROM salaries
        ORDER BY salary DESC
    )
SELECT *
FROM salaryRankings
WHERE posRank <= 3
    ┌─player────────────┬─team────────────────────┬─pos─┬─salary─┬─posRank─┐
 1. │ Robert Griffin    │ North Pamela Trojans    │ GK  │ 399999 │       1 │
 2. │ Scott Chavez      │ Maryhaven Generals      │ M   │ 399998 │       1 │
 3. │ Dan Conner        │ Michaelborough Rogue... │ M   │ 399998 │       1 │
 4. │ Benjamin Cline    │ Stephaniemouth Troja... │ D   │ 399998 │       1 │
 5. │ Nathan Thompson   │ Jimmyville Legionnai... │ D   │ 399998 │       1 │
 6. │ William Rubio     │ Nobleview Sages         │ M   │ 399997 │       3 │
 7. │ Juan Bird         │ North Krystal Knight... │ GK  │ 399986 │       2 │
 8. │ John Lewis        │ Andreaberg Necromanc... │ D   │ 399985 │       3 │
 9. │ Michael Holloway  │ Alyssaborough Sages     │ GK  │ 399984 │       3 │
10. │ Larry Buchanan    │ Eddieshire Discovere... │ F   │ 399973 │       1 │
11. │ Alexis Valenzuela │ Aaronport Crusaders     │ F   │ 399972 │       2 │
12. │ Mark Villegas     │ East Michaelborough ... │ F   │ 399972 │       2 │
    └───────────────────┴─────────────────────────┴─────┴────────┴─────────┘

这个查询有效,但是非常笨拙。现在我们有了 QUALIFY 子句,我们可以过滤数据,而无需引入 CTE,如下所示

SELECT player, team, position AS pos, salary,
       rank() OVER (PARTITION BY position ORDER BY salary DESC) AS posRank
FROM salaries
QUALIFY posRank <= 3
ORDER BY salary DESC;

我们将获得与之前相同的结果。

连接性能改进

由 Maksim Kita 贡献

对于非常特定的 JOIN 用例,也有一些性能改进。

第一个是在分析器确定何时可以将过滤条件应用于 JOIN 的两侧时,更好的谓词下推。

让我们看一个使用 OpenSky 数据集的例子,其中包含 2019-2021 年的空中交通数据。我们想要获得一份经过 旧金山 的十个航班的列表,我们可以使用以下查询来完成

SELECT
    l.origin,
    r.destination AS dest,
    firstseen,
    lastseen
FROM opensky AS l
INNER JOIN opensky AS r ON l.destination = r.origin
WHERE notEmpty(l.origin) AND notEmpty(r.destination) AND (r.origin = 'KSFO')
LIMIT 10
SETTINGS optimize_move_to_prewhere = 0

我们禁用 optimize_move_to_prewhere,以便 ClickHouse 不执行另一个优化,这将阻止我们看到 JOIN 改进的好处。如果我们在 24.3 上运行此查询,我们将看到以下输出

    ┌─origin─┬─dest─┬───────────firstseen─┬────────────lastseen─┐
 1.00WA   │ 00CL │ 2019-10-14 21:03:192019-10-14 22:42:012.00WA   │ ZGGG │ 2019-10-14 21:03:192019-10-14 22:42:013.00WA   │ ZGGG │ 2019-10-14 21:03:192019-10-14 22:42:014.00WA   │ ZGGG │ 2019-10-14 21:03:192019-10-14 22:42:015.00WA   │ ZGGG │ 2019-10-14 21:03:192019-10-14 22:42:016.00WA   │ ZGGG │ 2019-10-14 21:03:192019-10-14 22:42:017.00WA   │ ZGGG │ 2019-10-14 21:03:192019-10-14 22:42:018.00WA   │ YSSY │ 2019-10-14 21:03:192019-10-14 22:42:019.00WA   │ YSSY │ 2019-10-14 21:03:192019-10-14 22:42:0110.00WA   │ YSSY │ 2019-10-14 21:03:192019-10-14 22:42:01 │
    └────────┴──────┴─────────────────────┴─────────────────────┘

10 rows in set. Elapsed: 0.656 sec. Processed 15.59 million rows, 451.90 MB (23.75 million rows/s., 688.34 MB/s.)
Peak memory usage: 62.79 MiB.

让我们看一下 24.4

    ┌─origin─┬─dest─┬───────────firstseen─┬────────────lastseen─┐
 1.00WA   │ 00CL │ 2019-10-14 21:03:192019-10-14 22:42:012.00WA   │ ZGGG │ 2019-10-14 21:03:192019-10-14 22:42:013.00WA   │ ZGGG │ 2019-10-14 21:03:192019-10-14 22:42:014.00WA   │ ZGGG │ 2019-10-14 21:03:192019-10-14 22:42:015.00WA   │ ZGGG │ 2019-10-14 21:03:192019-10-14 22:42:016.00WA   │ ZGGG │ 2019-10-14 21:03:192019-10-14 22:42:017.00WA   │ ZGGG │ 2019-10-14 21:03:192019-10-14 22:42:018.00WA   │ YSSY │ 2019-10-14 21:03:192019-10-14 22:42:019.00WA   │ YSSY │ 2019-10-14 21:03:192019-10-14 22:42:0110.00WA   │ YSSY │ 2019-10-14 21:03:192019-10-14 22:42:01 │
    └────────┴──────┴─────────────────────┴─────────────────────┘

10 rows in set. Elapsed: 0.079 sec.

因此,速度快了大约 8 倍。如果我们通过 SELECT * 返回所有列,则 24.3 中此查询所用的时间将超过 4 秒,而在 24.4 中为 0.4 秒,提高了 10 倍。

让我们看看我们是否可以理解为什么它更快。感兴趣的两行是这些

INNER JOIN opensky AS r ON l.destination = r.origin
WHERE notEmpty(l.origin) AND notEmpty(r.destination) AND (r.origin = 'KSFO')

我们 WHERE 子句的最后一个谓词是 r.origin = 'KSFO'。在前一行中,我们说过我们只想在 l.destination = r.origin 时执行连接,这意味着 l.destination = 'KSFO' 也是如此。24.4 中的分析器知道这一点,因此可以更早地过滤掉大量行。

换句话说,我们的 WHERE 子句在 24.4 中实际上看起来像这样

INNER JOIN opensky AS r ON l.destination = r.origin
WHERE notEmpty(l.origin) AND notEmpty(r.destination) 
AND (r.origin = 'KSFO') AND (l.destination = 'KSFO')

第二个改进是,如果 JOIN 之后的谓词过滤掉任何未连接的行,则分析器现在将自动将 OUTER JOIN 转换为 INNER JOIN

例如,假设我们最初编写了一个查询来查找 旧金山 和 纽约 之间的航班,同时捕获直飞航班和有中途停留的航班。

SELECT
    l.origin,
    l.destination,
    r.destination,
    registration,
    l.callsign,
    r.callsign
FROM opensky AS l
LEFT JOIN opensky AS r ON l.destination = r.origin
WHERE notEmpty(l.destination) 
AND (l.origin = 'KSFO') 
AND (r.destination = 'KJFK') 
LIMIT 10

稍后我们添加了一个额外的过滤器,该过滤器仅返回 r.callsign = 'AAL1424' 的行。

SELECT
    l.origin,
    l.destination AS leftDest,
    r.destination AS rightDest,
    registration AS reg,
    l.callsign,
    r.callsign
FROM opensky AS l
LEFT JOIN opensky AS r ON l.destination = r.origin
WHERE notEmpty(l.destination) 
AND (l.origin = 'KSFO') 
AND (r.destination = 'KJFK') 
AND (r.callsign = 'AAL1424')
LIMIT 10
SETTINGS optimize_move_to_prewhere = 0

由于我们现在要求连接右侧的 callsign 列具有值,因此可以将 LEFT JOIN 转换为 INNER JOIN。让我们检查一下 24.3 和 24.4 中的查询性能。

24.3

    ┌─origin─┬─leftDest─┬─rightDest─┬─reg────┬─callsign─┬─r.callsign─┐
 1. │ KSFO   │ 01FA     │ KJFK      │ N12221 │ UAL423   │ AAL1424    │
 2. │ KSFO   │ 01FA     │ KJFK      │ N12221 │ UAL423   │ AAL1424    │
 3. │ KSFO   │ 01FA     │ KJFK      │ N12221 │ UAL423   │ AAL1424    │
 4. │ KSFO   │ 01FA     │ KJFK      │ N12221 │ UAL423   │ AAL1424    │
 5. │ KSFO   │ 01FA     │ KJFK      │ N12221 │ UAL423   │ AAL1424    │
 6. │ KSFO   │ 01FA     │ KJFK      │ N87527 │ UAL423   │ AAL1424    │
 7. │ KSFO   │ 01FA     │ KJFK      │ N87527 │ UAL423   │ AAL1424    │
 8. │ KSFO   │ 01FA     │ KJFK      │ N87527 │ UAL423   │ AAL1424    │
 9. │ KSFO   │ 01FA     │ KJFK      │ N87527 │ UAL423   │ AAL1424    │
10. │ KSFO   │ 01FA     │ KJFK      │ N87527 │ UAL423   │ AAL1424    │
    └────────┴──────────┴───────────┴────────┴──────────┴────────────┘

10 rows in set. Elapsed: 1.937 sec. Processed 63.98 million rows, 2.52 GB (33.03 million rows/s., 1.30 GB/s.)
Peak memory usage: 2.84 GiB.

24.4

    ┌─origin─┬─leftDest─┬─rightDest─┬─reg────┬─callsign─┬─r.callsign─┐
 1. │ KSFO   │ 01FA     │ KJFK      │ N12221 │ UAL423   │ AAL1424    │
 2. │ KSFO   │ 01FA     │ KJFK      │ N12221 │ UAL423   │ AAL1424    │
 3. │ KSFO   │ 01FA     │ KJFK      │ N12221 │ UAL423   │ AAL1424    │
 4. │ KSFO   │ 01FA     │ KJFK      │ N12221 │ UAL423   │ AAL1424    │
 5. │ KSFO   │ 01FA     │ KJFK      │ N12221 │ UAL423   │ AAL1424    │
 6. │ KSFO   │ 01FA     │ KJFK      │ N87527 │ UAL423   │ AAL1424    │
 7. │ KSFO   │ 01FA     │ KJFK      │ N87527 │ UAL423   │ AAL1424    │
 8. │ KSFO   │ 01FA     │ KJFK      │ N87527 │ UAL423   │ AAL1424    │
 9. │ KSFO   │ 01FA     │ KJFK      │ N87527 │ UAL423   │ AAL1424    │
10. │ KSFO   │ 01FA     │ KJFK      │ N87527 │ UAL423   │ AAL1424    │
    └────────┴──────────┴───────────┴────────┴──────────┴────────────┘

10 rows in set. Elapsed: 0.762 sec. Processed 23.22 million rows, 939.75 MB (30.47 million rows/s., 1.23 GB/s.)
Peak memory usage: 9.00 MiB.

在 24.4 中,速度快了不到三倍。

如果您想了解有关 JOIN 性能改进是如何实现的更多信息,请阅读 Maksim Kita 的博客文章,其中解释了一切。

这就是 24.4 版本的所有内容。我们希望您能加入我们 5 月 30 日的 24.5 版本发布电话会议。请务必 注册,以便您获得 Zoom 网络研讨会的所有详细信息。

分享这篇文章

订阅我们的新闻通讯

随时了解功能发布、产品路线图、支持和云服务!
正在加载表单...
关注我们
X imageSlack imageGitHub image
Telegram imageMeetup imageRss image
©2025ClickHouse, Inc. 总部位于加利福尼亚州湾区和荷兰阿姆斯特丹。