ClickHouse概述-安装部署、使用、原理了解
- 掘金:一头小阿牛
1.概述
什么是 ClickHouse?
ClickHouse是一个高性能的、面向列的 SQL 数据库管理系统 (DBMS),适用于在线分析处理 (OLAP)。它可作为 开源软件 和 云服务 提供。
什么是OLAP?
OLAP 代表联机分析处理(Online Analytical Processing)。这是一个广泛的术语,可以从技术和商业两个方面进行理解。在最高层次,你可以反向阅读这些词:
处理 一些源数据被处理…
分析 …以生成一些分析报告和见解…
在线 …实时进行。
什么是分析?
分析,也称为 OLAP(在线分析处理),是指在大规模数据集上执行复杂计算(例如聚合、字符串处理、算术)的 SQL 查询。
与只读取和写入每个查询仅几行的事务查询(或 OLTP,在线事务处理)不同,分析查询通常处理数十亿或数万亿行数据。
在许多用例中,分析查询必须是“实时的”,即结果必须在一秒钟以内返回。
数据复制和完整性
ClickHouse 使用异步多主复制方案,以确保数据在多个节点上冗余存储。在写入任何可用副本后,所有剩余副本在后台检索其副本。系统在不同副本上维护相同的数据。在大多数故障后,恢复是自动进行的,复杂情况时则为半自动。
基于角色的访问控制
ClickHouse 使用 SQL 查询实现用户帐户管理,并允许进行基于角色的访问控制配置,类似于 ANSI SQL 标准和流行的关系数据库管理系统中找到的配置。
SQL 支持
ClickHouse 支持基于 SQL 的 声明性查询语言,在许多情况下与 ANSI SQL 标准相同。支持的查询子句包括 GROUP BY、ORDER BY、FROM 中的子查询、JOIN 子句、IN 操作符、窗口函数 和标量子查询。
近似计算
ClickHouse 提供了一些方法来在性能和准确性之间进行权衡。例如,它的一些聚合函数大致计算不同值的数量、中位数和分位数。此外,可以在数据样本上运行查询以快速计算近似结果。最后,可以使用有限的键运行聚合,而不是对所有键进行聚合。根据键的分布情况,这可以提供一个合理准确的结果,使用的资源远少于精确计算。
自适应连接算法
ClickHouse 自适应选择连接算法,它首先使用快速哈希连接,如果有多个大型表,则退回到合并连接。
2.安装
在基于rpm的发行版上安装ClickHouse
建议使用官方预编译的 rpm 软件包来安装 CentOS、RedHat 以及其他所有基于rpm的 Linux 发行版。
设置RPM仓库
通过运行以下命令添加官方仓库:
sudo yum install -y yum-utils
sudo yum-config-manager --add-repo https://packages.clickhouse.com/rpm/clickhouse.repo
对于使用 zypper 包管理器的系统(如openSUSE,SLES),请运行:
sudo zypper addrepo -r https://packages.clickhouse.com/rpm/clickhouse.repo -g
sudo zypper --gpg-auto-import-keys refresh clickhouse-stable
在下面的步骤中,yum install 可以根据您使用的包管理器替换为 zypper install。
安装clickhouse服务端和客户端
要安装ClickHouse,运行以下命令:
sudo yum install -y clickhouse-server clickhouse-client
您可以将 stable 替换为 lts,以根据您的需求使用不同的 发布类型。 您可以从 packages.clickhouse.com/rpm 手动下载和安装软件包。 要指定特定版本,请在软件包名称的末尾添加 -$version,例如:
sudo yum install clickhouse-server-22.8.7.34
启动ClickHouse服务器 要启动ClickHouse服务器,请运行:
sudo systemctl enable clickhouse-server
sudo systemctl start clickhouse-server
sudo systemctl status clickhouse-server
要启动ClickHouse客户端,请运行:
clickhouse-client
如果您为服务器设置了密码,则需要运行:
clickhouse-client --password
clickhouse-client --password
安装独立的ClickHouse Keeper
提示 在生产环境中,我们强烈建议在专用节点上运行ClickHouse Keeper。 在测试环境中,如果您决定在同一服务器上运行ClickHouse Server和ClickHouse Keeper,则无需安装ClickHouse Keeper,因为它已包含在ClickHouse服务器中。
要在独立的ClickHouse Keeper服务器上安装 clickhouse-keeper,请运行:
sudo yum install -y clickhouse-keeper
启用并启动ClickHouse Keeper
sudo systemctl enable clickhouse-keeper
sudo systemctl start clickhouse-keeper
sudo systemctl status clickhouse-keeper
检验安装,查看当前数据库
show databases;
安装后的默认目录在/etc下
3. 使用
查看clickhouse的连接配置
sudo grep -A 10 '<listen_host>' /etc/clickhouse-server/config.xml
可以看到ClickHouse 默认没有启用任何监听地址(所有相关配置都被注释了
需要取消注释并修改监听配置:
- 编辑配置文件
sudo vim /etc/clickhouse-server/config.xml
2. 找到并修改以下部分(任选一种方案): 方案A:仅允许本地连接(最安全)
<listen_host>127.0.0.1</listen_host>
方案B:允许所有IPv4网络连接
<listen_host>0.0.0.0</listen_host>
方案C:允许所有IPv4和IPv6连接
<listen_host>::</listen_host>
<listen_host>0.0.0.0</listen_host>
取消注释,
修改配置,但是这里有一个问题,感兴趣的同学可以详细了解一下,快速解决可以参考这篇文章:
ClickHouse报错:Code: 210. DB::NetException: Connection refused (localhost:9000)
把::注释掉
- 保存后重启服务
sudo systemctl restart clickhouse-server
可视化工具连接

常用命令操作使用
数据类型
一、基础数据类型
- 整数类型 类型 范围 存储大小 示例
Int8 -128 到 127 1字节 Int8(10) Int16 -32768 到 32767 2字节 Int16(1000) Int32 -2³¹ 到 2³¹-1 4字节 Int32(10000) Int64 -2⁶³ 到 2⁶³-1 8字节 Int64(1e18) UInt8 0 到 255 1字节 UInt8(255) UInt16 0 到 65535 2字节 UInt16(5000) UInt32 0 到 2³²-1 4字节 UInt32(4e9) UInt64 0 到 2⁶⁴-1 8字节 UInt64(1e19)
- 浮点类型 类型 精度 存储大小 示例
Float32 7位有效数字 4字节 Float32(3.14) Fl> oat64 16位有效数字 8字节 Float64(3.14159)
- 布尔类型 Bool:实际上是UInt8的别名,0表示false,1表示true
二、日期时间类型
类型 格式 范围 示例
Date YYYY-MM-DD 1970-01-01 到 2106-02-07 Date('2023-01-01') Date32 YYYY-MM-DD 1900-01-01 到 2299-12-31 Date32('2100-01-01') DateTime YYYY-MM-DD HH:MM:SS 1970-01-01 00:00:00 到 2106-02-07 06:28:15 DateTime('2023-01-01 12:00:00') DateTime64 YYYY-MM-DD HH:MM:SS.SSS 可指定精度(0-9) DateTime64(3, '2023-01-01 12:00:00.123')
三、字符串类型
类型 描述 示例
String 任意长度文本,UTF-8编码 String('hello') FixedString(N) 固定长度N的字符串 FixedString(10) UUID 128位UUID UUID('123e4567-e89b-12d3-a456-426614174000')
四、复合类型
- 数组(Array)
Array(T) -- T可以是任意类型 Array(Int32) -- 整数数组 Array(String) -- 字符串数组
- 元组(Tuple)
Tuple(T1, T2, ...) -- 不同类型组合 Tuple(Int32, String, Float64) -- 包含整数、字符串和浮点数
- 嵌套(Nested)
Nested( Name1 Type1, Name2 Type2, ... ) Nested( product_id UInt32, quantity UInt8, price Float32 )
- 枚举(Enum)
Enum8('key1' = value1, 'key2' = value2) Enum16('key1' = value1, 'key2' = value2) Enum8('red' = 1, 'green' = 2, 'blue' = 3)
五、特殊类型
类型 描述 示例 Nullable(T) 允许为NULL的T类型 Nullable(Int32) LowCardinality(T) 低基数优化类型 LowCardinality(String) Decimal(P,S) 精确小数(P-精度,S-小数位数) Decimal(18,4) IPv4 IPv4地址 IPv4('192.168.0.1') IPv6 IPv6地址 IPv6('2001:db8::1')
六、建表示例
CREATE TABLE example_table ( id UInt64, user_name String, age UInt8, salary Decimal(18,2), is_active Bool, created_at DateTime, tags Array(String), address Tuple(city String, zip_code String), status Enum8('active' = 1, 'inactive' = 0), ip_address IPv4, metadata Nested( key String, value String ), optional_field Nullable(Float64) ) ENGINE = MergeTree() ORDER BY (id, created_at);
七、类型选择建议
优先选择最小满足需求的类型(如能用UInt8就不用UInt16)
高基数字符串考虑使用LowCardinality(String)
可能为NULL的字段使用Nullable(T)
精确计算使用Decimal而非Float
日期时间根据精度需求选择Date/DateTime/DateTime64
创建库、表
create database demo;
use demo;
CREATE TABLE demo_table
(
user_id UInt32,
message String,
timestamp DateTime,
metric Float32
)
ENGINE = MergeTree()
PRIMARY KEY (user_id, timestamp);
在上述示例中,my_first_table 是一个 MergeTree 表,包含四列:
user_id:一个 32 位无符号整数 message:一个 String 数据类型,替代了其他数据库系统中的 VARCHAR、BLOB、CLOB 等类型 timestamp:一个 DateTime 值,表示一个时间点 metric:一个 32 位浮点数 备注 表引擎决定了:
数据如何存储以及存储在哪里 支持哪些查询 数据是否被复制 有许多引擎可供选择,但对于单节点 ClickHouse 服务器上的简单表, MergeTree 可能是您的首选。
主键简要介绍 在您继续之前,重要的是要理解 ClickHouse 中主键的工作原理
ClickHouse 中的主键对于表中的每一行 不是唯一的 ClickHouse 表的主键决定了数据在写入磁盘时的排序方式。每 8,192 行或 10MB 数据(称为 索引粒度)会在主键索引文件中创建一个条目。该粒度概念创建了一个 稀疏索引,可以轻松装入内存,粒度表示在 SELECT 查询中处理的最小列数据量的条带。
可以使用 PRIMARY KEY 参数定义主键。如果您定义的表未指定 PRIMARY KEY,则键将成为 ORDER BY 子句中指定的元组。如果同时指定了 PRIMARY KEY 和 ORDER BY,则主键必须是排序顺序的前缀。
主键也是排序键,它是一个 (user_id, timestamp) 的元组。因此,存储在每个列文件中的数据将按 user_id 然后 timestamp 排序。
插入、删除、修改
一、插入数据(INSERT)
- 基本插入语句
-- 指定列名插入
INSERT INTO table_name (col1, col2, col3)
VALUES (val1, val2, val3), (val4, val5, val6);
-- 按表结构顺序插入(不推荐)
INSERT INTO table_name VALUES (val1, val2, val3);
2. 从查询结果插入
INSERT INTO target_table
SELECT col1, col2, col3 FROM source_table
WHERE condition;
3. 批量插入(高效方式)
INSERT INTO table_name FORMAT Values
(val1, val2, val3),
(val4, val5, val6);
4. 从文件导入
-- 从CSV文件导入
INSERT INTO table_name
FROM INFILE '/path/to/file.csv'
FORMAT CSV;
二、删除数据(DELETE)
- 条件删除(需要配置 mutations_sync = 1)
ALTER TABLE table_name DELETE WHERE condition;
2. 删除分区(高效方式)
ALTER TABLE table_name DROP PARTITION partition_expr;
3. 清空整个表
TRUNCATE TABLE table_name;
三、修改数据(UPDATE)
- 条件更新(需要配置 mutations_sync = 1)
ALTER TABLE table_name UPDATE col1 = val1, col2 = val2
WHERE condition;
2. 修改列属性
ALTER TABLE table_name MODIFY COLUMN col_name new_data_type;
四、实用示例
- 插入示例
-- 插入用户数据
INSERT INTO users (id, name, age, created_at)
VALUES
(1, '张三', 25, now()),
(2, '李四', 30, now());
-- 从查询结果插入
INSERT INTO user_activities
SELECT user_id, event_type, event_time
FROM raw_events
WHERE event_date = today();
2. 删除示例
-- 删除30天前的日志
ALTER TABLE access_logs DELETE
WHERE event_date < today() - 30;
-- 删除特定分区
ALTER TABLE time_series_data DROP PARTITION '2023-01';
3. 更新示例
-- 更新用户积分
ALTER TABLE users UPDATE points = points + 100
WHERE id IN (SELECT user_id FROM promotions);
-- 修改列类型
ALTER TABLE products MODIFY COLUMN price Decimal(20,2);
五、注意事项
性能考虑:
ClickHouse 的 DELETE 和 UPDATE 是异步操作(通过 ALTER TABLE 实现)
大量数据修改建议通过重建分区实现
配置要求:
-- 设置同步执行(生产环境谨慎使用)
SET mutations_sync = 1;
最佳实践:
大批量数据插入使用 INSERT...SELECT 或文件导入 频繁更新/删除的数据不适合使用ClickHouse 考虑使用 ReplacingMergeTree 引擎处理数据更新
版本差异:
ClickHouse 22.8+ 版本对 DML 操作有优化 旧版本可能需要使用 OPTIMIZE TABLE 强制合并
4. java使用clickhouse
依赖
implementation 'com.clickhouse:clickhouse-jdbc:0.4.6'
implementation 'com.zaxxer:HikariCP:5.0.1'
application.properties
# ClickHouse Database Configuration
clickhouse.datasource.url=jdbc:clickhouse://xxxxxxxxxxx:8123/default
clickhouse.datasource.username=default
clickhouse.datasource.password=
clickhouse.datasource.driver-class-name=com.clickhouse.jdbc.ClickHouseDriver
# Connection Pool Configuration
clickhouse.datasource.hikari.maximum-pool-size=10
clickhouse.datasource.hikari.minimum-idle=5
clickhouse.datasource.hikari.connection-timeout=30000
clickhouse.datasource.hikari.idle-timeout=600000
clickhouse.datasource.hikari.max-lifetime=1800000
# ClickHouse specific settings
clickhouse.datasource.settings.use_server_time_zone=false
clickhouse.datasource.settings.use_server_time_zone_for_dates=false
ClickHouseConfig
@Configuration
public class ClickHouseConfig {
@Value("${clickhouse.datasource.url}")
private String url;
@Value("${clickhouse.datasource.username}")
private String username;
@Value("${clickhouse.datasource.password}")
private String password;
@Value("${clickhouse.datasource.hikari.maximum-pool-size:10}")
private int maxPoolSize;
@Value("${clickhouse.datasource.hikari.minimum-idle:5}")
private int minIdle;
@Value("${clickhouse.datasource.hikari.connection-timeout:30000}")
private long connectionTimeout;
@Value("${clickhouse.datasource.hikari.idle-timeout:600000}")
private long idleTimeout;
@Value("${clickhouse.datasource.hikari.max-lifetime:1800000}")
private long maxLifetime;
@Bean
@Primary
public DataSource clickHouseDataSource() throws SQLException {
// Create ClickHouse specific properties
Properties properties = new Properties();
properties.setProperty("user", username);
if (password != null && !password.isEmpty()) {
properties.setProperty("password", password);
}
// ClickHouse specific settings
properties.setProperty("use_server_time_zone", "false");
properties.setProperty("use_server_time_zone_for_dates", "false");
properties.setProperty("session_timezone", "UTC");
// Create ClickHouse DataSource
ClickHouseDataSource clickHouseDataSource = new ClickHouseDataSource(url, properties);
// Configure HikariCP
HikariConfig hikariConfig = new HikariConfig();
hikariConfig.setDataSource(clickHouseDataSource);
hikariConfig.setMaximumPoolSize(maxPoolSize);
hikariConfig.setMinimumIdle(minIdle);
hikariConfig.setConnectionTimeout(connectionTimeout);
hikariConfig.setIdleTimeout(idleTimeout);
hikariConfig.setMaxLifetime(maxLifetime);
hikariConfig.setPoolName("ClickHouseHikariCP");
hikariConfig.setConnectionTestQuery("SELECT 1");
return new HikariDataSource(hikariConfig);
}
ClickHouseRepository
@Repository
public class ClickHouseRepository {
@Autowired
private DataSource dataSource;
/**
* 执行查询并返回结果列表
*/
public List<Map<String, Object>> executeQuery(String sql) throws SQLException {
List<Map<String, Object>> results = new ArrayList<>();
try (Connection connection = dataSource.getConnection();
PreparedStatement statement = connection.prepareStatement(sql);
ResultSet resultSet = statement.executeQuery()) {
ResultSetMetaData metaData = resultSet.getMetaData();
int columnCount = metaData.getColumnCount();
while (resultSet.next()) {
Map<String, Object> row = new HashMap<>();
for (int i = 1; i <= columnCount; i++) {
String columnName = metaData.getColumnName(i);
Object value = resultSet.getObject(i);
row.put(columnName, value);
}
results.add(row);
}
}
return results;
}
/**
* 执行更新操作(INSERT, UPDATE, DELETE)
*/
public int executeUpdate(String sql) throws SQLException {
try (Connection connection = dataSource.getConnection();
PreparedStatement statement = connection.prepareStatement(sql)) {
return statement.executeUpdate();
}
}
/**
* 批量执行SQL语句
*/
public int[] executeBatch(List<String> sqlList) throws SQLException {
try (Connection connection = dataSource.getConnection();
Statement statement = connection.createStatement()) {
for (String sql : sqlList) {
statement.addBatch(sql);
}
return statement.executeBatch();
}
}
/**
* 检查数据库连接
*/
public boolean testConnection() {
try (Connection connection = dataSource.getConnection()) {
return connection.isValid(5);
} catch (SQLException e) {
return false;
}
}
/**
* 获取数据库版本信息
*/
public String getDatabaseVersion() throws SQLException {
try (Connection connection = dataSource.getConnection();
PreparedStatement statement = connection.prepareStatement("SELECT version()");
ResultSet resultSet = statement.executeQuery()) {
if (resultSet.next()) {
return resultSet.getString(1);
}
return "Unknown";
}
}
}
5. 原理
lickHouse 的独特特性
真正的列式数据库管理系统
在真正的列式 DBMS 中,值旁边不会存储额外的数据。这意味着必须支持固定长度的值,以避免将其长度“数字”存储在值旁边。例如,十亿个 UInt8 类型的值在未压缩的情况下应该消耗大约 1 GB 的空间,否则将严重影响 CPU 的使用。即使在未压缩的情况下,紧凑存储数据(不包含任何“垃圾”)至关重要,因为解压速度(CPU 使用)主要取决于未压缩数据的量。
这与能够单独存储不同列值的系统形成对比,但由于针对其他场景的优化,无法有效处理分析查询,例如 HBase、Bigtable、Cassandra 和 Hypertable。在这些系统中,你的吞吐量大约是每秒十万个行,而不是每秒数亿行。
最后,ClickHouse 是一个数据库管理系统,而不是单一数据库。它允许在运行时创建表和数据库,加载数据,并运行查询,而无需重新配置和重启服务器。
数据压缩
一些列式 DBMS 不使用数据压缩。然而,数据压缩在实现卓越性能方面起着关键作用。
除了高效的通用压缩编码器,具有不同的磁盘空间和 CPU 消耗的权衡外,ClickHouse 还提供了针对特定类型数据的 专用编码器,使 ClickHouse 能够与更小众的数据库(例如时间序列数据库)竞争并超越它们。
数据的磁盘存储
通过主键物理排序存储数据使得可以根据特定值或值范围以低延迟提取数据,低于几十毫秒。一些列式 DBMS,如 SAP HANA 和 Google PowerDrill,只能在内存中工作。这种方法需要分配比实时分析所需的更大硬件预算。
ClickHouse 旨在在常规硬盘上工作,这意味着每 GB 数据存储的成本低,但如果有的话,SSD 和额外的 RAM 也会被充分利用。
多核心的并行处理
大型查询自然会并行化,使用当前服务器上可用的所有必要资源。
多服务器的分布式处理
几乎没有上述提到的列式 DBMS 支持分布式查询处理。
在 ClickHouse 中,数据可以位于不同的分片上。每个分片可以是用于容错的一组副本。所有分片都用于并行运行查询,对用户都是透明的。
SQL 支持
ClickHouse 支持与 ANSI SQL 标准大致兼容的 SQL 语言。
支持的查询包括 GROUP BY,ORDER BY,FROM 中的子查询,JOIN 子句,IN 运算符, 窗口函数 以及标量子查询。
在撰写本文时,不支持相关(依赖)子查询,但未来可能会提供。
向量计算引擎
数据不仅按列存储,还通过向量(列的部分)进行处理,这使得 CPU 效率高。
实时数据插入
ClickHouse 支持具有主键的表。为了快速在主键的范围内执行查询,数据使用合并树按增量排序。因此,数据可以不断添加到表中。新数据被摄入时不会加锁。
主索引
数据物理按主键排序使得可以根据特定值或值范围以低延迟提取数据,低于几十毫秒。
次级索引
与其他数据库管理系统不同,ClickHouse 的次级索引并不指向特定行或行范围。相反,它们使数据库能够提前知道某些数据部分中的所有行不会匹配查询过滤条件,因此根本不读取它们,因此称为 数据跳过索引。
适合在线查询
大多数 OLAP 数据库管理系统并不致力于寻求具有亚秒延迟的在线查询。在替代系统中,十几秒甚至几分钟的报告生成时间常常被视为可接受的。有时甚至需要更多时间,这迫使系统以离线(提前准备或以“稍后再来”响应)来准备报告。
在 ClickHouse 中,“低延迟”意味着查询可以在没有延迟的情况下处理,而不需要提前准备答案,正是在用户界面页面加载的同一时刻。换句话说,在线的。
支持近似计算
ClickHouse 提供多种方式用以权衡准确性与性能:
用于近似计算的聚合函数,计算独特值、均值和分位数的数量。 基于数据部分 (SAMPLE) 运行查询并获得近似结果。在这种情况下,比例更少的数据从磁盘中检索。 对有限数量的随机键运行聚合,而不是对所有键进行聚合。在数据中键分布的特定条件下,这提供了一个相当准确的结果,同时使用更少的资源。
自适应连接算法
ClickHouse 自适应地选择如何 JOIN 多个表,优先使用哈希连接算法,并在存在多个大型表时回退到合并连接算法。
数据复制和数据完整性支持
ClickHouse 使用异步多主复制。在写入任何可用副本后,所有剩余副本在后台检索其副本。系统在不同副本之间维护相同的数据。大多数故障后的恢复是自动执行的,在复杂情况下为半自动执行。
有关更多信息,请参见 数据复制 一节。
基于角色的访问控制
ClickHouse 使用 SQL 查询实现用户账户管理,并允许配置与 ANSI SQL 标准和流行关系数据库管理系统中相似的 基于角色的访问控制。
可视为缺点的特性
无完全事务。 缺乏以高速度和低延迟修改或删除已插入数据的能力。可用批量删除和更新来清洁或修改数据,例如,遵守 GDPR。 稀疏索引使 ClickHouse 在按键检索单行的点查询中效率不高。