我有一个非常大的无索引表,称为table,其行如下:
IP entrypoint timestamp
171.128.123.179 /page-title/?kw=abc 2016-04-14 11:59:52
170.45.121.111 /another-page/?kw=123 2016-04-12 04:13:20
169.70.121.101 /a-third-page/ 2016-05-12 09:43:30
我想做出最快的查询,给定30个IP和一个日期,将在该日期前一周搜索行,并返回包含每个IP“?kw =”的最新行。所以我想要DISTINCT入口点,但只需要最新入口点。
我对此感到困惑我知道这是一个相对简单的INNER JOIN,但我不知道最快的方法。
顺便说一下:我现在无法添加索引,因为它非常大并且在为网站提供服务的数据库上。我打算用索引表替换它,不用担心。
答案 0 :(得分:0)
表中的行
SELECT ...
FROM very_big_unindexed_table t
仅在过去一周内......
WHERE t.timestamp >= NOW() + INTERVAL - 1 WEEK
在入口点
中包含'?kw =' AND t.entrypoint LIKE '%?kw=%'
每个IP只有最新一行。有几种方法可以解决这个问题。一张非常大的无索引表上的相关子查询将吃掉你的午餐和午餐盒。如果没有索引,就无法完全扫描表格和“使用文件排序”操作。
鉴于不幸的情况,我们对性能的最佳选择可能是尽可能减少集合,然后执行排序,并避免任何连接操作(返回到该表)并避免相关子查询
所以,让我们从这样的事情开始,在入口点以“?kw =”返回过去一周所有行的行。这将是对表的完全扫描,以及排序操作......
SELECT t.ip
, t.timestamp
, t.entry_point
FROM very_big_unindexed_table t
WHERE t.timestamp >= NOW() + INTERVAL -1 WEEK
AND t.entrypoint LIKE '%?kw=%'
ORDER BY t.ip DESC, t.timestamp DESC
我们可以使用不受支持的技巧和用户定义的变量。 (MySQL参考手册特别警告不要使用这样的模式,因为行为是(正式)未定义。非正式地,MySQL 5.1和5.5中的优化器(至少)是非常可预测的。
如果过去一周的行数是整个表的重要子集,我认为这将与您将获得的一样好。如果有很多行满足谓词,那么这将创建一个相当大的中间结果集(派生表)。
SELECT q.ip
, q.entrypoint
, q.timestamp
FROM (
SELECT IF(t.ip = @prev_ip, 0, 1) AS new_ip
, @prev_ip := t.ip AS ip
, t.timestamp AS timestamp
, t.entrypoint AS entrypoint
FROM (SELECT @prev_ip := NULL) i
CROSS
JOIN very_big_unindexed_table t
WHERE t.timestamp >= NOW() + INTERVAL -1 WEEK
AND t.entrypoint LIKE '%?kw=%'
ORDER BY t.ip DESC, t.timestamp DESC
) q
WHERE q.new_ip
执行该查询将需要(根据需要花费的时间)