• 数据存取


    基础:

    GE的核心是一个处理原始二进制文件的键值存储。您可以将一个二进制数保存到由指定的键标识的GE键-值存储中,然后将其加载回程序中。密钥是64位整数;而引用GE数据对象(或GE术语中的单元格)的唯一本地方法就是通过这样一个键。但是,其他类型的键(如字符串键)可以通过将键散列到64位整数来轻松实现。注意,每个键最多只能与一个二进制数值关联;用同一个键保存两个二进制数值会导致一个被另一个覆盖。我们还将把键的概念称为id。

    GE支持具有原子性的单元上的高性能并发键值运算符。内置的原子单元操作符包括:AddCell、SaveCell、LoadCell和RemoveCell。使用特定键访问单元格是序列化的;每个人都按照一定的顺序观察一个作家所做的改变。

    GE的耐久性是可选的。在编写单元格之前,我们可以选择是否向本地持久存储提交预写日志(WAL)WAL保证了耐久性,但也带来了一些性能损失。明智的使用它。

    数据存取模式:

    GE提供了多种数据访问模式。在决定使用这里介绍的一种数据访问方法之前,我们应该权衡便利和性能之间的权衡。

    内置键值存储接口:

    最方便的方法是使用内置的键-值存储接口,比如SaveCell和LoadCell。在GE中,这些接口是在Trinity Global。用于分别在本地或集群上访问数据的CloudStorage。

    内置接口将单元格视为二进制数。在TSL中定义了自己的单元格类型之后,其他类型的键-值存储接口将绑定在Trinity.Global.LocalStorage Triity.Global.CloudStorage.TSL生成的接口采用LoadMyType和SaveMyType的形式。例如,如果我们在TSL中定义一种单元格类型如下:

    cell GraphNode
    {
    [Index]
       string       Name;
       float        Value;
       List<CellId> Neighbors;
    }

    两个新的接口LoadGraphNode和SaveGraphNode将绑定到LocalStorage和CloudStorage。为这些接口提供了多个方法重载。我们可以这样写:

    var root = Global.CloudStorage.LoadGraphNode(123); //123 as the cell id
    var sum  = 0.0f;

    foreach(var neighbor in root.
    Neighbors)

    {
        sum += Global.CloudStorage.LoadGraphNode(neighbor).Value;
    }

    远程选择性数据访问:

    GE支持服务器端计算。这意味着我们不需要将所有相关的单元放到网络上,然后在本地对他们应用操作。我们 通过网络传递我们感兴趣的信息。为此,我们为客户机-服务器通信定义了一个自定义协议。现在,我们可以向服务器发送用户定义的请求,以获得所需的结果,而不是发送单元格加载/保存操作。例如,我了获得一组GraphNode值的和,我们可以定义这样的协议,而不是将他们加载到客户端:

    struct RequestMessage
    {
        List<CellId> NodeSet;
    }

    struct ResponseMessage
    {
        float Result;
    }

    protocol CalculateSum
    {
        Type     : Syn;
        Request  : RequestMessage;
        Response : ResponseMessage;
    }

    在客户端,我们可以通过:

    var sum = 0.0f;

    for(int serverId = 0; serverId < Global.ServerCount; serverId++)
    {
       using(var request  = new RequestMessageWriter(new List<long>(){1,2,3}))
       {
          using(var response = Global.CloudStorage.CalculateSumToMyServer(serverId, request))
          {
             var sum += response.Result;
          }
       }
    }

    服务器端,逻辑实现如下:

    public override void CalculateSumHandler(
        RequestMessageReader  request,
        ResponseMessageWriter response)
    {
        response.Result = .0f;
        foreach(var nodeId in request.NodeSet)
        {
            response.Result += Global.LocalStorage.LoadGraphNode(nodeId).Value;
        }
    }

    单元访问器:

    实际上,我们可以通过键-值存储接口执行任何数据访问任务。但很快我们会注意到,即使我们只想访问单个数据字段,整个单元格也需要加载。在上面显示的代码片段中,对某个字段访问单元格。对于每个单元,GE首先在内存存储中确定其内存位置。然后它调用运行时来分配单元格对象,并将单元格内容从存储复制到对象。然后,从对象中读出该字段病将其输入到外部计算循环中。

    单元格修饰是一个棘手的问题。只需修改单元格的一小部分就需要三个单元格操作:加载单元格、修改单元格和保存 单元格。在这个过程中产生的内存 副本浪费了大量的内存和网络带宽。而且,即使每个单元操作都是原子操作,单元修改作为一个整体也不是原子操作,因为不能保证三个单独的单元操作作为一个整体执行。

    在了解了使用键-值存储接口造成的问题之后,我们现在给出了解决方法。在GE中,我们通过一种称为数据访问器的机制来解决上述问题。对于TSL脚本中定义的任何单元结构,TSL编译器将自动生成单元访问器。访问器不拥有任何数据。相反,所有字段都作为c#属性提供;对这些属性的操作将转换为对底层单元格二进制的就低内存操作。使服务器逻辑重写为:

    public override void ComputeAverageHandler(
        RequestMessageReader  request,
        ResponseMessageWriter response)
    {
        response.Result = .0f;
        foreach(var nodeId in request.NodeSet)
        {
            using( var neighbor = Global.LocalStorage.UseGraphNode(nodeId) )
            {
                response.Result += neighbor.Value;
            }
        }
    }

    在这个新版本中,访问相邻节点的值,将访问4个字节的内存。有关单元格访问器的更多信息,请参阅:我的博客-TSL 访问器

    只要可能,使用单元格访问器而不是键-值存储接口来访问数据。

    单元访问设置:

    我们可以为大多数单元格访问接口提供单元格访问选项。根据单元格接口,可以应用下面列出的一个或多个选项。

    public enum CellAccessOptions
    {
       // Throws an exception when a cell is not found.
       ThrowExceptionOnCellNotFound,

       // Returns null when a cell is not found.
       ReturnNullOnCellNotFound,

       // Creates a new cell when a cell is not found.
       CreateNewOnCellNotFound,

    // Specifies that write-ahead-log should be performed with strong durability.
       StrongLogAhead,

       // Specifies that write-ahead-log should be performed with weak durability.
       // This option brings better performance, but under certain circumstances
       // the log may fail to be persistent, for example, during a power outage
       // or equipment failure.
       WeakLogAhead
    }

    单元访问选项用于控制单元操作的行为。例如,我们可以为可能改变单元格状态的单元格访问接口制定write-ahead-log级别。

     long cellId = 1;

    // MyCell is a cell type defined in a TSL project
    Global.LocalStorage.SaveMyCell(CellAccessOptions.StrongLogAhead, cellId, ...);

    Global.LocalStorage.RemoveCell(CellAccessOptions.WeakLogAhead, cellId);

    using (var cell = Global.LocalStorage.UseMyCell(cellId, CellAccessOptions.ReturnNullOnCellNotFound))

     {
        // Do something here
    }

    using (var cell = Global.LocalStorage.UseMyCell(3, CellAccessOptions.CreateNewOnCellNotFound))
    {
        // Do something here
    }

    单元选择器:

    GE提供了用于迭代存储在本地内存存储中的单元格的枚举数。注意,目前不支持云内存存储上的枚举。

    对于TSL中定义的每个单元格类型,TSL编译器在本地内存存储上生成一个选择器接口:Global.LocalStorage.MyCellType_Selector。顾名思义,选择器接口选择给定类型的所有单元,并返回IEnumerable<MyCellType>集合。然后,我们可以使用foreach便利集合:

     foreach( var node in Global.LocalStorage.GraphNode_Selector() )
    {
        //Do something...
    }

    支持单元对象和单元访问器。这里是访问器对等物:

    foreach( var accessor in Global.LocalStorage.GraphNode_Accessor_Selector() )
    {
        //Do something...
    }

    通过选择器枚举单元格是线程安全的;多个枚举可以同时执行。但是,不允许缓存访问器,因为访问器对象将在枚举期间被重用泳衣指向其他单元。因此,以下代码将导致数据损坏或系统崩溃:

    // Attempting to cache cell accessors
    List<GraphNode_Accessor> accessor_cache = new List<GraphNode_Accessor>();
    foreach( var accessor in Global.LocalStorage.GraphNode_Accessor_Selector() )
    {
        accessor_cache.Add(accessor);
    }
    // It will crash on visiting the cached accessors!
    var f = accessor_cache.Sum(accessor.AdditionalData);

    LINQ:

    选择器实现了IEnumerable接口,因此他们支持LINQ/PLINQ查询。如果为某些单元格字段指定了子字符串索引属性,那么一些查询可以直接利用反向索引来加速查询处理。使用LINQ实现通用数据查询逻辑很方便,例如:

     var results = from node in Global.LocalStorage.GraphNode_Accessor_Selector()
                   where node.name.Contains("Alice")
                   select node.Value;
    var min     = results.Min();

    在本例中,node.name.Contains子句被转换为子字符串查询。然后结果投影到一个浮点数列表中,然后使用内置的LINQ接口Min()进行聚合。

    详细介绍参考:Language-Integrated Query

    子串查询:

    如果在TSL中的单元格字段中指定了子字符串索引属性,将为指定的单元格字段生成一组子字符串查询接口。子字符串查询接口接受一个或多个查询字符串,并返回匹配的单元格id列表。

    详细介绍参考:substring query

     

     

  • 相关阅读:
    Java 二分查找
    Java 快速排序法 冒泡排序法 选择排序法 插入排序法
    XAF-BI.Dashboard模块概述 web/win
    XAF-列表视图数据访问模式
    XAF-列表视图编辑模式
    XAF-UI元素概述
    如何为CriteriaOperator过滤对象转换为lambda表达式,即:linq to xpo的动态where语句
    XAF视频教程来啦,已出15课
    如何隐藏winform中报表设计器中的按钮
    ReportDesigner中实现保存,保存所有,注册ICommandHandler方式实现
  • 原文地址:https://www.cnblogs.com/v-haoz/p/9663457.html
Copyright © 2020-2023  润新知