如何找出不同数据库的查询执行时间不同的原因?

ux6nzvsh  于 2021-06-20  发布在  Mysql
关注(0)|答案(1)|浏览(274)

我有两个模式相同的数据库。一个数据库来自生产,另一个是测试数据库。我正在对数据库中的一个表进行查询。在生产表上,查询大约需要4.3秒,而在测试数据库上,查询大约需要130毫秒。但是,生产表只有不到50000条记录,而我为测试表添加了超过100000条记录。我比较了这两个表,它们都有相同的索引。对我来说,问题似乎出在数据上。在播种时,我尝试生成尽可能随机的数据,以便模拟生产条件,但仍然无法重现缓慢的查询。
我查看了 EXPLAIN 对于这两个查询。它们在最后两栏中有显著差异。
生产:

+-------+-------------------------+
| rows  | Extra                   |
+-------+-------------------------+
| 24459 | Using where             |
| 46    | Using where; Not exists |
+-------+-------------------------+

测试:

+------+------------------------------------+
| rows | Extra                              |
+------+------------------------------------+
| 3158 | Using index condition; Using where |
| 20   | Using where; Not exists            |
+------+------------------------------------+

生产表的create语句是:

CREATE TABLE `usage_logs` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `user_id` int(11) NOT NULL,
  `operation` varchar(30) COLLATE utf8_unicode_ci NOT NULL,
  `check_time` datetime NOT NULL,
  `check_in_log_id` int(11) DEFAULT NULL,
  `daily_usage_id` int(11) DEFAULT NULL,
  `duration_units` decimal(11,2) DEFAULT NULL,
  `is_deleted` tinyint(1) NOT NULL DEFAULT '0',
  `created_at` datetime DEFAULT NULL,
  `updated_at` datetime DEFAULT NULL,
  `facility_id` int(11) NOT NULL,
  `notes` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
  `mac_address` varchar(20) COLLATE utf8_unicode_ci NOT NULL DEFAULT '00:00:00:00:00:00',
  `login` varchar(40) COLLATE utf8_unicode_ci DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `index_usage_logs_on_user_id` (`user_id`),
  KEY `index_usage_logs_on_check_in_log_id` (`check_in_log_id`),
  KEY `index_usage_logs_on_facility_id` (`facility_id`),
  KEY `index_usage_logs_on_check_time` (`check_time`),
  KEY `index_usage_logs_on_mac_address` (`mac_address`),
  KEY `index_usage_logs_on_operation` (`operation`)
) ENGINE=InnoDB AUTO_INCREMENT=145147 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci

而在测试数据库中相同的是:

CREATE TABLE `usage_logs` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `user_id` int(11) NOT NULL,
  `operation` varchar(30) COLLATE utf8_unicode_ci NOT NULL,
  `check_time` datetime NOT NULL,
  `check_in_log_id` int(11) DEFAULT NULL,
  `daily_usage_id` int(11) DEFAULT NULL,
  `duration_units` decimal(11,2) DEFAULT NULL,
  `is_deleted` tinyint(1) NOT NULL DEFAULT '0',
  `created_at` datetime DEFAULT NULL,
  `updated_at` datetime DEFAULT NULL,
  `facility_id` int(11) NOT NULL,
  `notes` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
  `mac_address` varchar(20) COLLATE utf8_unicode_ci NOT NULL DEFAULT '00:00:00:00:00:00',
  `login` varchar(40) COLLATE utf8_unicode_ci DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `index_usage_logs_on_check_in_log_id` (`check_in_log_id`),
  KEY `index_usage_logs_on_check_time` (`check_time`),
  KEY `index_usage_logs_on_facility_id` (`facility_id`),
  KEY `index_usage_logs_on_mac_address` (`mac_address`),
  KEY `index_usage_logs_on_operation` (`operation`),
  KEY `index_usage_logs_on_user_id` (`user_id`)
) ENGINE=InnoDB AUTO_INCREMENT=104001 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci

完整查询是:

SELECT `usage_logs`.*
FROM `usage_logs`
LEFT OUTER JOIN usage_logs AS usage_logs_latest ON usage_logs.facility_id = usage_logs_latest.facility_id
AND usage_logs.user_id = usage_logs_latest.user_id
AND usage_logs.mac_address = usage_logs_latest.mac_address
AND usage_logs.check_time < usage_logs_latest.check_time
WHERE `usage_logs`.`facility_id` = 5
  AND `usage_logs`.`operation` = 'checkIn'
  AND (usage_logs.check_time >= '2018-06-08 00:00:00')
  AND (usage_logs.check_time <= '2018-06-08 11:23:05')
  AND (usage_logs_latest.id IS NULL)

我在同一台机器上对两个不同的数据库执行查询,所以我认为其他进程不会干扰结果。
这个结果意味着什么?我可以采取什么进一步的步骤来找出执行时间差异很大的原因?

dl5txlt9

dl5txlt91#

您使用的mysql版本是什么?
有许多因素导致优化器决定
从哪张table开始(我们看不出它们是否不同)
要使用的索引(我们看不见)
等。
一些因素:
目前指数值的分布情况,
mysql版本,
月亮的相位。
这些也可能导致不同的数字(估计)在未来 EXPLAIN ,这可能导致不同的查询计划。
此外,服务器中的其他活动可能会干扰cpu/io/etc的可用性。特别是数据缓存很容易显示10倍的差异。每个查询都运行了两次吗?查询缓存是否已关闭?是 innodb_buffer_pool_size 相同的?内存大小相同吗?
我懂了 Using index condition 没有“综合”指数。通常可以通过提供合适的综合指数来改善绩效。更多
我要看看这个问题!
播种
随机的,或者不是随机的,行可以影响优化器选择使用哪个索引(etc)。这可能会导致选择一种更好的方法来运行对“test”的查询。
我们得看看 EXPLAIN SELECT ... 进一步探讨这个Angular 。
综合指数
这可能对两台服务器都有帮助:

INDEX(facility_id, operation,   -- either order
      check_time)               -- last
INDEX(facility_id, user_id, max_address, check_time,  -- any order
      id)                       -- last

进步很快。与其查找所有后面的行,但不使用它们的内容,不如使用“半联接”,它要求不存在任何这样的行:

SELECT  `usage_logs`.*
    FROM  `usage_logs`
    WHERE  `usage_logs`.`facility_id` = 5
      AND  `usage_logs`.`operation` = 'checkIn'
      AND  (usage_logs.check_time >= '2018-06-08 00:00:00')
      AND  (usage_logs.check_time <= '2018-06-08 11:23:05')
      AND NOT EXISTS ( SELECT 1 FROM  usage_logs AS latest 
             WHERE  usage_logs.facility_id = latest.facility_id
               AND  usage_logs.user_id     = latest.user_id
               AND  usage_logs.mac_address = latest.mac_address
               AND  usage_logs.check_time  < latest.check_time )

(相同的索引就可以了。)
查询似乎得到了“除了最新的”;这就是你想要的吗?

相关问题