人群圈选业务痛点

  • 用户标签多、标签丰富,标签列可达成百甚至上千列。
  • 数据量庞大,用户数多,从而所需运算量也极大。
  • 圈选条件组合多样化,没有固定索引可以优化,存储空间占用极大。
  • 性能要求高,圈选结果要求及时响应,过长的延时会造成营销人群的不准确。
  • 数据更新时效要求高,用户画像要求近实时的更新,过期的人群信息也将直接影响圈选的精准性。

ClickHouse 特性

  • 优势
    1. 查询速度快
    2. 存储压缩率高,节省空间
    3. 架构简单,集群中所有节点功能相同,规避单点故障
    4. 社区活跃,应用广泛,产品成熟度高
  • 不足
    1. 不支持事务、异步删除与更新
    2. 不适合高并发场景
    3. 不支持标准 sql,JOIN 由内部实现
    4. 集群在线扩容支持不佳,运维成本高

下载安装包

sudo yum install yum-utils
sudo rpm --import https://repo.clickhouse.tech/CLICKHOUSE-KEY.GPG
sudo yum-config-manager --add-repo https://repo.clickhouse.tech/rpm/clickhouse.repo
sudo yum install clickhouse-server clickhouse-client

文件目录

# 默认数据存储路径
/var/lib/clickhouse

# 默认日志路径
/var/log/clickhouse-server

# 服务端配置文件路径
/etc/clickhouse-server

# 核心配置文件详解:https://www.e-learn.cn/topic/4050968
/etc/clickhouse-server/config.xml

# 客户端配置文件路径
/etc/clickhouse-client

命令

# 服务端命令
service clickhouse-server start | status | restart | stop

# 调试,日志打印在控制台
sudo -u clickhouse /usr/bin/clickhouse-server --config-file /etc/clickhouse-server/config.xml

# 启动客户端
clickhouse-client --port 9003

集群部署

  1. 在群集的所有机器上安装 ClickHouse 服务端

  2. 在配置文件中设置群集配置

    <!--config.xml配置-->
    <tcp_port>9003</tcp_port>
    <listen_host>::</listen_host>
    <!--incl属性表示可从外部文件中获取节点名-->
    <remote_servers incl="clickhouse_remote_servers" />
    <zookeeper incl="zookeeper-servers" optional="true" />
    <macros incl="macros" optional="true" />
    <!--使用外部扩展配置文件-->
    <include_from>/etc/clickhouse-server/metrika.xml</include_from>
    
    <!--metrika.xml配置-->
    <yandex>
      <!-- 集群配置 -->
      <clickhouse_remote_servers>
        <ck_cluster>
          <shard>
            <replica>
              <host>worker07</host>
              <port>9003</port>
            </replica>
            <replica>
              <host>worker08</host>
              <port>9003</port>
            </replica>
            <replica>
              <host>worker09</host>
              <port>9003</port>
            </replica>
          </shard>
        </ck_cluster>
      </clickhouse_remote_servers>
      <!--zookeeper相关配置-->
      <zookeeper-servers>
        <node index="3">
          <host>master01</host>
          <port>2181</port>
        </node>
        <node index="2">
          <host>worker01</host>
          <port>2181</port>
        </node>
        <node index="1">
          <host>worker02</host>
          <port>2181</port>
        </node>
        <node index="5">
          <host>worker03</host>
          <port>2181</port>
        </node>
        <node index="4">
          <host>worker04</host>
          <port>2181</port>
        </node>
      </zookeeper-servers>
      <!--环境变量 -->
      <macros>
        <shard>0</shard>
        <replica>worker07</replica>
      </macros>
    </yandex>
    
  3. 分发配置,其自动生效,查看集群

    select * from system.clusters;
    

一致性

internal_replication:表示是否只将数据写入其中一个副本,默认为 false

四种复制模式:

  • 非复制表,internal_replication=false。写入单机表时,不同服务器查询结果不同;插入到分布式表中的数据被插入到两个本地表中,如果在插入期间没有问题,则两个本地表上的数据保持同步。我们称之为“穷人的复制”,因为复制在网络出现问题的情况下容易发生分歧,没有一个简单的方法来确定哪一个是正确的复制。
  • 非复制表,internal_replication=true。数据只被插入到一个本地表中,但没有任何机制可以将它转移到另一个表中。因此,在不同主机上的本地表看到了不同的数据,查询分布式表时会出现非预期的数据。显然,这是配置 ClickHouse 集群的一种不正确的方法。
  • 复制表,internal_replication=true。插入到分布式表中的数据仅插入到其中一个本地表中,但通过复制机制传输到另一个主机上的表中。因此两个本地表上的数据保持同步。这是官方推荐配置。
  • 复制表,internal_replication=false。数据被插入到两个本地表中,但同时复制表的机制保证重复数据会被删除。数据会从插入的第一个节点复制到其它的节点。其它节点拿到数据后如果发现数据重复,数据会被丢弃。这种情况下,虽然复制保持同步,没有错误发生。但由于不断的重复复制流,会导致写入性能明显的下降。所以这种配置实际应该是避免的。

优化

  1. 读的时候是用 distribution 表去读取数据,轮询写入本地表
  2. 写入时尽量“大批量,少批次”写入
  3. 尽量使用大宽表代替 join,列宽支持 1w+

集群测试

-- 先创建本地表,再创建分布式表,往分布式表插入数据,数据随机分布在本地表
-- 在每个实例上创建本地表
CREATE TABLE default.cluster3s1r_local(`id` Int32,`website` String,`wechat` String,`FlightDate` Date,Year UInt16) ENGINE = MergeTree(FlightDate, (Year, FlightDate), 8192);

-- 创建一个分布式表(指定集群名、数据库名、数据表名、分片KEY)
CREATE TABLE default.cluster3s1r_all AS cluster3s1r_local ENGINE = Distributed(ck_cluster, default, cluster3s1r_local, rand());

-- 插入测试数据
INSERT INTO default.cluster3s1r_all
(id,website,wechat,FlightDate,Year)values(1,'https://niocoder.com/','java干货','2020-11-28',2020);
INSERT INTO default.cluster3s1r_all (id,website,wechat,FlightDate,Year)values(2,'http://www.merryyou.cn/','javaganhuo','2020-11-28',2020);
INSERT INTO default.cluster3s1r_all (id,website,wechat,FlightDate,Year)values(3,'http://www.xxxxx.cn/','xxxxx','2020-11-28',2020);

-- 查询分布式表
select * from cluster3s1r_all;
-- 查询本地表
select * from cluster3s1r_local;

-- 创建复制表,连接zk,不用创建分布式表,不配置集群clickhouse_remote_servers。数据写入本地表,其他节点自动同步数据
-- 创建复制表
CREATE TABLE default.cluster1s3r_local on cluster ck_cluster (`id` Int32,`website` String,`wechat` String,`FlightDate` Date,Year UInt16) ENGINE = ReplicatedMergeTree('/clickhouse/tables/{shard}/cluster1s3r_local', '{replica}', FlightDate, (Year, FlightDate), 8192);

INSERT INTO default.cluster1s3r_all
(id,website,wechat,FlightDate,Year)values(1,'https://niocoder.com/','java干货','2020-11-28',2020);

select * from cluster1s3r_all;

问题

  1. DB::Exception: Effective user of the process (root) does not match the owner of the data (clickhouse). Run under ‘sudo -u clickhouse’.

    sudo -u clickhouse clickhouse-server –config-file=/etc/clickhouse-server/config.xml

  2. Access to file denied: /var/log/clickhouse-server/clickhouse-server.log

    chown -R clickhouse /var/log/clickhouse-server/

  3. Address already in use

    更改 tcp 配置,切换 9000 端口为 9003

  4. There are two exactly the same ClickHouse instances

    单台节点只能部署一个分片副本,即 3 分片 2 副本需要 6 台服务器