任何人都可以在下面的查询中解释为什么我按原样运行它,需要2秒,如果我从select中删除“users.login”需要0.0秒。此外,如果我留下“users.login”并删除“users.usertypesid”,它还需要0.0秒。因此,只有选择了login和usertypesid,才能获得2秒的执行时间。 我不明白,因为这些字段是数字和简单的varchar,它们不会在where子句或order by中进行比较,排序或使用。我甚至尝试从表中删除所有TEXT列(因为我认为可能缓冲行以某种方式超出行)但是没有任何变化。 最奇怪的是,items和itemdescs是包含数千条记录的表,如果我省略或添加这些表中的一些字段(从itemdescs中选择几个字段而不是itemdescs。,或者还包括items。)性能总是一样的。但是表“users”只有5条记录,如果我选择的字段是数值(usertypesid),或者登录性能在8核心最新硬件上从0.00秒下降到2秒。 5个整数或5个字符串,每个8个字符的长度如何使该硬件处理它2秒钟?同样,这些字段从不用于排序或其他任何东西(比较等),只包含在select中!在这些字段上添加/删除索引也没有任何改变。这是查询:
select SQL_NO_CACHE itemdescs.*, users.id, users.usertypesid, users.login
from items, itemdescs, langs, users
where itemdescs.itemsid=items.id
and itemdescs.langsid=langs.id
and langs.tag='en'
and items.usersid=users.id
order by activationdate desc
limit 0,8
以下是表格定义。
CREATE TABLE `items` (
`id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
`usersid` INT(10) UNSIGNED NOT NULL DEFAULT '0',
`activestatesid` INT(10) UNSIGNED NOT NULL DEFAULT '0',
`activationdate` DATE NOT NULL DEFAULT '2013-01-01',
PRIMARY KEY (`id`),
INDEX `usersid` (`usersid`),
INDEX `activestatesid` (`activestatesid`),
INDEX `activationdate` (`activationdate`)
)
COLLATE='utf8_unicode_ci'
ENGINE=InnoDB
AUTO_INCREMENT=31065;
CREATE TABLE `itemdescs` (
`id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
`itemsid` INT(10) UNSIGNED NOT NULL DEFAULT '0',
`langsid` INT(10) UNSIGNED NOT NULL DEFAULT '0',
`name` VARCHAR(255) NULL DEFAULT NULL COLLATE 'utf8_unicode_ci',
`longdesc` VARCHAR(6000) NULL DEFAULT NULL COLLATE 'utf8_unicode_ci',
`synonyms` VARCHAR(500) NULL DEFAULT NULL COLLATE 'utf8_unicode_ci',
`shortdesc` VARCHAR(500) NULL DEFAULT NULL COLLATE 'utf8_unicode_ci',
`mediumdesc` VARCHAR(1000) NULL DEFAULT NULL COLLATE 'utf8_unicode_ci',
PRIMARY KEY (`id`),
UNIQUE INDEX `itemsid_langsid` (`itemsid`, `langsid`),
INDEX `itemsid` (`itemsid`),
INDEX `langid` (`langsid`),
INDEX `name` (`name`)
)
COLLATE='utf8_unicode_ci'
ENGINE=InnoDB
ROW_FORMAT=COMPACT
AUTO_INCREMENT=632465;
CREATE TABLE `langs` (
`id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
`name` VARCHAR(50) NOT NULL DEFAULT '0' COLLATE 'utf8_unicode_ci',
`tag` VARCHAR(50) NOT NULL DEFAULT '0' COLLATE 'utf8_unicode_ci',
PRIMARY KEY (`id`),
INDEX `tag` (`tag`)
)
COLLATE='utf8_unicode_ci'
ENGINE=InnoDB
AUTO_INCREMENT=19;
CREATE TABLE `users` (
`id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
`usertypesid` INT(10) UNSIGNED NOT NULL DEFAULT '0',
`login` VARCHAR(50) NULL DEFAULT '0' COLLATE 'utf8_unicode_ci',
PRIMARY KEY (`id`),
INDEX `usertypesid` (`usertypesid`)
)
COLLATE='utf8_unicode_ci'
ENGINE=InnoDB
ROW_FORMAT=COMPACT
AUTO_INCREMENT=5;
以下是按要求解释的结果:
慢一个(带登录字段): http://i.stack.imgur.com/bedvf.jpg
速度快: http://i.stack.imgur.com/xjssj.jpg
由于声誉低于10,我无法发布图片。
更新: 如果我重写这样的查询,我得到相同的结果,它立即工作(0.000秒):
select SQL_NO_CACHE
*
from
(select items.usersid, itemdescs.*
from items, itemdescs, langs
where itemdescs.itemsid=items.id
and itemdescs.langsid=langs.id
and langs.tag='en'
order by activationdate desc
limit 0,8) nousers, users
where
nousers.usersid=users.id
现在这样可行,但是没有解释为什么只有5条记录的表使查询变慢(在第一个例子中),而有数千行的表不影响它?我怎么知道何时重写这样的查询以及要注意哪个表?
答案 0 :(得分:0)
尝试这三件事,看看它是否能解决问题。 1)转换为ANSI SQL; 2)摆脱已知不会引起问题的一切; 3)删除提示。
即使ANSI SQL和你的SQL应该是等价的,我也发现了Oracle& MySQL以不同的方式处理查询。也许使用ANSI查询可以避免减速。这是查询的精简版本,转换为ANSI。
select users.usertypesid, users.login
from items
join itemdescs on itemdescs.itemsid = items.id
join langs on langs.id = itemdescs.langsid
join users on users.id = items.usersid
where langs.tag='en'
order by activationdate desc;
答案 1 :(得分:0)
使用索引时,您需要找到一个很好的平衡,如果您已经遇到SELECTS问题,这些表对于INSERTS,DELETES和UPDATES来说会非常慢。有很多索引,索引文件会变得相当大。尝试删除仅保留用于主键和联接的索引。
要考虑的另一件事是将SELECT中的语法更改为更新的连接方法。
SELECT SQL_NO_CACHE itemdescs.*, users.id, users.usertypesid, users.login
FROM itemdescs
INNER JOIN items ON itemdescs.itemsid = items.id
INNER JOIN users ON itemdescs.usersid = users.id
INNER JOIN langs ON itemdescs.langsid = langs.id
WHERE langs.tag = 'en'
ORDER BY items.activationdate
LIMIT 0, 8;
这里INDEXES的最佳候选人是:
items.id
users.id
langs.id
itemsdescs.itemsid
itemdescs.usersid
itemsdescs.langsid
items.activationdate
用于搜索,排序或分组的索引列,而不是您为输出选择的列。
答案 2 :(得分:0)
我同意其他两个答案的评论,但会扩展一些其他元素......你应该考虑“覆盖索引”,以便你的教育。引擎只会在每个表中使用一个最适合正在执行的查询的索引。也就是说,对于tg,items,langs有三个单独的索引将是一个糟糕的选择。相反,它应该是一个具有所有查询标准的SINGLE索引,并且可以显着提高性能......
为了防止需要返回原始数据页面,覆盖索引包括所需的字段。我会为各自的表提供以下索引。
table index
users ( id, usertypesid, login)
items ( id, usersid, activationdate desc )
langs ( id, tag )
itemdescs ( langsid, activationdate desc, itemsid )
和查询
select SQL_NO_CACHE
itemdescs.*,
users.id,
users.usertypesid,
users.login
from
itemdescs
JOIN langs
on itemdescs.langsid = langs.id
and langs.tag = 'en'
JOIN items
on itemdescs.itemsid = items.id
JOIN users
items.usersid = users.id
order by
items.activationdate desc
limit
0,8
另一个可能的优化是关键字“STRAIGHT_JOIN”,它告诉MySQL引擎按照您的布局顺序进行查询。有时这可以显着帮助查询运行。如果是这样,只需改变......
select SQL_NO_CACHE
到
select STRAIGHT_JOIN SQL_NO_CACHE
答案 3 :(得分:0)
最终解决方案是在项目表上使用强制索引。我添加了索引adate(activationdate desc)并且MySQL没有使用它,因此查询仍然很慢(大约2秒)。在我强迫索引之后:
select SQL_NO_CACHE itemdescs.*, users.id, users.usertypesid, users.login
from items force index(adate), itemdescs, langs, users
where itemdescs.itemsid=items.id
and itemdescs.langsid=langs.id
and langs.tag='en'
and items.usersid=users.id
order by activationdate desc
limit 0,8
查询在0.000秒内运行。有谁知道为什么MySQL无法弄清楚它需要使用这个索引?此外,因为我想把这部分放在视野中:
select SQL_NO_CACHE itemdescs.*, users.id, users.usertypesid, users.login
from items force index(adate), itemdescs, langs, users
where itemdescs.itemsid=items.id
and itemdescs.langsid=langs.id
and langs.tag='en'
and items.usersid=users.id
后来从客户端称之为(实际上已经在客户端,但性能不佳):
select * from myview (items force index(adate)) where DYNAMIC CONDITIONS GO HERE order by itemsactivationdate desc limit 0,10
然而,MySQL中没有这样的语法。那么有谁知道我该怎么做,或者甚至可能吗?如果不是很头疼,因为客户端应用程序一直使用视图,如果我不能对它们使用强制索引并且MySQL查询计划不好(情况就是如此)除了所有客户端上的重写代码之外基本上没有其他解决方案?我可以以某种方式重写查询,以便它使用正确的索引而不使用“强制索引”吗?