2015-04-19 160 views
5

我有一个MySQL表,10万行,捕捉一些服务器日志创建为没有索引的选择查询:更新,加入非常缓慢相比

CREATE TABLE `logs` ( 
    `id` INT NOT NULL AUTO_INCREMENT, 
    `ip` VARCHAR(16) NULL, 
    `date` DATETIME NULL, 
    `session_time` SMALLINT UNSIGNED NULL, 
    PRIMARY KEY (`id`)); 

我试图计算会话时间为相同IP的连续行之间的时间差异。我能做到这一点有以下选择查询花费不到一秒钟:

SELECT * FROM logs AS a 
LEFT JOIN (
    SELECT id, 
     from_unixtime(@diff) AS starttime, 
     date AS endtime, 
     IF(@diff = 0, 0, (unix_timestamp(date) - @diff)/60) AS session_time1, 
     @diff := unix_timestamp(date) 
    FROM logs, 
     (SELECT @diff := 0) AS x 
    ORDER BY ip, logs.date 
) AS b ON 
    a.id = b.id 

然而,当我尝试使用以前的查询在更新加入更新时间会话,以下更新查询需要超过600秒:

UPDATE logs AS a 
LEFT JOIN (
    SELECT id, 
     from_unixtime(@diff) AS starttime, 
     date AS endtime, 
     IF(@diff = 0, 0, (unix_timestamp(date) - @diff)/60) AS session_time1, 
     @diff := unix_timestamp(date) 
    FROM logs, 
     (SELECT @diff := 0) AS x 
    ORDER BY ip, logs.date 
) AS b ON 
    a.id = b.id 
SET session_time = session_time1; 

我错过了什么?

谢谢!

UPDATE:这里是selectEXPLAIN

+----+-------------+------------+--------+---------------+------+--------+ 
| id | select_type | table | type | possible_keys | key | rows | 
+----+-------------+------------+--------+---------------+------+--------+ 
| 1 | PRIMARY  | a   | ALL | NULL   | NULL | 109029 | 
| 1 | PRIMARY  | <derived2> | ALL | NULL   | NULL | 108680 | 
| 2 | DERIVED  | <derived3> | system | NULL   | NULL | 1  | 
| 2 | DERIVED  | logs  | ALL | NULL   | NULL | 109029 | 
| 3 | DERIVED  | NULL  | NULL | NULL   | NULL | NULL | 
+----+-------------+------------+--------+---------------+------+--------+ 
+1

'WHERE ip ='...''?看起来你正在更新所有100k条目,但是你正在选择的条目('LEFT JOIN' =不符合第一组中的选择规则的元素)。尝试使用'INNER JOIN'? –

+0

谢谢亚历杭德罗,但我不太理解你的评论。内连接为什么会更好? “where”条款是什么意思? – kahlo

+0

对不起,我应该更好地解释它。当你使用'UPDATE'时,你通常使用'WHERE'子句来过滤哪些数据应该被更新。在你的情况下,如果你想更新一个特定的IP地址,那么'WHERE IP ='''应该更好地处理那些具有特定IP的数据(我想你的查询正在处理你表中的每条记录,即使并非全部都在更新)。 –

回答

0

开始了与session_time是所有行NULL。更改查询方式有两种:

  • (末)
  • session_time添加WHERE session_time IS NULL到UPDATE到NULL,如果你没有截止时间,否则正确设置。

第一天晚上,它会像现在一样慢,但之后,它会快得多,因为它只会在少数“新”行上工作。

编辑

JOIN需求(通常)的ON子句。如何连接logsalogsPRIMARY KEYEXPLAIN显示它需要运行通过logsa的109K * 108K组合;它应该只有109K。

此外,删除LEFT,除非有一些需要它。

+0

谢谢里克,我包括你的建议。然而,这个查询已经包含了一天内生成的新信息(每天100k行)。我仍然对什么可能导致这种时间差异并修复它感兴趣。 – kahlo

+0

如果你正在运行5.6,请发布'EXPLAIN SELECT ...'和'EXPLAIN UPDATE ...'。也许这将表明优化器正在做一些不同的事情。 –

+0

我刚添加了'EXPLAIN SELECT ...',但由于我运行的版本是5.5.29,所以我不能添加'EXPLAN UPDATE ...',并且以前的5.6.3是不允许的。 – kahlo