数据库应该使用单独列存储计数吗? - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
mmnsghgn
V2EX    数据库

数据库应该使用单独列存储计数吗?

  •  
  •   mmnsghgn 2022-07-10 00:47:53 +08:00 2259 次点击
    这是一个创建于 1194 天前的主题,其中的信息可能已经有所发展或是发生改变。

    问题描述

    一个学校有 N 个班级,一个班级有 N 个学生。

    • 在展示学校列表的时候,需要展示该学校的班级数量、学生数量
    • 在展示班级列表的时候,需要展示该班级的学生数量

    以下两种方式如何选择:


    1.使用单独列

    学校classesCountstudentsCount 两个列来分别存储该校班级数量、该校学生数量,在班级表中使用 studentsCount 列存储该班学生数量。

    班级实体增、删时,更新学校表的 classesCount 列;在学生实体增、删时,更新班级表及学校表的 studentsCount 列。

    2.使用数据库查询

    班级数量和学生数量由数据库查询获得。

    select * from cmapus; select count(*) as classesCount from classes group by campus_id; select count(*) as studentsCount from students group by campus_id; 

    方式一在增、删时需要做额外操作,会存在事务不能保证正确的情况吗?

    方式二在数据量大的时候性能会很差吗?

    额外提问

    类似这种数据库设计问题,有推荐的书籍参阅吗?

    第 1 条附言    2022-07-10 02:04:04 +08:00
    感谢各位的回复。

    很抱歉我没有提供详细的信息。

    问题描述并不是我真实的场景,为了简单起见用了一个简化后的结构。

    1. 数据量级预估

    还是按照问题描述中的结构来说,每个学校至多有 20 个班级,每个班级至多有 500 个学生,学校数可能会很多,目前 600 左右。

    2. 更新频率

    更新主要是班级和学生的新增,查询较多。

    3. 数据库选型

    是 PostgreSQL
    11 条回复    2022-07-11 11:30:58 +08:00
    thinkershare
        1
    thinkershare  
       2022-07-10 01:00:47 +08:00   1
    方式一: 有很多种办法可以保持事务一致性, 这个是数据库并发写入时保证一致性的基本操作了.
    方式二: 并不会, 因为你这个结构就不可能有太多数据, 只要索引设计的正确, 不会有啥性能问题
    书籍嘛: 我想到的有: 数据库概论(本科的教材), 高性能 MySQL, 数据库索引设计与优化, MongoDB 相关的设计原则, 对照一下关系数据库和 NoSQL 数据库的差异, 然后思考在 2 种模式下如何平衡性能 /一致性, 每一种选择都有自己的缺陷, 要根据你的业务场景寻求最佳 Schema. 有时候并不要求最佳, 甚至只需要有效就行. 等性能出了问题再去有针对的优化, 看的稍微远一点, 但不要太远.
    noparking188
        2
    noparking188  
       2022-07-10 01:05:12 +08:00   2
    可以补充:
    1. 数据量级预估
    2. 更新频率
    3. 数据库选型

    一:
    classesCount 和 studentsCount 可以单独建表存
    二:
    补充一下
    lance6716
        3
    lance6716  
       2022-07-10 01:19:24 +08:00 via Android   2
    新加的这个计数引发的锁冲突,可能比你想象中的性能提升还要大
    CEBBCAT
        4
    CEBBCAT  
       2022-07-10 01:42:58 +08:00 via iPhone
    鉴于一个学校不会有 1k 个班,一个班平均不会有 1k 个人,我认为可以先用 count 的方法做。简单
    mxT52CRuqR6o5
        5
    mxT52CRuqR6o5  
       2022-07-10 05:42:37 +08:00 via Android
    点赞系统吗?主要看场景,就比如点赞这个场景数量有点差错没关系的
    lanlanye
        6
    lanlanye  
       2022-07-10 06:03:12 +08:00
    方案应该根据业务情况选择,考虑能否接受统计不一致的问题,通常来说高可用和强一致你只能追求一个。
    另外就是除非可预见上线时就需要面临很大压力,否则用 count 会简单些。(压力大不大可以用测试来量化)
    ktqFDx9m2Bvfq3y4
        7
    ktqFDx9m2Bvfq3y4  
       2022-07-10 06:36:23 +08:00 via iPhone   1
    我就做了,新增或删除学生后发消息到队列然后异步更新即可。尤其是 saas 更应该这么做。
    lmshl
        8
    lmshl  
       2022-07-10 17:07:21 +08:00   1
    你的数据量级我建议直接 count ,除非后面加个万字

    我也遇到了类似的场景,需要统计客户在我们平台存放了多少数据,我们是多租户 SaaS 软件。不同租户一般几千到几千万不等,冷热分区明显。根据我的场景我试用了如下方案

    1. 行数存单独的表,根据事件消息异步更新行数,这个方案必然会产生不一致,解决方案是每日一点重算昨日新增数据。
    2. 客户查询时,优先使用 pg estimate 估算,总行数一万以下走 count ,行数一万以上返回行数表 sum 数值

    在我的场景下,客户对数据量个位数增减敏感,但是对数据总量差 1%~10%并不敏感,所以保证最终一致性就足够了,每日校正一遍
    tairan2006
        9
    tairan2006  
       2022-07-10 20:32:24 +08:00 via Android
    量级太小 直接 count 即可
    zlowly
        10
    zlowly  
       2022-07-10 23:55:24 +08:00
    开始设计的方案肯定是追求一致性的。当性能问题严重才会考虑冗余数据来提高性能。如果你的数据库支持物化视图就可以考虑用它来做统计数据会较为省心,如果性能仍然有问题,这个数据量你就要从需求上考虑,是否真的对实时的统计数据有必要的需求,因为如果不是为了金融级别的结算等需求,应该不会有人介意这个统计数据其实是若干分钟甚至小时前的 ETL 加工数据,那方案就多了去了。
    SethShi
        11
    SethShi  
       2022-07-11 11:30:58 +08:00
    select `cmapus`.*, (select count(*) from `classes` where `cmapus`.`id` = `classes`.`campus_id`) as `classesCount` from `cmapus`
    关于     帮助文档   span class="snow">   自助推广系统     博客     API     FAQ     Solana     5178 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 24ms UTC 09:16 PVG 17:16 LAX 02:16 JFK 05:16
    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