利用主键快速聚合单表数千万MySQL的非索引字段

场景

很多时候,为了写入效率,在生产环境里业务大表(单表千万行以上)是不允许随意加索引的。而且就算加索引,因为锁表问题,对业务也是有影响的。

我们一般会用离线的从库进行一些数据统计,而生产环境的索引并不能很好的满足统计的需求。没有相应索引,我们又如何高效的进行字段聚合呢?

利用主键索引进行range,然后再进行聚合。

举例

表名:tbl_pay_orders

行数:29,311,362

引擎:InnoDB

字段:

+------------------------+---------------------+------+-----+---------+----------------+

| Field                  | Type                | Null | Key | Default | Extra          |

+------------------------+---------------------+------+-----+---------+----------------+

| id                    | bigint(20)          | NO  | PRI | NULL    | auto_increment |

| amt                    | bigint(20)          | NO  | | NULL    | |

| created_time                    | int(11)          | NO  | | NULL    | |

需求:按天统计tbl_pay_orders金额

不考虑索引的情况下,SQL像这样写:

select left(from_unixtime(created_time),10) as day, sum(amt) from tbl_pay_orders group by day;

由于created_time没有索引,MySQL 索引提示如下:

mysql> desc select left(from_unixtime(created_time),10) as day, sum(amt) from tbl_pay_orders group by day;

+----+-------------+------------------------+------+---------------+------+---------+------+------+---------------------------------+

| id | select_type | table                  | type | possible_keys | key  | key_len | ref  | rows | Extra                          |

+----+-------------+------------------------+------+---------------+------+---------+------+------+---------------------------------+

|  1 | SIMPLE      | tbl_pay_orders | ALL  | NULL          | NULL | NULL    | NULL | 29311362 | Using temporary; Using filesort |

+----+-------------+------------------------+------+---------------+------+---------+------+------+---------------------------------+

1 row in set (0.00 sec)

全表扫描,共29311362行。

那我们如果利用主键呢?SQL可能像这样:

select left(from_unixtime(created_time),10) as day, sum(amt) from tbl_pay_orders where id >= 18000000 and id < 19000000 group by day;

索引提示是这样的:

mysql> desc select left(from_unixtime(created_time),10) as day, sum(amt) from tbl_pay_orders where id >= 18000000 and id < 19000000 group by day;

+----+-------------+------------------------+-------+---------------+---------+---------+------+---------+----------------------------------------------+

| id | select_type | table                  | type  | possible_keys | key    | key_len | ref  | rows    | Extra                                        |

+----+-------------+------------------------+-------+---------------+---------+---------+------+---------+----------------------------------------------+

|  1 | SIMPLE      | tbl_pay_orders | range | PRIMARY      | PRIMARY | 8      | NULL | 1879550 | Using where; Using temporary; Using filesort |

+----+-------------+------------------------+-------+---------------+---------+---------+------+---------+----------------------------------------------+

1 row in set (0.01 sec)

我们发现,仍然没有走任何索引(当然了,因为我们并没有改变索引),但是扫描的行数一下子降到了1879550了。在这个量级我们就可以用MySQL方便的做聚合了

问题来了,我怎么知道每天的id范围呢?

答案是离线先按天建索引,生成一个day到start_id的映射关系。

for i in file("idx.txt"):

    last_rid, last_day = i.strip().split(",")

wf = open('idx.txt', 'a')

sql = "select id,left(from_unixtime(created_time), 10) as day from tbl_pay_orders where id > %s and id < %s" % (last_rid, int(last_rid) + 1000000)

items = dao.select_sql(sql)

for item in items:

    item["day"] = item["day"].replace("-", "").replace(" ", "")

    if item["day"] != last_day:

        wf.write("%s,%s\n" % (item["id"], item["day"]))

        last_day = item["day"]

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

  • 什么是数据库? 数据库是存储数据的集合的单独的应用程序。每个数据库具有一个或多个不同的API,用于创建,访问,管理...
    chen_000阅读 4,183评论 0 19
  • 1、MySQL启动和关闭(安装及配置请参照百度经验,这里不再记录。MySQL默认端口号:3306;默认数据类型格式...
    强壮de西兰花阅读 806评论 0 1
  • 此文是根据杨尚刚在【QCON高可用架构群】中,针对MySQL在单表海量记录等场景下,业界广泛关注的MySQL问题的...
    FrancisSoung阅读 2,821评论 1 79
  • 1.表中的任何列都可以作为主键, 只要它满足以下条件:任意两行都不具有相同的主键值;每一行都必须具有一个主键值( ...
    Cherryjs阅读 857评论 0 0
  • (围炉话杰出—模式)商业模式在创新,网行天下没有样。好的平台快打造,可惜人源不入行。人生往往失机会,有了行程不敢做...
    甘朝武阅读 214评论 0 0

友情链接更多精彩内容