You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

高流量ClickHouse事件分析系统数据模型选型咨询

高流量分析系统数据模型设计问询

当前架构与核心逻辑

  • 自定义应用事件存储于ClickHouse
  • 设备与用户记录存储于MongoDB,存储维度包括:os_nameos_versiondevice_modelapp_version、当前user_uuid
  • 核心概念:device_uuiduser_uuid、客户端事件timestamp、服务端received_at
  • 核心业务行为:
    • 设备初始为匿名状态,user_uuid = device_uuid
    • 同一设备后续可关联真实用户,user_uuid更新为真实用户标识
    • 关联后,该设备过往匿名事件需归属至真实用户

拟采用的数据模型设计

为避免直接在ClickHouse大型事件表中存入user_uuid带来的高昂更新成本,设计如下:

不可变events表(ClickHouse)

project_id 
device_uuid 
timestamp 
received_at 
event_code 
session_id 
properties

ownership归属表(ClickHouse)

project_id 
device_uuid 
valid_from 
valid_to 
user_uuid

通过device_uuid + received_at关联事件表与归属区间表解析用户归属,示例场景:

  • 10:00 设备D1处于匿名状态,归属为D1
  • 10:05和10:06 设备D1产生事件并插入
  • 10:10 用户登录,归属变为John
  • 过往匿名事件需归属至John

核心分析场景担忧

需支持的查询场景包括:

  • 完成app_open→permission_granted流程的独立用户数
  • 按用户维度的windowFunnel分析
  • 按用户维度的留存/ cohort分析
  • 展示后续完成转化的iOS 18.6设备的事件
  • 按事件属性+解析后用户+设备/应用维度进行细分
  • 包含大量用户与事件的高流量仪表盘

技术问询

  1. 在大规模场景下,不将user_uuid存入主事件表是否仍为正确方案?
  2. 在ClickHouse中单独设置归属表是否是可落地的生产级模型?
  3. 若设备/应用/OS维度留在MongoDB、事件存储于ClickHouse,是否会对分析造成不利影响?例如查询“后续完成转化的iOS 18.6设备的事件”是否会因依赖双库而变得繁琐?

补充说明

  • 优先选择内存、CPU、磁盘占用最优的方案
  • 曾考虑将user_uuid直接存入events表并使用ClickHouse轻量更新,但该功能仍处于实验阶段,风险较高
  • 已通过AI工具初步确认独立归属表方案,但未找到ClickHouse同类架构的中文文档/文章

针对数据模型设计的专业解答

问题1:大规模场景下不将user_uuid存入主事件表是否正确?

结论:是正确的方案
ClickHouse核心设计逻辑是面向批量写入和只读分析,大规模事件表的单条更新成本极高——不仅会触发违背列存优化逻辑的磁盘随机写,还会在分布式集群中引发分片同步开销,数据量达到千万/亿级后,这种成本会呈指数级上升。
你提到的实验性轻量更新功能,本身存在数据一致性风险(如更新丢失、分片同步延迟),在高流量生产环境中完全不可控。而通过独立归属表关联的方式,保持events表的不可变性,完全契合ClickHouse的最优使用模式,长期来看能大幅降低维护成本和资源消耗。

问题2:ClickHouse中单独设置归属表是否为可落地的生产级模型?

结论:完全可以落地,且是业内通用的用户身份关联方案
这种基于时间区间的归属映射模型,本质是缓慢变化维度(SCD Type 2)的ClickHouse实现,在用户行为分析领域已被广泛采用。落地时需注意以下优化点:

  • 表结构优化:将project_iddevice_uuid设为分区键+主键,确保归属区间查询能快速定位数据;valid_fromvalid_to设为DateTime类型,配合received_at做区间关联时可利用ClickHouse索引优化。
  • 写入逻辑优化:设备关联真实用户时,先将原归属区间的valid_to更新为当前时间(归属表更新量远小于事件表,成本可接受),再插入新的归属区间记录;初始匿名状态需写入一条valid_from为设备首次事件时间、valid_to'9999-12-31'的记录。
  • 查询优化:关联查询时使用ClickHouse的ASOF JOIN语法替代传统JOIN + BETWEEN,大幅提升关联性能,示例:
    SELECT e.*, o.user_uuid
    FROM events e
    ASOF JOIN ownership o
    ON e.project_id = o.project_id 
    AND e.device_uuid = o.device_uuid
    AND e.received_at BETWEEN o.valid_from AND o.valid_to
    
  • 性能保障:归属表数据量仅为事件表的万分之一甚至更低(单设备归属变更次数极少),高流量场景下内存和CPU占用可忽略不计。

问题3:设备/应用/OS维度留在MongoDB是否会影响分析?

结论:会带来明显查询繁琐性,建议将设备维度数据同步至ClickHouse
你的担忧完全合理:跨库查询不仅增加查询复杂度,还会引入双库依赖的稳定性风险(如MongoDB宕机、网络延迟),尤其是高流量仪表盘场景下,跨库关联性能会成为瓶颈。
针对这类维度数据,建议采用以下方案:

  • 同步设备维度至ClickHouse:建立device_profiles表,结构与MongoDB设备记录一致,通过CDC工具(如Debezium)将MongoDB的设备维度变更实时同步至ClickHouse,确保数据一致性。
  • 查询优化:分析时直接关联ClickHouse内的eventsownershipdevice_profiles三张表,避免跨库操作。例如查询“后续完成转化的iOS 18.6设备的事件”时,可直接通过device_profiles过滤os_name='iOS'os_version='18.6'device_uuid,再关联事件表和归属表。
  • 资源消耗对比:设备维度表数据量极小(单设备一条记录),同步和存储的资源消耗可忽略,远低于跨库查询带来的性能损耗。

内容的提问来源于stack exchange,提问作者Ozgur Baykal

火山引擎 最新活动