如何改进此查询?

ben*_*ier 3 mysql mariadb performance query-performance

大约一年前,我引入了一个查询,该查询返回一种“客户也已购买”数据集。当时它运行得相当快,但最近变得非常慢,有时需要 5 秒或更长时间。

SELECT p.*, COUNT(*) AS total
FROM orders_products AS op
JOIN products AS p ON p.products_id = op.products_id
JOIN (
  SELECT orders_id
  FROM orders_products
  WHERE products_id = 100
) AS opf ON opf.orders_id = op.orders_id
WHERE op.products_id <> 100
GROUP BY products_id
ORDER BY total DESC
LIMIT 5;
Run Code Online (Sandbox Code Playgroud)

输出explain extended

+------+-------------+-----------------+--------+-----------------------+-------------+---------+------------------------------------+------+----------+---------------------------------+
| id   | select_type | table           | type   | possible_keys         | key         | key_len | ref                                | rows | filtered | Extra                           |
+------+-------------+-----------------+--------+-----------------------+-------------+---------+------------------------------------+------+----------+---------------------------------+
|    1 | SIMPLE      | orders_products | ref    | products_id,orders_id | products_id | 4       | const                              | 4511 |   100.00 | Using temporary; Using filesort |
|    1 | SIMPLE      | op              | ref    | products_id,orders_id | orders_id   | 4       | database.orders_products.orders_id |    2 |   100.00 | Using where                     |
|    1 | SIMPLE      | p               | eq_ref | PRIMARY               | PRIMARY     | 4       | database.op.products_id            |    1 |   100.00 |                                 |
+------+-------------+-----------------+--------+-----------------------+-------------+---------+--------------------------------------+------+----------+-------------------------------+
Run Code Online (Sandbox Code Playgroud)

SHOW CREATE TABLE products

+----------+----------------------------------------------------------------+
| Table    | Create Table                                                   
+----------+----------------------------------------------------------------+
| products | CREATE TABLE `products` (
  `products_id` int(11) NOT NULL AUTO_INCREMENT,
  `products_model` varchar(128) DEFAULT NULL,
  `products_price` decimal(15,4) NOT NULL DEFAULT '0.0000',
  `products_date_added` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
  `products_last_modified` datetime DEFAULT NULL,
  `products_status` tinyint(1) NOT NULL DEFAULT '0',
  PRIMARY KEY (`products_id`),
  KEY `idx_products_date_added` (`products_date_added`),
  KEY `products_model` (`products_model`),
  KEY `products_price` (`products_price`),
  KEY `products_status` (`products_status`),
) ENGINE=InnoDB DEFAULT CHARSET=utf8 |
+----------+----------------------------------------------------------------+
Run Code Online (Sandbox Code Playgroud)

SHOW INDEXES FROM products

+----------+------------+----------------------------------+--------------+----------------------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table    | Non_unique | Key_name                         | Seq_in_index | Column_name                | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+----------+------------+----------------------------------+--------------+----------------------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| products |          0 | PRIMARY                          |            1 | products_id                | A         |        4356 |     NULL | NULL   |      | BTREE      |         |               |
| products |          1 | idx_products_date_added          |            1 | products_date_added        | A         |        4356 |     NULL | NULL   |      | BTREE      |         |               |
| products |          1 | products_model                   |            1 | products_model             | A         |        4356 |     NULL | NULL   | YES  | BTREE      |         |               |
| products |          1 | products_price                   |            1 | products_price             | A         |        1089 |     NULL | NULL   |      | BTREE      |         |               |
| products |          1 | products_status                  |            1 | products_status            | A         |           4 |     NULL | NULL   |      | BTREE      |         |               |
+----------+------------+----------------------------------+--------------+----------------------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
Run Code Online (Sandbox Code Playgroud)

SHOW CREATE TABLE orders_products

+-----------------+--------------------------------------------------------+
| Table           | Create Table                                           
+-----------------+--------------------------------------------------------+
| orders_products | CREATE TABLE `orders_products` (
  `orders_products_id` int(11) NOT NULL AUTO_INCREMENT,
  `orders_id` int(11) NOT NULL DEFAULT '0',
  `products_id` int(11) NOT NULL DEFAULT '0',
  PRIMARY KEY (`orders_products_id`),
  KEY `products_id` (`products_id`),
  KEY `orders_id` (`orders_id`),
) ENGINE=InnoDB DEFAULT CHARSET=utf8 |
+-----------------+--------------------------------------------------------+
Run Code Online (Sandbox Code Playgroud)

SHOW INDEXES FROM orders_products

+-----------------+------------+-------------+--------------+--------------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table           | Non_unique | Key_name    | Seq_in_index | Column_name        | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+-----------------+------------+-------------+--------------+--------------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| orders_products |          0 | PRIMARY     |            1 | orders_products_id | A         |     3134198 |     NULL | NULL   |      | BTREE      |         |               |
| orders_products |          1 | products_id |            1 | products_id        | A         |        5014 |     NULL | NULL   |      | BTREE      |         |               |
| orders_products |          1 | orders_id   |            1 | orders_id          | A         |     1567099 |     NULL | NULL   |      | BTREE      |         |               |
+-----------------+------------+-------------+--------------+--------------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
Run Code Online (Sandbox Code Playgroud)

有什么明显的我在这里失踪了吗?在大多数方面,数据库似乎都经过了很好的调整。我们在 InnoDB/XtraDB 存储引擎下运行 MariaDB 5.5.30。

ype*_*eᵀᴹ 5

这个 ( orders_products) 是一个多对多表。我认为在这样的表上有 2 个复合索引是很常见的,因为它有助于许多常见的查询。

我肯定会添加两个(唯一的)索引, on(orders_id, products_id)和 on (products_id, orders_id)

不确定是否将它们都定义为UNIQUEMariaDB 优化器的进一步改进。

如果没有特殊原因,您可以删除自动递增orders_products_id列。您可以通过订单和产品 ID 来标识表中的行。我认为该列只会在表和索引中增加更多空间,而没有任何价值。