【问题标题】:MySQL slow query: order by datetimeMySQL慢查询:按日期时间排序
【发布时间】:2016-02-25 14:54:17
【问题描述】:

我有一张大约有 5kk 条记录的表格

CREATE TABLE IF NOT EXISTS `termo` (
  `id` int(11) NOT NULL auto_increment,
  `date` timestamp NOT NULL default '0000-00-00 00:00:00' on update     CURRENT_TIMESTAMP,
  `sensor` varchar(16) NOT NULL,
  `temp` float NOT NULL default '-255'
) ENGINE=MyISAM DEFAULT CHARSET=cp1251 AUTO_INCREMENT=5466795 ;

索引

ALTER TABLE `termo` ADD PRIMARY KEY  (`id`), ADD KEY `date_3` (`date`,`sensor`);

查询

select pressure, humidity, temp_a, voltage, aqua_temp, home_temp, home_hum, current from
(select temp as pressure from termo force index(date_3) where sensor='000461965E3901' order by date desc limit 0,1) as pressure,
(select temp as humidity from termo force index(date_3) where sensor='000461965E3903' order by date desc limit 0,1) as humidity,
(select temp as temp_a from termo force index(date_3) where sensor='000461965E3902' order by date desc limit 0,1) as temp_a,
(select temp as voltage from termo force index(date_3) where sensor='000461965E3904' order by date desc limit 0,1) as voltage,
(select temp as aqua_temp from termo force index(date_3) where sensor='000461965E3907' order by date desc limit 0,1) as aqua_temp,
(select temp as home_temp from termo force index(date_3) where sensor='000461965E3905' order by date desc limit 0,1) as home_temp,
(select temp as current from termo force index(date_3) where sensor='000461965E3911' order by date desc limit 0,1) as current,
(select temp as home_hum from termo force index(date_3) where sensor='000461965E3906' order by date desc limit 0,1) as home_hum

工作很慢...大约 30 秒。 请帮助优化。

解释看起来像

id  select_type     table   type    possible_keys   key     key_len     ref     rows    Extra   
1   PRIMARY     <derived2>  system  NULL    NULL    NULL    NULL    1   
1   PRIMARY     <derived3>  system  NULL    NULL    NULL    NULL    1   
1   PRIMARY     <derived4>  system  NULL    NULL    NULL    NULL    1   
1   PRIMARY     <derived5>  system  NULL    NULL    NULL    NULL    1   
1   PRIMARY     <derived6>  system  NULL    NULL    NULL    NULL    1   
1   PRIMARY     <derived7>  system  NULL    NULL    NULL    NULL    1   
1   PRIMARY     <derived8>  system  NULL    NULL    NULL    NULL    1   
1   PRIMARY     <derived9>  system  NULL    NULL    NULL    NULL    1   
9   DERIVED     termo   index   NULL    date_3  3232    NULL    5193950     Using where

8   DERIVED     termo   index   NULL    date_3  3232    NULL    5193950     Using where

7   DERIVED     termo   index   NULL    date_3  3232    NULL    5193950     Using where

6   DERIVED     termo   index   NULL    date_3  3232    NULL    5193950     Using where

5   DERIVED     termo   index   NULL    date_3  3232    NULL    5193950     Using where

4   DERIVED     termo   index   NULL    date_3  3232    NULL    5193950     Using where

3   DERIVED     termo   index   NULL    date_3  3232    NULL    5193950     Using where

2   DERIVED     termo   index   NULL    date_3  3232    NULL    5193950     Using where

【问题讨论】:

  • 性能问题应该包括EXPLAIN ANALYZE和一些关于表大小、索引、当前时间性能、期望时间等的信息。Slow是一个相对术语,我们需要一个真实的值来比较。请阅读How-to-Ask 这里是START 了解如何提高问题质量并获得更好答案的好地方。
  • 如果更高的 id 每次也是更新的日期?
  • 使用 IN() 代替所有这些选择
  • 顺便说一句,如果您需要使用force index(),也许您应该检查优化器为什么不自己使用它。非常聪明的 ppl 致力于制作一个好的优化器,如果他不使用索引,可能是有原因的。
  • @sagi 也许你可以写那个选择?我看不出IN() 在这里有什么帮助。

标签: mysql performance date sql-order-by


【解决方案1】:

我认为这可能是一个更好的方法,但需要添加 INDEX 只为 sensor 所以 IN 语句工作得更快。

SELECT termo.`sensor`, `temp`
FROM termo
JOIN (
    SELECT `sensor`, MAX(`date`) as `lastTemp`
    FROM termo 
    WHERE sensor IN ('000461965E3901', '000461965E3903', '000461965E3902',
                     '000461965E3904', '000461965E3907', '000461965E3905',
                     '000461965E3911', '000461965E3906')
    GROUP BY sensor
    ) T
 ON termo.`date` = T.`lastTemp`
AND termo.`sensor` = T.`sensor`

还可以考虑使用复合 INDEX (temp, date, sensor) 进行测试

要获得正确的标签,请使用

SELECT 
   MAX(CASE WHEN termo.sensor = '000461965E3901' THEN temp END) as pressure,
   MAX(CASE WHEN termo.sensor = '000461965E3903' THEN temp END) as humidity ,
   MAX(CASE WHEN termo.sensor = '000461965E3902' THEN temp END) as temp_a,
   MAX(CASE WHEN termo.sensor = '000461965E3904' THEN temp END) as voltage ,
   MAX(CASE WHEN termo.sensor = '000461965E3907' THEN temp END) as aqua_temp ,
   MAX(CASE WHEN termo.sensor = '000461965E3905' THEN temp END) as home_temp ,
   MAX(CASE WHEN termo.sensor = '000461965E3911' THEN temp END) as current ,
   MAX(CASE WHEN termo.sensor = '000461965E3906' THEN temp END) as home_hum
FROM termo
JOIN (
    SELECT `sensor`, MAX(`date`) as `lastTemp`
    FROM termo 
    WHERE sensor IN ('000461965E3901', '000461965E3903', '000461965E3902',
                     '000461965E3904', '000461965E3907', '000461965E3905',
                     '000461965E3911', '000461965E3906')
    GROUP BY sensor
    ) T
 ON termo.`date` = T.`lastTemp`
AND termo.`sensor` = T.`sensor`

【讨论】:

  • 立即尝试。只需添加表的名称
  • 好吧 10 秒还不错 :)
【解决方案2】:

尝试像这样修改每个子选择。我知道这不是最好的方法。我稍后会发送一个较短的查询。

(SELECT temp AS pressure FROM termo WHERE id = (SELECT max(id) FROM termo WHERE sensor='000461965E3901'))

并添加一个键

ALTER TABLE `termo`  ADD KEY `sensor` (`sensor`);

【讨论】:

  • 查询耗时 91.5878 秒。
  • 你有添加密钥吗? ALTER TABLE termo ADD KEY sensor (sensor);
  • yes :) ALTER TABLE termo ADD PRIMARY KEY (id), ADD KEY sensor (sensor);
猜你喜欢
  • 1970-01-01
  • 2015-09-12
  • 1970-01-01
  • 1970-01-01
  • 2012-12-03
  • 1970-01-01
  • 2022-11-11
  • 1970-01-01
  • 2014-01-11
相关资源
最近更新 更多