【问题标题】:Why does MySQL ignore index in this query?为什么 MySQL 在此查询中忽略索引?
【发布时间】:2021-12-21 22:54:51
【问题描述】:

我有以下表格:

CREATE TABLE `sms` (
  `sms_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `sms_datetime` datetime DEFAULT NULL,
  `sms_number` varchar(40) NOT NULL,
  `sms_text` text,
  `sms_status` int(3) DEFAULT '0',
  `sms_last_tm` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  `sms_csinteve_id` int(11) unsigned NOT NULL DEFAULT '0',
  `sms_handler_user_id` int(11) unsigned NOT NULL DEFAULT '0',
  PRIMARY KEY (`sms_id`),
  KEY `sms_number` (`sms_number`),
  KEY `sms_last_tm` (`sms_last_tm`),
  KEY `sms_csinteve_id` (`sms_csinteve_id`),
  KEY `sms_handler_user_id` (`sms_handler_user_id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;

sms 的行数比 2,000,000 多。

还有下表:

CREATE TABLE `customer_service_interaction_events` (
  `csinteve_id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `csinteve_interaction_id` int(11) unsigned NOT NULL DEFAULT '0',
  `csinteve_datetime` datetime DEFAULT NULL,
  PRIMARY KEY (`csinteve_id`),
  KEY `csinteve_interaction_id` (`csinteve_interaction_id`),
  KEY `csinteve_datetime` (`csinteve_datetime`)
) ENGINE=InnoDB AUTO_INCREMENT=21153 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;

20,000 行。

然后这个查询:

EXPLAIN SELECT COUNT(csinteve_id) as interactions_number
FROM customer_service_interaction_events
JOIN sms
    ON csinteve_id = sms_csinteve_id
WHERE csinteve_interaction_id = 3085
AND sms_handler_user_id = 0;

说:

{
    "data":
    [
        {
            "id": 1,
            "select_type": "SIMPLE",
            "table": "customer_service_interaction_events",
            "partitions": null,
            "type": "ref",
            "possible_keys": "PRIMARY,csinteve_interaction_id",
            "key": "csinteve_interaction_id",
            "key_len": "4",
            "ref": "const",
            "rows": 1,
            "filtered": 100,
            "Extra": "Using index"
        },
        {
            "id": 1,
            "select_type": "SIMPLE",
            "table": "sms",
            "partitions": null,
            "type": "ALL",
            "possible_keys": "sms_csinteve_id,sms_handler_user_id",
            "key": null,
            "key_len": null,
            "ref": null,
            "rows": 2083577,
            "filtered": 99.4,
            "Extra": "Using where; Using join buffer (Block Nested Loop)"
        }
    ]
}

说对于sms 表MySQL 不使用任何索引(类型ALL)。 而sms 表在与customer_service_interaction_events 连接的sms_csinteve_id 列上有一个索引,而customer_service_interaction_events 表在csinteve_interaction_id 上有一个索引。

查询的目的是返回未处理的短信数量(sms),每条短信绑定一个客服事件(customer_service_interaction_events),多个客服事件有相同的交互id(customer_service_interaction_events.csinteve_interaction_id) , 在示例中为 3085)。

谢谢。

编辑:

我尝试添加 Akina 的索引:

CREATE INDEX idx ON customer_service_interaction_events (csinteve_interaction_id, csinteve_id);
CREATE INDEX idx ON sms (sms_handler_user_id, sms_csinteve_id);

但是解释:

EXPLAIN SELECT COUNT(csinteve_id) as interactions_number
FROM customer_service_interaction_events
JOIN sms
    ON csinteve_id = sms_csinteve_id
WHERE csinteve_interaction_id = 3085
AND sms_handler_user_id = 0;

仍然为sms 输出类型ALL

{
    "data":
    [
        {
            "id": 1,
            "select_type": "SIMPLE",
            "table": "customer_service_interaction_events",
            "partitions": null,
            "type": "ref",
            "possible_keys": "PRIMARY,csinteve_interaction_id,csinteve_interaction_id__csinteve_id",
            "key": "csinteve_interaction_id",
            "key_len": "4",
            "ref": "const",
            "rows": 1,
            "filtered": 100,
            "Extra": "Using index"
        },
        {
            "id": 1,
            "select_type": "SIMPLE",
            "table": "sms",
            "partitions": null,
            "type": "ref",
            "possible_keys": "sms_csinteve_id,sms_handler_user_id,sms_csinteve_id_2,sms_handler_user_id_2,sms_csinteve_id_3,sms_handler_user_id_3,sms_csinteve_id_4,sms_handler_user_id_4,sms_handler_user_id__sms_csinteve_id",
            "key": "sms_handler_user_id__sms_csinteve_id",
            "key_len": "8",
            "ref": "const,local_bluelinks.customer_service_interaction_events.csinteve_id",
            "rows": 2083577,
            "filtered": 100,
            "Extra": "Using index"
        }
    ]
}

【问题讨论】:

    标签: mysql sql database-indexes secondary-indexes


    【解决方案1】:

    测试一下:

    CREATE INDEX idx ON customer_service_interaction_events (csinteve_interaction_id, csinteve_id);
    CREATE INDEX idx ON sms (sms_handler_user_id, sms_csinteve_id);
    

    【讨论】:

    • 那不行,添加你提到的索引后我仍然看到"rows": 1786395,type: ALL
    • @tonix 总表行数的百分比与csinteve_interaction_id = 3085 匹配? sms_handler_user_id = 0?
    • 只有csinteve_interaction_id = 3085csinteve_id = sms_csinteve_id的几条记录,但是sms_handler_user_id = 0的行有很多,唯一的问题是我只对sms_handler_user_id = 0的行感兴趣其中csinteve_interaction_id = 3085csinteve_id = sms_csinteve_id '因为这两个条件是我认为应该将查询范围缩小到要检查的一小组行的条件。关于如何提升此查询以及为什么 MySQL 使用类型 ALL 来表示 sms 即使我加入 csinteve_id = sms_csinteve_id,还有其他想法吗?
    • @tonix 只有少数 csinteve_interaction_id = 3085 的记录 如果是这样,那么我的答案中的第一个索引必须有效。 sms_handler_user_id = 0 的行有很多如果是这样,那么我的答案中的第二个索引无效,sms_csinteve_id 的索引是合适的。
    • @tonix 您的计划显示服务器选择了错误的表格扫描顺序。尝试交换表格并使用FROM sms JOIN customer_service_interaction_events。如果计划不会更改,请添加STRAIGHT_JOIN
    猜你喜欢
    • 2011-12-15
    • 1970-01-01
    • 1970-01-01
    • 2014-03-31
    • 1970-01-01
    • 2012-12-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多