› MySQL 5.5 Community Server
› MySQL 5.6 Community Server
› Percona Configuration Wizard
› XtraBackup 搭建主从复制
Great Sites on MySQL
› Percona
› MySQL Performance Blog
› Severalnines
推荐管理工具
› Sequel Pro
› phpMyAdmin
推荐书目
› MySQL Cookbook
MySQL 相关项目
› MariaDB
› Drizzle
参考文档
› http://mysql-python.sourceforge.net/MySQLdb.html
Alucns
V2EX  ›  MySQL

SELECT COUNT(*)超级慢,讨论一下解决方案

  •  
  •   Alucns · May 14, 2018 · 21161 views
    This topic created in 3104 days ago, the information mentioned may be changed or developed.

    使用场景: 1、回复表 1 亿多数据,每天增长 1 万+; 2、发回复的时候得统计回复用户回复数量;

    使用的语句:SELECT COUNT(*) FROM answer WHERE uid='10' 查询结果:6963911 使用时间:101.618 秒 其中 uid 已经索引,也用过其它 COUNT(其它列字段) 查询一样很慢; 如果用 aid 已经索引,aid='10' 查出结果数量少,查询很快;

    还能通过优化 SQL 语句来优化吗,如果不行,只能通过 insert +1 及 delete -1 来解决了。

    67 replies  •  2018-05-16 09:47:55 +08:00
    3dwelcome
    3dwelcome
    May 14, 2018
    1
    用 MyISAM 表示速度奇快。而且用 mysql_list_tables 之类的结构查询,就能直接看一共有多少记录。
    linpf
    linpf
    May 14, 2018
    3
    单表数据上亿,早就该拆表了吧。
    Alucns
    Alucns
    OP
    May 14, 2018
    4
    @3dwelcome 现在就是用 MyISAM,数据量小很快的。
    kran
    kran
    May 14, 2018 via iPhone
    5
    explain 一下
    yiqiao
    yiqiao
    May 14, 2018❤️1
    6
    回复数量不少应该记录在类似文章表里面吗。。。
    RorschachZZZ
    RorschachZZZ
    May 14, 2018
    7
    按照用户分组全量统计一次,保存起来。以后关于用户回复数的增删改查都来操作这次保存的数据。而且你这个表太大了是个隐患,最好拆。
    zhaishunqi
    zhaishunqi
    May 14, 2018
    8
    我印象中,select count(*) 和 select count(1) 的效率,在数据量大的情况下,差别还是很大的.
    只是前者后者用法有略微的差别,会用就能避坑。
    q397064399
    q397064399
    May 14, 2018❤️1
    9


    如果并发量不是很大的话,+1 -1 应该是个不错的选择
    glues
    glues
    May 14, 2018
    10
    这个问题快成日经贴了,不要用 MySQL 不就好了
    doubleflower
    doubleflower
    May 14, 2018
    11
    这种东西明显是要保存每个分组的 count,以后发贴+1
    mchl
    mchl
    May 14, 2018
    12
    试一下
    SELECT COUNT(uid) FROM answer WHERE uid='10';
    FrailLove
    FrailLove
    May 14, 2018
    13
    物化视图查询重写 了解一下
    ourzhang
    ourzhang
    May 14, 2018
    14
    COUNT ( 1 ) 试试。
    xi4oh4o
    xi4oh4o
    May 14, 2018❤️1
    15
    如果需求不用很精准的话,可以尝试用 explain select count(*) from table 取
    af463419014
    af463419014
    May 14, 2018
    16
    分区表,了解一下

    比如: PARTITION BY HASH (uid) PARTITIONS 1000
    sagaxu
    sagaxu
    May 14, 2018 via Android
    17
    主流关系数据库 count 需要遍历,时间复杂度是 O(n)
    akstrom
    akstrom
    May 14, 2018
    18
    SELECT COUNT(uid) FROM answer WHERE uid=10;
    zqguo
    zqguo
    May 14, 2018
    19
    回答 count(1)的认真看过题主的问题吗?
    tianzx
    tianzx
    May 14, 2018 via Android
    20
    m
    VoidChen
    VoidChen
    May 14, 2018
    21
    count 个字段,不要 count * 。然后就是搞分区。还不行就上 mpp,我用起来感觉 mpp 比 oracle 要快一丢丢,千万级数据的表,没实测,有时间打算试一下
    puritania
    puritania
    May 14, 2018❤️2
    22
    这种东西用 redis 做个计数器不就完了吗,动态更新计数器。
    VoidChen
    VoidChen
    May 14, 2018
    23
    没看到下面还有详细的不好意思。。。我觉得这种回复用 count 有点反人类了,给个字段 update +1 -1 吧。。。。
    linbiaye
    linbiaye
    May 14, 2018
    24
    说 count(*)不如 count(1), count 字段的都是瞎说,加了索引还慢就考虑其他方案把。看看有没有可能自己维护计数器,估计跑不了拆分了。
    kkeiko
    kkeiko
    May 14, 2018
    25
    说不要 count(*) 的 估计不知道查询优化器这个东西吧,至于楼主这个问题,拆表吧。
    Alucns
    Alucns
    OP
    May 14, 2018
    26
    @mchl 这样也是很慢的,试过了,估计是内容太多的问题。
    dante3imin
    dante3imin
    May 14, 2018
    27
    我看好多人说 count (*)和 count (字段)的区别,之前看到一篇[博文]( https://blog.ti-node.com/blog/6343811157316337664)
    jelinet
    jelinet
    May 14, 2018❤️1
    28
    我也维护了一个计数的,但因为计数前业务复杂,写了很多逻辑代码,后来又加了锁,有时候真想 count 了之,但理智告诉我绝对不行,继续维护吧。
    lihongming
    lihongming
    May 14, 2018 via Android
    29
    记得 discuz 的做法是+-1,且定时重算
    defclass
    defclass
    May 14, 2018 via Android
    30
    uid 是 string 还是 integer ? 遇到过一个类似的坑
    elarity
    elarity
    May 14, 2018
    31
    @VoidChen 说反了,对于 innodb 引擎,毫无疑问用 count(*),count ( col )弄不好会更慢
    Raymon111111
    Raymon111111
    May 14, 2018❤️2
    32
    讨论 count(*) 和 count(1) 根本没找到重点

    没有使用场景?

    如果仅仅希望有一个数量, 两个方法

    简单一点直接 redis

    复杂一点, 把索引带上时间, 今天以前的回复数量是固定的, 每天零点的时候用离线任务把今天以前的回复数量全统计一遍扔到一个地方. 然后再实时计算今天的回复数量, 索引踩上 (uid,ctime) uid=#{uid} and ctime >#{betweenTime} and ctime< #{endTime}

    另外一个上亿每天增长千万的表应该要分库分表或者归档
    kn007
    kn007
    May 14, 2018❤️1
    33
    建议读取一次后,存入 noSQL,做 incr 和 decr。
    aa6563679
    aa6563679
    May 14, 2018 via iPhone
    34
    只有当天的数据实时统计,以前的数据专门换个表归档
    scnace
    scnace
    May 14, 2018 via Android
    35
    鸟书上推荐的是维护字段+- 1 只是这样就要引入锁机制(
    yangqi
    yangqi
    May 14, 2018
    36
    慢说明索引已经不优化了,先分析优化下索引。
    lgh
    lgh
    May 14, 2018 via iPhone
    37
    @defclass +1,这个 uid 字段的类型到底是数字还是字符串真的要搞清楚先
    rahuahua
    rahuahua
    May 14, 2018❤️1
    38
    uid=10 的数量已经近 700W,(如果数据库服务器内存不是足够大 + 要检索的索引文件不在内存里)索引本身的检索就会造成大量的 I/O,从数据库层面已经无法再优化了。只能从应用层优化了
    is99zsq
    is99zsq
    May 14, 2018❤️1
    39
    添加一个表,记录累计值

    然后每日做个批量,

    查询变成 历史上的累计值+本日实时统计值
    vus520
    vus520
    May 14, 2018❤️2
    40
    我的老哥,ES 了解一下
    kavana
    kavana
    May 15, 2018
    41
    收藏 count*处理办法
    agostop
    agostop
    May 15, 2018❤️1
    42
    最懒的办法,就是你先手动 count,然后再做个触发器,incr 和 decr
    VoidChen
    VoidChen
    May 15, 2018
    43
    @elarity 好的,我去学习下
    Alucns
    Alucns
    OP
    May 15, 2018
    44
    Alucns
    Alucns
    OP
    May 15, 2018
    45
    @defclass 这个 UID 肯定是 int 了,存放用户 ID 的不可能用 string,默认都不为 null
    defclass
    defclass
    May 15, 2018
    46
    @Aluhao 可以看 explain 先看看. 我之前遇到的一个坑是, 类型写错了, 无法使用到索引. 如果是 Int , sql 应该是 `uid= 10` ?
    checgg
    checgg
    May 15, 2018❤️1
    47
    目前怀疑是 myisam 表锁的问题。
    建议把数据库 copy 到本地,在没有写和更新的情况下查询一下。
    Reign
    Reign
    May 15, 2018
    48
    话说, https://www.v2ex.com/t/433836 ,每天回复增长 1 万+的理财论坛,居然还没钱只能用 SELECT COUNT(*), 你咋不专门雇个员工一行一行的去数呢?
    eslizn
    eslizn
    May 15, 2018❤️1
    49
    @af463419014 实际经验,少用分区表。线上 ddl 是噩梦
    reus
    reus
    May 15, 2018
    50
    内存不够。
    Alucns
    Alucns
    OP
    May 15, 2018
    51
    @defclass
    没法上传图片;
    WHERE `uid`='10' 和 WHERE uid='10' 差别不大;
    defclass
    defclass
    May 15, 2018 via Android
    52
    我的意思是 10 不要引号
    plko345
    plko345
    May 15, 2018 via Android
    53
    @VoidChen 请问测试时用什么工具,数据是模拟的吗?
    icebay
    icebay
    May 15, 2018
    54
    @scnace #35 请问书名是什么。
    darklowly
    darklowly
    May 15, 2018❤️1
    55
    这类表一般是插入后不怎么修改,

    1 分表
    2 新建统计表

    可以用通过触发器,在插入的时候触发一下更新统计表
    crist
    crist
    May 15, 2018
    56
    SELECT COUNT (. 人 .)
    colincat
    colincat
    May 15, 2018❤️1
    57
    个人经验是需要单独创建一个表存数量,然后累加即可,这个也不要特别精准
    shiny
    shiny
    May 15, 2018
    58
    MySQL 是跑在 SSD 上吗
    Wysten
    Wysten
    May 15, 2018❤️1
    59
    前几天刚遇到类似的问题,单表 500W 数据,count(*) 和 count(id) 都很慢。最后用了 Redis Incr,先 count 一次,存入 redis,以后就直接在 redis 加 1 了 。
    scnace
    scnace
    May 15, 2018 via Android❤️1
    60
    @IceBay 高性能 MySQL
    sleshep
    sleshep
    May 15, 2018 via Android
    61
    触发器解决问题
    randyzhao
    randyzhao
    May 15, 2018
    62
    10 号用户评论量有近 700W。。。
    dobelee
    dobelee
    May 15, 2018 via Android❤️1
    63
    InnoDB 的 count 是无解的,如果对精准度没有要求的话可定时统计存储,否则需要维护一个计数器。
    lgh
    lgh
    May 15, 2018 via iPhone
    64
    @Aluhao 你没理解 @defclass 的意思,应该写成 WHERE uid=10,否则 uid 的索引是不会起作用的,很多人叫你 explain 一下,你也没做吗?如果做了也能看出没走索引的。
    fox0001
    fox0001
    May 16, 2018 via Android❤️1
    65
    我很懒,超过 99 条后,就显示 99+
    Alucns
    Alucns
    OP
    May 16, 2018
    66
    @lgh 这个试过了,加了和没加效果一样呢;
    sagaxu
    sagaxu
    May 16, 2018 via Android
    67
    @lgh 用不用索引,看用索引这个字段过滤后,剩下的条数占的比例,如果不够稀疏,比如说大于 10%,索引的质量就很低,走不走索引就不一定了。

    最常见的毫无意义的索引就是性别
    About   ·   Help   ·   Advertise   ·   Blog   ·   API   ·   FAQ   ·   Privacy   ·   Solana   ·   4797 Online   Highest 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 113ms · UTC 06:15 · PVG 14:15 · LAX 22:15 · JFK 01:15
    ♥ Do have faith in what you're doing.