我有一个包含数百万个事件的游戏文件,文件大小可以> 10gb
每行都是游戏动作,例如:
player 1, action=kill, timestamp=xxxx(ms granularity)
player 1, action=jump, timestamp=xxxx
player 2, action=fire, timestamp=xxxx
对于此数据集,每个操作都是唯一且有限的。
我想对此文件进行分析,例如每秒的事件总数,同时跟踪该秒内的单个操作数。
我的半伪代码计划:
lastReadGameEventTime = DateTime.MinValue;
while(line=getNextLine() != null)
{
parse_values(lastReadGameEventTime, out var timestamp, out var action);
if(timestamp == MinValue)
{
lastReadGameEventTime = timestamp;
}
else if(timestamp.subtract(lastReadGameEventTime).TotalSeconds > 1)
{
notify_points_for_this_second(datapoints);
datapoints = new T();
}
if(!datapoints.TryGetValue(action, out var act))
act = new Dictionary<string,int>();
act[action] = 0;
else
act[action]++;
}
lastReadGameEventTime = parse_time(line)
我担心这太天真了。我在想,也许要数一整分钟,并获得每秒的平均值。但是我当然会错过比赛事件的高峰。 而且,如果我要计算5天的平均值,则会进一步降低结果集的质量。 有什么聪明的主意吗?
答案 0 :(得分:1)
您在这里问几个不同的问题。都相关。您的要求不是很详细,但我想我可以为您指明正确的方向。我将假设您想要的只是过去一段时间内的每秒事件数。因此,我们所需要的就是在此期间内每秒保持一个整数(事件数)的方法。
每天有86,400秒。假设您需要10天的信息。您可以构建一个大小为864,000的循环缓冲区,以容纳10天的计数:
const int SecondsPerDay = 86400;
const int TenDays = 10 * SecondsPerDay;
int[] TenDaysEvents = new int[TenDays];
因此,您始终拥有最近10天的计数。
假设您有一个事件处理程序可以读取套接字数据并将信息传递给函数,则可以轻松地保持数据更新:
DateTime lastEventTime = DateTime.MinValue;
int lastTimeIndex = 0;
void ProcessReceivedEvent(string event)
{
// here, parse the event string to get the DateTime
DateTime eventTime = GetEventDate(event);
if (lastEventTime == DateTime.MinValue)
{
lastTimeIndex = 0;
}
else if (eventTime != lastEventTime)
{
// get number of seconds since last event
var elapsedTime = eventTime - lastEventTime;
var elapsedSeconds = (int)elapsedTime.TotalSeconds;
// For each of those seconds, set the number of events to 0
for (int i = 1; i <= elapsedSeconds; ++i)
{
lastTimeIndex = (lastTimeIndex + 1) % TenDays; // wrap around if we get past the end
TenDaysEvents[lastTimeIndex] = 0;
}
}
// Now increment the count for the current time index
++TenDaysEvents[lastTimeIndex];
}
这将始终保留最后10天的内存,并且易于更新。报告比较困难,因为开始可能在数组中间。也就是说,如果当前索引为469301,则开始时间为469302。这是一个循环缓冲区。对此进行报告的天真的方法是将循环缓冲区复制到另一个数组或列表中,起始点在新集合中的位置0,然后进行报告。或者,您可以编写一个自定义枚举器,该枚举器从当前位置开始倒数并从此处开始。创建起来并不难。
上面的好处是您的数组保持静态。您只需分配一次,然后重复使用即可。不过,您可能想要添加额外的60个条目,以便在当前时间和10天前的时间之间存在一些“缓冲区”。这样可以防止查询期间更改10天前的数据。再增加300个项目,让自己有5分钟的缓冲时间。
另一个选项是创建条目的链接列表。再一次,每秒一次。这样,您就可以将项目添加到列表的末尾,并从前面删除较旧的项目。每当事件再次出现时,请将事件条目添加到列表的末尾,然后从列表的开头删除超过10天(或任何阈值)的条目。您仍然可以使用LINQ来报告事物,如另一个答案所建议。
您也可以使用混合动力车。随着时间的流逝,将一条记录写入数据库,并保留最后一分钟或一小时,或任何其他内存。这样,您就可以在内存中获取最新数据,以进行快速报告和实时更新,但是您也可以使用数据库自首次开始收集数据以来的任何时间进行报告。
无论您做出什么决定,都可能应该保留某种数据库,因为您不能保证系统不会崩溃。实际上,您几乎可以保证系统 会在某个时候崩溃。丢失数据,或者必须扫描数TB的日志数据以重新构建随着时间的推移收集的数据,这并不是一件有趣的事情。