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)
+------+-------------+-----------------+--------+-----------------------+-------------+---------+------------------------------------+------+----------+---------------------------------+
| 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。
这个 ( orders_products) 是一个多对多表。我认为在这样的表上有 2 个复合索引是很常见的,因为它有助于许多常见的查询。
我肯定会添加两个(唯一的)索引, on(orders_id, products_id)和 on (products_id, orders_id)。
不确定是否将它们都定义为UNIQUEMariaDB 优化器的进一步改进。
如果没有特殊原因,您可以删除自动递增orders_products_id列。您可以通过订单和产品 ID 来标识表中的行。我认为该列只会在表和索引中增加更多空间,而没有任何价值。