Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/mysql/56.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
MySQL连接是扫描表而不是使用索引_Mysql_Join_Query Optimization_Sql Execution Plan - Fatal编程技术网

MySQL连接是扫描表而不是使用索引

MySQL连接是扫描表而不是使用索引,mysql,join,query-optimization,sql-execution-plan,Mysql,Join,Query Optimization,Sql Execution Plan,我有以下表格: 显示创建表访问\u令牌\u状态 CREATE TABLE `access_token_status` ( `id` int(11) NOT NULL AUTO_INCREMENT, `status` varchar(10) NOT NULL, PRIMARY KEY (`id`), UNIQUE KEY `status` (`status`), KEY `idx_access_token_status_status_lookup_2` (`status`,`i

我有以下表格:

显示创建表访问\u令牌\u状态

CREATE TABLE `access_token_status` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `status` varchar(10) NOT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `status` (`status`),
  KEY `idx_access_token_status_status_lookup_2` (`status`,`id`),
  KEY `idx_access_token_status_status_lookup_1` (`id`,`status`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci

显示创建表的用户

CREATE TABLE `user` (
  `id` varchar(17) NOT NULL,
  `short_lived_access_token_status_id` int(11) NOT NULL,
  PRIMARY KEY (`id`),
  KEY `idx_user_status_lookup_1` (`id`,`short_lived_access_token_status_id`),
  KEY `idx_user_status_lookup_2` (`short_lived_access_token_status_id`,`id`),
  KEY `ix_user_short_lived_access_token_status_id` (`short_lived_access_token_status_id`),
  CONSTRAINT `user_ibfk_1` FOREIGN KEY (`short_lived_access_token_status_id`) REFERENCES `access_token_status` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci

显示创建表帐户

CREATE TABLE `account` (
  `id` varchar(17) NOT NULL,
  `user_id` varchar(17) NOT NULL,
  `track` tinyint(1) NOT NULL,
  `estimated_time_to_regain_access` int(11) NOT NULL,
  `media_list_fetched_at` datetime NOT NULL,
  PRIMARY KEY (`id`),
  KEY `idx_account_next_fetch_lookup` (`user_id`,`estimated_time_to_regain_access`,`track`,`media_list_fetched_at`),
  CONSTRAINT `account_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`),
  CONSTRAINT `account_chk_1` CHECK ((`track` in (0,1)))
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
当我试图解释以下问题时

explain select a.*
        from account a
        inner join user u
        on a.user_id = u.id
        inner  join access_token_status as s
        on u.short_lived_access_token_status_id = s.id and s.status = 'valid'
        where
            u.short_lived_access_token_status_id = 3
            and a.estimated_time_to_regain_access = 0
            and a.track = true
            and a.media_list_fetched_at > '2020-05-30 12:31:01'
        limit 1
        for update of a SKIP LOCKED
我得到这个输出:

# id, select_type, table, partitions, type, possible_keys, key, key_len, ref, rows, filtered, Extra
'1', 'SIMPLE', 's', NULL, 'const', 'PRIMARY,status,idx_access_token_status_status_lookup_2,idx_access_token_status_status_lookup_1', 'PRIMARY', '4', 'const', '1', '100.00', NULL
'1', 'SIMPLE', 'a', NULL, 'index', 'idx_account_next_fetch_lookup', 'idx_account_next_fetch_lookup', '80', NULL, '2', '50.00', 'Using where; Using index'
'1', 'SIMPLE', 'u', NULL, 'eq_ref', 'PRIMARY,idx_user_status_lookup_1,idx_user_status_lookup_2,ix_user_short_lived_access_token_status_id', 'PRIMARY', '70', 'media_meta.a.user_id', '1', '100.00', 'Using where'
有些表似乎没有使用索引。这对于我来说是一个问题,因为扫描的行被锁定,而其他查询将跳过它们(因为跳过锁定是确保查询彼此不被阻止所必需的)

我不确定我缺少什么索引,或者我是否需要更改查询中的某些内容

在您的查询中,您使用“u.short\u live\u access\u token\u status\u id=3”,因此与“s”的连接看起来要么是不可能的(如果s.status for s.id=3不是“有效的”)要么是多余的(如果是)。除非您正在从s获取其他列

现在让我们看看“u”的用法:

因此,您使用的是基于u.short_live_access_token_status_id=3的主选择标准,您需要从中获取u.id。因此,您的
idx\u用户\u状态\u查找\u 2
应该作为覆盖索引

为什么不呢?可能是因为表太小了,不太重要,或者是因为与S的连接干扰了优化器(您会注意到表u是第三次计算的)

如果可能的话,试着用s删除连接,看看会发生什么。

似乎是“过度规范化”

我不认为将
状态
从表中移到另一个表中有任何好处。这样做会使优化复杂化,并且不会节省太多(如果有的话)空间

事实上,您可以使用一个非空的
枚举('invalid','valid',…)
,对于
INT
,它将占用1个字节而不是4个字节

在InnoDB中,当您有
主键(id)
时,您有一个按
id
排序的B树。因此,任何以
id
开头的二级索引都是多余的。还请注意,根据定义,
主键是唯一的

    where
        u.short_lived_access_token_status_id = 3
        and a.estimated_time_to_regain_access = 0
        and a.track = true
        and a.media_list_fetched_at > '2020-05-30 12:31:01'
你想用哪一种

INDEX(short_lived_access_token_status_id, ...)
但这似乎不太可能,因为基数很低,或者

INDEX(estimated_time_to_regain_access, track,   -- in either order
      media_list_fetched_at)    -- last, since it is a 'range'
更好的是,这个“覆盖”指数:


请注意,没有ORDER BY的限制是相当没有意义的。我会尝试更改索引的顺序,以便用户id位于末尾。此外,我想不出一个id不是整数的好理由。在我的用例中,顺序无关紧要,我没有提供这样的细节以避免分心。但这会影响执行计划吗?你对索引是正确的,同样它的业务逻辑可能超出了问题的范围,除非你认为这正在改变执行计划(我确实尝试了整数ID,但是计划没有改变),我确实尝试在末尾添加一个新的带有UsReSid的索引,这是可行的,谢谢你考虑到了这一点。请随意回答问题并接受自己的答案,或者删除问题。
INDEX(estimated_time_to_regain_access, track,   -- in either order
      media_list_fetched_at)    -- last, since it is a 'range'
INDEX(estimated_time_to_regain_access, track,
      media_list_fetched_at, user_id)