在分页表中显示大量数据而不会严重影响DB

时间:2014-10-02 20:58:49

标签: php mysql database redis predis

当前的实现是一个包含多个连接和临时表的复杂查询,但是对我的MySQL施加了太大的压力,并且加载表需要超过30秒。 PHP通过JavaScript Ajax调用检索数据并显示在网页上。以下是涉及的表格:

Table: table_companies
Columns: company_id, ...

Table: table_manufacture_line
Columns: line_id, line_name, ...

Table: table_product_stereo
Columns: product_id, line_id, company_id, assembly_datetime, serial_number, ...

Table: table_product_television
Columns: product_id, line_id, company_id, assembly_datetime, serial_number, warranty_expiry, ...

单个公司可以在两个产品表之间分配100k +项目。产品表由line_name联合和过滤,然后按assembly_datetime排序,并根据分页进行限制。日期时间值也依赖于时区,这将作为查询的一部分应用(另一个JOIN +临时表)。 line_name也是返回列之一。

我在考虑将line_name过滤器从产品联合查询中分离出来。本质上,我确定与过滤器对应的行的ID,然后使用WHERE条件WHERE line_id IN (<results from previous query>)执行UNION查询。这将减少对连接和临时表的需求,我可以将line_name应用于PHP中的line_id和时区修改,但我不确定这是解决问题的最佳方法。

我也看过可能使用Redis,但是大量的单个产品在通过PHP(20-30秒)将所有数据推送到Redis时导致类似的长等待时间,即使它刚被拉直接来自产品表。

  • 是否可以调整现有查询以提高效率?
  • 我可以将一些处理推送到PHP以减少SQL服务器上的负载吗? Redis怎么样?
  • 有没有办法更好地构建表格?
  • 您会建议哪些其他解决方案?

感谢您提供的任何意见。

修改

现有查询:

SELECT line_name,CONVERT_TZ(datetime,'UTC',timezone) datetime,... FROM (SELECT line_name,datetime,... FROM ((SELECT line_id,assembly_datetime datetime,... FROM table_product_stereos WHERE company_id=# ) UNION (SELECT line_id,assembly_datetime datetime,... FROM table_product_televisions WHERE company_id=# )) AS union_products INNER JOIN table_manufacture_line USING (line_id)) AS products INNER JOIN (SELECT timezone FROM table_companies WHERE company_id=# ) AS tz ORDER BY datetime DESC LIMIT 0,100

这里的格式是为了一些可读性。

SELECT line_name,CONVERT_TZ(datetime,'UTC',tz.timezone) datetime,... 
  FROM (SELECT line_name,datetime,... 
          FROM (SELECT line_id,assembly_datetime datetime,... 
                    FROM table_product_stereos WHERE company_id=# 

                 UNION 
                SELECT line_id,assembly_datetime datetime,... 
                  FROM table_product_televisions 
                 WHERE company_id=# 
               ) AS union_products 
         INNER JOIN table_manufacture_line USING (line_id)
        ) AS products 
INNER JOIN (SELECT timezone 
            FROM table_companies 
            WHERE company_id=# 
            ) AS tz 
ORDER BY datetime DESC LIMIT 0,100

ID已编入索引;主键是每列的第一个键。

3 个答案:

答案 0 :(得分:2)

让我们从其组成部分构建此查询,以查看我们可以优化的内容。

观察:您从两个大型产品表的联合中获取100个最新行。

因此,让我们首先尝试优化从产品表中提取东西的子查询。这是其中之一。

              SELECT line_id,assembly_datetime datetime,... 
                FROM table_product_stereos 
               WHERE company_id=#

但是看,你这里只需要100个最新的条目。所以,让我们添加

               ORDER BY assembly_datetime DESC
               LIMIT 100

此查询。此外,您应该在此表上放置一个复合索引,如下所示。这将允许索引满足WHERE和ORDER BY查找。

 CREATE INDEX id_date ON table_product_stereos (company_id, assembly_datetime)

所有相同的注意事项都适用于table_product_televisions的查询。按时间排序,将其限制为100,并将其编入索引。

如果您需要应用其他选择条件,可以将它们放在这些内部查询中。例如,在评论中,您提到了基于子字符串搜索的选择。您可以按照以下方式执行此操作

              SELECT t.line_id,t.assembly_datetime datetime,... 
                FROM table_product_stereos AS t
                JOIN table_manufacture_line AS m   ON m.line_id = t.line_id 
                                                  AND m.line_name LIKE '%test'
               WHERE company_id=#
               ORDER BY assembly_datetime DESC
               LIMIT 100

接下来,您使用UNION将这两个查询结果集合并为一个。 UNION具有消除重复的功能,这非常耗时。 (你知道你没有重复,但MySQL没有。)请改用UNION ALL

将这一切放在一起,最里面的子查询就变成了这个。我们必须包装子查询,因为SQL在同一查询级别被UNIONORDER BY子句混淆。

           SELECT * FROM (
              SELECT line_id,assembly_datetime datetime,... 
                FROM table_product_stereos 
               WHERE company_id=#
               ORDER BY assembly_datetime DESC 
               LIMIT 100
                         ) AS st
           UNION ALL 
           SELECT * FROM (
             SELECT line_id,assembly_datetime datetime,... 
               FROM table_product_televisions 
              WHERE company_id=#
              ORDER BY assembly_datetime DESC 
              LIMIT 100
                         ) AS tv

这会让你获得200行。它应该很快得到那些行。

确保200行足以在您执行外部ORDER BY ... LIMIT操作后为您提供100个最新项目。但是这个操作只需要处理200行,而不是100K +,所以它会快得多。

最后在您的外部查询材料中包装此查询。加入table_manufacture_line信息,然后修复时区。

如果您之前执行索引编制和ORDER BY ... LIMIT操作,则此查询应该变得非常快。

您问题中的评论对话框向我表明您可能有多种产品类型,而不仅仅是两种产品类型,并且您的分页显示有复杂的选择条件。在大量行上使用UNION ALL可以获得性能:它将多个索引表转换为无法有效搜索的内部行列表。

您真的应该考虑将两种产品数据放在一个表中,而不是必须使用UNION ALL多个产品表。您现在拥有的设置不够灵活,不容易扩展。如果使用主产品表构建模式,并且可能使用某些属性表来构建特定于产品的信息,那么两年后您会发现自己更快乐。认真。请考虑进行更改。

答案 1 :(得分:1)

记住:索引速度快,数据速度慢。在嵌套查询上使用连接。嵌套查询返回所有数据字段,而连接只考虑过滤器(应该全部编入索引 - 确保在table_product _ *。line_id上​​有唯一索引)。已经有一段时间了,但我很确定你可以加入&#34; ON company_id =#&#34;这应该会在早期削减结果。

在这种情况下,所有结果都指向同一家公司(或更小的子集),因此单独运行该查询是有意义的(并且它使查询更易于维护)。

所以你的数据源是:

(table_product_stereos as prod
INNER JOIN table_manufacture_line AS ml ON prod.line_id = ml.line_id and prod.company_id=#
UNION
table_product_televisions as prod
INNER JOIN table_manufacture_line as ml on prod.line_id = ml.line_id and prod.company_id=#)

您可以从中选择产品。或ml。字段根据需要。

答案 2 :(得分:0)

PHP根本不是解决方案...... Redis可以成为一种解决方案。

但我要改变的主要是表的索引创建(添加缺少索引)...如果你正在运行临时表,你没有为表创建好索引。而且几乎没有100k行。

但是,如果没有任何表创建语句以及您运行的查询,我无法帮助您。

确保你的&#34;在哪里?&#34;是从左到右的youf btree索引的一部分。