lun*_*unr 9 mysql performance count query-performance
我有一个超过 15m 行的表。我需要总行数。所以:
SELECT COUNT(*) FROM thetable;
Run Code Online (Sandbox Code Playgroud)
大约需要 50 秒才能完成。解释给了我Select tables optimized away。我想这意味着只有使用索引才能找到结果,那为什么还需要这么长时间?以下是有关id列上索引的一些信息(不可为空):
索引类型:BTREE(聚集)
基数:14623100
唯一:是
如何提高此查询的性能?谢谢。
注:数据库为 MySQL 5.7.1,使用 InnoDB 引擎。
编辑:
创建语句:
CREATE TABLE `properties` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`address` varchar(255) DEFAULT NULL,
`locality` varchar(50) DEFAULT NULL,
`latitude` decimal(13,9) DEFAULT NULL,
`longitude` decimal(13,9) DEFAULT NULL,
`state` varchar(10) DEFAULT NULL,
`created_at` datetime DEFAULT NULL,
`updated_at` datetime DEFAULT NULL,
.....
PRIMARY KEY (`id`),
KEY `index_properties_on_address` (`address`),
KEY `index_properties_on_latitude` (`latitude`),
KEY `index_properties_on_longitude` (`longitude`),
KEY `index_properties_on_state` (`state`),
KEY `index_properties_on_created_at` (`created_at`),
.....
) ENGINE=InnoDB AUTO_INCREMENT=28267712 DEFAULT CHARSET=utf8 ROW_FORMAT=COMPRESSED;
Run Code Online (Sandbox Code Playgroud)
注意:我省略了一些行,有 44 列。
解释计划:
+----+-------------+-------+------------+------+-- -------------+------+---------+------+------+----- -----+------------------------------+ | 身份证 | 选择类型 | 表| 分区 | 类型 | 可能的密钥| 关键| 密钥长度 | 参考 | 行 | 过滤| 额外 | +----+-------------+-------+------------+------+-- -------------+------+---------+------+------+----- -----+------------------------------+ | 1 | 简单 | 空 | 空 | 空 | 空 | 空 | 空 | 空 | 空 | 空 | 选择优化掉的表 | +----+-------------+-------+------------+------+-- -------------+------+---------+------+------+----- -----+------------------------------+
回到默认情况下 mysql 在事务上不健全的时候(当人们经常使用 myISAM 表而不是 InnoDB 时,因为这是默认值,或者回到过去,因为它还不存在)“SELECT * FROM some_table”没有任何过滤子句是人们对 mySQL 在其他数据库引擎上更快的查询类型之一。
在事务安全的环境中,一般来说,数据库引擎需要检查每一行,并确保它对当前会话可见(即它不是尚未提交(或未提交)的事务的一部分此会话活动事务的开始)或当前正在回滚) - 检查每一行意味着需要执行表扫描或(如果存在)聚集索引扫描。
这将是可能的发动机来跟踪每一个对象可见的行数的每个活动会话/交易,但据推测,设计师们不是判断这是值得参与,所以我认为它没有通常被认为是额外的处理实际- 我可以想象会有一些相当复杂的锁定要求来处理并发性,这会过多地损害其他操作的性能。您可以通过保留一个表来实现这一点,其中记录了感兴趣表中的行数,并让您的所有代码精心维护该值,但这会相当麻烦,并且可能由于以下原因而过于容易出错错误意味着计数会随着时间的推移而偏离真实(并且您可能会在应用程序层添加潜在的死锁源和/或锁定瓶颈)。
使用行级安全性的情况使这更加复杂 - 以及需要检查与当前事务相关的行/页的状态,然后引擎也需要再次检查当前用户,因为安全规则是动态缓存这些信息是不切实际的,每次都需要扫描以防万一。下一版本 ( https://msdn.microsoft.com/en-us/library/dn765131.aspx )中将行级安全添加到 MS SQL Server 中,并且已经存在于 postgres ( http://www.postgresql .org/docs/9.5/static/ddl-rowsecurity.html),我不知道它在其他 RDBMS 中的状态。
小智 5
补充@david-spillett答案,您可以通过将查询中的 替换count(*)为 a来更改您的查询count(id),变成:
SELECT COUNT(id) FROM thetable;
Run Code Online (Sandbox Code Playgroud)
因为id列不为空,所以有索引(实际上它是主键),这意味着所有行都不为空,因此,有多少id行就有多少 s。
但是,即使您替换count(*)为count(0),或者count("Hi, I'm a row")您也会获得相同的性能,因为它们在内部会导致相同的操作。EXPLAIN EXTENDED ...您可以通过比较所有查询的结果来检查它:
EXPLAIN EXTENDED SELECT COUNT(*) FROM thetable;
EXPLAIN EXTENDED SELECT COUNT(id) FROM thetable;
EXPLAIN EXTENDED SELECT COUNT(0) FROM thetable;
EXPLAIN EXTENDED SELECT COUNT("Hi, I'm a row") FROM thetable;
Run Code Online (Sandbox Code Playgroud)
目前对于InnoDB来说,select count(<whatever>) from table_name ;不带任何条件,并不是最佳实践。
这种类型的查询在以下情况下表现更好:
varchar(200))上,但不要只是为了改进这种类型的选择而添加它。这是因为索引越小,InnoDB 需要扫描的数据就越少;WHERE条件,缩小要计数的行数。这是您最好的选择。| 归档时间: |
|
| 查看次数: |
17215 次 |
| 最近记录: |