简体   繁体   English

小型 MySQL 服务器上的慢查询

[英]Slow query on smaller MySQL server

I am running MySQL 5.7 on both my server and my local machine.我在我的服务器和本地机器上运行 MySQL 5.7。 I am using Symfony 4.4 and Doctrine.我正在使用 Symfony 4.4 和 Doctrine。 On my dev machine the following query (with the same DB dumped from the server) executes in ~2s, while it takes 35s+ on the server.在我的开发机器上,以下查询(使用从服务器转储的相同数据库)在 ~2 秒内执行,而在服务器上需要 35 秒以上。 I assume this is linked to limitations of the server (less RAM, etc.) but I can't really throw additional memory in there.我认为这与服务器的限制(更少的 RAM 等)有关,但我不能在其中添加额外的 memory。 Therefore, I am looking at how I could improve the following the query - originally generated by Doctrine.因此,我正在研究如何改进以下查询 - 最初由 Doctrine 生成。 I replicated the same slowness by executing the same query directly in phpMyAdmin on the server so I know for sure the query is responsible.我通过直接在服务器上的 phpMyAdmin 中执行相同的查询来复制同样的缓慢,所以我确定查询是负责任的。 I am a bit stuck here and would appreciate any help or pointers in the right direction: Do I need to try to split the queries?我有点卡在这里,希望有任何帮助或正确方向的指示:我需要尝试拆分查询吗? Should I try to add indexes (besides the PK and FK the column referenced in the where clause are not indexed)?我应该尝试添加索引(除了 PK 和 FK,where 子句中引用的列没有索引)? Thank you all for the help!谢谢大家的帮助!

SELECT DISTINCT id_0 FROM (
    SELECT DISTINCT id_0, pivot_price_5 FROM (
        SELECT b0_.id AS id_0, b0_.price_drop AS price_drop_1, o1_.id AS id_2, o1_.price AS price_3, o1_.currency AS currency_4, o1_.pivot_price AS pivot_price_5, o1_.price_drop AS price_drop_6, o1_.date AS date_7, p2_.id AS id_8, p2_.name AS name_9, p2_.description AS description_10, p2_.normal_price AS normal_price_11, p2_.link AS link_12, p2_.image_link AS image_link_13, p2_.image_thumb_link AS image_thumb_link_14, p2_.merchant_product_id AS merchant_product_id_15, p2_.slug AS slug_16, p2_.created_at AS created_at_17, p2_.updated_at AS updated_at_18, p3_.id AS id_19, p3_.ean AS ean_20, p3_.last_game_check_date AS last_game_check_date_21, p3_.created_at AS created_at_22, p3_.updated_at AS updated_at_23, g4_.id AS id_24, g4_.game_system_key AS game_system_key_25, g4_.created_at AS created_at_26, g4_.updated_at AS updated_at_27 
        FROM best_offer b0_ 
        INNER JOIN offer o1_ ON b0_.offer_id = o1_.id 
        INNER JOIN product_version p2_ ON o1_.product_version_id = p2_.id 
        INNER JOIN product p3_ ON b0_.product_id = p3_.id 
        INNER JOIN product_game_system p5_ ON p3_.id = p5_.product_id 
        INNER JOIN game_system g4_ ON g4_.id = p5_.game_system_id 
        WHERE (o1_.date >= '2020-07-29 00:00:00' AND o1_.date <= '2020-07-29 23:59:59') 
        AND o1_.pivot_price >= '0' 
        AND o1_.pivot_price <= '2208' 
        AND g4_.game_system_key IN ('NSW', 'PS4', 'ONE')
    ) dctrn_result_inner 
    ORDER BY pivot_price_5 ASC
) dctrn_result LIMIT 8 OFFSET 40

For completion sake, the PHP code is:为了完整起见,PHP 代码为:

// In Repository
$qb = $this->createQueryBuilder('best_offer')
            ->join('best_offer.offer', 'offer')
            ->addSelect('offer')
            ->join('offer.productVersion', 'productVersion')
            ->addSelect('productVersion')
            ->join('best_offer.product', 'product')
            ->addSelect('product')
            ->join('product.gameSystems', 'gameSystems')
            ->addSelect('gameSystems')
        ;

        
            $qb
                ->join('product.game', 'game')
                ->join('game.ratings', 'game_ratings')
                ->andWhere('game_ratings.type = :gameRatingType')
                ->setParameter('gameRatingType', GameRating::TYPE_METACRITIC)
                ->andWhere('game_ratings.rating > :gameRatingValue')
                ->setParameter('gameRatingValue', $minMetacritic)
            ;
        

            $qb = $qb->addCriteria(OfferRepository::createCriteriaOnDate($datetime, 'offer'));

            $qb->andWhere('offer.pivotPrice >= :minPivotPrice')
                ->setParameter('minPivotPrice', $minPivotPrice*100)
            ;

            $qb = $qb->addCriteria(OfferRepository::createCriteriaMaxPivotPrice($maxPivotPrice, 'offer'));

            $qb = $qb->addCriteria(GameSystemRepository::createCriteriaSystemsIn($gameSystems, 'gameSystems'));

            $qb = $qb->setMaxResults($limit);

            foreach ($sortBy as $sortKey => $sortValue) {
                $qb = $qb->orderBy($sortKey, $sortValue);
            }
       return $qb;

called by the PagerFanta in the Controller:由 Controller 中的 PagerFanta 调用:

// In Controller
$adapter = new DoctrineORMAdapter($qb);
$pagerFanta = new Pagerfanta($adapter);
$pagerFanta->setMaxPerPage(8);
$pagerFanta->setCurrentPage($page);

Explain results:解释结果:

+----+-------------+------------+------------+--------+--------------------------------------------------+-----------------------+---------+--------------------------------+------+----------+-----------------------------------------------------------+--+
| id | select_type |   table    | partitions |  type  |                  possible_keys                   |          key          | key_len |              ref               | rows | filtered |                           Extra                           |  |
+----+-------------+------------+------------+--------+--------------------------------------------------+-----------------------+---------+--------------------------------+------+----------+-----------------------------------------------------------+--+
|  1 | PRIMARY     | <derived2> | NULL       | ALL    | NULL                                             | NULL                  | NULL    | NULL                           | 2268 | 100.00   | Using temporary                                           |  |
|  2 | DERIVED     | g4_        | NULL       | range  | PRIMARY,UNIQ_B478BC43A9F4C69F                    | UNIQ_B478BC43A9F4C69F | 14      | NULL                           |    3 | 100.00   | Using where; Using index; Using temporary; Using filesort |  |
|  2 | DERIVED     | p5_        | NULL       | ref    | PRIMARY,IDX_1857225C4584665A,IDX_1857225C233EEA7 | IDX_1857225C233EEA7   | 4       | vgdeals.g4_.id                 |  377 | 100.00   | Using index                                               |  |
|  2 | DERIVED     | p3_        | NULL       | eq_ref | PRIMARY                                          | PRIMARY               | 4       | vgdeals.p5_.product_id         |    1 | 100.00   | Using index                                               |  |
|  2 | DERIVED     | b0_        | NULL       | ref    | UNIQ_8B8D09A53C674EE,IDX_8B8D09A4584665A         | IDX_8B8D09A4584665A   | 4       | vgdeals.p5_.product_id         |   40 | 100.00   | NULL                                                      |  |
|  2 | DERIVED     | o1_        | NULL       | eq_ref | PRIMARY,IDX_29D6873ED8DB782E                     | PRIMARY               | 4       | vgdeals.b0_.offer_id           |    1 | 5.00     | Using where                                               |  |
|  2 | DERIVED     | p2_        | NULL       | eq_ref | PRIMARY                                          | PRIMARY               | 4       | vgdeals.o1_.product_version_id |    1 | 100.00   | Using index                                               |  |
+----+-------------+------------+------------+--------+--------------------------------------------------+-----------------------+---------+--------------------------------+------+----------+-----------------------------------------------------------+--+

The SHOW CREATE TABLE for the involved tables is below (sorry I couldn't find a way to format this properly in SO):所涉及表的 SHOW CREATE TABLE 如下(抱歉,我找不到在 SO 中正确格式化它的方法):

BEST_OFFER最好的报价

CREATE TABLE `best_offer` (
     `id` int(11) NOT NULL AUTO_INCREMENT,
     `product_id` int(11) NOT NULL,
     `offer_id` int(11) NOT NULL,
     `price_drop` int(11) DEFAULT NULL,
     PRIMARY KEY (`id`),
     UNIQUE KEY `UNIQ_8B8D09A53C674EE` (`offer_id`),
     KEY `IDX_8B8D09A4584665A` (`product_id`),
     CONSTRAINT `FK_8B8D09A4584665A` FOREIGN KEY (`product_id`) REFERENCES `product` (`id`),
     CONSTRAINT `FK_8B8D09A53C674EE` FOREIGN KEY (`offer_id`) REFERENCES `offer` (`id`)
    ) ENGINE=InnoDB AUTO_INCREMENT=317260 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci

OFFER提供

CREATE TABLE `offer` (
 `id` int(11) NOT NULL AUTO_INCREMENT,
 `product_version_id` int(11) NOT NULL,
 `price` int(10) unsigned NOT NULL,
 `currency` varchar(3) COLLATE utf8mb4_unicode_ci NOT NULL,
 `pivot_price` int(11) NOT NULL,
 `price_drop` int(11) DEFAULT NULL,
 `date` datetime NOT NULL,
 PRIMARY KEY (`id`),
 KEY `IDX_29D6873ED8DB782E` (`product_version_id`),
 CONSTRAINT `FK_29D6873ED8DB782E` FOREIGN KEY (`product_version_id`) REFERENCES `product_version` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=497233 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci

PRODUCT_VERSION PRODUCT_VERSION

CREATE TABLE `product_version` (
 `id` int(11) NOT NULL AUTO_INCREMENT,
 `merchant_id` int(11) NOT NULL,
 `product_id` int(11) NOT NULL,
 `name` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
 `description` longtext COLLATE utf8mb4_unicode_ci,
 `normal_price` int(10) unsigned DEFAULT NULL,
 `link` varchar(4000) COLLATE utf8mb4_unicode_ci NOT NULL,
 `image_link` varchar(4000) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
 `image_thumb_link` varchar(4000) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
 `merchant_product_id` varchar(30) COLLATE utf8mb4_unicode_ci NOT NULL,
 `slug` varchar(300) COLLATE utf8mb4_unicode_ci NOT NULL,
 `created_at` datetime NOT NULL,
 `updated_at` datetime NOT NULL,
 PRIMARY KEY (`id`),
 UNIQUE KEY `UNIQ_6EC5C873989D9B62` (`slug`),
 KEY `IDX_6EC5C8736796D554` (`merchant_id`),
 KEY `IDX_6EC5C8734584665A` (`product_id`),
 CONSTRAINT `FK_6EC5C8734584665A` FOREIGN KEY (`product_id`) REFERENCES `product` (`id`),
 CONSTRAINT `FK_6EC5C8736796D554` FOREIGN KEY (`merchant_id`) REFERENCES `merchant` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=10775 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci

PRODUCT产品

CREATE TABLE `product` (
 `id` int(11) NOT NULL AUTO_INCREMENT,
 `ean` char(13) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
 `created_at` datetime NOT NULL,
 `updated_at` datetime NOT NULL,
 `game_id` int(11) DEFAULT NULL,
 `last_game_check_date` datetime DEFAULT NULL,
 PRIMARY KEY (`id`),
 UNIQUE KEY `UNIQ_D34A04AD67B1C660` (`ean`),
 KEY `IDX_D34A04ADE48FD905` (`game_id`),
 CONSTRAINT `FK_D34A04ADE48FD905` FOREIGN KEY (`game_id`) REFERENCES `game` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=6450 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci

PRODUCT_GAME_SYSTEM PRODUCT_GAME_SYSTEM

CREATE TABLE `product_game_system` (
 `product_id` int(11) NOT NULL,
 `game_system_id` int(11) NOT NULL,
 PRIMARY KEY (`product_id`,`game_system_id`),
 KEY `IDX_1857225C4584665A` (`product_id`),
 KEY `IDX_1857225C233EEA7` (`game_system_id`),
 CONSTRAINT `FK_1857225C233EEA7` FOREIGN KEY (`game_system_id`) REFERENCES `game_system` (`id`) ON DELETE CASCADE,
 CONSTRAINT `FK_1857225C4584665A` FOREIGN KEY (`product_id`) REFERENCES `product` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci

GAME_SYSTEM GAME_SYSTEM

CREATE TABLE `game_system` (
 `id` int(11) NOT NULL AUTO_INCREMENT,
 `game_system_key` varchar(3) COLLATE utf8mb4_unicode_ci NOT NULL,
 `created_at` datetime NOT NULL,
 `updated_at` datetime NOT NULL,
 PRIMARY KEY (`id`),
 UNIQUE KEY `UNIQ_B478BC43A9F4C69F` (`game_system_key`)
) ENGINE=InnoDB AUTO_INCREMENT=18 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci

Finally, here is the SHOW TABLE STATUS最后,这里是SHOW TABLE STATUS

+---------------------+--------+----+---------+--------+------+----------+---+---------+---------+--------+---------------------+---------------------+---------------------+---------------------+--+--------------------+--+--------------------+--+----------+--+--+--+--+
|     best_offer      | InnoDB | 10 | Dynamic | 307651 |  46  | 14172160 | 0 | 9469952 | 5242880 | 317260 | 2020-07-29 22:16:33 | 2020-07-31 07:59:09 |                     |          NULL       |  | utf8mb4_unicode_ci |  |          NULL      |  |          |  |  |  |  |
+---------------------+--------+----+---------+--------+------+----------+---+---------+---------+--------+---------------------+---------------------+---------------------+---------------------+--+--------------------+--+--------------------+--+----------+--+--+--+--+
| game_system         | InnoDB | 10 | Dynamic |     17 |  963 |    16384 | 0 |   16384 |       0 |     18 | 2020-07-29 22:16:36 |                     |     NULL            |                     |  |     NULL           |  | utf8mb4_unicode_ci |  |     NULL |  |  |  |  |
| offer               | InnoDB | 10 | Dynamic | 460330 |   60 | 27836416 | 0 | 7880704 | 6291456 | 497233 | 2020-07-29 22:16:44 | 2020-07-31 07:59:09 |                     |     NULL            |  | utf8mb4_unicode_ci |  |     NULL           |  |          |  |  |  |  |
| product             | InnoDB | 10 | Dynamic |   6432 |   63 |   409600 | 0 |  294912 |       0 |   6450 | 2020-07-29 22:16:44 | 2020-07-31 08:00:57 |                     |     NULL            |  | utf8mb4_unicode_ci |  |     NULL           |  |          |  |  |  |  |
| product_game_system | InnoDB | 10 | Dynamic |   6419 |   33 |   212992 | 0 |  229376 |       0 |        |     NULL            |                     | 2020-07-29 22:16:44 | 2020-07-31 07:57:15 |  |     NULL           |  | utf8mb4_unicode_ci |  |     NULL |  |  |  |  |
| product_version     | InnoDB | 10 | Dynamic |  10749 | 2297 | 24690688 | 0 | 1916928 | 7340032 |  10775 | 2020-07-29 22:16:50 | 2020-07-31 07:59:00 |                     |     NULL            |  | utf8mb4_unicode_ci |  |     NULL           |  |          |  |  |  |  |
+---------------------+--------+----+---------+--------+------+----------+---+---------+---------+--------+---------------------+---------------------+---------------------+---------------------+--+--------------------+--+--------------------+--+----------+--+--+--+--+

The ORDER BY pivot_price_5 ASC is useless. ORDER BY pivot_price_5 ASC没有用。 This is because a subquery is, but definition, an unordered set.这是因为子查询,但定义,是一个无序集。 (Adding a LIMIT makes it no useless.) But it seems like you should get rid of the inner subquery. (添加一个LIMIT使它毫无用处。)但看起来你应该摆脱内部子查询。

DISTINCT with LIMIT -- you are aware that the DISTINCT happens first? DISTINCT with LIMIT - 你知道DISTINCT先发生吗?

There are two ranges and one IN in the main WHERE ;主要WHERE有两个范围和一个IN only one of them can use an index.只有其中一个可以使用索引。 I suggest you have each of thefollowing so that the Optimizer can pick the better.我建议您拥有以下各项,以便优化器可以选择更好的。 (Note: With a different dataset, the Optimizer may pick a different INDEX , with different performance.) (注意:使用不同的数据集,优化器可能会选择不同的INDEX ,具有不同的性能。)

INDEX(pivot_price)
INDEX(date)

Please provide EXPLAINs , CREATE TABLEs , and SHOW TABLE STATUS .请提供EXPLAINsCREATE TABLEs表和SHOW TABLE STATUS (I want to analyze whether using partitioning for your "2-dimensional" WHERE would be worth pursuing.) (我想分析为您的“二维” WHERE使用分区是否值得追求。)

It looks like you are fetching several columns from many of the tables, only to eventually ignore those extra columns.看起来您正在从许多表中获取几列,但最终会忽略那些额外的列。 Cleaning that up will help performance.清理它将有助于性能。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM