我有一个包含数百万条记录的大表,我必须为某个条件执行count(*)
,而且我无法摆脱它。count()
与InnoDB
是非常昂贵的。我一直试图找出不同的配置为MySQL,但都是徒劳的。不能加快计数。应用程序要求结果小于1秒,因为有其他依赖查询运行。
由于InnoDB计数的方式,任何索引都没有帮助。
mysql> EXPLAIN SELECT count(*) FROM `callrequests` WHERE active_call = 1;
+----+-------------+--------------+-------+---------------+-------------+---------+------+---------+--------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+--------------+-------+---------------+-------------+---------+------+---------+--------------------------+
| 1 | SIMPLE | callrequests | index | NULL | active_call | 6 | NULL | 5271135 | Using where; Using index |
+----+-------------+--------------+-------+---------------+-------------+---------+------+---------+--------------------------+
mysql> show index from callrequests;
+--------------+------------+------------------------------+--------------+--------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+--------------+------------+------------------------------+--------------+--------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| callrequests | 0 | PRIMARY | 1 | id | A | 5271135 | NULL | NULL | | BTREE | | |
| callrequests | 0 | PRIMARY | 2 | campaign_id | A | 5271135 | NULL | NULL | | BTREE | | |
| callrequests | 0 | unique_contact | 1 | campaign_id | A | 4849 | NULL | NULL | | BTREE | | |
| callrequests | 0 | unique_contact | 2 | contact_id | A | 5271135 | NULL | NULL | | BTREE | | |
| callrequests | 0 | unique_contact | 3 | contact | A | 5271135 | NULL | NULL | | BTREE | | |
| callrequests | 1 | fk_callrequest_campaign1_idx | 1 | campaign_id | A | 10 | NULL | NULL | | BTREE | | |
| callrequests | 1 | index4 | 1 | campaign_id | A | 2506 | NULL | NULL | | BTREE | | |
| callrequests | 1 | index4 | 2 | contact | A | 5271135 | NULL | NULL | | BTREE | | |
| callrequests | 1 | phonbook_id_index | 1 | phonebook_id | A | 10 | NULL | NULL | | BTREE | | |
| callrequests | 1 | dnc_group_id_index | 1 | dnc_group_id | A | 2 | NULL | NULL | | BTREE | | |
| callrequests | 1 | active_call | 1 | campaign_id | A | 12 | NULL | NULL | | BTREE | | |
| callrequests | 1 | active_call | 2 | active_call | A | 16 | NULL | NULL | YES | BTREE | | |
| callrequests | 1 | call_status | 1 | call_status | A | 2518 | NULL | NULL | | BTREE | | |
| callrequests | 1 | call_status | 2 | processed | A | 2518 | NULL | NULL | | BTREE | | |
| callrequests | 1 | call_status | 3 | active_call | A | 2518 | NULL | NULL | YES | BTREE | | |
+--------------+------------+------------------------------+--------------+--------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
服务器为Xeon machine with 12 CPU cores and 64 GB RAM dedicated 5.6.14-62.0 Percona Server
我的innodb_buffer_pool_size
是38 GB
,所有数据都位于innodb缓冲池中。
5条答案
按热度按时间x33g5p2x1#
innodb和myisam在计数方面的差异
请注意,使用
WHERE
进行计数在InnoDB中并不比在MyISAM中慢。使用MyISAM可以更快地计算,因为此数字存储在MyISAM的表元数据中。
例如,如果您有一个带有
WHERE
约束条件的查询:查询需要访问两个存储引擎中的表数据,MyISAM和InnoDB之间应该没有显著的性能差异。
关于你的具体问题
请注意,您的查询没有使用任何合适的索引,这并不是因为InnoDB“更喜欢”全表扫描,而是因为不存在合适的索引。
您有一个组合索引
(campaign_id, active_calls)
,但是active_calls
是索引的第二部分,只要第一部分没有在查询中使用,MySQL就不容易访问第二部分。对于这个简单的计数查询,您需要的是仅在这一列上的另一个索引
(active_calls)
,这样它应该运行得很快。qnyhuwrf2#
我发现了提高count(*)性能的方法:
b4lqfgs43#
Innodb表的COUNT()- Percona数据库性能博客https://www.percona.com/blog/2006/12/01/count-for-innodb-tables/
因此,如果您有类似**SELECT COUNT的查询()来自用户对于 MyISAM 来说,这将是
faster
(MEMORY和其他一些)表,因为它们只是从存储值中读取表中的行数。但是,Innodb 需要执行full table scan
或full index scan
,因为它没有这样的计数器,它也不能通过Innodb表的简单计数器来解决,因为不同的事务可能看到表中不同数量的行。如果您有类似SELECT COUNT()FROM IMAGE WHERE USER_ID=5**的查询,则通过执行
index range scan
,将以相同的方式对MyISAM和Innodb表执行此查询。根据不同的条件,对于MyISAM和Innodb,此查询可能会更快或更慢。因此,请记住Innodb对于所有COUNT()查询并不慢,但仅适用于不带WHERE子句的COUNT(*)查询的非常特定的情况。
2ledvvac4#
我发现COUNT(一个二级索引)的速度很快,但我需要尝试一下,以鼓励mySQL使用PRIMARY索引。
我能够使用PRIMARY索引,并在没有id〉0的情况下获得显著的速度提升:
我还必须以这样一种方式编写查询,即列只被引用一次(用于mysql-rewriter插件),并发现这种类似的查询工作(成本是+10%的时间比上述)
(In mySQL 5.7),它适用于辅助索引、可空/不可空索引和唯一/非唯一索引。在我的简单测试案例中,它显示所需时间减少了27倍。
我会把这个作为Mark Khor's应答器下的一个评论(因为它是高度衍生的),但是没有足够的代表。
nsc4cvqm5#
我也遇到过类似的问题。我也有一个int型自动增量主键列。所以我这样做来解决这个问题:
还有@mark-khor(其中id〉0)的建议对我也有用,但我不明白为什么,所以我选择了max(id)-value...