MySQL - 优化查询(LIMIT、OFFSET、JOIN)> 2500 万行

Posted

技术标签:

【中文标题】MySQL - 优化查询(LIMIT、OFFSET、JOIN)> 2500 万行【英文标题】:MySQL - optimize query (LIMIT, OFFSET, JOIN) > 25 million rows 【发布时间】:2018-11-09 09:33:44 【问题描述】:

我想获得过去拍卖中观看次数最多的 1000 个拍品中的 100 个(随机)。我有一个 mysql 数据库,所有表都使用 InnoDB。

硬件:Intel i7 6700、32 GB RAM、SSD(不知道多少IOPS)。

关于我的 my.cnf 的一些信息:

innodb_log_buffer_size = 256M
innodb_log_file_size = 1G
innodb_write_io_threads = 16
innodb_flush_log_at_trx_commit = 0

# mysqltuner
query_cache_type    = 1
join_buffer_size    = 256K
tmp_table_size      = 32M
max_heap_table_size = 32M
innodb_buffer_pool_size = 11G

MySQL-Tuner 输出:

>>  MySQLTuner 1.6.0 - Major Hayden <major@mhtx.net>
 >>  Bug reports, feature requests, and downloads at http://mysqltuner.com/
 >>  Run with '--help' for additional options and output filtering
[--] Skipped version check for MySQLTuner script
[OK] Currently running supported MySQL version 5.7.24-0ubuntu0.16.04.1
[OK] Operating on 64-bit architecture

-------- Storage Engine Statistics -------------------------------------------
[--] Status: +ARCHIVE +BLACKHOLE +CSV -FEDERATED +InnoDB +MRG_MYISAM
[--] Data in InnoDB tables: 28G (Tables: 456)
[!!] Total fragmented tables: 18

-------- Security Recommendations  -------------------------------------------
[OK] There is no anonymous account in all database users
[OK] All database users have passwords assigned
[--] There is 605 basic passwords in the list.

-------- Performance Metrics -------------------------------------------------
[--] Up for: 18d 5h 43m 18s (80M q [51.384 qps], 827K conn, TX: 539B, RX: 28B)
[--] Reads / Writes: 58% / 42%
[--] Binary logging is disabled
[--] Total buffers: 11.3G global + 1.1M per thread (151 max threads)
[OK] Maximum reached memory usage: 11.3G (36.31% of installed RAM)
[OK] Maximum possible memory usage: 11.5G (36.76% of installed RAM)
[OK] Slow queries: 0% (0/80M)
[OK] Highest usage of available connections: 11% (17/151)
[OK] Aborted connections: 0.00%  (11/827974)
[OK] Query cache efficiency: 74.7% (50M cached / 67M selects)
[!!] Query cache prunes per day: 650750
[OK] Sorts requiring temporary tables: 0% (1K temp sorts / 744K sorts)
[!!] Temporary tables created on disk: 26% (160K on disk / 593K total)
[OK] Thread cache hit rate: 99% (70 created / 827K connections)
[!!] Table cache hit rate: 0% (416 open / 169K opened)
[OK] Open file limit used: 2% (21/1K)
[OK] Table locks acquired immediately: 100% (624 immediate / 624 locks)

-------- MyISAM Metrics -----------------------------------------------------
[!!] Key buffer used: 18.2% (3M used / 16M cache)
[OK] Key buffer size / total MyISAM indexes: 16.0M/43.0K
[!!] Read Key buffer hit rate: 77.3% (172 cached / 39 reads)

-------- InnoDB Metrics -----------------------------------------------------
[--] InnoDB is enabled.
[!!] InnoDB buffer pool / data size: 11.0G/28.3G
[!!] InnoDB buffer pool instances: 8
[OK] InnoDB Used buffer: 98.87% (712671 used/ 720852 total)
[OK] InnoDB Read buffer efficiency: 99.77% (20288463287 hits/ 20335997890 total)
[!!] InnoDB Write buffer efficiency: 0.00% (0 hits/ 1 total)
[OK] InnoDB log waits: 0.00% (0 waits / 733034 writes)

-------- AriaDB Metrics -----------------------------------------------------
[--] AriaDB is disabled.

-------- Replication Metrics -------------------------------------------------
[--] No replication slave(s) for this server.
[--] This is a standalone server..

-------- Recommendations -----------------------------------------------------
General recommendations:
    Run OPTIMIZE TABLE to defragment tables for better performance
    When making adjustments, make tmp_table_size/max_heap_table_size equal
    Reduce your SELECT DISTINCT queries which have no LIMIT clause
    Increase table_open_cache gradually to avoid file descriptor limits
    Read this before increasing table_open_cache over 64:
    Beware that open_files_limit (1024) variable
    should be greater than table_open_cache ( 431)
Variables to adjust:
    query_cache_size (> 16M)
    tmp_table_size (> 32M)
    max_heap_table_size (> 32M)
    table_open_cache (> 431)
    innodb_buffer_pool_size (>= 28G) if possible.
    innodb_buffer_pool_instances(=11)

我在 php 中使用随机的OFFSET 管理随机部分:$offsetRand = rand(0, 1000-100); 然后将随机偏移量附加到 sql 查询中。

这会导致以下查询:

SELECT l.id FROM Auction a 
INNER JOIN Lot l ON a.id = l.auction_id 
WHERE a.startDate < "2018-11-09" 
ORDER BY l.views DESC LIMIT 100 OFFSET 543

Explain查询结果:

+----+-------------+-------+------------+-------+-------------------------------+-----------------------+---------+--------------------+-------+----------+-----------------------------------------------------------+
| id | select_type | table | partitions | type  | possible_keys                 | key                   | key_len | ref                | rows  | filtered | Extra                                                     |
+----+-------------+-------+------------+-------+-------------------------------+-----------------------+---------+--------------------+-------+----------+-----------------------------------------------------------+
|  1 | SIMPLE      | a     | NULL       | range | PRIMARY,auction_startDate_idx | auction_startDate_idx | 5       | NULL               | 33864 |   100.00 | Using where; Using index; Using temporary; Using filesort |
|  1 | SIMPLE      | l     | NULL       | ref   | IDX_33CC6FFB57B8F0DE          | IDX_33CC6FFB57B8F0DE  | 5       | lotsearch_new.a.id |   320 |   100.00 | NULL                                                      |
+----+-------------+-------+------------+-------+-------------------------------+-----------------------+---------+--------------------+-------+----------+-----------------------------------------------------------+

Auction: 70000 行 表Lot:2700万行

索引Lot 表:

+-------+------------+-----------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table | Non_unique | Key_name              | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+-------+------------+-----------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| lot   |          0 | PRIMARY               |            1 | id          | A         |    23945704 |     NULL | NULL   |      | BTREE      |         |               |
| lot   |          0 | UNIQ_33CC6FFB989D9B62 |            1 | slug        | A         |    23945900 |     NULL | NULL   | YES  | BTREE      |         |               |
| lot   |          1 | IDX_33CC6FFB57B8F0DE  |            1 | auction_id  | A         |       74748 |     NULL | NULL   | YES  | BTREE      |         |               |
| lot   |          1 | lot_visible_idx       |            1 | visible     | A         |           1 |     NULL | NULL   |      | BTREE      |         |               |
| lot   |          1 | lot_hammerPrice_idx   |            1 | hammerPrice | A         |       59499 |     NULL | NULL   | YES  | BTREE      |         |               |
| lot   |          1 | lot_views_idx         |            1 | views       | A         |        3440 |     NULL | NULL   | YES  | BTREE      |         |               |
+-------+------------+-----------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+

索引Auction 表:

+---------+------------+-----------------------+--------------+-----------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table   | Non_unique | Key_name              | Seq_in_index | Column_name     | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+---------+------------+-----------------------+--------------+-----------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| auction |          0 | PRIMARY               |            1 | id              | A         |       67729 |     NULL | NULL   |      | BTREE      |         |               |
| auction |          0 | UNIQ_1159CC0FED9CD316 |            1 | md5Image_id     | A         |       57263 |     NULL | NULL   | YES  | BTREE      |         |               |
| auction |          1 | IDX_1159CC0F38248176  |            1 | currency_id     | A         |          24 |     NULL | NULL   | YES  | BTREE      |         |               |
| auction |          1 | IDX_1159CC0F47EE7BD5  |            1 | auctionhouse_id | A         |         752 |     NULL | NULL   | YES  | BTREE      |         |               |
| auction |          1 | IDX_1159CC0F8BAC62AF  |            1 | city_id         | A         |         100 |     NULL | NULL   | YES  | BTREE      |         |               |
| auction |          1 | auction_visible_idx   |            1 | visible         | A         |           1 |     NULL | NULL   |      | BTREE      |         |               |
| auction |          1 | auction_startDate_idx |            1 | startDate       | A         |        8810 |     NULL | NULL   |      | BTREE      |         |               |
+---------+------------+-----------------------+--------------+-----------------+-----------+-------------+----------+--------+------+------------+---------+---------------+

Lot

CREATE TABLE `Lot` (
 `id` int(11) NOT NULL AUTO_INCREMENT,
 `auction_id` int(11) DEFAULT NULL,
 `title` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
 `description` longtext COLLATE utf8mb4_unicode_ci,
 `visible` tinyint(1) NOT NULL DEFAULT '1',
 `url` varchar(2000) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
 `number` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
 `created` datetime NOT NULL,
 `views` int(11) DEFAULT '0',
 `startPrice` bigint(20) NOT NULL DEFAULT '0',
 `endPrice` bigint(20) DEFAULT NULL,
 `hammerPrice` bigint(20) DEFAULT NULL,
 `trained` tinyint(1) NOT NULL DEFAULT '0',
 `classifiedByCategory` tinyint(1) NOT NULL DEFAULT '0',
 `goldStandard` tinyint(1) NOT NULL DEFAULT '0',
 `slug` varchar(150) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
 PRIMARY KEY (`id`),
 UNIQUE KEY `UNIQ_33CC6FFB989D9B62` (`slug`),
 KEY `IDX_33CC6FFB57B8F0DE` (`auction_id`),
 KEY `lot_visible_idx` (`visible`),
 KEY `lot_hammerPrice_idx` (`hammerPrice`),
 KEY `lot_views_idx` (`views`),
 KEY `id` (`id`,`auction_id`,`views`),
 CONSTRAINT `FK_33CC6FFB57B8F0DE` FOREIGN KEY (`auction_id`) REFERENCES `Auction` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=39363610 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci

餐桌拍卖:

CREATE TABLE `Auction` (
 `id` int(11) NOT NULL AUTO_INCREMENT,
 `currency_id` int(11) DEFAULT NULL,
 `auctionhouse_id` int(11) DEFAULT NULL,
 `title` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
 `startDate` datetime NOT NULL,
 `endDate` datetime DEFAULT NULL,
 `created` datetime NOT NULL,
 `visible` tinyint(1) NOT NULL,
 `url` varchar(2000) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
 `priceType` smallint(6) NOT NULL COMMENT '0: Aufrufpreis | 1: Schätzpreis | 2: Limitpreis; Standard: 0',
 `identifier` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
 `address` varchar(1000) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
 `viewing` longtext COLLATE utf8mb4_unicode_ci,
 `useLocalImages` tinyint(1) NOT NULL DEFAULT '0',
 `md5Image_id` int(11) DEFAULT NULL,
 `city_id` int(11) DEFAULT NULL,
 `importedViaApi` tinyint(1) NOT NULL DEFAULT '0',
 `salecode` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
 PRIMARY KEY (`id`),
 UNIQUE KEY `UNIQ_1159CC0FED9CD316` (`md5Image_id`),
 KEY `IDX_1159CC0F38248176` (`currency_id`),
 KEY `IDX_1159CC0F47EE7BD5` (`auctionhouse_id`),
 KEY `IDX_1159CC0F8BAC62AF` (`city_id`),
 KEY `auction_visible_idx` (`visible`),
 KEY `auction_startDate_idx` (`startDate`),
 CONSTRAINT `FK_1159CC0F38248176` FOREIGN KEY (`currency_id`) REFERENCES `Currency` (`id`),
 CONSTRAINT `FK_1159CC0F47EE7BD5` FOREIGN KEY (`auctionhouse_id`) REFERENCES `Auctionhouse` (`id`) ON DELETE CASCADE,
 CONSTRAINT `FK_1159CC0F8BAC62AF` FOREIGN KEY (`city_id`) REFERENCES `City` (`id`),
 CONSTRAINT `FK_1159CC0FED9CD316` FOREIGN KEY (`md5Image_id`) REFERENCES `AuctionMd5Image` (`id`) ON DELETE SET NULL
) ENGINE=InnoDB AUTO_INCREMENT=116337 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci

FK 约束:Lot.auction_id Auction.id(只有待优化查​​询所需的 FK)

此查询未在合理的时间内(SELECT id FROM Auction WHERE startDate < :date;,查询将在 80 毫秒内完成。 如果我执行SELECT id FROM Lot ORDER BY views DESC LIMIT 100 OFFSET 50,查询也会在 20 毫秒内完成。所以看起来join操作确实需要很长时间。

那么,为什么在与连接结合使用时会如此缓慢?如何加快查询速度?

【问题讨论】:

请分享两个表的索引详细信息(如果有定义)。 你在使用 InnoDB 吗?你的桌子有什么关系?您是否在为您的 PK 和 FK 编制索引?如果您首先将所有表转换为 InnoDB,这可能会有所帮助。你甚至可以尝试使用 Facebook 的 MyRocks 而不是 InnoDB。同样最重要的是您的硬件是否有针对 I/O 的优化硬件。调整 Mysql 也很有帮助,尤其是当您的表增长到 2400 万行时,这已经是一个很大的规模了。 添加了有关硬件、mysql conf 和索引配置的信息。你认为我需要建立一个处理 2700 万行的 NoSQL 数据库吗?我还使用sphinxsearch 在 2700 万行中进行全文搜索... 你为什么使用左连接?您需要内部...除非您希望查询返回 NULL。 并且可以在很多(auctionid, views, id)上创建索引。 【参考方案1】:
SELECT *
    FROM (
        SELECT ...
            LIMIT 1000
         ) AS x
    ORDER BY RAND()
    LIMIT 100

子查询将首先获得 1000 个“观看次数最多”。外部查询将从 1000 中随机选择 100。

对于您的目的,外部查询“足够快”,所以我们需要关注内部查询,因为它似乎需要在两个表之间进行混乱的 JOIN?您提到了“观看次数最多”,然后您提到了“早于 :Date”。请将您的问题更新为可以找到 1000 个的有效查询。然后也许我们可以帮助您优化它。

请为表格提供SHOW CREATE TABLE

【讨论】:

观看次数最多的是在我的查询中由ORDER 函数(ORDER BY l.views DESC)计算得出的。 LIMIT 100 将获得 100 个结果,OFFSET 将随机选择一个起点来检索我在 1000 个观看次数最多的 100 个观看次数最多的结果。日期只是“2018-09-11” f.e.

以上是关于MySQL - 优化查询(LIMIT、OFFSET、JOIN)> 2500 万行的主要内容,如果未能解决你的问题,请参考以下文章

Mysql limit性能优化(小offset与大offset)

Mysql-Limit 优化

关于mysql中limit方面的查询优化

在使用limit的offset比较大的时候,使用子查询优化sql,减少回表操作

MySQL的limit用法和分页查询的性能分析及优化

MySQL分页查询LIMIT