当前位置: 首页 > 面试题库 >

尽管覆盖索引,MySQL MyISAM慢count()查询

季阳朔
2023-03-14
问题内容

我正在拔头发试图找出我做错了什么。该表非常简单:

CREATE TABLE `icd_index` (
  `icd` char(5) NOT NULL,
  `core_id` int(11) NOT NULL,
  `dx_order` tinyint(4) NOT NULL,
  PRIMARY KEY (`icd`,`dx_order`,`core_id`),
  KEY `core` (`core_id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;

如您所见,我创建了一个覆盖该表的所有三列的覆盖索引,并在上添加了一个core_id潜在索引的附加索引。这是一对多链接表,每个链接表都core_id映射到一个或多个链接表icd。该表包含6500万行。

所以,这就是问题所在。假设我想知道有多少人的icd代码为“ 25000”。[如果您想知道的话,那就是糖尿病]。我写了一个查询,看起来像这样:

SELECT COUNT(core_id) FROM icd_index WHERE icd='25000'

这需要60秒钟以上的时间才能执行。我曾以为,由于icd列在涵盖索引中排在第一位,因此算起来会很快。

更令人困惑的是,一旦我运行了一次查询,它现在就会非常快速地运行。我以为那是因为查询被缓存了,但是即使I RESET QUERY CACHE,查询现在也可以在几分之一秒内运行。但是,如果我等待足够长的时间,它似乎又会变慢-而且我不知道为什么。

我缺少明显的东西。我是否需要一个索引icd?这是6500万行所能达到的最佳性能吗?为什么运行查询然后重置缓存会影响速度?结果是否存储在索引中?

编辑:我正在运行MySQL 5.6(以防万一)。

这是EXPLAIN查询的:

id  select_type table   type    possible_keys   key key_len ref rows    Extra
1   SIMPLE  icd_index   ref PRIMARY PRIMARY 15  const   910104  Using where; Using index

问题答案:

这是怎么回事。

The SELECT COUNT (...) icd_index where icd='25000'

将使用索引,该索引是与数据分开的BTree。但是它以这种方式扫描:

  1. 查找具有icd =‘25000’的第一个条目。这几乎是瞬时的。
  2. 向前扫描,直到找到icd的变化。这将仅扫描索引,而不接触数据。根据EXPLAIN,将有大约910,104个索引条目要扫描。

现在,让我们看一下该索引的BTree。根据索引中的字段,每行将恰好是22个字节,加上一些开销(估计为40%)。MyISAM索引块为1KB(参见InnoDB的16KB)。我估计每个块有33行。910,104
/
33说,要读取COUNT,需要读取大约27K块。(注意COUNT(core_id)需要检查是否core_id为空,COUNT(*)不是这样;这是一个很小的差异。)在普通硬盘驱动器上读取27K块大约需要270秒。您很幸运能在60秒内完成。

第二次运行在key_buffer中找到了所有这些块(假设key_buffer_size至少为27MB),因此它不必等待磁盘。因此,它要快得多。(这将忽略查询缓存,您可以明智地刷新或使用SQL_NO_CACHE。)

5.6恰好无关紧要(但是感谢您提及它),因为此过程自4.0或更低版本以来没有更改(除了utf8不存在;更多内容请参见下文)。

切换到InnoDB可以通过两种方式提供帮助。主键将与数据“聚集”在一起,而不是存储为单独的BTree。因此,一旦数据或PK被缓存,另一个立即可用。块的数量将更像是5K,但它们将是16KB块。如果高速缓存很冷,这些文件可能会更快地加载。

您问“我是否需要一个单独的icd索引?”-那么这会使MyISAM
BTree的大小缩小到每行约21个字节,因此BTree的大小约为21/27倍,没有太大的改进(至少对于冷缓存情况)。

另一个想法是, 如果 icd始终是数字且始终是数字,则使用MEDIUMINT UNSIGNEDZEROFILL如果它可以有前导零,则继续使用。

糟糕,我没注意到字符集。(我已经固定了上面的数字,但让我详细说明。)

  • CHAR(5)允许5个 字符
  • ascii每个 字符 占用1 个字节 。 __
  • utf8每个 字符 最多占用3 个字节 。 __
  • 因此,CHAR(5)CHARACTER SET utf8 始终 占用15 个字节 。 __

将列更改为CHAR(5) CHARACTER SET ascii会将其缩小到5个字节。

将其更改为MEDIUMINT UNSIGNED ZEROFILL会将其缩小为3个字节。

缩小数据将使I / O速度大致成比例(在另外两个字段中再允许6个字节之后)。



 类似资料:
  • 主要内容:什么是覆盖索引查询?,使用覆盖索引查询你可能听说过列索引是通过最大限度地减少查询所需的磁盘访问次数来优化查询性能的好方法。MongoDB 有一个字段索引的特定应用程序,称为覆盖索引查询(Covered Queries),其中查询的所有列都被进行索引。因为 MongoDB 不必检查除索引之外的任何文档,所以覆盖索引查询非常快。本节我们就来学习一下如何使用覆盖索引查询更快地查询数据。 什么是覆盖索引查询? 根据 MongoDB 官方文档,

  • 问题内容: 在下面的查询 如果我有2个单独的索引,一个在 col3上 ,另一个在 col4上 ,则此查询中将使用哪个索引? 我在某处读到,查询中的每个表仅使用一个索引。这是否意味着查询无法使用两个索引? 其次,如果我同时使用 col3* 和 col4 创建了一个复合索引,但仅在 WHERE 子句中使用了 col3 ,那么性能会更糟吗?例: * 最后,在所有情况下仅使用Covering索引是否更好?

  • 本文向大家介绍MySQL覆盖索引的使用示例,包括了MySQL覆盖索引的使用示例的使用技巧和注意事项,需要的朋友参考一下 什么是覆盖索引 创建一个索引,该索引包含查询中用到的所有字段,称为“覆盖索引”。 使用覆盖索引,MySQL 只需要通过索引就可以查找和返回查询所需要的数据,而不必在使用索引处理数据之后再进行回表操作。 覆盖索引可以一次性完成查询工作,有效减少IO,提高查询效率。 使用示例 查询语

  • 考虑以下代码: 测试可按如下方式执行: 覆盖报告将考虑涵盖的所有第100%行,即使启用分支覆盖: 然而,这段代码有一个错误,那些有敏锐眼光的人可能已经看到了。如果它进入“其他”分支,将会有一个例外: 修复该错误很容易:将未定义的名称更改为文本。还可以很容易地添加另一个测试来揭示错误:。这也不是这个问题所要问的。我想知道如何找到尽管有100%的代码覆盖率报告但从未实际执行过的代码段。 使用条件表达式

  • 我是声纳新手,试图使用声纳进行代码分析,包括代码覆盖,单元测试报告等。我能够使用OpenCover生成报告,我使用C#。我的项目是詹金斯工作的一部分,该工作构建解决方案,运行OpenCover脚本,然后使用“执行声纳Qube扫描仪”连接到声纳。我能够将结果视为一行代码气味,声纳中的问题。但是单元测试部分显示所有部分为0(失败,错误,测试)。下面是我的声纳设置sonar-project.proper

  • 问题内容: 我的一个模型具有一个delete标志,该标志用于全局隐藏对象: 如何覆盖Django管理模块使用的默认查询集以包括已删除的对话? 问题答案: 你可以在模型管理类中重写 方法。 请注意,在Django <= 1.5中,该方法被命名为just 。