优化mysql计数查询

and*_*bd1 13 mysql sql query-optimization

有没有办法进一步优化这个或者我是否应该满足于计算11M行需要9秒?

devuser@xcmst > mysql --user=user --password=pass -D marctoxctransformation -e "desc record_updates"                                                                    
+--------------+----------+------+-----+---------+-------+
| Field        | Type     | Null | Key | Default | Extra |
+--------------+----------+------+-----+---------+-------+
| record_id    | int(11)  | YES  | MUL | NULL    |       | 
| date_updated | datetime | YES  | MUL | NULL    |       | 
+--------------+----------+------+-----+---------+-------+
devuser@xcmst > date; mysql --user=user --password=pass -D marctoxctransformation -e "select count(*) from record_updates where date_updated > '2009-10-11 15:33:22' "; date                         
Thu Dec  9 11:13:17 EST 2010
+----------+
| count(*) |
+----------+
| 11772117 | 
+----------+
Thu Dec  9 11:13:26 EST 2010
devuser@xcmst > mysql --user=user --password=pass -D marctoxctransformation -e "explain select count(*) from record_updates where date_updated > '2009-10-11 15:33:22' "      
+----+-------------+----------------+-------+--------------------------------------------------------+--------------------------------------------------------+---------+------+----------+--------------------------+
| id | select_type | table          | type  | possible_keys                                          | key                                                    | key_len | ref  | rows     | Extra                    |
+----+-------------+----------------+-------+--------------------------------------------------------+--------------------------------------------------------+---------+------+----------+--------------------------+
|  1 | SIMPLE      | record_updates | index | idx_marctoxctransformation_record_updates_date_updated | idx_marctoxctransformation_record_updates_date_updated | 9       | NULL | 11772117 | Using where; Using index | 
+----+-------------+----------------+-------+--------------------------------------------------------+--------------------------------------------------------+---------+------+----------+--------------------------+
devuser@xcmst > mysql --user=user --password=pass -D marctoxctransformation -e "show keys from record_updates"
+----------------+------------+--------------------------------------------------------+--------------+--------------+-----------+-------------+----------+--------+------+------------+---------+
| Table          | Non_unique | Key_name                                               | Seq_in_index | Column_name  | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment |
+----------------+------------+--------------------------------------------------------+--------------+--------------+-----------+-------------+----------+--------+------+------------+---------+
| record_updates |          1 | idx_marctoxctransformation_record_updates_date_updated |            1 | date_updated | A         |        2416 |     NULL | NULL   | YES  | BTREE      |         | 
| record_updates |          1 | idx_marctoxctransformation_record_updates_record_id    |            1 | record_id    | A         |    11772117 |     NULL | NULL   | YES  | BTREE      |         | 
+----------------+------------+--------------------------------------------------------+--------------+--------------+-----------+-------------+----------+--------+------+------------+---------+
Run Code Online (Sandbox Code Playgroud)

更新 - 我的解决方案在这里:http: //code.google.com/p/xcmetadataservicestoolkit/wiki/ResumptionToken

Bre*_*ley 21

如果mysql必须计算11M行,那么加速简单计数真的没有多少办法.至少不要让它达到低于1秒的速度.你应该重新考虑如何计算.一些想法:

  1. 向表中添加自动增量字段.它看起来你不会从表中删除,所以你可以使用简单的数学来找到记录计数.选择初始较早日期的最小自动增量编号和后一日期的最大值,并从另一个中减去一个以获得记录计数.例如:

    SELECT min(incr_id) min_id FROM record_updates WHERE date_updated BETWEEN '2009-10-11 15:33:22' AND '2009-10-12 23:59:59';
    SELECT max(incr_id) max_id FROM record_updates WHERE date_updated > DATE_SUB(NOW(), INTERVAL 2 DAY);`
    
    Run Code Online (Sandbox Code Playgroud)
  2. 创建另一个表,总结每天的记录数.然后,您可以查询该表以获取总记录.每年只有365条记录.如果您需要更精细的时间,请查询摘要表中的整天,并查询当前表,仅查看开始日和结束日的记录计数.然后将它们全部加在一起.

如果数据没有改变,它看起来不像,那么汇总表将易于维护和更新.它们将大大加快速度.

  • +1汇总表建议.在这种情况下,您可以选择反规范化一些可用于生成所需数字的少量信息.正确维护冗余数据时要非常小心 (2认同)

ajr*_*eal 5

由于>'2009-10-11 15:33:22'包含了大部分记录,
我建议做一个反向匹配<'2009-10-11 15:33:22'(mysql工作越少越好,涉及的行越少)

select 
  TABLE_ROWS -
  (select count(*) from record_updates where add_date<"2009-10-11 15:33:22") 
from information_schema.tables 
where table_schema = "marctoxctransformation" and table_name="record_updates"
Run Code Online (Sandbox Code Playgroud)

你可以结合编程语言(比如bash shell)
来使这个计算更聪明......
例如先做执行计划来计算哪个比较会使用较小的行

根据我的测试(大约10M记录),正常比较大约需要3秒,
现在减少到大约0.25 秒


Jef*_*and 5

由于版本控制,MySQL 不会“优化”InnoDB 中的 count(*) 查询。索引中的每个项目都必须进行迭代和检查,以确保版本正确显示(例如,不是开放提交)。由于可以在数据库中修改任何数据,因此范围选择和缓存将不起作用。但是,您可以通过使用触发器来实现。这种疯狂有两种方法。

第一种方法存在减慢事务速度的风险,因为它们都不能真正并行运行:使用插入后和删除后触发器来递增/递减计数器表。第二个技巧:使用这些插入/删除触发器来调用存储过程,该存储过程将输入到外部程序中,该外部程序类似地向上和向下调整值,或者对非事务表进行操作。请注意,如果发生回滚,这将导致数字不准确。

如果您不需要确切的数字,请查看此查询:

select table_rows from information_schema.tables
where table_name = 'foo';
Run Code Online (Sandbox Code Playgroud)

差异示例:count(*): 1876668,table_rows: 1899004。table_rows 值是一个估计值,即使数据库没有更改,每次也会得到不同的数字。

出于我自己的好奇心:您需要每秒更新的确切数字吗?如果是这样,为什么?


Dor*_*ron 2

您应该在“date_updated”字段上添加索引。

如果您不介意更改表的结构,您可以做的另一件事是使用“int”格式的日期时间戳而不是“datetime”格式,而且它可能会更快。如果您决定这样做,查询将是

select count(date_updated) from record_updates where date_updated > 1291911807
Run Code Online (Sandbox Code Playgroud)

  • -1 建议将日期时间存储为“日期时间”字段以外的任何内容,即使它更快。巨大的成本,微小的(如果有的话)收益。 (2认同)