數據存取

基礎: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.
   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

相關文章
相關標籤/搜索