【问题标题】:2gb table with 10 million rows, late pagination select is slow1000万行的2gb表,后期分页选择很慢
【发布时间】:2016-07-11 14:48:24
【问题描述】:

我在 MySQL 中有 1000 万行和 2 GB 数据的表 选择 IN LIFO 格式数据很慢

表格引擎是=InnoDB

表有一个主键和一个唯一键

SELECT * FROM link LIMIT 999999 , 50;

我如何提高表的性能。 ?

表结构

id  int(11) NO  PRI NULL    auto_increment
url varchar(255)    NO  UNI NULL    
website varchar(100)    NO      NULL    
state   varchar(10) NO      NULL    
type    varchar(100)    NO      NULL    
prio    varchar(100)    YES     NULL    
change  varchar(100)    YES     NULL    
last    varchar(100)    YES     NULL

注意: SELECT * FROM link LIMIT 1 , 50; 正在占用 .9ms 但当前 sql 正在占用 1000ms 它的 1000 次占用更多时间

【问题讨论】:

  • 你需要表中的所有列吗?你能详细说明一下慢 - 需要多长时间?还有什么是列的数据类型?
  • 你可以在下面运行查询并粘贴结果EXPLAIN SELECT * FROM link LIMIT 999999 , 50;
  • 它几乎需要 1 秒,但是当行数更大时,获取时间会更长 SELECT * FROM link LIMIT 1 , 50;需要 0.9 毫秒,但当前 sql 需要 1000 毫秒,其 100 次需要更多时间。
  • 1,000,000,000,000 行存储在 5 GB 中,即每字节 200 行,太棒了。那么实际的数字/大小是多少?
  • 你真的坐在那里点击[下一步]按钮 20,000 次?我想不是。如果没有ORDER BY,则无法保证获得任何特定的 50 行!

标签: mysql sql query-optimization


【解决方案1】:

这很可能是由于“早期行查找”

可以强制 MySQL 进行“延迟行查找”。试试下面的查询

SELECT  l.*
FROM    (
        SELECT  id
        FROM    link
        ORDER BY
                id
        LIMIT 999999 , 50
        ) q
JOIN    link l
ON      l.id = q.id

查看这篇文章

MySQL limit clause and rate low lookups

【讨论】:

  • 我不相信,直到我自己测试它。在我的测试中,它是 50% 的性能胜利。为什么 MySQL 在没有帮助的情况下仍然无法优化此类查询?
  • @PaulSpiegel:当我第一次遇到它时,我也感到很惊讶。
【解决方案2】:

对于 NextPrev 按钮,您可以使用 WHERE 子句代替 OFFSET

示例(使用LIMIT 10 - 示例数据解释如下):您在某个页面上显示了 10 行 ID 为 [2522,2520,2514,2513,2509,2508,2506,2504,2497,2496]。在我的情况下,这是用

创建的
select *
from link l
order by l.id desc
limit 10
offset 999000

下一页你会使用

limit 10
offset 999010

获取ID为[2495,2494,2493,2492,2491,2487,2483,2481,2479,2475]的行。

上一页你会使用

limit 10
offset 998990

获取ID为[2542,2541,2540,2538,2535,2533,2530,2527,2525,2524]的行。

以上所有查询都在 500 毫秒内执行。使用 Sanj 建议的“技巧”仍然需要 250 毫秒

现在有了minId=2496maxId=2522 的给定页面,我们可以使用WHERE 子句为NextLast 按钮创建查询。

下一步按钮:

select *
from link l
where l.id < :minId -- =2496
order by l.id desc
limit 10

结果 ID:[2495,2494,2493,2492,2491,2487,2483,2481,2479,2475]

上一个按钮:

select *
from link l
where l.id > :maxId -- =2522
order by l.id asc
limit 10

结果 ID:[2524,2525,2527,2530,2533,2535,2538,2540,2541,2542]

要反转顺序,您可以在子选择中使用查询:

select *
from (
    select *
    from link l
    where l.id > 2522
    order by l.id asc
    limit 10
) sub
order by id desc

结果 ID:[2542,2541,2540,2538,2535,2533,2530,2527,2525,2524]

这些查询“立即”执行(少于 1 毫秒)并提供相同的结果。

您不能使用此解决方案来创建页码。但我认为您不会输出 200K 页码。

测试数据:

用于示例和基准测试的数据是用

创建的
CREATE TABLE `link` (
    `id` INT(11) NOT NULL AUTO_INCREMENT,
    `url` VARCHAR(255) NOT NULL,
    `website` VARCHAR(100) NULL DEFAULT NULL,
    `state` VARCHAR(10) NULL DEFAULT NULL,
    `type` VARCHAR(100) NULL DEFAULT NULL,
    `prio` VARCHAR(100) NULL DEFAULT NULL,
    `change` VARCHAR(100) NULL DEFAULT NULL,
    `last` VARCHAR(100) NULL DEFAULT NULL,
    PRIMARY KEY (`id`),
    UNIQUE INDEX `url` (`url`)
) COLLATE='utf8_general_ci' ENGINE=InnoDB;

insert into link
    select i.id
        , concat(id, '-', rand()) url
        , rand() website
        , rand() state
        , rand() `type`
        , rand() prio
        , rand() `change`
        , rand() `last`
    from test._dummy_indexes_2p23 i
    where i.id <= 2000000
      and rand() < 0.5

其中test._dummy_indexes_2p23 是一个包含 2^23 个 id(大约 8M)的表。因此,数据包含大约 1M 行,每个第二个 id 随机丢失。表大小:228 MB

【讨论】:

    【解决方案3】:

    由于数据量很大,

    有一些提高查询响应时间的技巧:

    1. 将存储引擎 Innodb 更改为 myisam。
    2. 创建表分区 (https://dev.mysql.com/doc/refman/5.7/en/partitioning-management.html)
    3. Mysql 集群 (http://dev.mysql.com/doc/refman/5.7/en/mysql-cluster-overview.html)
    4. 增加硬件容量。

    谢谢

    【讨论】:

      【解决方案4】:

      首先,在没有任何顺序的情况下在您的表上运行并不能保证如果运行两次,您的查询将返回相同的数据。 最好添加一个ORDER BY 子句。将 id 作为一个很好的候选者,因为它是您的主键并且看起来是独一无二的(因为它是一个 auto_increment 值)。

      你可以用它作为你的基础:

      SELECT * FROM link ORDER BY id LIMIT 50;
      

      这将为您提供表中的前 50 行。

      现在对于接下来的 50 行,我们可以保存查询中的最后一个位置,而不是使用 OFFSET

      您将保存上一个查询中最后一行的 id 并在下一个查询中使用它:

      SELECT * FROM link WHERE id > last_id ORDER BY id LIMIT 50;
      

      这将为您提供最后一个 id 之后的下 50 行。

      您的查询在OFFSET 的高值上运行缓慢的原因是mysql 必须在给定OFFSET 中的所有行上运行并返回最后LIMIT 行数。这意味着OFFSET 越大,查询运行的速度就越慢。

      我上面展示的解决方案不依赖于OFFSET,因此查询将以与当前页面无关的相同速度运行。

      另请参阅这篇有用的文章,其中解释了您可以选择的其他一些选项:http://www.iheavy.com/2013/06/19/3-ways-to-optimize-for-paging-in-mysql/

      【讨论】:

        【解决方案5】:

        我已将我的 SQL 查询更新为此,这花费的时间更少。

         SELECT * FROM link ORDER BY id LIMIT 999999 , 50  ;
        

        【讨论】:

          猜你喜欢
          • 2014-09-18
          • 1970-01-01
          • 2021-11-07
          • 2023-03-12
          • 1970-01-01
          • 1970-01-01
          • 2022-07-25
          • 1970-01-01
          • 2023-03-11
          相关资源
          最近更新 更多