由于在我的SQL代码中使用了“ORDER BY”语句,我遇到了一些非常重要的性能问题。
只要我没有在SQL中使用ORDER BY语句,一切都很好。但是,一旦我在SQL代码中引入ORDER BY:s,由于缺少正确的索引,一切都会显着减慢。人们会认为解决这个问题是微不足道的,但从论坛讨论等方面来看,这似乎是一个相当普遍的问题,我还没有看到这个问题的明确和简明的答案。
问题:鉴于下表......
CREATE TABLE values_table ( id int(11) NOT NULL auto_increment, ... value1 int(10) unsigned NOT NULL default '0', value2 int(11) NOT NULL default '0', PRIMARY KEY (id), KEY value1 (value1), KEY value2 (value2), ) ENGINE=MyISAM AUTO_INCREMENT=2364641 DEFAULT CHARSET=utf8;
...如何在对 value1 的值进行排序时,在查询表时使用的索引??
目前,在不使用ORDER BY子句时,提取正常。
请参阅以下EXPLAIN QUERY输出:
OK, when NOT using ORDER BY: EXPLAIN select ... from values_table this_ where this_.value1 between 12345678 and 12349999 limit 10; +----+-------------+-------+-------+---------------+----------+---------+------+------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+-------+---------------+----------+---------+------+------+-------------+ | 1 | SIMPLE | this_ | range | value1 | value1 | 4 | NULL | 3303 | Using where | +----+-------------+-------+-------+---------------+----------+---------+------+------+-------------+
However, when using ORDER BY I get "Using filesort": EXPLAIN select ... from values_table this_ where this_.value1 between 12345678 and 12349999 order by this_.value2 asc limit 10; +----+-------------+-------+-------+---------------+----------+---------+------+------+-----------------------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+-------+---------------+----------+---------+------+------+-----------------------------+ | 1 | SIMPLE | this_ | range | value1 | value1 | 4 | NULL | 3303 | Using where; Using filesort | +----+-------------+-------+-------+---------------+----------+---------+------+------+-----------------------------+
有关表格内容的一些其他信息:
SELECT MIN(value1), MAX(value1) FROM values_table; +---------------+---------------+ | MIN(value1) | MAX(value2) | +---------------+---------------+ | 0 | 4294967295 | +---------------+---------------+ ... SELECT MIN(value2), MAX(value2) FROM values_table; +---------------+---------------+ | MIN(value2) | MAX(value2) | +---------------+---------------+ | 1 | 953359 | +---------------+---------------+
如果需要任何进一步的信息来回答这个问题,请告诉我。
提前多多感谢!
更新#1:添加新的复合索引( ALTER TABLE values_table ADD INDEX(value1,value2); )无法解决问题。添加这样的索引后,你仍会得到“使用filesort”。
更新#2:我在我的问题中没有提到的一个约束是我宁愿改变表的结构(比如添加索引等)而不是改变使用的SQL查询。 SQL查询是使用Hibernate自动生成的,所以要考虑那些或多或少固定的。
答案 0 :(得分:19)
在这种情况下,您不能使用索引,因为您使用RANGE
过滤条件。
如果你使用类似的东西:
SELECT *
FROM values_table this_
WHERE this_.value1 = @value
ORDER BY
value2
LIMIT 10
,然后在(VALUE1, VALUE2)
上创建复合索引将用于过滤和排序。
但是你使用远程条件,这就是你无论如何都需要进行排序的原因。
您的复合索引将如下所示:
value1 value2 ----- ------ 1 10 1 20 1 30 1 40 1 50 1 60 2 10 2 20 2 30 3 10 3 20 3 30 3 40
,如果您在1
中选择2
和value1
,则仍然无法获得value2
的整个有序集。
如果value2
上的索引不是很有选择性(即表中的DISTINCT value2
不多),您可以尝试:
CREATE INDEX ix_table_value2_value1 ON mytable (value2, value1)
/* Note the order, it's important */
SELECT *
FROM (
SELECT DISTINCT value2
FROM mytable
ORDER BY
value2
) q,
mytable m
WHERE m.value2 >= q.value2
AND m.value2 <= q.value2
AND m.value1 BETWEEN 13123123 AND 123123123
这称为SKIP SCAN
访问方法。 MySQL
不直接支持它,但可以像这样模拟它。
在这种情况下将使用RANGE
访问权限,但除非DISTINCT value2
包含少于约1%
行,否则您可能无法获得任何性能优势。
注意使用:
m.value2 >= q.value2
AND m.value2 <= q.value2
而不是
m.value2 = q.value2
这使MySQL
对每个循环执行RANGE
检查。
答案 1 :(得分:0)
在我看来,你有两个完全独立的键,一个用于value1,一个用于value2。
因此,当您使用value1键进行检索时,记录不一定按value2的顺序返回,因此必须对它们进行排序。这仍然比全表扫描更好,因为您只对满足“where value1”子句的记录进行排序。
我认为(如果在MySQL中这是可能的),(value1,value2)上的复合键可以解决这个问题。
尝试:
CREATE TABLE values_table (
id int(11) NOT NULL auto_increment,
...
value1 int(10) unsigned NOT NULL default '0',
value2 int(11) NOT NULL default '0',
PRIMARY KEY (id),
KEY value1 (value1),
KEY value1and2 (value1,value2),
) ENGINE=MyISAM AUTO_INCREMENT=2364641 DEFAULT CHARSET=utf8;
(或等效的ALTER TABLE
),假设MySQL中的复合键是正确的语法。
在我知道的所有数据库中(我必须承认MySQL不是其中之一),这会导致数据库引擎选择value1and2键来检索行,并且它们已经在value2-within-value1中排序订单,所以不需要文件排序。
如果需要,您仍然可以保留value2键。