English 中文(简体)
在内存中查询复杂的数据结构。
原标题:
  • 时间:2008-11-27 06:50:36
  •  标签:

第一次在问题网站上发帖,但我有一个比较复杂的问题,我已经看了几天了。

Background At work we re implementing a new billing system. However, we want to take the unprecedented move of actually auditing the new billing system against the old one which is significantly more robust on an ongoing basis. The reason is the new billing system is alot more flexible for our new rate plans, so marketing is really on us to get this new billing system in place.

我们花了一大笔钱让IT小组开发了一份报告,每天早上8点运行昨天的数据,比较记录以获取字节计数差异,并生成报告。对我们来说并不是很有用,因为首先它会在第二天运行,其次如果它显示出糟糕的结果,我们无法知道为什么前一天可能有问题。

因此,我们想要建立自己的系统,可以连接到任何可能的数据来源(首先只有新旧系统的用户数据记录(UDR)),并在几乎实时地比较结果。

关于规模的一些注意事项,每个计费系统每天产生大约6百万条记录,总文件大小约为1 GB。

My Proposed set-up Essentially, buy some servers, we have budget for several 8 core / 32GB of RAM machines, so I d like to do all the processing and storage in in-memory data structures. We can buy bigger server s if necessary, but after a couple days, I don t see any reason to keep the data in memory any longer (written out to persistent storage) and Aggregate statistics stored in a database.

每个记录基本上包含平台上的记录ID、关联ID、用户名、登录时间、持续时间、传入字节数、传出字节数以及一些其他字段。

我想使用相当复杂的数据结构进行处理。每个记录都将被分成一个用户对象和一个属于平台A或平台B的记录对象。在顶层,将是一个基于用户名的自平衡二叉搜索树。下一步类似于基于日期的跳跃列表,因此我们将有下一个匹配记录,下一个日期,下一个小时,下一个月,下一个年等等。最后,我们将拥有我们匹配的记录对象,实质上只是一个持有者,引用来自系统A的UDR记录对象和来自系统B的UDR记录对象。

当添加数据时,我会运行多个内部分析,以查看新的计费系统是否出现故障,与旧系统相比是否存在大的差异,并向我们的操作中心发送警报以进行调查。我个人没有任何问题。

Problem The problem I have is aggregate statistics are great, but I want to see if I can come up with a sort of query language where the user can enter a query, for say the top contributors to this alarm, and see what records contributed to the discrepancy, and dig in and investigate. Originally, I wanted to use a syntax similar to a filter in wireshark, with some added in SQL.

例子:

udr.bytesin > 1000 && (udr.analysis.discrepancy > 100000 || udr.analysis.discrepency_percent > 100) && udr.started_date >  2008-11-10 22:00:44  order by udr.analysis.discrepancy DESC LIMIT 10

另一个选择就是使用 DLINQ,但是我已经离开C#游戏一年半了,所以不是100%了解.net 3.5的内容。而且我不确定它能否处理我计划使用的数据结构。真正的问题是,我能否得到任何关于如何获取用户的查询字符串,解析它并将其应用于数据结构(它具有比上述更多的属性),并获取返回的结果列表的反馈。我可以自己处理其余部分。

我已经准备好了,可以硬编码很多可能的查询,只需要将它们视为在某些参数下运行的报告,但如果有一种漂亮干净的方法来处理这种查询语法,我认为这将是一个非常酷的功能添加。

问题回答

事实上,对于上述类型的查询,动态 LINQ是非常合适的。否则,你仍然需要编写基本相同的解析器和将其映射到属性的机制。不幸的是,这不是一个完全的匹配,因为你需要分割的内容如OrderBy,和日期需要被参数化 - 但这里有一个工作示例:

class Udr { // formatted for space
    public int BytesIn { get; set; }
    public UdrAnalysis Analysis { get; set; }
    public DateTime StartedDate { get; set; }
}
class UdrAnalysis {
    public int Discrepency { get; set; }
    public int DiscrepencyPercent { get; set; }
}    
static class Program {
    static void Main() {
        Udr[] data = new [] {
              new Udr { BytesIn = 50000, StartedDate = DateTime.Today,
                 Analysis = new UdrAnalysis { Discrepency = 50000, DiscrepencyPercent = 130}},
              new Udr { BytesIn = 500, StartedDate = DateTime.Today,
                 Analysis = new UdrAnalysis { Discrepency = 50000, DiscrepencyPercent = 130}}
        };
        DateTime when = DateTime.Parse("2008-11-10 22:00:44");
        var query = data.AsQueryable().Where(
            @"bytesin > 1000 && (analysis.discrepency > 100000
                || analysis.discrepencypercent > 100)
                && starteddate > @0",when)
            .OrderBy("analysis.discrepency DESC")
            .Take(10);
        foreach(var item in query) {
            Console.WriteLine(item.BytesIn);
        }
    }
}

当然,你可以使用动态LINQ示例并自定义解析器,以实现更多你所需的功能...

无论您是否使用DLINQ,我怀疑您将希望在解决方案中的某个地方使用LINQ,因为它提供了您想要的许多功能。

你需要多少用户保护以及他们的技术水平如何?如果这只是为了一些非常技术化的内部员工(例如已经是开发人员),那么你可以让他们编写一个C#表达式,然后使用CSharpCodeProvider来编译代码 - 然后将其应用于您的数据。

显然,这需要您的用户能够编写C#代码 - 或者至少只需足够的语义编写查询表达式 - 以及 它需要您相信他们不会破坏服务器。(您可以将代码加载到单独的AppDomain中,赋予它低权限并在超时后拆除AppDomain,但这种操作非常复杂 - 并且您不希望在AppDomain边界上跨越大量数据) 。)

关于 LINQ 的一般问题 - 再次说明,由于您的规模问题,它非常适合。

Just some notes on the scale, each billing system produces roughly 6 million records / day at a total file size of about 1 gig.

LINQ 可以完全与流式解决方案一起使用。例如,您的“源”可以是文件阅读器。然后 Where 将对数据进行迭代,检查单个行,而无需将整个数据缓冲到内存中:

    static IEnumerable<Foo> ReadFoos(string path) {
        return from line in ReadLines(path)
               let parts = line.Split( | )
               select new Foo { Name = parts[0],
                   Size = int.Parse(parts[1]) };
    }
    static IEnumerable<string> ReadLines(string path) {
        using (var reader = File.OpenText(path)) {
            string line;
            while ((line = reader.ReadLine()) != null) {
                yield return line;
            }
        }
    }

这是现在的懒加载... 我们只读取一行。您需要使用 AsQueryable() 来与动态 LINQ 一起使用,但它仍然是懒惰的。

如果您需要对相同的数据执行多个聚合操作,则Push LINQ是一个很好的选择;如果您需要对数据进行分组,则这特别适用,因为它不会缓存所有内容。

最后 - 如果您想要二进制存储,可以使用类似protobuf-net的序列化器创建流式解决方案。目前它与Push LINQ的“推送”方法最搭配,但如果需要,我可以将其反转为常规的IEnumerable<T>





相关问题
热门标签