1.4.2.3 操作

NdbTransaction 由一系列操作組成,每個操作都由 NdbOperationNdbScanOperationNdbIndexOperationNdbIndexScanOperation 的實例表示 (即 NdbOperation 或其子類別之一)。

關於 NDB Cluster 存取操作類型的基本資訊,請參閱第 1.4.2.3.1 節「NDB 存取類型」

1.4.2.3.1 NDB 存取類型

資料節點程序有一些簡單的建構,用於存取 NDB Cluster 中的資料。我們建立了一個非常簡單的基準來檢查每個建構的效能。

共有四種存取方法

  • 主鍵存取。 這是通過其主鍵存取記錄。在最簡單的情況下,一次僅存取一筆記錄,這表示設定多個 TCP/IP 訊息和多個內容切換成本由這單一請求承擔。在一個批次中傳送多個主鍵存取的情況下,這些存取會共用設定必要 TCP/IP 訊息和內容切換的成本。如果 TCP/IP 訊息的目的地不同,則需要設定其他 TCP/IP 訊息。

  • 唯一鍵存取。 唯一鍵存取與主鍵存取類似,差別在於唯一鍵存取會先在索引表上執行讀取,然後在該表上進行主鍵存取。不過,MySQL 伺服器只會傳送一個請求,而索引表的讀取由資料節點處理。這類請求也受益於批次處理。

  • 完整資料表掃描。 如果資料表查詢沒有任何索引,則會執行完整資料表掃描。這會以單一請求的形式傳送至 ndbd 程序,然後該程序會將資料表掃描分割成在所有 NDB 資料節點程序上的平行掃描集合。

  • 使用排序索引的範圍掃描。 使用排序索引時,執行掃描的方式與完整資料表掃描相同,差別在於它只掃描 MySQL 伺服器 (SQL 節點) 傳輸的查詢所使用範圍內的記錄。當所有繫結索引屬性包含分割鍵中的所有屬性時,會平行掃描所有分割區。

1.4.2.3.2 單列操作

在使用 NdbTransaction::getNdbOperation() 或 NdbTransaction::getNdbIndexOperation() 建立操作後,會透過下列三個步驟定義它

  1. 使用 NdbOperation::readTuple() 指定標準操作類型。

  2. 使用 NdbOperation::equal() 指定搜尋條件。

  3. 使用 NdbOperation::getValue() 指定屬性動作。

以下是說明此流程的兩個簡短範例。為求簡潔,我們省略了錯誤處理。

第一個範例使用 NdbOperation

// 1. Retrieve table object
myTable= myDict->getTable("MYTABLENAME");

// 2. Create an NdbOperation on this table
myOperation= myTransaction->getNdbOperation(myTable);

// 3. Define the operation's type and lock mode
myOperation->readTuple(NdbOperation::LM_Read);

// 4. Specify search conditions
myOperation->equal("ATTR1", i);

// 5. Perform attribute retrieval
myRecAttr= myOperation->getValue("ATTR2", NULL);

如需更多這類範例,請參閱第 2.5.2 節「使用同步交易的 NDB API 範例」

第二個範例使用 NdbIndexOperation

// 1. Retrieve index object
myIndex= myDict->getIndex("MYINDEX", "MYTABLENAME");

// 2. Create
myOperation= myTransaction->getNdbIndexOperation(myIndex);

// 3. Define type of operation and lock mode
myOperation->readTuple(NdbOperation::LM_Read);

// 4. Specify Search Conditions
myOperation->equal("ATTR1", i);

// 5. Attribute Actions
myRecAttr = myOperation->getValue("ATTR2", NULL);

另一個此類型範例可以在第 2.5.6 節「NDB API 範例:在掃描中使用次要索引」中找到。

現在,我們將更詳細地討論建立和使用同步交易的每個步驟。

  1. 定義單列操作類型。 支援以下操作類型

    所有這些操作都對唯一元組鍵進行操作。使用 NdbIndexOperation 時,每個操作都會對已定義的唯一雜湊索引進行操作。

    注意

    如果想要在同一個交易中定義多個操作,則需要針對每個操作呼叫 NdbTransaction::getNdbOperation()NdbTransaction::getNdbIndexOperation()

  2. 指定搜尋條件。 搜尋條件用於選取元組。搜尋條件使用 NdbOperation::equal() 設定。

  3. 指定屬性動作。 接著,有必要判斷應該讀取或更新哪些屬性。務必記住

    • 刪除無法讀取或設定值,只能刪除它們。

    • 讀取只能讀取值。

    • 更新只能設定值。一般而言,屬性是依名稱識別,但也可能使用屬性的識別碼來判斷屬性。

    NdbOperation::getValue() 會傳回包含讀取值的 NdbRecAttr 物件。若要取得實際值,可以使用兩種方法之一;應用程式可以

    當呼叫 Ndb::closeTransaction() 時,會釋放 NdbRecAttr 物件。因此,在後續呼叫 Ndb::closeTransaction() 後,應用程式無法參考此物件。在呼叫 NdbTransaction::execute() 之前,嘗試從 NdbRecAttr 物件讀取資料會產生未定義的結果。

1.4.2.3.3 掃描操作

掃描大致相當於 SQL 資料指標,提供執行高速列處理的方法。可以在資料表上 (使用 NdbScanOperation) 或排序的索引 (透過 NdbIndexScanOperation) 上執行掃描。

掃描操作具有下列特性

  • 它們可以執行可能是共用、獨佔或髒讀的讀取操作。

  • 它們可以潛在地處理多行資料。

  • 它們可以用於更新或刪除多行資料。

  • 它們可以並行地在多個節點上操作。

在使用 NdbTransaction::getNdbScanOperation()NdbTransaction::getNdbIndexScanOperation() 建立操作之後,其執行方式如下:

  1. 使用 NdbScanOperation::readTuples() 定義標準操作類型。

    注意

    請參閱 NdbScanOperation::readTuples(),以獲取有關使用獨佔鎖執行同時且相同的掃描時可能發生的死鎖的更多資訊。

  2. 使用 NdbScanFilterNdbIndexScanOperation::setBound() 或兩者來指定搜尋條件。

  3. 使用 NdbOperation::getValue() 指定屬性動作。

  4. 使用 NdbTransaction::execute() 執行交易。

  5. 透過連續呼叫 NdbScanOperation::nextResult() 來遍歷結果集。

以下是兩個簡短的範例,說明此流程。再次強調,為了保持簡短和簡單,我們省略了任何錯誤處理。

第一個範例使用 NdbScanOperation 執行表格掃描。

// 1. Retrieve a table object
myTable= myDict->getTable("MYTABLENAME");

// 2. Create a scan operation (NdbScanOperation) on this table
myOperation= myTransaction->getNdbScanOperation(myTable);

// 3. Define the operation's type and lock mode
myOperation->readTuples(NdbOperation::LM_Read);

// 4. Specify search conditions
NdbScanFilter sf(myOperation);
sf.begin(NdbScanFilter::OR);
sf.eq(0, i);   // Return rows with column 0 equal to i or
sf.eq(1, i+1); // column 1 equal to (i+1)
sf.end();

// 5. Retrieve attributes
myRecAttr= myOperation->getValue("ATTR2", NULL);

第二個範例使用 NdbIndexScanOperation 來執行索引掃描。

// 1. Retrieve index object
myIndex= myDict->getIndex("MYORDEREDINDEX", "MYTABLENAME");

// 2. Create an operation (NdbIndexScanOperation object)
myOperation= myTransaction->getNdbIndexScanOperation(myIndex);

// 3. Define type of operation and lock mode
myOperation->readTuples(NdbOperation::LM_Read);

// 4. Specify search conditions
// All rows with ATTR1 between i and (i+1)
myOperation->setBound("ATTR1", NdbIndexScanOperation::BoundGE, i);
myOperation->setBound("ATTR1", NdbIndexScanOperation::BoundLE, i+1);

// 5. Retrieve attributes
myRecAttr = MyOperation->getValue("ATTR2", NULL);

以下是對執行掃描所需的每個步驟的一些額外討論:

  1. 定義掃描操作類型。 請務必記住,每個掃描操作僅支援單一操作 (NdbScanOperation::readTuples()NdbIndexScanOperation::readTuples())。

    注意

    如果您想要在同一個交易中定義多個掃描操作,則需要為每個操作分別呼叫 NdbTransaction::getNdbScanOperation()NdbTransaction::getNdbIndexScanOperation()

  2. 指定搜尋條件。 搜尋條件用於選取元組。如果未指定搜尋條件,則掃描將傳回表格中的所有列。搜尋條件可以是 NdbScanFilter (可用於 NdbScanOperationNdbIndexScanOperation) 或範圍 (只能用於索引掃描 - 請參閱 NdbIndexScanOperation::setBound())。索引掃描可以使用 NdbScanFilter 和範圍。

    注意

    當使用 NdbScanFilter 時,會檢查每一列,無論它是否實際傳回。但是,當使用範圍時,只會檢查範圍內的列。

  3. 指定屬性動作。 接下來,有必要定義應讀取哪些屬性。與交易屬性一樣,掃描屬性是依名稱定義的,但也可以使用屬性的識別碼來定義屬性。如本文檔的其他地方所討論 (請參閱 第 1.4.2.2 節「同步交易」),讀取的值會由 NdbOperation::getValue() 方法以 NdbRecAttr 物件的形式傳回。

1.4.2.3.4 使用掃描來更新或刪除列

掃描也可以用於更新或刪除列。其執行方式如下:

  1. 使用 NdbOperation::LM_Exclusive 進行獨佔鎖掃描。

  2. (在遍歷結果集時:) 對於每一列,選擇性地呼叫 NdbScanOperation::updateCurrentTuple()NdbScanOperation::deleteCurrentTuple()

  3. (如果執行 NdbScanOperation::updateCurrentTuple():) 只需使用 NdbOperation::setValue() 設定記錄的新值。在這種情況下不應呼叫 NdbOperation::equal(),因為主索引鍵是從掃描中擷取的。

重要

更新或刪除實際上要到下次呼叫 NdbTransaction::execute() 時才會執行,就像單列操作一樣。NdbTransaction::execute() 也必須在釋放任何鎖之前呼叫;如需更多資訊,請參閱 第 1.4.2.3.5 節「掃描的鎖定處理」

索引掃描的特定功能。 執行索引掃描時,可以使用 NdbIndexScanOperation::setBound() 來掃描表格的子集。此外,可以使用 NdbIndexScanOperation::readTuples(),以遞增或遞減順序對結果集進行排序。請注意,除非將 sorted 設定為 true,否則預設會傳回未排序的列。

同樣重要的是要注意,當將 NdbIndexScanOperation::BoundEQ (請參閱 NdbIndexScanOperation::BoundType) 與分割區索引鍵搭配使用時,只會實際掃描包含列的片段。最後,當執行排序的掃描時,以 NdbIndexScanOperation::readTuples() 方法的 parallel 引數傳遞的任何值都將被忽略,並改為使用最大平行處理能力。換句話說,在這種情況下,可以掃描的所有片段都會同時且平行地掃描。

1.4.2.3.5 掃描的鎖定處理

在表格或索引上執行掃描可能會傳回大量記錄;但是,Ndb 一次只會鎖定每個片段的預定數量的列。每個片段鎖定的列數由傳遞給 NdbScanOperation::readTuples() 的 batch 參數控制。

為了讓應用程式能夠處理鎖定如何釋放,NdbScanOperation::nextResult() 具有布林參數 fetchAllowed。如果呼叫 NdbScanOperation::nextResult()fetchAllowed 等於 false,則該函式呼叫可能不會釋放任何鎖定。否則,可能會釋放目前批次的鎖定。

下一個範例顯示了以有效方式處理鎖定的掃描刪除。為了簡潔起見,我們省略了錯誤處理。

int check;

// Outer loop for each batch of rows
while((check = MyScanOperation->nextResult(true)) == 0)
{
  do
  {
    // Inner loop for each row within the batch
    MyScanOperation->deleteCurrentTuple();
  }
  while((check = MyScanOperation->nextResult(false)) == 0);

  // When there are no more rows in the batch, execute all defined deletes
  MyTransaction->execute(NoCommit);
}

如需掃描的更完整範例,請參閱 第 2.5.5 節「NDB API 基本掃描範例」

1.4.2.3.6 錯誤處理

錯誤可能發生在定義構成交易的操作時,或實際執行交易時。擷取和處理任何一種錯誤都需要測試 NdbTransaction::execute() 傳回的值,然後,如果指示錯誤 (也就是說,如果此值等於 -1),則使用以下兩種方法來識別錯誤的類型和位置:

這個簡短的範例說明如何偵測錯誤,以及如何使用這兩種方法來識別錯誤:

theTransaction = theNdb->startTransaction();
theOperation = theTransaction->getNdbOperation("TEST_TABLE");
if(theOperation == NULL)
  goto error;

theOperation->readTuple(NdbOperation::LM_Read);
theOperation->setValue("ATTR_1", at1);
theOperation->setValue("ATTR_2", at1);  //  Error occurs here
theOperation->setValue("ATTR_3", at1);
theOperation->setValue("ATTR_4", at1);

if(theTransaction->execute(Commit) == -1)
{
  errorLine = theTransaction->getNdbErrorLine();
  errorOperation = theTransaction->getNdbErrorOperation();
}

在這裡,errorLine3,因為錯誤發生在對 NdbOperation 物件呼叫的第三個方法(在此例中,為 theOperation)。如果 NdbTransaction::getNdbErrorLine() 的結果為 0,則表示錯誤發生在執行操作時。在這個範例中,errorOperation 是一個指向 theOperation 物件的指標。NdbTransaction::getNdbError() 方法會回傳一個 NdbError 物件,其中包含錯誤的相關資訊。

注意

當發生錯誤時,交易不會自動關閉。您必須呼叫 Ndb::closeTransaction()NdbTransaction::close() 來關閉交易。

請參閱 Ndb::closeTransaction()NdbTransaction::close()

處理交易失敗(也就是說,當報告錯誤時)的一種建議方式如下所示

  1. 透過呼叫 NdbTransaction::execute(),並為 type 參數使用特殊的 ExecType 值來回滾交易。

    請參閱 NdbTransaction::execute()NdbTransaction::ExecType,以取得有關如何執行此操作的更多資訊。

  2. 透過呼叫 NdbTransaction::close() 來關閉交易。

  3. 如果錯誤是暫時性的,請嘗試重新啟動交易。

當交易包含多個同時執行的操作時,可能會發生幾個錯誤。在這種情況下,應用程式必須檢查所有操作並查詢它們各自的 NdbError 物件,以找出實際發生的情況。

重要

即使回報提交成功,也可能發生錯誤。為了處理這種情況,NDB API 提供額外的 NdbTransaction::commitStatus() 方法,用來檢查交易的提交狀態。

請參閱 NdbTransaction::commitStatus()