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

问题描述 投票:3回答:2

我有以下表。

SHOW CREATE TABLE access_token_status;

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

SHOW CREATE TABLE user;

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

SHOW CREATE TABLE account;

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'

似乎有些表没有使用索引。这是我的情况下的一个问题,因为扫描的行被锁定,其他查询将跳过它们(由于SKIP LOCKED是需要的,以确保查询不被相互阻塞)。

我不知道我漏掉了什么索引,或者我是否需要改变查询中的一些内容。

mysql join query-optimization sql-execution-plan
2个回答
0
投票

在你的查询中,你使用了 "u.short_lived_access_token_status_id = 3",所以用 "s "连接看起来要么是不可能的(如果s.status for s.id = 3不是 "有效的"),要么是多余的(如果是)。除非你要从s中获取其他列。

现在让我们看看 "u "在哪里被使用。

on a.user_id = u.id
...
    on u.short_lived_access_token_status_id = ...

所以你使用了一个基于u.short_lived_access_token_status_id = 3的主选择标准,你需要从这里得到u.id。所以你的 idx_user_status_lookup_2 应该作为一个覆盖性的索引。

为什么不这样做呢?可能是因为表太小了,以至于关系不大,或者是因为与s的连接干扰了优化器(你会注意到表u被评估为第三个)。

如果有可能的话,试着去掉与s的连接,看看会发生什么。


0
投票

似乎是 "过度正态化"。

我看不出移动 status 的表到另一个表中。 这样做会使优化变得复杂,而且不会节省很多空间,如果有的话。

事实上,您可以使用一个 ENUM('invalid', 'valid', ...) NOT NULL,这将占用1个字节,而不是4个字节的 INT.

在InnoDB中,当你有 PRIMARY KEY(id)你有一个BT树,由 id. 因此,任何二级指数 开始id 是多余的。 还请注意,一个 PRIMARY KEY 根据定义,是。UNIQUE.

    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'

更好的是这个 "覆盖 "指数。

INDEX(estimated_time_to_regain_access, track,
      media_list_fetched_at, user_id)
© www.soinside.com 2019 - 2024. All rights reserved.