【问题标题】:Execution time running long. What do I index?执行时间长。我要索引什么?
【发布时间】:2020-11-28 22:50:59
【问题描述】:

我有以下表格:

客户

  • cus_id(主键)
  • driver_licence_id (int UNIQUE)

汽车

  • car_id(主键)
  • cus_idcustomer 的外键并建议此客户拥有此车)
  • date_created(日期时间)

零件(汽车的不同零件,例如发动机、火花塞等)

  • part_id(主键)
  • manufacturers_id (int)

parts_in_car(车里有哪些零件)

  • part_in_car_id(主键)
  • car_idcar 表的外键)
  • part_idparts 表的外键)

我实际上是在尝试获取所有manufacturers_id 的串联字符串,该字符串位于特定driver_licence_id 拥有的汽车中。以下 SQL 查询按我的意愿工作,但它接管 1 second 来执行。我有超过 100 万行。我测试的查询结果为 20 行。

SELECT GROUP_CONCAT(p.manufacturers_id ORDER BY p.manufacturers_id) as mids
FROM car c INNER JOIN
     parts_in_car pic
     ON c.car_id = pic.car_id JOIN
     parts p
     ON pic.part_id = p.part_id JOIN
     customers cus
     ON c.cus_id = cus.cus_id 
WHERE cus.driver_licence_id = 5555555
group by c.car_id, c.date_created
ORDER BY c.date_created

我仅出于此查询的目的尝试执行以下索引。谁能告诉我要创建哪些索引。

# Customer
CREATE INDEX customer_driver_licence_id_idx
ON customer (driver_licence_id);

# cars
CREATE INDEX cars_cus_id_idx
ON cars (cus_id);

# parts
CREATE INDEX parts_manufacturers_id_idx
ON parts (manufacturers_id);

# parts_in_car
CREATE INDEX parts_in_car_part_id_idx
ON parts_in_car (part_id);

CREATE INDEX parts_in_car_car_id_idx
ON parts_in_car (car_id);

更新:问题是group by,我已经索引(car_id,date_add)来尝试解决问题

#EXPLAIN SELECT
+-------+-------------------------------------+
| table |                 key                 |
+-------+-------------------------------------+
| a     | cus_id                              |
| o     | cars_cus_id_car_id_date_created_idx |
| pip   | parts_in_car_car_id_idx             |
| p     | PRIMARY                             |
+-------+-------------------------------------+

【问题讨论】:

  • 缺少 (c.car_id, c.date_created) 上的索引和 date_created 上的另一个索引(不确定如果 mysql 足够聪明可以重用它,您是否只创建 (c.date_created,c.car_id) “分组依据”)
  • @e2-e4 尝试制作该索引。还是一样的
  • 你可以尝试重建统计数据。
  • @AntonínLejsek 这是什么意思?

标签: mysql sql database select indexing


【解决方案1】:

对于这个查询:

SELECT GROUP_CONCAT(p.SELECT GROUP_CONCAT(p.manufacturers_id ORDER BY p.manufacturers_id) as mids
FROM car c INNER JOIN
     parts_in_car pic
     ON c.car_id = pic.car_id JOIN
     parts p
     ON pic.part_id = p.part_id JOIN
     customers cus
     ON c.cus_id = cus.cus_id 
WHERE cus.driver_licence_id = 5555555
group by c.car_id, c.date_created
ORDER BY c.date_created 

你想要索引:

  • customers(license_id, cus_id)
  • car(cus_id, car_id, date_created)
  • parts_in_car(car_id, part_id)
  • parts(part_id, manufacturers_id)

【讨论】:

  • @James - 请提供EXPLAIN SELECT...
【解决方案2】:

这可能需要两个排序:

    group by  c.car_id, c.date_created
    ORDER BY  c.date_created

这会更快地为您提供类似的结果:

    group by  c.date_created, c.car_id
    ORDER BY  c.date_created, c.car_id

因为它现在可以对两个步骤进行一次排序。

请提供EXPLAIN SELECT...。同时,我猜优化器更愿意从唯一带有过滤的表开始:

cus:  (driver_licence_id, cus_id)
c:    (cus_id, date_created, car_id)
pic:  (car_id, part_id)  -- This, or the swapped version could be the PK
p:    (part_id, manufacturers_id) -- probably useless, since part_id is PK

每个都是一个“覆盖索引”,因此所有工作都可以在INDEX BTrees 中完成。注意:c 的索引差异(与 Gorden 的建议相比)可能有帮助,也可能没有帮助。我基于修改后的GROUP BY

对于简单的JOINs(与INNER JOIN 相同),优化器几乎总是从有过滤的表开始(WHERE...)。之后,查询中表的顺序由ON 子句强制。因此,确定所需的 4 个索引相对简单。

在其他情况下,执行连接的最佳顺序并不明显,某些索引可能需要翻转。

特别是,如果您删除WHERE,最佳起点将是c 上的索引,从GROUP BY 中的两列开始。那可能会消除一种。接下来是cuspicp 会出现在 pic. 之后

多:多

parts_in_car 是“多对多”表吗?如果是这样,摆脱你拥有的PK;它伤害性能。更多讨论请参见:http://mysql.rjweb.org/doc.php/index_cookbook_mysql#many_to_many_mapping_table

【讨论】:

  • 我认为你是对的,它做了 2 种。我为car 创建了所有可能的索引组合。我创建了 11 个索引(加上默认创建为主键的 outfit_id 索引)。它使用索引(av_id, outfit_id, date_created)ORDER BY c.date_created 是问题所在。我将订单更改为ORDER BY c.date_created, c.car_id。修复了问题
  • 不过只有一个问题。最后,是主要的ORDER BY c.date_created, c.car_iddate_createdcar_id排序。因为对我来说最主要的是它只需要按日期排序
  • 我无法更改分组依据,因为它需要按特定顺序分组。首先是 car_id,然后是 date_created
  • @James - 想想GROUP BYORDER BY 各自组合指定的列,然后使用组合进行分组和排序。将ORDER BY 想象成由lastname, firstname 排序的列表。这是一对,不是一个或另一个。
  • @what you have is the date ordered,其中汽车在某个日期的某个日期,可能是随机的。所拥有的是订购日期,每个日期内的汽车都按 id 顺序排列。也就是说,我保留了您的排序和分组。但我指定了如何处理汽车的二次订购。
【解决方案3】:

索引策略取决于您的工作量(即执行次数最多的查询)和列的基数级别(即列中的重复值)。

您的所有工作负载(即执行的查询)都使用索引。它们不仅仅用于一些特定的查询。这样做的原因是,如果您有太多查询,引擎可能会错误地为其他一些查询选择索引并导致它们运行缓慢,同时有助于改进您在设计查询时考虑的一个查询。如果该查询是您执行次数最多的查询之一,那么当然值得权衡。

与表中的基数或其他索引相比,引擎会更喜欢基数较低的索引。因此,当您将多个列组合成一个索引(复合索引)时,您需要考虑每列增加了多少索引的基数。

https://www.mysqltutorial.org/mysql-index/mysql-index-cardinality/

确保您的表上有聚集索引(即主键),并将其设为单列数字主键(与复合主键或非数字主键相对)。此主键将自动包含在您的所有索引中。引擎需要某种从索引到表的映射方式,而主键在二级索引中用于此目的。如果您没有主键,引擎会尝试在列中找到一些唯一性,从而降低效率。

https://www.mysqltutorial.org/mysql-index/mysql-clustered-index/

覆盖索引是包含执行查询所需的表中所有列的索引。当存在覆盖索引时,引擎将使用它代替表 b/c 它具有完成查询的所有列。即使覆盖索引的基数与表相同,这仍然可能是首选,因为它的列数比原始表少,索引大小会很小(因此 I/O 更少)。

https://blog.toadworld.com/2017/04/06/speed-up-your-queries-using-the-covering-index-in-mysql

因此,在不了解所有这些参数的情况下,很难设计索引。我可以建议使用以下覆盖索引进行测试,但如果任何列显着增加基数,请添加另一个索引没有该列。

### Assuming `cust_id`, `car_id`, `part_id` are primary keys 
### Assuming `(car_id, part_id)` is the composite primary key of `parts_in_car` 
### (with the assumption that it is always used as many-many relationship table) 
### (thanks to @rick-james for pointing out many-to-many PK tip) 

# Customer
CREATE INDEX customer_driver_licence_id_idx
ON customer (driver_licence_id);

# cars
CREATE INDEX cars_cus_id_idx
ON cars (cus_id, date_created);

# parts
CREATE INDEX parts_manufacturers_id_idx
ON parts (manufacturers_id);

【讨论】:

  • 这里是关于生成索引的食谱:mysql.rjweb.org/doc.php/index_cookbook_mysql
  • Int 主键 -- 多:多表是一个常见示例,其中复合主键(可能是 varchar)比人工 INT 好得多。此评论适用于parts_in_car。请注意所有答案如何建议该表的复合 PK。
  • @RickJames 如果它始终用作“多对多映射”表,那么您在“parts_id_car”上是对的(即,连接使用了构成复合键的所有列—— - 如果部分使用,那就是另一回事了)。更新了我的答案
【解决方案4】:

使用子选择会更快吗?
这里有一些伪代码:

select manufacturer_id from manufacturer_parts where part_id in (select part_id from part_car where car_id in (select car_id from cars where driverlicense = ?)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-12-08
    • 1970-01-01
    • 1970-01-01
    • 2019-07-28
    • 2015-11-19
    • 1970-01-01
    相关资源
    最近更新 更多