我有2个myISAM表,称为'tests'和'completed_tests',一个有170个条目,另一个有118k个条目。当我运行此查询时:
SELECT ct.archive, ct.status, ct.score, ct.users_LOGIN, t.lessons_ID, t.content_ID, t.keep_best
FROM completed_tests ct,tests t
WHERE ct.status != 'deleted' and ct.status != 'incomplete' and t.id=ct.tests_ID and t.lessons_ID=10;
然后需要大约30秒才能完成。对相同查询或相关查询(例如,不同的lessons_ID)的后续调用要快得多。即使我重置查询缓存或重启mysql服务器,它们仍然保持更快。我想这意味着表被缓存到内存中(并留在那里)。 我的问题是,这个特定的查询似乎在运行此应用程序的高流量站点上引起问题(我猜因为服务器内存很慢并且清空其缓存?)。 我的问题是:
运行解释给出:
mysql> explain SELECT ct.archive, ct.status, ct.score, ct.users_LOGIN, t.lessons_ID, t.content_ID, t.keep_best FROM completed_tests ct,tests t WHERE ct.status != 'deleted' and ct.status != 'incomplete' and t.id=ct.tests_ID and t.lessons_ID=10;
+----+-------------+-------+------+-----------------+----------+---------+---------------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+-----------------+----------+---------+---------------+------+-------------+
| 1 | SIMPLE | t | ref | PRIMARY,idx1 | idx1 | 3 | const | 4 | |
| 1 | SIMPLE | ct | ref | tests_ID,status | tests_ID | 3 | firstcho.t.id | 1025 | Using where |
+----+-------------+-------+------+-----------------+----------+---------+---------------+------+-------------+
根据我的理解,这表明索引已成功使用。 谢谢大家。
表格结构
>show create table 'tests';
CREATE TABLE `tests` (
`id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT,
`active` tinyint(1) NOT NULL DEFAULT '1',
`content_ID` mediumint(8) unsigned NOT NULL DEFAULT '0',
`lessons_ID` mediumint(8) unsigned NOT NULL DEFAULT '0',
`name` varchar(255) NOT NULL DEFAULT '',
`mastery_score` tinyint(4) unsigned NOT NULL DEFAULT '0',
`description` text,
`options` text,
`publish` tinyint(1) DEFAULT '1',
`keep_best` tinyint(1) DEFAULT '0',
PRIMARY KEY (`id`),
KEY `idx1` (`lessons_ID`)
) ENGINE=MyISAM AUTO_INCREMENT=171 DEFAULT CHARSET=utf8
>show create table completed_tests;
CREATE TABLE `completed_tests` (
`id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT,
`users_LOGIN` varchar(100) DEFAULT NULL,
`tests_ID` mediumint(8) unsigned NOT NULL DEFAULT '0',
`test` longblob,
`status` varchar(255) DEFAULT NULL,
`timestamp` int(10) unsigned NOT NULL DEFAULT '0',
`archive` tinyint(1) NOT NULL DEFAULT '0',
`time_start` int(10) unsigned DEFAULT NULL,
`time_end` int(10) unsigned DEFAULT NULL,
`time_spent` int(10) unsigned DEFAULT NULL,
`score` float DEFAULT NULL,
`pending` tinyint(1) NOT NULL DEFAULT '0',
PRIMARY KEY (`id`),
KEY `users_login` (`users_LOGIN`),
KEY `tests_ID` (`tests_ID`),
KEY `status` (`status`),
KEY `timestamp` (`timestamp`),
KEY `archive` (`archive`),
KEY `score` (`score`),
KEY `pending` (`pending`)
) ENGINE=MyISAM AUTO_INCREMENT=117996 DEFAULT CHARSET=utf8
答案 0 :(得分:2)
对于运行此问题且存在问题的高流量网站上的其他用户,完全有可能他们的数据库配置问题超出您的控制范围。除此之外,这里有一些有用的建议。
提高效果
以下假定测试的1:n关系:completed_tests基于t.id:ct.tests_id,其中在completed_tests中n行中的测试中总会有一行。
建议使用以下附加索引来帮助加入
CREATE INDEX `ct_to_tests` ON completed_tests (tests_id,status);
此外,如果您能够将ct.status更改为ENUM('deleted','status',..... any other possibilieis ....)
(假设可用的状态数量有限,这完全可行)那么这也将提高性能,因为它将删除唯一的文本搜索
我建议ENUM
的原因很简单。 status
看起来是一个定义为VARCHAR(255)
的字段,该字段以编程方式填充,因此将具有有限数量的离散值。如果您能够将VARCHAR
更改为ENUM
,那么MySQL将能够将其视为数字字段。这是因为幕后的ENUM
中的每个字符串都有一个数字索引,它是在使用ENUM
时匹配VARCHAR
而不是完整字符串时使用的索引,这反过来效率更高。
SELECT
t.lessons_ID,
t.content_ID,
t.keep_best,
ct.archive,
ct.status,
ct.score,
ct.users_LOGIN
FROM tests t
INNER JOIN completed_tests ct
ON ct.status NOT IN ('deleted,'status')
AND ct.tests_id = t.id
WHERE t.lessons_ID = 10
答案 1 :(得分:0)
尝试 LEFT JOIN
SELECT ct.archive, ct.status, ct.score, ct.users_LOGIN,
t.lessons_ID, t.content_ID, t.keep_best
FROM completed_tests ct
LEFT JOIN tests t ON (t.id=ct.tests_ID)
WHERE t.lessons_ID=10 AND ct.status != 'deleted' AND ct.status != 'incomplete' ;
答案 2 :(得分:0)
我首先想要确切地找出问题所在。
您是否在发生问题的服务器上安装了phpmyadmin?如果是这样,您可以将查询提交给" sql"选项卡" profiling"选中复选框。您将获得整个查询执行的详细描述,这可能会让您深入了解需要解决的问题或如何复制问题。另外,如果您还没有完成此操作,请尝试将索引添加到您在where子句中提到的所有列。
答案 3 :(得分:0)
最后,我将表格拆分为2,将blob对象移动到第二个表格并在需要的地方加入。不幸的是,这涉及到更改许多代码行。