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

涉及范围时,高基数列在索引中排在首位?

蒙麒
2023-03-14
问题内容
CREATE TABLE `files` (
  `did` int(10) unsigned NOT NULL DEFAULT '0',
  `filename` varbinary(200) NOT NULL,
  `ext` varbinary(5) DEFAULT NULL,
  `fsize` double DEFAULT NULL,
  `filetime` datetime DEFAULT NULL,
  PRIMARY KEY (`did`,`filename`),
  KEY `fe` (`filetime`,`ext`),          -- This?
  KEY `ef` (`ext`,`filetime`)           -- or This?
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ;

表中有一百万行。文件时间大部分是不同的。ext值的数量有限。因此,filetime具有高基数而ext具有低得多的基数。

查询涉及extfiletime

WHERE ext = '...'
  AND filetime BETWEEN ... AND ...

这两个指数中哪个更好?又为什么呢


问题答案:

首先,让我们尝试FORCE INDEX选择effe。时间太短而无法清楚地知道哪个更快,但是`EXPLAIN显示出一个不同:

首先强制范围filetime。(注意:顺序WHERE不受影响。)

mysql> EXPLAIN SELECT COUNT(*), AVG(fsize)
    FROM files FORCE INDEX(fe)
    WHERE ext = 'gif' AND filetime >= '2015-01-01'
                      AND filetime <  '2015-01-01' + INTERVAL 1 MONTH;
+----+-------------+-------+-------+---------------+------+---------+------+-------+-----------------------+
| id | select_type | table | type  | possible_keys | key  | key_len | ref  | rows  | Extra                 |
+----+-------------+-------+-------+---------------+------+---------+------+-------+-----------------------+
|  1 | SIMPLE      | files | range | fe            | fe   | 14      | NULL | 16684 | Using index condition |
+----+-------------+-------+-------+---------------+------+---------+------+-------+-----------------------+

首先强制低基数ext

mysql> EXPLAIN SELECT COUNT(*), AVG(fsize)
    FROM files FORCE INDEX(ef)
    WHERE ext = 'gif' AND filetime >= '2015-01-01'
                      AND filetime <  '2015-01-01' + INTERVAL 1 MONTH;
+----+-------------+-------+-------+---------------+------+---------+------+------+-----------------------+
| id | select_type | table | type  | possible_keys | key  | key_len | ref  | rows | Extra                 |
+----+-------------+-------+-------+---------------+------+---------+------+------+-----------------------+
|  1 | SIMPLE      | files | range | ef            | ef   | 14      | NULL |  538 | Using index condition |
+----+-------------+-------+-------+---------------+------+---------+------+------+-----------------------+

显然,这种rows说法ef更好。但是,让我们检查一下Optimizer跟踪。输出相当庞大;我只展示有趣的部分。没有FORCE需要;
跟踪将显示两个选项,然后选择更好的。

             ...
             "potential_range_indices": [
                ...
                {
                  "index": "fe",
                  "usable": true,
                  "key_parts": [
                    "filetime",
                    "ext",
                    "did",
                    "filename"
                  ]
                },
                {
                  "index": "ef",
                  "usable": true,
                  "key_parts": [
                    "ext",
                    "filetime",
                    "did",
                    "filename"
                  ]
                }
              ],

              "analyzing_range_alternatives": {
                "range_scan_alternatives": [
                  {
                    "index": "fe",
                    "ranges": [
                      "2015-01-01 00:00:00 <= filetime < 2015-02-01 00:00:00"
                    ],
                    "index_dives_for_eq_ranges": true,
                    "rowid_ordered": false,
                    "using_mrr": false,
                    "index_only": false,
                    "rows": 16684,
                    "cost": 20022,               <-- Here's the critical number
                    "chosen": true
                  },
                  {
                    "index": "ef",
                    "ranges": [
                      "gif <= ext <= gif AND 2015-01-01 00:00:00 <= filetime < 2015-02-01 00:00:00"
                    ],
                    "index_dives_for_eq_ranges": true,
                    "rowid_ordered": false,
                    "using_mrr": false,
                    "index_only": false,
                    "rows": 538,
                    "cost": 646.61,               <-- Here's the critical number
                    "chosen": true
                  }
                ],

          "attached_conditions_computation": [
            {
              "access_type_changed": {
                "table": "`files`",
                "index": "ef",
                "old_type": "ref",
                "new_type": "range",
                "cause": "uses_more_keyparts"   <-- Also interesting
              }
            }

使用fe(第一个范围列),可以使用范围,但它估计扫描了16684行以获得ext='gif'

使用ef(低基数ext优先),它可以使用索引的两列并在BTree中更有效地向下钻取。然后,它发现估计有538行,所有这些行都对查询有用-
无需进一步过滤。

结论:

  • INDEX(filetime, ext) 仅使用第一列。
  • INDEX(ext, filetime) 使用两列。
  • 考虑基数, 将涉及=测试的列放在索引的 第一位 。 __
  • 查询计划不会超出第一个“范围”列。
  • “基数”与 复合索引和这种查询 无关。

(“使用索引条件”表示存储引擎(InnoDB)将使用索引中用于过滤的列之外的列。”)



 类似资料:
  • 问题内容: 我决定通过添加身份验证使用和数据库来修改我的应用程序。在使用XML 和XML 进行普通身份验证之前。工作正常。 我看起来像这样 但是当我尝试进行身份验证时,我遇到了异常 XML文件中的语法有什么问题? 问题答案: Spring Security希望用户查询按顺序列出三列: 用户名 密码 启用(布尔值) 你没有最后一个。如果您的数据库中没有等效的“启用”功能,则可以使用常量。

  • 问题内容: 我想从一维numpy数组(或向量)中选择多个不相邻的范围。 假设: 当然,这可行: 这可以通过单个索引获取: 但是,如果我要选择范围,该怎么办? 我试过了: 有没有简单的方法可以执行此操作,或者我需要分别生成它们并进行连接? 问题答案: 您需要在索引之前或之后进行串联。 使它变得容易 扩展切片并将其连接。 您可以混合切片和列表: 在索引之前进行连接可能比在之后进行连接要快,但是对于这样

  • 问题内容: 我在读取CSV文件时遇到此错误(无标题,3列,第二和第三字符串): 这是下面代码的一部分。坚持起来简直是愚蠢的简单事情,但是对于如何工作我只是空白。我是编码的新手,但是之前已经处理过csv模块,并且从来没有遇到过这方面的问题,只是在记事本中制作了一些测试csv文件,以查看是否可以从相同的代码中读取它,并且可以。我不知道。 问题答案: 尝试检查空白行。另外,请避免将其用作变量名。是打开的

  • 问题内容: 我已经开始处理我的第一个Java程序,它是一个简单的计算器,但是我得到一个错误,声称我的数组超出范围。我尝试对其进行调试,以了解这样做的原因和原因,并遵循纸上的代码,两者均显示了我期望和期望的结果。因此,我看不到问题出在哪里。代码不完整。 根据调试器,此行出现错误: 这是我当前拥有的代码的主要部分: 这是我收到的错误消息: 问题答案: 当等于中的最后一个元素索引时,则将大于最后一个元素

  • 所以我创建了这个注释: 但当我运行它时,我得到了这个异常: 无法为映射设置参数:ParameterMapping{Property='name',mode=in,javatype=class java.lang.String,JDBCType=varchar,NumericScale=null,ResultMapid='null',JDBCTypeName='null',Expression='n

  • 问题内容: 我真的很想在我的Swift代码中使用一个更简单的经典try catch块,但是我找不到能做到这一点的任何东西。 我只需要: 这是我的难题,当TableView重新加载新数据时,某些信息仍位于RAM中,该信息会调用具有新的空数据源的tableView崩溃。 所以我经常抛出异常 我已经试过了: 我也尝试过这个: 这是一个优先级很低的代码块,我花了大量的时间进行反复试验,弄清楚swift内置