【问题标题】:MySQL statement takes more than minute to executeMySQL 语句需要超过一分钟才能执行
【发布时间】:2016-02-13 10:56:17
【问题描述】:

我正在开发一个数据库很大的网站。当时表中有 100 万条记录。当我执行查询时,执行需要花费太多时间。下面给出了一个示例查询:

select * from `ratings` order by id limit 499500, 500

每个查询都需要一分钟以上,但是当我将表删除到 10000 条记录时,此查询执行得很快。

据我所知,一个表中 100 万条记录没有问题,因为在数据库表中,没有大记录的问题。

我在 Stack Overflow 问题 How do I add indices to MySQL tables? 的帮助下使用了表中的 id 索引,但我仍然遇到了同样的问题。

*** 我在项目中使用CodeIgniter

【问题讨论】:

  • 您在表中使用indexes 吗?另外,100 万条记录对于 MySQL 来说并没有那么大。
  • 表有三列ranking_id、id和网站……我没有索引的概念……但是1M的记录应该没有问题
  • 索引到位了吗?您要带回 500 行,而不是 1M(或一半)
  • 能否给出查询执行计划的快照。这应该对我们所有人都有帮助。
  • SELECT * 是个问题,ORDER BY LIMIT X ,y 只能通过扫描来完成,必须先得到所有的行才能排序

标签: php mysql


【解决方案1】:

注意,这并不是建议您暂时使用 MyISAM。我只用它来让我的 ids、min、max 和 count 排队。所以请忽略引擎。

create table ratings
(   id int auto_increment primary key,
    thing int null
)engine=MyISAM;
insert ratings (thing) values (null),(null),(null),(null),(null),(null),(null),(null),(null);
insert ratings (thing) select thing from ratings;
insert ratings (thing) select thing from ratings;
insert ratings (thing) select thing from ratings;
insert ratings (thing) select thing from ratings;
insert ratings (thing) select thing from ratings;
insert ratings (thing) select thing from ratings;
insert ratings (thing) select thing from ratings;
insert ratings (thing) select thing from ratings;
insert ratings (thing) select thing from ratings;
insert ratings (thing) select thing from ratings;

insert ratings (thing) select thing from ratings;
insert ratings (thing) select thing from ratings;
insert ratings (thing) select thing from ratings;
insert ratings (thing) select thing from ratings;
insert ratings (thing) select thing from ratings;
insert ratings (thing) select thing from ratings;
insert ratings (thing) select thing from ratings;

insert ratings (thing) select thing from ratings;
insert ratings (thing) select thing from ratings;

我现在有 470 万行

select count(*),min(id),max(id) from ratings;
+----------+---------+---------+
| count(*) | min(id) | max(id) |
+----------+---------+---------+
|  4718592 |       1 | 4718592 |
+----------+---------+---------+
select * from `ratings` order by id limit 499500, 500;
-- 1 second on a dumpy laptop

.

explain select * from `ratings` order by id limit 499500, 500;
+----+-------------+---------+------+---------------+------+---------+------+---------+----------------+
| id | select_type | table   | type | possible_keys | key  | key_len | ref  | rows    | Extra          |
+----+-------------+---------+------+---------------+------+---------+------+---------+----------------+
|  1 | SIMPLE      | ratings | ALL  | NULL          | NULL | NULL    | NULL | 4718592 | Using filesort |
+----+-------------+---------+------+---------------+------+---------+------+---------+----------------+

.

explain select * from `ratings` where id>=499501 limit 500;
+----+-------------+---------+-------+---------------+---------+---------+------+---------+-----------------------+
| id | select_type | table   | type  | possible_keys | key     | key_len | ref  | rows    | Extra                 |
+----+-------------+---------+-------+---------------+---------+---------+------+---------+-----------------------+
|  1 | SIMPLE      | ratings | range | PRIMARY       | PRIMARY | 4       | NULL | 4198581 | Using index condition |
+----+-------------+---------+-------+---------------+---------+---------+------+---------+-----------------------+

故事的寓意可能是使用 where 子句。

不能排除死锁的可能性。

【讨论】:

  • 谢谢。按哪里条件和顺序,它的工作。
  • ">=" 仅在 id 中没有间隙的情况下才有效! (而且,是的,InnoDB 和 MyISAM 对于 this 的情况应该类似。)
  • @RickJames 选择引擎是因为 innodb 在自插入期间会导致范围分配间隙
  • 人们总是可以像here 这样的innodb 模式搞砸,但我现在基本上不适合它。
  • ALTER TABLE table_name ENGINE=InnoDB;
【解决方案2】:

首先检查查询的执行计划以确定瓶颈并在需要时创建索引。我认为您至少应该在 Id 列上有一个索引。

有许多因素也会影响您的查询性能:

  1. 数据页面的碎片化
  2. 表的统计信息未更新
  3. 许多请求并行运行

还有更多......

按照以下链接获取执行计划并确定性能下降因素: http://www.sitepoint.com/using-explain-to-write-better-mysql-queries/

How to optimise MySQL queries based on EXPLAIN plan

如果您遇到任何麻烦,请告诉我。

【讨论】:

    【解决方案3】:

    清单:

    • 按 ASC 按“id”列顺序创建聚集索引(索引)
    • 从命令行检查您的查询时间,see this
    • 检查您的PHP 代码(我看到您使用CodeIgniter)。关于您的算法:您是否使用了 CodeIgniter 数据库库 see this
      如果您使用的是原始查询,请检查您的循环。

    另一种方法是尝试使用活动索引进行查询:

    select * from `ratings` where id>=456789 limit 500;
    

    【讨论】:

      【解决方案4】:

      检查索引。每个表都应该有一个主键 并且您的查询的where 子句中可能存在不属于主索引或聚集索引的属性。

      【讨论】:

      • 我没有在查询中使用 where 条件。
      【解决方案5】:

      这里是 Tuts-plus 的链接,可以帮助您解决问题。

      最可能的解决方案是索引数据库,而不是 (*) 使用列名 Better way to Query

      【讨论】:

        【解决方案6】:

        如果您的表使用InnoDB 引擎,请将其更改为MyISAM。读取操作更快。 When to use MyISAM and InnoDB.

        此外,如果您有带有搜索条件的查询,请确保将常用列编入索引。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2018-02-13
          • 2016-03-10
          • 1970-01-01
          • 2017-12-26
          • 1970-01-01
          • 1970-01-01
          • 2018-05-20
          • 1970-01-01
          相关资源
          最近更新 更多