【问题标题】:Why count query runs slower in less data than more data in MyISAM为什么计数查询在较少数据中的运行速度比在 MyISAM 中的更多数据中运行得慢
【发布时间】:2016-09-14 03:03:06
【问题描述】:

我有两个表,user和sku_pro,两个表中的count查询如下

```

mysql> SELECT COUNT(id) FROM user WHERE user.is_active = 1 \G;

计数(id):239568

一组中的 1 行(0.20 秒)

mysql> SELECT COUNT(id) FROM sku_pro WHERE is_agent=1 \G;

计数(id):1254286

集合中的 1 行(0.11 秒)

```

user有61列,运行show table status in user table,结果是

mysql> show table status like 'user' \G; *************************** 1. row *************************** Name: user Engine: MyISAM Version: 10 Row_format: Dynamic Rows: 239606 Avg_row_length: 252 Data_length: 60483836 Max_data_length: 281474976710655 Index_length: 34577408 Data_free: 0 Auto_increment: 239623 Create_time: 2016-08-24 12:01:55 Update_time: 2016-09-14 10:48:10 Check_time: 2016-08-24 12:02:04 Collation: utf8_bin Checksum: NULL Create_options: Comment: sku_pro 有 16 列,运行 show table status in sku_pro table,结果是

mysql> show table status like 'sku_pro' \G; *************************** 1. row *************************** Name: sku_pro Engine: MyISAM Version: 10 Row_format: Fixed Rows: 1281901 Avg_row_length: 53 Data_length: 67940753 Max_data_length: 14918173765664767 Index_length: 52064256 Data_free: 0 Auto_increment: 1988051 Create_time: 2016-09-09 14:06:37 Update_time: 2016-09-14 10:19:39 Check_time: 2016-09-09 14:06:44 Collation: utf8_general_ci Checksum: NULL Create_options: Comment: 1 row in set (0.01 sec)

我不知道为什么 user 中的 count(id) 比 sku_pro 中的 count(id) 慢。

【问题讨论】:

  • 我要做的第一件事是比较每个查询的 EXPLAIN 计划。 user.is_activesku_pro.is_agent 列是否都已编入索引?
  • @Phil 没有索引。也许我已经找到原因了。 user 表中的 row_format 是动态的,但在 sku_pro 表中是固定的。与 sku_pro.is_agent 相比,我需要更多时间来查找 user.is_active 的值。

标签: mysql myisam


【解决方案1】:

简答:缓存。

很长的答案:

先做一些笔记

COUNT(ID) 需要计算 non-NULL ID 的数量。除非您确实需要避免 NULL,否则不要使用该构造。

现在,我将查看您的特定查询,这两个查询看起来都像

SELECT COUNT(id)
    FROM tbl
    WHERE flag=1;

正如您所提到的,这些表是“大”的。这意味着所需的数据/索引块可能会或可能不会缓存在 RAM 中。这一问题可以很容易地使查询运行速度快/慢 10 倍。试试这个:重启mysql,运行两次查询。第二次运行将比第一次快 10 倍。

我假设您已关闭“查询缓存”。如果它在使用中,那会增加一个很大的皱纹。

剖析您的查询

要优化您的查询,您需要这个复合索引:

INDEX(flag, id) -- with the columns in that order.

鉴于此,查询将完全在索引中执行。我们称之为“覆盖”索引。 (MyISAM 和 InnoDB 在这里的行为相同。)由于您为 flag 指定了一个值,它只会对部分索引进行“范围”扫描。 (因此,我们不能轻易说出需要触摸多少个磁盘块。)

如果您只有INDEX(flag),优化器可能会忽略它并进行全表扫描。

全表扫描(如果表未缓存)将在与表大小成正比的时间内运行 (Data_length)。

InnoDB

本讨论假设 WHERE 子句。并使用COUNT(*)

(请原谅我提供一些关于 InnoDB 的无关信息。)

MyISAM 会准确计算每个表中的行数。因此,没有WHERESELECT COUNT(*) FROM myisam_table 总是几乎是瞬时的。 (这个 一个 查询是 MyISAM 始终优于 InnoDB 的唯一一个查询。)

另一方面,InnoDB 不能。这是因为不同的连接可以运行不同的事务,其中任何一个都可能被回滚,从而与任何尝试航位推算精确计数的尝试相混淆。相反,InnoDB 会找到“最小”的索引并对其进行扫描。

InnoDB 保留一个近似的行数,它来自于对 BTree 的少量随机探测。这个数字在SHOW TABLE STATUSinformation_schema.TABLES 中可见。 avg_row_length 是从 Data_length/Rows 派生的,所以它也不精确。而且这两个数字都会不时变化。 (更改频率与最近的版本发生了显着变化。)

With WHERE 子句将两个引擎置于非常相似的基础上。这就涉及到索引是如何工作的。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-09-16
    • 2019-11-13
    • 2017-08-25
    • 1970-01-01
    • 2016-10-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多