首页   注册   登录
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
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
V2EX  ›  MySQL

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

  •  
  •   Aluhao · 133 天前 · 6822 次点击
    这是一个创建于 133 天前的主题,其中的信息可能已经有所发展或是发生改变。

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


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

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

    没有使用场景?

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

    简单一点直接 redis

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

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

    然后每日做个批量,

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

    1 分表
    2 新建统计表

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

    最常见的毫无意义的索引就是性别
    关于   ·   FAQ   ·   API   ·   我们的愿景   ·   广告投放   ·   感谢   ·   实用小工具   ·   3460 人在线   最高记录 3762   ·  
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.1 · 22ms · UTC 03:16 · PVG 11:16 · LAX 20:16 · JFK 23:16
    ♥ Do have faith in what you're doing.
    沪ICP备16043287号-1