我有一个Joomla表,其中包含数千行内容(约300万)。在查询表时,我在重写数据库查询以尽可能快的速度时遇到了一些麻烦。
以下是我的完整查询:
SELECT cc.title AS category, a.id, a.title, a.alias, a.title_alias, a.introtext, a.fulltext, a.sectionid, a.state, a.catid, a.created, a.created_by, a.created_by_alias, a.modified, a.modified_by, a.checked_out, a.checked_out_time, a.publish_up, a.publish_down, a.attribs, a.hits, a.images, a.urls, a.ordering, a.metakey, a.metadesc, a.access, CASE WHEN CHAR_LENGTH(a.alias) THEN CONCAT_WS(":", a.id, a.alias) ELSE a.id END AS slug, CASE WHEN CHAR_LENGTH(cc.alias) THEN CONCAT_WS(":", cc.id, cc.alias) ELSE cc.id END AS catslug, CHAR_LENGTH( a.`fulltext` ) AS readmore, u.name AS author, u.usertype, g.name AS groups, u.email AS author_email
FROM j15_content AS a
LEFT JOIN j15_categories AS cc
ON a.catid = cc.id
LEFT JOIN j15_users AS u
ON u.id = a.created_by
LEFT JOIN j15_groups AS g
ON a.access = g.id
WHERE 1
AND a.access <= 0
AND a.catid = 108
AND a.state = 1
AND ( publish_up = '0000-00-00 00:00:00' OR publish_up <= '2012-02-08 00:16:26' )
AND ( publish_down = '0000-00-00 00:00:00' OR publish_down >= '2012-02-08 00:16:26' )
ORDER BY a.title, a.created DESC
LIMIT 0, 10
以下是EXPLAIN:的输出
+----+-------------+-------+--------+-------------------------------------------------------+-----------+---------+---------------------------+---------+-----------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+--------+-------------------------------------------------------+-----------+---------+---------------------------+---------+-----------------------------+
| 1 | SIMPLE | a | ref | idx_access,idx_state,idx_catid,idx_access_state_catid | idx_catid | 4 | const | 3108187 | Using where; Using filesort |
| 1 | SIMPLE | cc | const | PRIMARY | PRIMARY | 4 | const | 1 | |
| 1 | SIMPLE | u | eq_ref | PRIMARY | PRIMARY | 4 | database.a.created_by | 1 | |
| 1 | SIMPLE | g | eq_ref | PRIMARY | PRIMARY | 1 | database.a.access | 1 | |
+----+-------------+-------+--------+-------------------------------------------------------+-----------+---------+---------------------------+---------+-----------------------------+
要显示存在的索引,请从j15_content:显示索引
+-------------+------------+------------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment |
+-------------+------------+------------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+
| j15_content | 0 | PRIMARY | 1 | id | A | 3228356 | NULL | NULL | | BTREE | |
| j15_content | 1 | idx_section | 1 | sectionid | A | 2 | NULL | NULL | | BTREE | |
| j15_content | 1 | idx_access | 1 | access | A | 1 | NULL | NULL | | BTREE | |
| j15_content | 1 | idx_checkout | 1 | checked_out | A | 2 | NULL | NULL | | BTREE | |
| j15_content | 1 | idx_state | 1 | state | A | 2 | NULL | NULL | | BTREE | |
| j15_content | 1 | idx_catid | 1 | catid | A | 6 | NULL | NULL | | BTREE | |
| j15_content | 1 | idx_createdby | 1 | created_by | A | 1 | NULL | NULL | | BTREE | |
| j15_content | 1 | title | 1 | title | A | 201772 | 4 | NULL | | BTREE | |
| j15_content | 1 | idx_access_state_catid | 1 | access | A | 1 | NULL | NULL | | BTREE | |
| j15_content | 1 | idx_access_state_catid | 2 | state | A | 2 | NULL | NULL | | BTREE | |
| j15_content | 1 | idx_access_state_catid | 3 | catid | A | 7 | NULL | NULL | | BTREE | |
| j15_content | 1 | idx_title_created | 1 | title | A | 3228356 | 8 | NULL | | BTREE | |
| j15_content | 1 | idx_title_created | 2 | created | A | 3228356 | NULL | NULL | | BTREE | |
+-------------+------------+------------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+
正如您所看到的,有一些数据是从数据库中提取的。现在,我通过简化查询来测试真正的问题在于ORDERBY子句。在没有对结果进行排序的情况下,查询的响应性很高,下面是一个解释:
+----+-------------+-------+--------+-------------------------------------------------------+-----------+---------+---------------------------+---------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+--------+-------------------------------------------------------+-----------+---------+---------------------------+---------+-------------+
| 1 | SIMPLE | a | ref | idx_access,idx_state,idx_catid,idx_access_state_catid | idx_catid | 4 | const | 3108187 | Using where |
| 1 | SIMPLE | cc | const | PRIMARY | PRIMARY | 4 | const | 1 | |
| 1 | SIMPLE | u | eq_ref | PRIMARY | PRIMARY | 4 | database.a.created_by | 1 | |
| 1 | SIMPLE | g | eq_ref | PRIMARY | PRIMARY | 1 | database.a.access | 1 | |
+----+-------------+-------+--------+-------------------------------------------------------+-----------+---------+---------------------------+---------+-------------+
正如您所看到的,是致命的文件端口正在扼杀服务器。有了这么多行,我正在尽我所能通过索引优化所有内容,但有些地方仍然不对劲。如有任何意见,我们将不胜感激。
尝试使用FORCE INDEX无效:
explain SELECT cc.title AS category, a.id, a.title, a.alias, a.title_alias, a.introtext, a.fulltext, a.sectionid, a.state, a.catid, a.created, a.created_by, a.created_by_alias, a.modified, a.modified_by, a.checked_out, a.checked_out_time, a.publish_up, a.publish_down, a.attribs, a.hits, a.images, a.urls, a.ordering, a.metakey, a.metadesc, a.access, CASE WHEN CHAR_LENGTH(a.alias) THEN CONCAT_WS(":", a.id, a.alias) ELSE a.id END AS slug, CASE WHEN CHAR_LENGTH(cc.alias) THEN CONCAT_WS(":", cc.id, cc.alias) ELSE cc.id END AS catslug, CHAR_LENGTH( a.`fulltext` ) AS readmore, u.name AS author, u.usertype, g.name AS groups, u.email AS author_email
-> FROM bak_content AS a
-> FORCE INDEX (idx_title_created)
-> LEFT JOIN bak_categories AS cc
-> ON a.catid = cc.id
-> LEFT JOIN bak_users AS u
-> ON u.id = a.created_by
-> LEFT JOIN bak_groups AS g
-> ON a.access = g.id
-> WHERE 1
-> AND a.access <= 0
-> AND a.catid = 108
-> AND a.state = 1
-> AND ( publish_up = '0000-00-00 00:00:00' OR publish_up <= '2012-02-08
-> AND ( publish_down = '0000-00-00 00:00:00' OR publish_down >= '2012-0
-> ORDER BY a.title, a.created DESC
-> LIMIT 0, 10;
产品:
+----+-------------+-------+--------+---------------+---------+---------+-------
| id | select_type | table | type | possible_keys | key | key_len | ref
+----+-------------+-------+--------+---------------+---------+---------+-------
| 1 | SIMPLE | a | ALL | NULL | NULL | NULL | NULL
| 1 | SIMPLE | cc | const | PRIMARY | PRIMARY | 4 | const
| 1 | SIMPLE | u | eq_ref | PRIMARY | PRIMARY | 4 | database
| 1 | SIMPLE | g | eq_ref | PRIMARY | PRIMARY | 1 | database
+----+-------------+-------+--------+---------------+---------+---------+-------
AFAIK使用索引、提示或查询本身的重组无法合理解决此问题。
之所以速度慢,是因为它需要2M行的文件端口,这实际上需要很长时间。如果按指定为ORDER BY a.title, a.created DESC
的顺序放大订单。问题是在多个列上排序和具有DESC部分的组合。Mysql不支持降序索引(CREATE INDEX语句中支持关键字DESC,但仅用于将来使用)。
建议的解决方法是创建一个额外的列"reverse_created",该列将自动填充,以便您的查询可以使用ORDER BY a.title, a.reverse_created
。所以你用max_time - created_time
填充它。然后在该组合上创建一个索引,并(如果需要)指定该索引作为提示。
关于这个话题,有几篇非常好的博客文章可以更好地解释这一点,并举例说明:
- http://www.mysqlperformanceblog.com/2006/05/09/descending-indexing-and-loose-index-scan/
- http://www.mysqlperformanceblog.com/2007/02/16/using-index-for-order-by-vs-restricting-number-of-rows/
-更新-您应该能够通过在查询中从订单中删除"DESC"部分来对此进行快速测试。结果在功能上是错误的,但它应该使用您现有的索引(否则,力应该起作用)。
有时MySQL很难找到正确的索引。你可以通过提示正确的索引来解决这个问题。
提示语法:http://dev.mysql.com/doc/refman/4.1/en/index-hints.html
确保你有正确的索引,并通过实验来调整它的性能。
干杯!
你能试试这个变体吗:
SELECT cc.title AS category, ...
FROM
( SELECT *
FROM j15_content AS a
USE INDEX (title) --- with and without the hint
WHERE 1
AND a.access <= 0
AND a.catid = 108
AND a.state = 1
AND ( publish_up = '0000-00-00 00:00:00'
OR publish_up <= '2012-02-08 00:16:26' )
AND ( publish_down = '0000-00-00 00:00:00'
OR publish_down >= '2012-02-08 00:16:26' )
ORDER BY a.title, a.created DESC
LIMIT 0, 10
) AS a
LEFT JOIN j15_categories AS cc
ON a.catid = cc.id
LEFT JOIN j15_users AS u
ON u.id = a.created_by
LEFT JOIN j15_groups AS g
ON a.access = g.id
我认为(catid, state, title)
上的索引会更好。
也许尝试一下可能会有所帮助:
CREATE INDEX idx_catid_title_created ON j15_content (catid,title(8),created);
DROP INDEX idx_catid ON j15_content;
您是否尝试过增加这些值tmp_table_size和max_heap_table_size:
这里有一个简短的解释,也链接到每一个细节。
希望这能有所帮助!
我希望这在语法上是正确的
SELECT
cc.title AS category,
a.id, a.title, a.alias, a.title_alias,
a.introtext, a.fulltext, a.sectionid,
a.state, a.catid, a.created, a.created_by,
a.created_by_alias, a.modified, a.modified_by,
a.checked_out, a.checked_out_time,
a.publish_up, a.publish_down, a.attribs,
a.hits, a.images, a.urls, a.ordering, a.metakey,
a.metadesc, a.access,
CASE WHEN CHAR_LENGTH(a.alias) THEN CONCAT_WS(":", a.id, a.alias) ELSE a.id END AS slug,
CASE WHEN CHAR_LENGTH(cc.alias) THEN CONCAT_WS(":", cc.id, cc.alias) ELSE cc.id END AS catslug, CHAR_LENGTH( a.`fulltext` ) AS readmore,
u.name AS author, u.usertype, g.name AS groups, u.email AS author_email
FROM
(
SELECT aa.*
FROM
(
SELECT id FROM
FROM j15_content
WHERE catid=108 AND state=1
AND a.access <= 0
AND (publish_up = '0000-00-00 00:00:00' OR publish_up <= '2012-02-08 00:16:26')
AND (publish_down = '0000-00-00 00:00:00' OR publish_down >= '2012-02-08 00:16:26')
ORDER BY title,created DESC
LIMIT 0,10
) needed_keys
LEFT JOIN j15_content aa USING (id)
) a
LEFT JOIN j15_categories AS cc ON a.catid = cc.id
LEFT JOIN j15_users AS u ON a.created_by = u.id
LEFT JOIN j15_groups AS g ON a.access = g.id;
您将需要一个子查询needed_keys 的支持索引
ALTER TABLE j15_content ADD INDEX subquery_ndx (catid,state,access,title,created);
试试看!!!