查询内存中的复杂数据结构

时间:2008-11-27 06:50:37

标签: c# .net

第一次发布到问题网站,但我有一个复杂的问题,我已经看了几天。

背景的 在工作中,我们正在实施一个新的计费系统。但是,我们希望采用前所未有的实际审核新计费系统的方式,而不是旧计费系统,这种方法在持续的基础上更加强大。原因是新的计费系统对于我们的新费率计划来说更加灵活,因此营销对我们来说真正有了这个新的计费系统。

我们的IT小组每天早上8点为昨天的数据制作一份可笑的金额报告,比较获取字节数差异的记录,并生成报告。这对我们来说并不是很有用,因为第二天它运行一次,其次如果它显示不好的结果,我们没有任何迹象表明为什么我们可能在前一天遇到问题。

因此,我们希望构建我们自己的系统,该系统可以挂接到任何可能的数据源(首先只有新旧系统用户数据记录(UDR)),并且几乎实时地比较结果。

只是规模上的一些注释,每个计费系统每天产生约600万条记录,总文件大小约为1 gig。

我建议的设置 基本上,购买一些服务器,我们有几台8核/ 32GB RAM机器的预算,所以我想在内存数据结构中进行所有处理和存储。如果需要,我们可以购买更大的服务器,但几天之后,我认为没有任何理由将数据保留在内存中(写入持久存储)和存储在数据库中的聚合统计信息。

每条记录基本上都包含来自平台的记录ID,关联ID,用户名,登录时间,持续时间,字节输入,字节输出和其他一些字段。

我在考虑使用相当复杂的数据结构进行处理。每条记录将被分解为用户对象,记录对象属于平台a或平台b。在顶层,将是用户名上的二叉搜索树(自平衡)。下一步有点像基于日期的跳过列表,所以我们将有下一个matched_record,第二天,下一个小时,下个月,明年等等。最后我们将有我们匹配的记录对象,基本上只是一个持有者引用系统a中的udr_record对象和系统b中的udr记录对象。

我会运行一些内部分析,因为添加了数据以查看新的结算系统是否已经窒息,与旧系统相比开始出现较大差异,并向我们的运营中心发送警报进行调查。我自己对这部分没有任何问题。

问题的 我遇到的问题是聚合统计数据很好,但我想知道我是否可以提出一种查询语言,用户可以在其中输入查询,比如这个警报的主要贡献者,并查看哪些记录有助于差异,挖掘和调查。最初,我想在wireshark中使用类似于过滤器的语法,其中一些在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的速度。此外,我不确定它是否可以处理我计划使用的数据结构。真正的问题是,我是否可以获得有关如何处理从用户获取查询字符串,解析它以及将其应用于数据结构(其具有上面列出的更多属性)以及获得结果的任何反馈列表回来。我可以自己处理其余的事情。

我已经完全准备好对大部分可能的查询进行硬编码,并且将它们作为使用某些参数运行的报表更多,但是如果有一种很好的干净方式来执行这种类型的查询语法,我认为它会添加非常酷的功能。

3 个答案:

答案 0 :(得分:1)

实际上,对于上述类型的查询,dynamic 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示例并自定义解析器以完成您需要的更多内容...

答案 1 :(得分:1)

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

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

显然,这需要您的用户能够编写C# - 或者至少只能编写C# - 它要求您信任它们不要丢弃服务器。 (您可以将代码加载到单独的AppDomain中,为其提供低权限并在超时后拆除AppDomain,但这样做很复杂 - 而且您并不真正希望大量数据跨越AppDomain边界。 )

答案 2 :(得分:0)

关于LINQ的主题 - 再次,由于你的尺寸问题,一个很好的契合:

  

关于比例的一些注释,每个   计费系统大约产生6个   总共有百万条记录/天   大小约1演出。

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;
            }
        }
    }

现在这是延迟加载...我们一次只读一行。

如果您需要对同一数据执行多个聚合,那么Push LINQ非常合适;如果你需要group data,这个效果特别好,因为它不会缓冲所有内容。

最后 - 如果您想要二进制存储,可以使用protobuf-net之类的序列化程序来创建流式解决方案。目前,它最适合使用Push LINQ的“推送”方法,但我希望如果需要,我可以将其反转为常规AsQueryable()