[不懂就问] MySQL 中大量数据的分组查询问题 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
wuxi889
V2EX    问与答

[不懂就问] MySQL 中大量数据的分组查询问题

  •  
  •   wuxi889 2023 年 3 月 31 日 1795 次点击
    这是一个创建于 1022 天前的主题,其中的信息可能已经有所发展或是发生改变。
    # 场景

    有一个 orders 表,表中含有 order_id, status, financial_status,paid_at,customer_id 字段(还有一对的其他字段),表索引有所有字段各自的索引,还有组合索引 status, financial_status,paid_at (是有效订单的必然前置条件)。现在我的数据跨度有 3 年,数据行数有 400 万条。

    # 目的

    计算复购率。复购率逻辑:比如查询 2022.01.01-2023.03.01 范围时,在这个时间段内出现 customer_id 重复有 2 次及以上时算复购,复购率=复购用户数 /用户数唯一数。

    # 问题

    目前在现有的条件查询中,查询 1.5 年数据时计算结果长达 150s 。因为查询的时间范围太大了,导致了 MySQL 的选择器在处理时组合索引没有使用,变成了扫全表。

    请问大佬们,有没有什么方法可以压缩这个请求结果的时长到 3s 以内的?

    # 其他
    无法使用缓存,如果使用缓存的话所需要缓存的数量为 C(N,2) = N*(N-1)/2)
    第 1 条附言    2023 年 4 月 1 日
    算了,帖子结束吧。我自己再想想
    9 条回复    2023-04-02 10:16:21 +08:00
    noparking188
        1
    noparking188  
       2023 年 3 月 31 日
    能信息给全点嘛,看了想骂人
    silypie
        2
    silypie  
       2023 年 3 月 31 日
    能把 customer_id 作为主键吗
    silypie
        3
    silypie  
       2023 年 3 月 31 日
    要不按月查询,这样就能用上索引,然后做一下汇总
    wuxi889
        4
    wuxi889  
    OP
       2023 年 4 月 1 日
    @silypie 这种复购的计算逻辑就是要按规定的时间范围去重,按月去汇总是没有意义的(一个月内也许没有复购,但是第二个月复购了。在查询第一个月时不算复购,查询第一到第二个月时,就算复购了)。
    meta
        5
    meta  
       2023 年 4 月 1 日
    你这种情况,如果对数据的实时性要求不高,可以先在后台做好每个用户的摘要数据,不必要每次都去流水里面查询。如果要即时数据,也可以先把历史数据做好摘要,即时数据只在当月的流水里面查询就可以了。
    iluhcm
        6
    iluhcm  
       2023 年 4 月 1 日 via iPhone
    这种涉及到汇总的需求,要看投入的资源怎么样,ROI 值不值得。是否有必要做实时?离线 t+1 或者 h+1 行不行?
    一种可行解是新建一张汇总表去做存储,按周期更新,按照用户维度去汇总。
    iluhcm
        7
    iluhcm  
       2023 年 4 月 1 日 via iPhone
    @iluhcm 对实时性有要求的,可以考虑其他 olap 引擎。
    qiayue
        8
    qiayue  
    PRO
       2023 年 4 月 1 日
    拆分问题,第一步找出所有购买两次以上的用户数量,第二步找出所有购买用户数量,用两个 sql 分别查一次,之后用程序算复购比例,这会比只用一个 sql 查询更快。
    第一个 sql 找出所有购买了 2 次以上的用户数量,这里假设 status = 1 表示订单已支付:
    select count(t.customer_id) buy_2_times_customer_c from (select customer_id,count(distinct order_id) order_c from order where paid_at>='2022.01.01' and paid_at<='2023.03.01' and status=1 group by customer_id having order_c>=2) t

    第二个 sql 找出所有购买用户数量:
    select count(distinct customer_id) total_customer_c from order where paid_at>='2022.01.01' and paid_at<='2023.03.01' and status=1

    最后用 buy_2_times_customer_c / total_customer_c 就是比例了。
    qiayue
        9
    qiayue  
    PRO
       2023 年 4 月 2 日
    我还帮你问了 gpt4
    https://gpt.best/NoMhNsx1
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     2722 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 30ms UTC 14:42 PVG 22:42 LAX 06:42 JFK 09:42
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86