4

当前的实现是具有多个连接和临时表的单个复杂查询,但是给我的 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 排序,并根据分页进行限制。datetime 值也依赖于时区,这作为查询的一部分应用(另一个 JOIN + temp 表)。line_name 也是返回的列之一。

我正在考虑将 line_name 过滤器从产品联合查询中分离出来。本质上,我会确定与过滤器相对应的行的 ID,然后使用 WHERE 条件进行 UNION 查询WHERE line_id IN (<results from previous query>)。这将消除对连接和临时表的需求,我可以将 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 被索引;主键是每列的第一个键。

4

3 回答 3

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 被同一查询级别的UNION和子句混淆了。ORDER 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多个产品表中。您现在拥有的设置不灵活,不会轻易扩大。如果你用一个主产品表和一些产品特定信息的属性表来构建你的模式,那么两年后你会发现自己更快乐。严重地。请考虑进行更改。

于 2014-10-03T01:45:38.157 回答
1

记住:索引快,数据慢。在嵌套查询上使用连接。嵌套查询返回所有数据字段,而连接只考虑过滤器(应该全部编入索引 - 确保 table_product_*.line_id 上有唯一索引)。已经有一段时间了,但我很确定您可以加入“ON company_id=#”,这应该会尽早减少结果。

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

所以你的数据源是:

(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=#)

您可以从中选择 prod。或毫升。根据需要填写字段。

于 2014-10-02T22:41:47.383 回答
0

PHP 根本不是一个解决方案…… Redis 可以是一个解决方案。

但是我要更改的主要内容是为表创建索引(添加缺少的索引)...如果您遇到临时表,则您没有为表创建好索引。100k 行的行数并不多。

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

确保您的“where 部分”是从左到右的 youf btree 索引的一部分。

于 2014-10-02T21:08:07.890 回答