如何检查数据库中的记录数是否超过x次

时间:2014-06-16 06:49:49

标签: mysql sql perl

我有一个MySQL表,它包含以下方案中的记录:

hostname | appname | timestamp | message

我使用perl脚本查询数据库以获取最后一分钟的所有记录。该脚本每60秒激活一次。

Perl脚本收集时间戳大于当前时间戳的所有记录--60秒。 然后,我可以检查是否有超过,比如5,记录,这使我在脚本中做了一些其他的事情。

我想要实现的是在我的脚本中有一个“窗口”,以便能够查看每60秒脚本激活之间是否存在重叠,以确保可能还有超过5条记录。< / p>

我举个例子:

该脚本在08:42:00执行,它收集从08:41:00到开始时间的所有记录:

foo.net spbx 16-06-2014 08:41:51
foo.net spbx 16-06-2014 08:41:55
foo.net spbx 16-06-2014 08:41:57
foo.net spbx 16-06-2014 08:42:59

所以它的4条记录 - 无所事事。

脚本在08:43:00执行,从08:42:00到开始时间:

foo.net spbx 16-06-2014 08:42:01
foo.net spbx 16-06-2014 08:42:02
foo.net spbx 16-06-2014 08:42:03
foo.net spbx 16-06-2014 08:42:04

仍然没有错误。但是如果你看起来精确地具有60秒的“动态间隔”,则在60秒间隔内发生的事件超过5次,而这种简单的方法是看不到的。

我想总是收集最后120秒,然后在60s窗口中从最旧的条目向上检查是否有超过5个事件。但我想知道这个“问题”是否有更好的方法?

如果这个动态方法发现超过5个事件,那么它需要删除这些条目,以便在下次执行时不收集它们(或者只是在db中标记它们)

目前的代码部分如下:

my $dbConnect = DBI->connect("DBI:mysql:database=$dbName;host=$dbHost","$dbUser","$dbPass", { RaiseError => 1, AutoCommit => 0})
    or die "ERROR - Can't connect to MySQL-Database: ".$DBI::errstr."\n";
debug("Connect to database successfull");

my $dbQuery = $dbConnect->prepare("SELECT message,timestamp FROM $dbTable WHERE hostname='$hostname' AND appname='$appname' AND timestamp > ?");
my $date = Time::Piece->strptime(localtime->epoch-$threshold[1],"%s");
# adding the timezone offset - workaround for FAP-CENTREON
$date += $date->localtime->tzoffset;
my $starttime = $date->strftime("%Y-%m-%d %H:%M:%S");
debug("query: SELECT message,timestamp FROM $dbTable WHERE hostname=$hostname AND appname=$appname AND timestamp > ".$starttime);
$dbQuery->execute($starttime);

my $amount = $dbQuery->rows;

$alarmMessage = "$amount errors in ".$threshold[1]."s!\n";

if($amount < $threshold[0]) {
    $alarmMessage = "$amount errors in ".$threshold[1]."s - this is OK! \n";
    $exitCode = 0;
} else {
    while(my @resultrows = $dbQuery->fetchrow_array) {
        $alarmMessage = $alarmMessage.$resultrows[1]." ".$resultrows[0]."\n";
    }
    $exitCode = 2;
}

$dbQuery->finish();
$dbConnect->disconnect();

print "$alarmMessage \n";

2 个答案:

答案 0 :(得分:2)

您可以使用单个查询执行第一部分:

SELECT a.timestamp, COUNT(*) ct
FROM $dbTable AS a
JOIN $dbTable AS b ON b.timestamp BETWEEN a.timestamp AND DATE_ADD(a.timestamp, INTERVAL 1 MINUTE)
WHERE a.timestamp > DATE_SUB(NOW(), INTERVAL 2 MINUTE)
GROUP BY a.timestamp
HAVING ct > 5

要查找应删除或标记为已被注意为超出的邮件,您可以将此子查询加入到表中,并选择时间戳后1分钟内的所有邮件。

答案 1 :(得分:1)

关于缩放的评论,如果你不介意的话。

仅针对SQL的解决方案的性能,将主动写入表自身加入,并不能很好地扩展。如果你确实希望每秒排1行,那你就没事了。当你达到每秒100行时会受到伤害。

在这种情况下,您可能希望有一个定期任务,将表的短块总结到辅助表中,然后扫描辅助表以查找有趣的事件。如果您的辅助表对于每个10秒的块有一行,则无论原始表中有多少事件,您都可以在大致恒定的时间内对这些COUNT进行求和。

如果你发现超过一分钟的总和超过你的阈值,那么,取决于你是否真的对你来说是否真的很重要是在59秒和61秒内看到n个事件,或者你是否需要粗略估计,然后,您可以在原始表上执行更具体的SELECT,以获得准确的答案。

几年前我在这里写过这篇文章:

http://beta.slashdot.org/journal/93006