V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
v2exblog
V2EX  ›  程序员

各位程序员大佬,在线求助一个代码设计问题! 大数据,对某个时间范围的历史数据,执行删除+插入操作的最佳实践是什么??我用的 ClickHouse,本质上是事务问题。目前有 2 个疑问请教大家,附上我的思考过程......感觉我要提桶跑路了

  •  
  •   v2exblog · 2022-10-15 23:20:12 +08:00 · 1431 次点击
    这是一个创建于 530 天前的主题,其中的信息可能已经有所发展或是发生改变。

    如题,各位,之前用的是 PostgreSQL ,现在迁移到 ClickHouse ,请教关于 ClickHouse 的 2 个问题:

    我们有一些数据从第三方 API 来,表示某天某个国家某个 APP 的点击数和花了多少钱,

    数据表的字段大概这样:
    date: 数据时间(主键)
    coutry: 国家 (主键)
    app: 应用名(主键)
    clicks: 点击数 (整数
    spends: 花费的金额 (浮点数)

    现在的需求是这样:

    第三方 API 提供的历史数据可能有变化,需要及时处理,以便于报表能够及时更新
    比如:
    今天是 2022 年 10 月 15 日,但 2022 年 1 月 1 日~2022 年 1 月 10 日的历史数据需要更新
    那应该先删除 2022 年 1 月 1 日~2022 年 1 月 10 日的历史数据,再插入该时间范围新的数据

    该时间范围数据更新操作,产生了下面三种情况:

    1. 对于 date+country+app 维度,历史数据和新数据都有,相当于覆盖了历史数据
    2. 对于 date+country+app 维度,历史数据没有,相当于新加了数据
    3. 对于 date+country+app 维度,历史数据有,但新数据没有,相当于删了该数据

    有两个问题想要请教大家:

    1. 对于这种场景,ClickHouse 有没有什么最佳实践?
      我通过互联网了解到,只能先删除,然后轮询数据库,等删除结束了再插入
    2. 基于问题 1 ,我知道 ClickHouse 没有事务,并且删除和插入都是异步的,也都不是原子的,
      假如某个时间范围的数据需要更新,并且有多个并行任务,
      同时对 该时间范围 的历史数据,执行删除+插入的动作,
      ClickHouse 能够保证数据最终正确吗? (保证最后一个任务的删除+插入数据是完整的)
      这种情况业务上不想加锁该咋办?(业务原因确实会遇到这样的问题)

    我的思考过程:

    方案 1 用版本号解决,只插入数据,每批新数据都是比旧数据的版本号大

    该方案有问题,比如数据时间范围是 2022 年 1 月~2022 年 12 月,版本号是 1
    这时候删除+插入 2022 年 6 月的数据,版本号是 2 如果我按照 2022 年 1 月到 2022 年 12 月聚合数据,我得咋查出来啊?? 再就是没办法表示已经删除的数据,除非增加一列,is_deleted ,问题是我怎么查询。。。

    方案 2 在数据表设计一个更新时间字段,类似版本号 V2 版本

    该方案的问题在于,删除数据是要删除比当前时间小的数据,那么问题来了 假如有这种情况: 任务 1 一共 100 条数据,插入了 50 条还没插完,这时候任务 2 开始执行了 因为任务 1 的更新时间要小于任务 2 ,任务 2 会先执行删除操作,所以会不会出现删不全的数据?

    5 条回复    2022-10-17 10:11:50 +08:00
    yongjing
        1
    yongjing  
       2022-10-16 00:13:29 +08:00
    没有 clickhouse 使用经验,不过大数据仓库都类似。
    大概搜了一下,针对前两种情况,表引擎采用 ReplacingMergeTree ,做一次 insert 就好。
    第三种情况,查不到的数据,如果不要求回溯,从表里直接删除就好。加版本号方案也没问题,自己权衡吧。
    reter
        2
    reter  
       2022-10-16 10:20:00 +08:00   ❤️ 1
    如果数据时间的精度是日,那就比较好办, 如 #1 所说,使用 ReplacingMergeTree

    把所有操作都转换成插入操作, 例如,删除操作可以转换为插入 clicks 和 spends 字段都为 0 的记录, 或者你想要的其他字段,用于覆盖旧记录。

    用时间戳做版本号 ver (不要程序产生,用 sql 产生时间戳,为了保持 ver 近似单调递增)

    查询时候可以认为, 主键相同的所有记录中,ver 最大的记录才是最终的记录:SELECT * FROM table1 GROUP BY 主键 ORDER BY ver LIMIT 1
    v2exblog
        3
    v2exblog  
    OP
       2022-10-16 21:45:32 +08:00
    @yongjing
    @reter
    谢谢,我仔细想一想
    Maxwe11
        4
    Maxwe11  
       2022-10-17 00:37:01 +08:00
    1 、至少是我个人没有看到过类似的最佳实践;
    2 、clickhouse 是 olap 统计分析数据库,它只统计分析但是并不考虑数据本身的过多变化;
    3 、参考 #2 楼朋友的描述,如果 clickhouse 不变,那么就增加一列 insert_time ,不用去刻意删除历史数据,统计的时候以最新 inserttime 为准,就是同时间周期的 max(insert_time)数据的聚合。
    v2exblog
        5
    v2exblog  
    OP
       2022-10-17 10:11:50 +08:00
    @Maxwe11 仔细想了一下,打个比方:对于 date+country+app 维度,如果按照某个时间范围更新数据,有一种情况是历史数据有,但新数据没有,相当于删了该数据,查询也不应该查出来,增加版本号没办法避免这种情况
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   994 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 29ms · UTC 20:00 · PVG 04:00 · LAX 13:00 · JFK 16:00
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.