3

我们正在改进站点周围的一些 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_type 表结构

索引

索引2

EXPLAIN 的输出

解释输出

环境:

OS X 运行 MySQL 5.5.23 和 16GB RAM。

4

1 回答 1

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 不能使用修改(优化)的文件排序算法,但必须使用原始算法。原始算法的一个缺点是它读取表行两次,并且以随机方式读取它们,这比顺序读取要慢得多:

这种方法的一个问题是它读取了两次行:一次是在评估 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 
;
于 2013-09-22T23:50:10.670 回答