8

假设我有下表:

CREATE TABLE `occurences` (
  `object_id` int(10) NOT NULL,
  `seen_timestamp` int(10) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8

其中包含对象的 ID(不是唯一的,它重复)和观察到此对象 ID 的时间戳。

观察 24/7 运行,并插入带有当前时间戳的每个对象 ID。

现在我想编写查询来选择在任何 10 分钟内至少出现 7 次的所有对象 ID。

它的功能应该类似于入侵检测。

类似的算法用于检查无效 SSH 登录的拒绝主机脚本中。如果在配置的时间段内找到配置的出现次数,它会阻止 IP。

有什么好的建议吗?

4

3 回答 3

4

这应该有效:

SET @num_occurences = 7; -- how many occurences should occur in the interval
SET @max_period = 10; -- your interval in seconds

SELECT offset_start.object_id FROM 
(SELECT @rownum_start := @rownum_start+1 AS idx, object_id, seen_timestamp 
 FROM occurences, (SELECT @rownum_start:=0) r ORDER BY object_id ASC, seen_timestamp ASC) offset_start
JOIN
(SELECT @rownum_end := @rownum_end + 1 AS idx, object_id, seen_timestamp 
 FROM occurences, (SELECT @rownum_end:=0) r ORDER BY object_id ASC, seen_timestamp ASC) offset_end
   ON offset_start.object_id = offset_end.object_id 
  AND offset_start.idx + @num_occurences - 1 = offset_end.idx
  AND offset_end.seen_timestamp - offset_start.seen_timestamp <= @max_period
GROUP BY offset_start.object_id;

您可以移动@num_occurences@num_occurences到您的代码并将这些设置为您的语句的参数。根据您的客户,您还可以移动查询的初始化@rownum_start@rownum_end查询的前面,这可能会提高查询性能(尽管如此,您应该测试一下,只是看一下两个版本的解释的直觉)

以下是它的工作原理:

它选择整个表两次,并将 的每一行offset_startoffset_end偏移量为的行连接起来@num_occurences。(这是使用@rownum_*变量来创建每行的索引,模拟其他 rdbms 已知的 row_number() 功能)。
然后它只是检查两行是否引用相同的 object_id 并满足周期要求。
由于对每个出现的行都执行此操作,因此如果出现的次数实际上大于 ,则 object_id 将被多次返回@max_occurences,因此最后将其分组以使返回object_id的 s 唯一

于 2012-04-17T10:10:06.437 回答
1

你可以试试

SELECT COUNT(seen_timestamp) AS tot FROM occurences
WHERE seen_timestamp BETWEEN
    DATE_ADD(your_dt, INTERVAL -10 MINUTES) AND your_dt
GROUP BY object_id
HAVING tot >= 7

我不明白你为什么使用int(10)for seen_timestamp:你可以使用datetime...

于 2012-04-05T12:37:38.077 回答
1

您可以使用以下语句:

SELECT oc1.object_id 
    FROM occurences oc1 
        JOIN occurences oc2 ON oc1.object_id = oc2.object_id  
            AND oc1.seen_timestamp >= (oc2.seen_timestamp -600)
            AND oc1.seen_timestamp < oc2.seen_timestamp
    GROUP BY oc1.object_id, oc1.seen_timestamp
    HAVING COUNT(oc2.object_id)>=7;

它不是很快,也不是很干净,如果有人找到更好的解决方案,请告诉我!

于 2012-04-17T10:45:45.640 回答