基础:html
GE的核心是一个处理原始二进制文件的键值存储。您能够将一个二进制数保存到由指定的键标识的GE键-值存储中,而后将其加载回程序中。密钥是64位整数;而引用GE数据对象(或GE术语中的单元格)的惟一本地方法就是经过这样一个键。可是,其余类型的键(如字符串键)能够经过将键散列到64位整数来轻松实现。注意,每一个键最多只能与一个二进制数值关联;用同一个键保存两个二进制数值会致使一个被另外一个覆盖。咱们还将把键的概念称为id。node
GE支持具备原子性的单元上的高性能并发键值运算符。内置的原子单元操做符包括:AddCell、SaveCell、LoadCell和RemoveCell。使用特定键访问单元格是序列化的;每一个人都按照必定的顺序观察一个做家所作的改变。c#
GE的耐久性是可选的。在编写单元格以前,咱们能够选择是否向本地持久存储提交预写日志(WAL)WAL保证了耐久性,但也带来了一些性能损失。明智的使用它。缓存
数据存取模式:安全
GE提供了多种数据访问模式。在决定使用这里介绍的一种数据访问方法以前,咱们应该权衡便利和性能之间的权衡。服务器
内置键值存储接口:网络
最方便的方法是使用内置的键-值存储接口,好比SaveCell和LoadCell。在GE中,这些接口是在Trinity Global。用于分别在本地或集群上访问数据的CloudStorage。并发
内置接口将单元格视为二进制数。在TSL中定义了本身的单元格类型以后,其余类型的键-值存储接口将绑定在Trinity.Global.LocalStorage Triity.Global.CloudStorage.TSL生成的接口采用LoadMyType和SaveMyType的形式。例如,若是咱们在TSL中定义一种单元格类型以下:ide
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.