当前的实现是一个具有多个连接和临时表的复杂查询,但是对我的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, ...
单个公司可以在两个产品表之间分配10万个项目.产品表由line_name联合和过滤,然后按assembly_datetime排序,并根据分页进行限制.日期时间值也依赖于时区,这将作为查询的一部分应用(另一个JOIN临时表). line_name也是返回列之一.
我在考虑从产品联合查询中拆分line_name过滤器.本质上,我确定与过滤器对应的行的ID,然后使用WHERE条件WHERE line_id IN(<以前的查询结果>)执行UNION查询.这将减少对连接和临时表的需求,我可以在PHP中将line_name应用于line_id和时区修改,但我不确定这是解决问题的最佳方法.
我也看过可能使用Redis,但是大量的单个产品在通过PHP将所有数据推送到Redis(20-30秒)时导致同样长的等待时间,即使它只是直接从产品表.
>是否可以调整现有查询以提高效率?
>我可以将一些处理推送到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已编入索引;主键是每列的第一个键.
解决方法:
让我们从其组成部分构建此查询,以查看我们可以优化的内容.
观察:您从两个大型产品表的并集中获取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多个产品表.您现在拥有的设置不灵活,无法轻松扩展.如果使用主产品表构建模式,并且可能使用某些属性表来构建特定于产品的信息,那么两年后您会发现自己更快乐.认真.请考虑进行更改.