同一个表的相同查询,不同的响应时间。

问题描述 投票:0回答:1

我有一个开发服务器,访问数据有些问题,用户反映有时候太慢了。设置是这样的。

* virtual server;
* 4 virtual CPU;
* 8 GB of virtual memory ;
* 80 GB of virtual HD (the real HD is a SDD one), I had still 36 GB available;
* OS Debian 9;
* Mysql 5.6.47;

为了避免所有关于网络和Web App的问题,我干脆直接在安装Mysql的主机上进行查询。 我已经对慢速查询进行了记录,并找到了最慢的查询。这个查询是从某个表开始的,我在下面报告。

CREATE TABLE `MALICIOUS_TABLE` (
  `column_1` int(11) NOT NULL AUTO_INCREMENT,
  `column_2` varchar(8) NOT NULL,
  `column_3` datetime NOT NULL,
  `column_4` int(11) NOT NULL,
  `column_5` int(11) DEFAULT NULL,
  `column_6` int(11) DEFAULT NULL,
  `column_7` int(11) DEFAULT NULL,
  `column_8` tinyint(1) DEFAULT NULL,
  `column_9` datetime DEFAULT NULL,
  `column_10` int(11) DEFAULT NULL,
  `column_11` varchar(2048) DEFAULT 'column_11',
  `column_12` tinyint(1) DEFAULT NULL,
  `column_13` datetime DEFAULT NULL,
  `column_14` tinyint(1) DEFAULT NULL,
  PRIMARY KEY (`column_1`),
  KEY `fk_ual_aut_idx` (`column_2`),
  KEY `fk_aul_c_idx` (`column_4`),
  KEY `kf_ual_po_idx` (`column_5`),
  KEY `fk_ual_ute_idx` (`column_10`),
  KEY `column_1` (`column_1`),
  KEY `column_2` (`column_2`),
  CONSTRAINT `fk_aul_c` FOREIGN KEY (`column_4`) REFERENCES `t_table2` (`column_4`) ON DELETE NO ACTION ON UPDATE NO ACTION,
  CONSTRAINT `fk_ual_aut` FOREIGN KEY (`column_2`) REFERENCES `t_tabl3` (`column_2`) ON DELETE NO ACTION ON UPDATE NO ACTION,
  CONSTRAINT `fk_ual_po` FOREIGN KEY (`column_5`) REFERENCES `t_table4` (`column_5`) ON DELETE NO ACTION ON UPDATE NO ACTION,
  CONSTRAINT `fk_ual_ute` FOREIGN KEY (`column_10`) REFERENCES `t_table5` (`column_10`) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB AUTO_INCREMENT=2357917 DEFAULT CHARSET=latin1 

该表有一个不小的记录数。

select count(*) from `MALICIOUS_TABLE`;
+----------+
| count(*) |
+----------+
|  2308414 |
+----------+
1 row in set (2,67 sec)

如果我尝试最慢的查询,总是从服务器上的mysql命令行,每隔10秒左右,我得到了不同的响应时间,这是生产服务器,所以用户不断插入数据。

SELECT count(*) FROM `MALICIOUS_TABLE` WHERE column_4 = 1 AND (column_8 is null) AND column_3 > CURDATE() - INTERVAL 30 DAY;
+----------+
| count(*) |
+----------+
|   666411 |
+----------+
1 row in set (4,39 sec)
SELECT count(*) FROM `MALICIOUS_TABLE` WHERE column_4 = 1 AND (column_8 is null) AND column_3 > CURDATE() - INTERVAL 30 DAY;
+----------+
| count(*) |
+----------+
|   666477 |
+----------+
1 row in set (4,94 sec)
SELECT count(*) FROM `MALICIOUS_TABLE` WHERE column_4 = 1 AND (column_8 is null) AND column_3 > CURDATE() - INTERVAL 30 DAY;
+----------+
| count(*) |
+----------+
|   666752 |
+----------+
1 row in set (17,02 sec)

最后一次尝试的响应时间有很大的变化。一开始我想可能是索引的问题,我把索引丢掉,重新创建。但是我却得到了响应时间的巨大变化。 服务器的内存是好的,仍然有大约2千兆的可用内存。查询缓存的Mysql它的活动,也许第二次尝试检索查询从缓存,和最后一个没有.任何建议,我可以检查什么,以了解问题?机器,db(现在我正尝试修改查询缓存设置)还是表本身?

mysql sql time response query-performance
1个回答
4
投票

为了让MySQL高效处理你的查询,你需要创建一个复合索引。如果没有索引,MySQL必须检查你表中的每一条记录,以计算你想要的记录。这意味着它必须从SSD中读取每一行。这需要时间。

你的过滤标准是...

WHERE column_4 = 1 
  AND column_8 is null 
  AND column_3 > CURDATE() - INTERVAL 30 DAY

你需要一个能有效满足你的查询的索引。因为两个标准是对常量值的匹配,所以这两列应该排在第一位。然后,最后一个标准过滤一个范围的值。从过去的某个日期开始,它过滤记录。

所以,你的索引应该在 (column_4, column_8, column_3). 你可以这样创建。

 CREATE INDEX tocount ON malicious_table (column_4, column_8, column_3)

你可以把这些索引(他们使用了一种叫做BTREE的索引技术)看作是按顺序排列的数据列表。所以,查询规划器可以随机访问索引到第一条符合条件的行,然后依次扫描索引,直到最后一条符合条件的行。这比读取整张表的效率要高得多。

你提到你的查询时间是不可预知的。你说的没错,这对你的用户来说是不愉快的。很难知道具体的原因,即使是一个能够访问你的服务器的专家。但这个一般观察是适用的。如果其他人使用同一台服务器做其他事情 他们的操作和你的操作可能会以不可预知的方式相互干扰。

专业提示 请注意,我给你设计的索引是为了匹配你给出的查询。不同的查询可能需要不同的索引。例如,如果你的查询是

  SELECT COUNT(*), MAX(column_13), SUM(column_7)
    FROM malicious_table
   WHERE column_4 = 1 
     AND column_8 is null 
     AND column_3 > CURDATE() - INTERVAL 30 DAY

它将满足这个覆盖索引。

CREATE INDEX tosummarize ON malicious_table 
    (column_4, column_8, column_3, column_7, column_13);

我最初建议的那个索引被这个索引弄得多余了 因为这个索引和那个索引的顺序是一样的。

一般来说,你应该避免创建大量的单列索引,希望使查询速度更快。随着数据库的增长,一般需要添加(或有时放弃)索引来匹配数据。索引很酷的地方在于你可以在不影响数据或程序的情况下添加和删除它们。这里有一个很好的参考。 https: /use-the-index-luke.com.

编辑: 查询之间的性能差异可能是由于许多难以辨别的因素造成的。你的MySQL服务器是否在繁忙的虚拟机主机上运行?你的MySQL服务器进程是否为你的应用程序以外的应用程序服务? 你的系统有多忙?是否有很多其他进程在与你的计数查询同时INSERT或UPDAT你的表中的行?如果是这样,变化可能是由于该并发工作负载的时刻变化造成的。直截了当地说,祝你在任何细节上都能搞清楚这个问题! 你可能想继续研究其他数据库性能问题。

有一件事可以试试。在你的计数查询前面加上这个t隔离等级 设置。

SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;

这可以减少你的计数查询锁定的机会 等待表上其他操作的完成。阅读这个以了解更多细节。https:/stackoverflow.comquestions7937472setting-transaction-isolation-level-of-mysql。 你的查询仍然会返回一个有用的结果,但是它可能不会接收到与你的计数请求同时发生的表的变化。对你的应用程序来说,在这个计数中漏掉一些最近的记录可能是可以接受的。

而且,MySQL的5.6版本已经相当老了。它是在2013年2月首次发布的。从那时起,已经有了bug和安全修复,不过,自从5.6版本出来后,许多程序员几十载的努力都花在了后来的版本上。其中大部分都用于提高性能和可靠性。如果可以的话,请升级。

© www.soinside.com 2019 - 2024. All rights reserved.