簡體   English   中英

mysql查詢起先慢后快

[英]mysql query slow at first fast afterwards

我有2個myISAM表,分別稱為“測試”和“ 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

對於高流量站點上運行此命令並有問題的其他用戶,他們完全有可能在您的控制范圍之外遇到數據庫配置問題。 除此之外,這里有一些建議會有所幫助。

改善表現

以下假設基於t.id:ct.tests_id的tests:completed_tests與1:n的關系,其中在complete_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

最后,我將表拆分為2,將blob對象移動到第二個表,並在需要的地方進行連接。 不幸的是,這涉及到更改許多代碼行。

嘗試使用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' ;

我將首先嘗試找出問題所在。

發生問題的服務器上是否有phpmyadmin? 如果是這樣,您可以將查詢提交到“ sql”選項卡,同時選中“分析”復選框。 您將獲得整個查詢執行的詳細記錄,也許可以使您深入了解需要解決的問題或如何復制問題。 另外,如果還沒有這樣做,請嘗試將索引添加到您在where子句中提到的所有列中。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM