【问题标题】:Pagination query mysql+php take 20-40 seconds分页查询mysql+php耗时20-40秒
【发布时间】:2018-01-16 22:05:45
【问题描述】:

您好,我在我的应用程序中使用 Angular js 进行分页,我将数据发送到包含用户设置的过滤器的大查询

更新 SQL_CALC_FOUND_ROWS 这是我的问题。如何计算特定过滤器的行数。 100,000 行花了我 2 秒我需要分页的数字作为总数

更新: 我在这里错过了以下内部查询:

(select count(*) from students as inner_st where st.name = inner_st.name) 作为名字,

当我删除上面的内部查询时要快得多

行数:50,000 用户表:4行 类表:4行 索引:只有 id 作为主键

查询时间20-40秒

tables: students.

columns : id, date ,class, name,image,status,user_id,active 

table user
coloumn: id,full_name,is_admin

查询

SELECT  SQL_CALC_FOUND_ROWS st.id, 
        st.date,
        st.image,
        st.user_id,
        st.status,
        st, 
        ck.name AS class_name,
        users.full_name,
        (select count(*) from students AS inner_st where st.name = inner_st.name) AS names,
FROM students AS st
    LEFT JOIN  users ON st.user_id = users.user_id
    LEFT JOIN  classes AS ck  ON st.class = ck.id
WHERE date BETWEEN '2018-01-17' AND DATE_ADD('2018-01-17', INTERVAL 1 DAY)
    AND DATE_FORMAT(date,'%H:%i') >= '00:00'
    AND DATE_FORMAT(date,'%H:%i') <= '23:59'
    AND st.active=1 
    -- here I can concat filters from  web like "and class= 1"
ORDER BY st.date DESC
LIMIT  0, 10

我怎样才能让它更快?当我删除订单时,SQL_CALC_FOUND_ROWS 会更快,但我需要它们 我听说过索引,但只有主键是索引

【问题讨论】:

  • 如果不知道表中有多少行,哪些列被索引,很难判断。
  • @JoeP。 hi - id 作为主键和 50000 行
  • 添加st的别名。告诉我们额外的过滤器列将在哪些表中。(它在优化方面有很大的不同。)请提供SHOW CREATE TABLE
  • @RickJames 我已经更新了我有另一个内部查询的问题,当我更快地删除它时,我该如何索引? (select count(*) from students as inner_st where st.name = inner_st.name) 作为名字,
  • 1.将所有联接转换为内部联接。 2.对student表中的user_id、class、date进行索引。

标签: php mysql indexing


【解决方案1】:

在推荐对此查询的不同方法之前,很少有 cmets:

  • 您是否考虑过删除SQL_CALC_FOUND_ROWS 并改为运行两个查询(一个计算数据,一个选择数据)?在某些情况下,这可能比将它们都加入一个查询更快。
  • 这些条件的目标是什么?你想达到什么目的?我们可以删除它们(因为它们似乎总是返回 true?) - AND DATE_FORMAT(st.date, '%H:%i') &gt;= '00:00' AND DATE_FORMAT(st.date, '%H:%i') &lt;= '23:59'
  • 您只需要 10 个结果,但数据库必须在 LIMIT 之前为每个结果运行“名称”子查询(可能很多?)。因此,我建议将 SELECT 子句中的子查询提取到临时表中,对其进行索引并加入其中(请参阅下面的固定查询)。

为了优化查询,让我们从添加这些索引开始:

ALTER TABLE `classes` ADD INDEX `classes_index_1` (`id`, `name`);
ALTER TABLE `students` ADD INDEX `students_index_1` (`active`, `user_id`, `class`, `name`, `date`);
ALTER TABLE `users` ADD INDEX `users_index_1` (`user_id`, `full_name`);

现在创建临时表(最初这是 SELECT 子句中的子查询)并为其编制索引:

-- Transformed subquery to a temp table to improve performance
CREATE TEMPORARY TABLE IF NOT EXISTS temp1 AS SELECT
        count(*) AS names,
        name 
    FROM
        students AS inner_st 
    WHERE
        1 = 1 
    GROUP BY
        name 
    ORDER BY
        NULL

-- This index is required for optimal temp tables performance
ALTER TABLE
  `temp1`
ADD
  INDEX `temp1_index_1` (`name`, `names`);

以及修改后的查询:

SELECT
        SQL_CALC_FOUND_ROWS st.id,
        st.date,
        st.image,
        st.user_id,
        st.status,
        ck.name AS class_name,
        users.full_name,
        temp1.names 
    FROM
        students AS st 
    LEFT JOIN
        users 
            ON st.user_id = users.user_id 
    LEFT JOIN
        classes AS ck 
            ON st.class = ck.id 
    LEFT JOIN
        temp1 
            ON st.name = temp1.name 
    WHERE
        st.date BETWEEN '2018-01-17' AND DATE_ADD('2018-01-17', INTERVAL 1 DAY) 
        AND st.active = 1 
    ORDER BY
        st.date DESC LIMIT 0,
        10

【讨论】:

  • 感谢 SQL_CALC_FOUND_ROWS 这是我的问题。如何计算特定过滤器的行数。 100,000 行花了我 2 秒我需要分页的数字
  • @Guz - 我不确定我是否理解你的问题。你能澄清一下吗?您是否要求 SQL_CALC_FOUND_ROWS 的替代方案,因为它会减慢您的查询速度?
  • 是的,我能做些什么来替代该功能。我必须使用那个号码
  • @Guz - 您可以决定将工作拆分为两个查询,因此其中一个将是相同查询逻辑的计数(),它将执行一次并返回您您要查找的号码。您可以在此处阅读有关 SQL_CALC_FOUND_ROWS 和 COUNT() 之间性能比较的更多信息:percona.com/blog/2007/08/28/…
【解决方案2】:

先试试看:

INDEX(active, date)

user_idusers 的 PK 吗? class_idclasses 的 PK 吗?如果不是,那么他们应该是INDEXed

为什么要分开测试时间?

修复测试,让每列在哪个表中一目了然。

你真的需要LEFT JOIN吗?或者JOIN 就足够了?在后一种情况下,有更多的优化选项。

举一些其他SELECTs的现实例子;可能需要不同的索引。

“第一”页慢吗?还是只有后面的页面?请参阅this 进行分页优化——not 使用OFFSET

【讨论】:

  • user_id 和 class 不是 PK。只是适合用户和类表的 id。我试过你写的索引。还是很慢。我想我可以从这里删除连接/左连接,但类表包含 4 行,用户 2 行
  • 我已经更新了我有另一个内部查询的问题,当我更快地删除它时,我怎样才能索引它? (select count(*) from students as inner_st where st.name = inner_st.name) 作为名字,
  • users:INDEX(user_id)classes:INDEX(class_id)
  • @Guz - 如果还不是PRIMARY KEY,则需要INDEX(name)
  • 我将那个子查询移到加入并提高了很多性能,但索引根本没有帮助我,可能是 0.1 秒
猜你喜欢
  • 1970-01-01
  • 2018-05-09
  • 1970-01-01
  • 1970-01-01
  • 2015-02-14
  • 2018-03-10
  • 1970-01-01
  • 1970-01-01
  • 2023-02-14
相关资源
最近更新 更多