在未索引的 table 上使用 WHERE 子句进行 MySQL 查询 select 的最快方法
Fastest way to do MySQL query select with WHERE clauses on an unindexed table
我有一个非常大的未编入索引的 table,名为 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 和一个日期,将搜索该日期前一周的行和 return 包含“?kw=”的最近行每个IP。所以我想要 DISTINCT 入口点,但只有最近的入口点。
我被这个卡住了,我知道这是一个相对简单的 INNER JOIN,但我不知道最快的方法。
顺便说一下:我现在无法添加索引,因为它非常大并且在为网站提供服务的数据库上。我将用索引 table 替换它,别担心。
来自 table
的行
SELECT ...
FROM very_big_unindexed_table t
仅在过去一周内...
WHERE t.timestamp >= NOW() + INTERVAL - 1 WEEK
在入口点中包含“?kw=”
AND t.entrypoint LIKE '%?kw=%'
只有每个 IP 的最新行。有几种方法可以解决这个问题。一个非常大的未索引 table 上的相关子查询将吃掉你的午餐和午餐盒。如果没有索引,就无法绕过 table 和 "Using filesort" 操作的完整扫描。
考虑到不幸的情况,我们最好的性能选择可能是尽可能地减少集合,然后执行排序,并避免任何连接操作(回到 table) 并避免相关子查询。
那么,让我们从这样的事情开始,到 return all 过去一周的行,入口点为 '?kw='。这将是 table 的完整扫描和排序操作...
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 中的优化器(至少)非常有预测性table.
如果过去一周的行数是整个 table 的重要子集,我认为这将与您将获得的一样好。如果有很多行满足谓词,这将创建一个相当大的中间结果集(派生 table)。
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
执行该查询需要(根据需要花费的时间)
- table 的完整扫描(没有办法解决)
- 排序操作(同样,没有办法解决)
- 具体化包含满足谓词的所有行的派生 table
- 通过派生的 table 为每个 IP
拉出 "latest" 行
我有一个非常大的未编入索引的 table,名为 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 和一个日期,将搜索该日期前一周的行和 return 包含“?kw=”的最近行每个IP。所以我想要 DISTINCT 入口点,但只有最近的入口点。
我被这个卡住了,我知道这是一个相对简单的 INNER JOIN,但我不知道最快的方法。
顺便说一下:我现在无法添加索引,因为它非常大并且在为网站提供服务的数据库上。我将用索引 table 替换它,别担心。
来自 table
的行SELECT ...
FROM very_big_unindexed_table t
仅在过去一周内...
WHERE t.timestamp >= NOW() + INTERVAL - 1 WEEK
在入口点中包含“?kw=”
AND t.entrypoint LIKE '%?kw=%'
只有每个 IP 的最新行。有几种方法可以解决这个问题。一个非常大的未索引 table 上的相关子查询将吃掉你的午餐和午餐盒。如果没有索引,就无法绕过 table 和 "Using filesort" 操作的完整扫描。
考虑到不幸的情况,我们最好的性能选择可能是尽可能地减少集合,然后执行排序,并避免任何连接操作(回到 table) 并避免相关子查询。
那么,让我们从这样的事情开始,到 return all 过去一周的行,入口点为 '?kw='。这将是 table 的完整扫描和排序操作...
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 中的优化器(至少)非常有预测性table.
如果过去一周的行数是整个 table 的重要子集,我认为这将与您将获得的一样好。如果有很多行满足谓词,这将创建一个相当大的中间结果集(派生 table)。
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
执行该查询需要(根据需要花费的时间)
- table 的完整扫描(没有办法解决)
- 排序操作(同样,没有办法解决)
- 具体化包含满足谓词的所有行的派生 table
- 通过派生的 table 为每个 IP 拉出 "latest" 行