修复使用位置; 使用临时; 使用filesort

Pav*_*vel 6 mysql temporary explain filesort

我有两个简单的表:

CREATE TABLE cat_urls (
  Id int(11) NOT NULL AUTO_INCREMENT,
  SIL_Id int(11) NOT NULL,
  SiteId int(11) NOT NULL,
  AsCatId int(11) DEFAULT NULL,
  Href varchar(2048) NOT NULL,
  ReferrerHref varchar(2048) NOT NULL DEFAULT '',
  AddedOn datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
  GroupId int(11) DEFAULT NULL,
  PRIMARY KEY (Id),
  INDEX SIL (SIL_Id, AsCatId)
)

CREATE TABLE products (
  Id int(11) NOT NULL AUTO_INCREMENT,
  CatUrlId int(11) NOT NULL,
  Href varchar(2048) NOT NULL,
  SiteIdentity varchar(2048) NOT NULL,
  Price decimal(12, 2) NOT NULL,
  IsAvailable bit(1) NOT NULL,
  ClientCode varchar(256) NOT NULL,
  PRIMARY KEY (Id),
  INDEX CatUrl (CatUrlId)
)
Run Code Online (Sandbox Code Playgroud)

我有一个非常简单的查询:

SELECT cu.Href, COUNT(p.CatUrlId) FROM cat_urls cu
       JOIN products p ON p.CatUrlId=cu.Id
       WHERE sil_id=4601038
GROUP by cu.Id
Run Code Online (Sandbox Code Playgroud)

EXPLAIN说:

id  select_type table   type    possible_keys   key     key_len ref                     rows    Extra
1   SIMPLE      cu      ref     PRIMARY,SIL     SIL     4       const                   303     Using where; Using temporary; Using filesort
1   SIMPLE      p       ref     CatUrl          CatUrl  4       blue_collar_logs.cu.Id  6       Using index
Run Code Online (Sandbox Code Playgroud)

请告诉我有没有办法修复"使用where;使用临时;使用filesort"并提高此查询的性能?

axi*_*iac 2

看起来,出于某种原因,MySQL选择使用SIL第一个表上的索引,并将其用于查找 ( WHERE sil_id = 4601038) 和分组 ( GROUP BY cu.Id)。

你可以告诉它使用PK表的

SELECT cu.Href, COUNT(p.CatUrlId) FROM cat_urls cu
    USE INDEX FOR JOIN (PRIMARY)
JOIN products p ON p.CatUrlId=cu.Id
WHERE sil_id=4601038
GROUP by cu.Id
Run Code Online (Sandbox Code Playgroud)

它将产生这个执行计划:

id | select_type | table | type  | possible_keys | key     | key_len | ref              | rows | Extra
---+-------------+-------+-------+---------------+---------+---------+------------------+------+-------------
1  | SIMPLE      | cu    | index | PRIMARY       | PRIMARY | 4       | NULL             | 1    | Using where
1  | SIMPLE      | p     | ref   | CatUrl        | CatUrl  | 4       | cbs-test-1.cu.Id | 1    | Using index
Run Code Online (Sandbox Code Playgroud)

忽略列中报告的值rows;它们不正确,因为我的桌子是空的。

请注意,该Extra列现在仅包含Using where,但还要注意连接列从(非常好)type更改为(全索引扫描,不太好)。refindex

更好的解决方案是在列上添加索引SIL_Id。我知道,SIL_Id是索引的前缀SIL(SIL_Id, AsCatId),理论上列上的另一个索引SIL_Id是完全无用的。但似乎它解决了本案的问题。

ALTER TABLE cat_urls
  ADD INDEX (SIL_Id)
;
Run Code Online (Sandbox Code Playgroud)

现在在查询中使用它:

SELECT cu.Href, COUNT(p.CatUrlId) FROM cat_urls cu
    USE INDEX FOR JOIN (SIL_Id)
JOIN products p ON p.CatUrlId=cu.Id
WHERE sil_id=4601038
GROUP by cu.Id
Run Code Online (Sandbox Code Playgroud)

查询执行计划现在看起来好多了:

id | select_type | table | type | possible_keys | key    | key_len | ref              | rows | Extra
---+-------------+-------+------+---------------+--------+---------+------------------+------+-------------
1  | SIMPLE      | cu    | ref  | SIL_Id        | SIL_Id | 4       | const            | 1    | Using where
1  | SIMPLE      | p     | ref  | CatUrl        | CatUrl | 4       | cbs-test-1.cu.Id | 1    | Using index
Run Code Online (Sandbox Code Playgroud)

缺点是我们有一个(理论上)无用的额外索引。它占用存储空间,并且每次添加、删除行或SIL_Id修改其字段时都会消耗处理器周期。