MySQL的; LEFT OUTER加入DATEDIFF,索引耗时300秒(但仅限第一次)

时间:2013-04-15 14:43:42

标签: mysql database-performance datediff

MySQL的内置缓存确实让这个问题在一天中的大部分时间都没有问题,但是第一次运行以下查询时,性能可怕:第一次超过300秒而后续查询可以在几毫秒内完成。使用SQL_NO_CACHE运行它需要2-4秒(!),这在这个实例中是非常可接受的 - 但是初始运行时不是。

SELECT DATEDIFF( bt.`datetime`, st.`datetime`) AS 'day_separation'
FROM `smallerTable` AS st
LEFT OUTER JOIN `bigTable` AS bt ON bt.item_id = st.item_id
  AND bt.code = 'X'
  AND bt.`datetime` > st.`datetime`
  AND DATEDIFF ( bt.datetime, st.datetime) < 11
  AND st.`datetime` > '2012-07-01' AND st.`datetime` < 'yesterdays-date 23:59:59'

我已经介绍了多列索引(thanks to this question)但它仍然无法解决这个特定问题。 This solution看起来很有启发,但我认为它不适用,因为我不确定如何将这些结果结合起来。

较小的表有大约8000条记录,我想现在计算/包含所有这些记录。它最终将变得更大,并包含2012-07-01之前的项目。

bigTable有1000万条记录,我只想将这些记录的“配对”与较小的表格相匹配。部分麻烦在于他们无法共享将它们链接在一起的直接密钥或引用,因此我留下LEFT OUTER JOIN并猜测如果两个事件的时间戳是&lt;相隔11天(并分享其他条件),他们必须相关。

排除测试DATEDIFF ( bt.datetime, st.datetime) < 11创建的14k'结果',说明“需要发生”的DATEDIFF计算次数为14k-8k(也称为6k)。< / p>

INDEXES :每个表的datetime字段,codeitem_id

我在(item_id, datetime)的顺序上对两个表都有复合索引。根据我的理解,这是必要的顺序,因为我们以DATEDIFF( bt.datetime, st.datetime)的形式使用select语句中的datetime字段。

(code, item_id, datetime)上的合并索引是否会彻底改变此查询?(是的,确实如此!)

这个解释对我未经训练的眼睛几乎没有透露,除了它使用临时桌子,我明白这可能很耗时。

id * select_type * table * type  * possible_keys * key                * key_len * ref           * rows * extra
1  * SIMPLE      * st    * index * NULL          * items_for_datetime * 59      * NULL          * 8295 * using index; using temporary; using filesort
1  * SIMPLE      * BT    * ref   * [many]        * items_for_datetime * 51      * master.st.item_id * 3    *

根据MySQL的突发奇想,bigTable 有时表明它更喜欢item_id密钥而不是items_for_datetime。我应该鼓励使用我的联合指数,相信我知道的更好吗?

一些额外信息:

  • 这些表中的插入每天发生一次(BT中1~5k记录)
  • 不会发生任何更新或删除
  • 我可能会运行两个查询 - 将此更改为INNER JOIN,然后运行第二个查询以从总记录中减去结果数,以找到BT中没有相应结果的数字
  • 我们已经在BT上执行了phpmyadmin的Check TableDefragmentationOptimize Table

[旁白]使用像Mongo这样的NoSQL数据库这可能是一个很好的场景吗?

为什么第一轮和第二轮会有这样的差距?更重要的是:可以采取哪些措施来改善首次运行的时间安排?

更新:新尝试需要新的一天才能找到效果。明天我将使用BETWEENDATE_ADD尝试Barmar的建议。我还在(code, item_id, datetime)上创建了一个综合索引。我明天将报告结果,但欢迎任何其他想法。

更新:成功!查询的第一次运行现在只用了6秒,考虑到它的来源,这是惊人的。随后的查询只花了0.035秒!真是个梦想。 (code, item_id, datetime)的综合指数无疑有助于取得这一成功。这是新的查询:谢谢大家!

SELECT DATEDIFF( bt.`datetime`, st.`datetime` ) AS  'day_separation'
FROM  `smallerTable` AS st
LEFT OUTER JOIN bigTable AS bt USE INDEX (  `cmd_item_time` ) 
ON bt.item_id = st.item_id
  AND bt.code =  'X'
  AND bt.`datetime` BETWEEN st.`datetime` AND DATE_ADD( st.`datetime`, INTERVAL 10 DAY ) 
  AND st.datetime BETWEEN '2012-07-01' AND  'yesterdays-date 23:59:59'

2 个答案:

答案 0 :(得分:1)

尝试更改:

AND bt.`datetime` > st.`datetime`
AND DATEDIFF ( bt.datetime, st.datetime) < 11

为:

AND bt.`datetime` BETWEEN st.`datetime` AND date_add(st.`datetime`, interval 11 day)

这可能允许使用bt.datetime上的索引。

如果code = 'X'过滤掉bigTable的大部分内容,(code, item_id)上的复合索引应该有所帮助。

答案 1 :(得分:0)

您查询的问题很可能是该行:AND st。datetime&gt; '2012-07-01'AND st。datetime&lt; '昨天 - 约会23:59:59'

通过将日期时间转换为字符串(为了进行比较),您将失去索引的优势......