修改MySQL查询以停止将临时表写入磁盘

时间:2013-09-20 22:10:45

标签: mysql sql

我们正在改进网站周围的一些SQL查询,并注意到特定SQL查询的瓶颈似乎将结果转储到临时磁盘表中。这很昂贵,当然非常慢。

我们已经考虑将MySQL临时目录移到RAMDISK,但我们可以尝试先优化它。

以下是转储到磁盘的命令:

select
  d.url,
  d.lid,
  d.title, 
  d.description, 
  d.date, 
  d.hits, 
  d.downloadratingsummary, 
  d.totalvotes, 
  d.totalcomments, 
  d.filesize, 
  d.version, 
  d.homepage, 
  d.ns_compat, 
  d.ns_des_img, 
  t.type
from downloads_downloads d
LEFT JOIN downloads_type t on d.lid = t.lid
where cid=91
ORDER BY FIELD(COALESCE(t.type,-1),1,-1,2,4,5,0,3), date DESC limit 0,20

有没有简单的方法来重新组织/修改上述查询以最小化磁盘上的临时表创建?

虽然我们有MySQL的经验,但性能优化对我们来说仍然是一个新的东西,所以任何人都可以给予的帮助总是非常感激。

感谢您提前提供任何帮助。

两个引用表的结构

downloads_downloads

downloads_downloads table structure

索引

indexes

downloads_type

downloads_type table structure

索引

indexes2

EXPLAIN的输出

EXPLAIN output

环境:

OS X运行带有16GB RAM的MySQL 5.5.23。

1 个答案:

答案 0 :(得分:2)

我认为这些链接可以帮助优化此查询:
 http://dev.mysql.com/doc/refman/5.5/en/order-by-optimization.html
http://dev.mysql.com/doc/refman/5.5/en/internal-temporary-tables.html

第一个结论 - 无法避免文件排序(临时表),因为ORDER BY子句中的查询包含来自两个表的列,并且还包含表达式,因此mySql无法使用它来优化它指数:

  

在某些情况下,MySQL无法使用索引来解决ORDER BY ................这些情况包括以下内容:
   - 将ORDER BY与包含键列名称以外的术语的表达式一起使用    - 您正在连接多个表,并且ORDER BY中的列不是来自用于检索行的第一个非常量表。


第二个结论 - 因为该表包含TEXT列,MySql不能使用修改的(优化)文件排序算法,但必须使用 oryginal 算法。 oryginal算法的一个缺点是它读取表行两次,并以随机方式读取它们,这比顺序读取慢得多:

  

这种方法的一个问题是它读取行两次:一次是在评估WHERE子句时,另一次是在对值对之后进行排序。即使第一次连续访问行(例如,如果进行了表扫描),第二次随机访问它们。 (排序键是有序的,但行位置不是。)


第三个结论 - 由于该表包含TEXT列,MySql不能使用内存临时表,但必须将其存储在磁盘上:

  

某些情况会阻止使用内存临时表,在这种情况下,服务器会使用磁盘表:    - 表格中存在BLOB或TEXT列



考虑到上述情况,将TEXT列移到另一个表中:

create table downloads_description(
  lid int(11) not null unique,
  description text,
  constraint dd_fk foreign key  (lid)
  references downloads_downloads( lid )
);

insert into downloads_description( lid, description )
select lid, description
from downloads_downloads;

alter table downloads_downloads
drop column description;

然后将查询重写为:

SELECT 
  d.url,
  d.lid,
  d.title, 
  dd.description, 
  d.date, 
  d.hits, 
  d.downloadratingsummary, 
  d.totalvotes, 
  d.totalcomments, 
  d.filesize, 
  d.version, 
  d.homepage, 
  d.ns_compat, 
  d.ns_des_img, 
  t.type
FROM downloads_downloads d 
JOIN(
    select   d.lid, t.type
    FROM downloads_downloads d
    LEFT JOIN downloads_type t
    ON d.lid = t.lid
        WHERE cid = 81
    ORDER BY FIELD(COALESCE(t.type,-1),1,-1,2,4,5,0,3), 
          d.date DESC 
    limit 0,20
) t ON d.lid = t.lid
JOIN downloads_description dd
ON dd.lid = d.lid
ORDER BY FIELD(COALESCE(t.type,-1),1,-1,2,4,5,0,3), 
         d.date DESC 
;