clickhouse简介
核心简介操作官方网站 https://clickhouse.tech/docs/zh/introduction/distinctive-features
ClickHouse是一个用于联机分析(OLAP)的列式数据库管理系统(DBMS)。
数据压缩
在一些列式数据库管理系统中(例如:InfiniDB CE 和 MonetDB) 并没有使用数据压缩。但是, 若想达到比较优异的性能,数据压缩确实起到了至关重要的作用。
除了在磁盘空间和CPU消耗之间进行不同权衡的高效通用压缩编解码器之外,ClickHouse还提供针对特定类型数据的专用编解码器,这使得ClickHouse能够与更小的数据库(如时间序列数据库)竞争并超越它们。
数据的磁盘存储
许多的列式数据库(如 SAP HANA, Google PowerDrill)只能在内存中工作,这种方式会造成比实际更多的设备预算。
ClickHouse被设计用于工作在传统磁盘上的系统,它提供每GB更低的存储成本,但如果可以使用SSD和内存,它也会合理的利用这些资源。
多核心并行处理
ClickHouse会使用服务器上一切可用的资源,从而以最自然的方式并行处理大型查询。
多服务器分布式处理
上面提到的列式数据库管理系统中,几乎没有一个支持分布式的查询处理。
在ClickHouse中,数据可以保存在不同的shard上,每一个shard都由一组用于容错的replica组成,查询可以并行地在所有shard上进行处理。这些对用户来说是透明的
支持SQL
ClickHouse支持一种基于SQL的声明式查询语言,它在许多情况下与ANSI SQL标准相同。
支持的查询GROUP BY, ORDER BY, FROM, JOIN, IN以及非相关子查询。
相关(依赖性)子查询和窗口函数暂不受支持,但将来会被实现。
向量引擎
为了高效的使用CPU,数据不仅仅按列存储,同时还按向量(列的一部分)进行处理,这样可以更加高效地使用CPU。
实时的数据更新
ClickHouse支持在表中定义主键。为了使查询能够快速在主键中进行范围查找,数据总是以增量的方式有序的存储在MergeTree中。因此,数据可以持续不断地高效的写入到表中,并且写入的过程中不会存在任何加锁的行为。
索引
按照主键对数据进行排序,这将帮助ClickHouse在几十毫秒以内完成对数据特定值或范围的查找。
适合在线查询
在线查询意味着在没有对数据做任何预处理的情况下以极低的延迟处理查询并将结果加载到用户的页面中。
支持近似计算
ClickHouse提供各种各样在允许牺牲数据精度的情况下对查询进行加速的方法:
- 用于近似计算的各类聚合函数,如:distinct values, medians, quantiles
- 基于数据的部分样本进行近似查询。这时,仅会从磁盘检索少部分比例的数据。
- 不使用全部的聚合条件,通过随机选择有限个数据聚合条件进行聚合。这在数据聚合条件满足某些分布条件下,在提供相当准确的聚合结果的同时降低了计算资源的使用。
clickhouse 集群部署
Merge Tree
MergeTree
系列的引擎被设计用于插入极大量的数据到一张表当中。数据可以以数据片段的形式一个接着一个的快速写入,数据片段在后台按照一定的规则进行合并。相比在插入时不断修改(重写)已存储的数据,这种策略会高效很多。
主要特点:
- 存储的数据按主键排序。
这使得你能够创建一个小型的稀疏索引来加快数据检索。 - 支持数据分区
在相同数据集和相同结果集的情况下 ClickHouse 中某些带分区的操作会比普通操作更快。查询中指定了分区键时 ClickHouse 会自动截取分区数据。这也有效增加了查询性能。 - 支持数据副本。
- 支持数据采样。
ReplicatedMergeTree
ReplicatedMergeTree 是MergeTree的一个 扩展,主要是用户表级别的复制
测试集群规划
1: 下载并且在3台机器上安装clickhouse, 安装步骤参见 官方文档
https://clickhouse.tech/docs/zh/getting-started/install/
2: 安装zookeeper
3台物理机器(ubantu18.04)
node1: 172.16.1.x1
node2: 172.16.1.x2
node3: 172.16.1.x3
3 个shard,2 个副本
重点: clickhouse 一个物理节点保存 同一个表的同一个shard的副本数据。测试环境需要在3台物理机器上面
启动6个节点,由于端口等限制。 测试环境 每一个物理节点启动两个 clickhouse的进程,分别监听不同的端口
划分为两个逻辑的集群 集群1(9000), 集群2(9002)
物理规划图
其中 shard1-01表示 shard1的第一副本,以此类推
配置文件编写
ubantu 安装的linux 的配置文件路径为: /etc/clickhouse-server
配置文件复制
复制该目录下的config.xml
cp config.xml config1.xml
config.xml 配置文件为9000 节点使用
config1.xml 配置文件为9001 节点使用
创建服务通用配置,设置分片和副本
创建配置文件内容如下。后续在config.xml和config1.xml的 include_form中需要引用该文件
clickhouse_self.xml
<yandex>
<clickhouse_remote_servers>
<cluster_3shards_1replicas>
<shard>
<internal_replication>true</internal_replication>
<replica>
<host>172.16.1.x1</host>
<port>9000</port>
<user>default</user>
<password>admin123</password>
</replica>
<replica>
<host>172.16.1.x2</host>
<port>9001</port>
<user>default</user>
<password>admin123</password>
</replica>
</shard>
<shard>
<internal_replication>true</internal_replication>
<replica>
<host>172.16.1.x2</host>
<port>9000</port>
<user>default</user>
<password>admin123</password>
</replica>
<replica>
<host>172.16.1.x3</host>
<port>9001</port>
<user>default</user>
<password>admin123</password>
</replica>
</shard>
<shard>
<internal_replication>true</internal_replication>
<replica>
<host>172.16.1.x3</host>
<port>9000</port>
<user>default</user>
<password>admin123</password>
</replica>
<replica>
<host>172.16.1.x1</host>
<port>9001</port>
<user>default</user>
<password>admin123</password>
</replica>
</shard>
</cluster_3shards_1replicas>
</clickhouse_remote_servers>
<zookeeper-servers>
<node index="1">
<host>172.16.1.x1</host>
<port>2181</port>
</node>
<node index="2">
<host>172.16.1.x2</host>
<port>2181</port>
</node>
<node index="3">
<host>172.16.1.x3</host>
<port>2181</port>
</node>
</zookeeper-servers>
</yandex>
修改config.xml和config1.xml
config.xml修改如下
引入外部配置
<include_from>/etc/clickhouse-server/clickhouse_self.xml</include_from>
修改macros
<macros>
<shard>1</shard>
<replica>172.16.1.x1-1</replica>
</macros>
config1.xml的改动 (需要改动日志存储文件名称, 数据文件存储的路径,监听的端口等)
备注: 如果物理机器足够直接在不同的物理上启动 6个节点。不要做这些改动。
需要提前创建server1目录
数据文件存储改动
<path>/var/lib/clickhouse/server1/</path>
<!-- Path to temporary data for processing hard queries. -->
<tmp_path>/var/lib/clickhouse/server1/tmp/</tmp_path>
<!-- Policy from the <storage_configuration> for the temporary files.
If not set <tmp_path> is used, otherwise <tmp_path> is ignored.
Notes:
- move_factor is ignored
- keep_free_space_bytes is ignored
- max_data_part_size_bytes is ignored
- you must have exactly one volume in that policy
-->
<!-- <tmp_policy>tmp</tmp_policy> -->
<!-- Directory with user provided files that are accessible by 'file' table function. -->
<user_files_path>/var/lib/clickhouse/server1/user_files/</user_files_path>
<!-- Path to folder where users and roles created by SQL commands are stored. -->
<access_control_path>/var/lib/clickhouse/server1/access/</access_control_path>
引入外部文件
<include_from>/etc/clickhouse-server/clickhouse_self.xml</include_from>
端口改动如下
<http_port>8124</http_port>
<tcp_port>9001</tcp_port>
<mysql_port>9005</mysql_port>
日志改动如下
<logger>
<level>trace</level>
<log>/var/log/clickhouse-server/clickhouse-server1.log</log>
<errorlog>/var/log/clickhouse-server/clickhouse-server1.err.log</errorlog>
<size>1000M</size>
<count>10</count>
</logger>
修改macors
<macros>
<shard>3</shard>
<replica>172.16.1.3x-2</replica>
</macros>
配置其他服务器
分别在node2 nod3 做相同的处理, 完成集群的配置工作
启动集群(每个节点做相同的工作)
启动clickhouse-server1的时候,可能出现文件权限问题,需要根据提示 修改相应的文件权限(server1的文件权限)
nohup /usr/bin/clickhouse-server --config=/etc/clickhouse-server/config1.xml
--pid-file=/run/clickhouse-server/clickhouse-server1.pid &
nohup /usr/bin/clickhouse-server --config=/etc/clickhouse-server/config.xml
--pid-file=/run/clickhouse-server/clickhouse-server.pid &
查看集群信息
任意服务器执行
clickhouse-client --password xxxxx
进入集群
查看集群配置
select cluster, shard_num, replica_num,host_name, port from system.clusters;
创建复制表
只是需要在一个节点上创建,采用分布式ddl操作
create database test_log on cluster cluster_3shards_1replicas
use test_log 切换数据库
CREATE TABLE log_test on cluster cluster_3shards_1replicas
(
event_name String,
event_type String,
event_time DateTime64(3,'Asia/Shanghai'),
module String,
page String,
user_id Int,
user_type String,
property String
) ENGINE = ReplicatedMergeTree('/clickhouse/tables/{shard}', '{replica}')
ORDER BY event_time
partition by toYYYYMM(event_time)
创建分布式表
CREATE TABLE test_log_all AS test_log
ENGINE = Distributed(cluster_3shards_1replicas, log_test, test_log, rand())
真个集群中每个节点都包含了 test_log 数据库和 log_test 复制表
插入数据到节点1
java 集成mybatis 写入数据到clickhouse(本地表)
读取的时候,读分布式表
pom文件
<dependency>
<groupId>com.github.housepower</groupId>
<artifactId>clickhouse-native-jdbc</artifactId>
<version>2.5.4</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.6</version>
</dependency>
mybatis的配置
<environments default="clickhouse">
<!-- 连接环境信息,取一个任意唯一的名字 -->
<environment id="clickhouse">
<!-- mybatis使用jdbc事务管理方式 -->
<transactionManager type="jdbc"/>
<!-- mybatis使用连接池方式来获取连接 -->
<dataSource type="pooled">
<!-- 配置与数据库交互的4个必要属性 -->
<property name="driver" value="com.github.housepower.jdbc.ClickHouseDriver"/>
<property name="url" value="jdbc:clickhouse://172.16.1.x1:9000/test_log"/>
<property name="username" value="default"/>
<property name="password" value="xxxxxx"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="ClickhouseMapper.xml"/>
</mappers>
LogEvent定义
@Data
public class LogEvent {
ZonedDateTime eventTime;
String eventName;
String eventType;
String module;
String page;
Integer userId;
String userType;
String properties;
}
Mapper定义
@Mapper
public interface ClickhouseMapper {
void insertIntoData(@Param("event") LogEvent event);
}
Mapper.xml
<insert id="insertIntoData" parameterType="com.clickhouse.LogEvent">
insert into log_test(event_time,
event_name,
event_type,
module,
page,
user_id,
user_type,
property)
values(
#{event.eventTime},
#{event.eventName},
#{event.eventType},
#{event.module},
#{event.page},
#{event.userId},
#{event.userType},
#{event.properties})
</insert>
插入java代码
public class DriverClickHouseWriter {
public static void main(String[] args) throws Exception {
Reader reader = Resources.getResourceAsReader("mybatis-config.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
SqlSession sqlSession = sqlSessionFactory.openSession();
ClickhouseMapper mapper = sqlSession.getMapper(ClickhouseMapper.class);
LogEvent event = new LogEvent();
Map<String, String> map = new HashMap<>();
map.put("question", "商品问题");
map.put("order_id", "923456");
ObjectMapper objectMapper = new ObjectMapper();
String pro = objectMapper.writeValueAsString(map);
event.setEventTime(ZonedDateTime.ofInstant(Instant.ofEpochMilli(System.currentTimeMillis()), ZoneId.of("+8")));
event.setEventName("page_index_click");
event.setEventType("click");
event.setModule("im");
event.setPage("index");
event.setUserId(12344);
event.setUserType("supplier");
event.setProperties(pro);
mapper.insertIntoData(event);
sqlSession.close();
}
}
总结
通过上面的不走,写入数据到复制表中(本地表),只要写入其中一个节点, 集群回通过zk自动复制 数据到对应的 节点。 主节点挂掉之后,不影响分布式表的读取。保证了集群的高可用