我有 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 服务器,它们仍然更快。我想这意味着表被缓存到内存中(并留在那里)。 我的问题是这个特定的查询似乎在运行这个应用程序的高流量站点上引起了问题(我猜是因为服务器内存很慢并且清空了它的缓存?)。 我的问题是:
- 有没有办法在我的系统上始终如一地复制 30 英寸的延迟,以便我可以尝试优化查询?例如,我应该清空系统缓存吗?
- 有没有办法优化上面的查询?运行解释给出:
运行解释给出:
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
最佳答案
对于高流量站点上运行此程序并遇到问题的其他用户,他们完全有可能遇到您无法控制的数据库配置问题。除此之外,这里有一些有用的建议。
提高性能
以下假设来自 tests:completed_tests 的 1:n 关系基于 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
关于mysql查询先慢后快,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9409518/