gpt4 book ai didi

php - 如何在多对多查询中避免 "Using temporary"?

转载 作者:行者123 更新时间:2023-11-29 01:33:05 26 4
gpt4 key购买 nike

这个查询非常简单,我想做的就是获取给定类别中按 last_updated 字段排序的所有文章:

SELECT
`articles`.*
FROM
`articles`,
`articles_to_categories`
WHERE
`articles`.`id` = `articles_to_categories`.`article_id`
AND `articles_to_categories`.`category_id` = 1
ORDER BY `articles`.`last_updated` DESC
LIMIT 0, 20;

但是运行起来很慢。这是 EXPLAIN 所说的内容:

select_type  table                   type     possible_keys           key         key_len  ref                                rows  Extra
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
SIMPLE articles_to_categories ref article_id,category_id article_id 5 const 5016 Using where; Using temporary; Using filesort
SIMPLE articles eq_ref PRIMARY PRIMARY 4 articles_to_categories.article_id 1

有没有办法重写此查询或向我的 PHP 脚本添加额外的逻辑以避免 Using temporary;使用文件排序 并加快速度?

表结构:

*articles*
id | title | content | last_updated

*articles_to_categories*
article_id | category_id

更新

我有 last_updated 索引。我想我的情况在 d ocumentation 中有解释。 :

In some cases, MySQL cannot use indexes to resolve the ORDER BY, although it still uses indexes to find the rows that match the WHERE clause. These cases include the following:

The key used to fetch the rows is not the same as the one used in the ORDER BY: SELECT * FROM t1 WHERE key2=constant ORDER BY key1;

You are joining many tables, and the columns in the ORDER BY are not all from the first nonconstant table that is used to retrieve rows. (This is the first table in the EXPLAIN output that does not have a const join type.)

但我仍然不知道如何解决这个问题。

最佳答案

这是我前段时间针对类似性能相关问题所做的一个简化示例,它利用了 innodb 集群主键索引(显然仅适用于 innodb !!)

您有 3 个表:category、product 和 product_category,如下所示:

drop table if exists product;
create table product
(
prod_id int unsigned not null auto_increment primary key,
name varchar(255) not null unique
)
engine = innodb;

drop table if exists category;
create table category
(
cat_id mediumint unsigned not null auto_increment primary key,
name varchar(255) not null unique
)
engine = innodb;

drop table if exists product_category;
create table product_category
(
cat_id mediumint unsigned not null,
prod_id int unsigned not null,
primary key (cat_id, prod_id) -- **note the clustered composite index** !!
)
engine = innodb;

最重要的是 product_catgeory 聚簇复合主键的顺序,因为此场景的典型查询总是由 cat_id = x 或 cat_id in (x,y,z...) 引导。

我们有 50 万 类别、100 万 产品和1.25 亿 产品类别。

select count(*) from category;
+----------+
| count(*) |
+----------+
| 500000 |
+----------+

select count(*) from product;
+----------+
| count(*) |
+----------+
| 1000000 |
+----------+

select count(*) from product_category;
+-----------+
| count(*) |
+-----------+
| 125611877 |
+-----------+

那么让我们看看这个架构如何针对与您的查询类似的查询执行。所有查询都冷运行(在 mysql 重启后),缓冲区为空且没有查询缓存。

select
p.*
from
product p
inner join product_category pc on
pc.cat_id = 4104 and pc.prod_id = p.prod_id
order by
p.prod_id desc -- sry dont a date field in this sample table - wont make any difference though
limit 20;

+---------+----------------+
| prod_id | name |
+---------+----------------+
| 993561 | Product 993561 |
| 991215 | Product 991215 |
| 989222 | Product 989222 |
| 986589 | Product 986589 |
| 983593 | Product 983593 |
| 982507 | Product 982507 |
| 981505 | Product 981505 |
| 981320 | Product 981320 |
| 978576 | Product 978576 |
| 973428 | Product 973428 |
| 959384 | Product 959384 |
| 954829 | Product 954829 |
| 953369 | Product 953369 |
| 951891 | Product 951891 |
| 949413 | Product 949413 |
| 947855 | Product 947855 |
| 947080 | Product 947080 |
| 945115 | Product 945115 |
| 943833 | Product 943833 |
| 942309 | Product 942309 |
+---------+----------------+
20 rows in set (0.70 sec)

explain
select
p.*
from
product p
inner join product_category pc on
pc.cat_id = 4104 and pc.prod_id = p.prod_id
order by
p.prod_id desc -- sry dont a date field in this sample table - wont make any diference though
limit 20;

+----+-------------+-------+--------+---------------+---------+---------+------------------+------+----------------------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+--------+---------------+---------+---------+------------------+------+----------------------------------------------+
| 1 | SIMPLE | pc | ref | PRIMARY | PRIMARY | 3 | const | 499 | Using index; Using temporary; Using filesort |
| 1 | SIMPLE | p | eq_ref | PRIMARY | PRIMARY | 4 | vl_db.pc.prod_id | 1 | |
+----+-------------+-------+--------+---------------+---------+---------+------------------+------+----------------------------------------------+
2 rows in set (0.00 sec)

那是 0.70 秒的冷 - 哎哟。

希望这有帮助:)

编辑

刚刚阅读了您对我上面的评论的回复,您似乎可以做出以下两个选择之一:

create table articles_to_categories
(
article_id int unsigned not null,
category_id mediumint unsigned not null,
primary key(article_id, category_id), -- good for queries that lead with article_id = x
key (category_id)
)
engine=innodb;

或者。

create table categories_to_articles
(
article_id int unsigned not null,
category_id mediumint unsigned not null,
primary key(category_id, article_id), -- good for queries that lead with category_id = x
key (article_id)
)
engine=innodb;

取决于您对如何定义集群 PK 的典型查询。

关于php - 如何在多对多查询中避免 "Using temporary"?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5472241/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com