AnNdbTransaction
由一系列操作组成,每个操作都由NdbOperation
、
NdbScanOperation
、
NdbIndexOperation
或
NdbIndexScanOperation
(即NdbOperation
其子类的或其中之一)的一个实例表示。
有关 NDB Cluster 访问操作类型的一般信息, 请参阅第 1.4.2.3.1 节,“NDB 访问类型” 。
数据节点进程有许多简单的构造,用于访问 NDB Cluster 中的数据。我们已经创建了一个非常简单的基准来检查每一个的性能。
访问方式有四种:
主键访问。 这是通过其主键访问记录。在最简单的情况下,一次只访问一个记录,这意味着设置多个 TCP/IP 消息的全部成本和上下文切换的多个成本都由这个单个请求承担。在一批发送多个主键访问的情况下,这些访问共享设置必要的 TCP/IP 消息和上下文切换的成本。如果 TCP/IP 消息用于不同的目的地,则需要设置额外的 TCP/IP 消息。
唯一的密钥访问。 唯一键访问类似于主键访问,不同之处在于唯一键访问是作为对索引表的读取执行的,然后是对表的主键访问。但是MySQL Server只发出一个请求,索引表的读取由数据节点处理。此类请求也受益于批处理。
全表扫描。 当不存在用于查找表的索引时,将执行全表扫描。这作为单个请求发送到ndbd进程,然后将表扫描分成一组对所有
NDB
数据节点进程的并行扫描。使用有序索引进行范围扫描。 当使用有序索引时,它以与全表扫描相同的方式执行扫描,只是它只扫描那些在 MySQL 服务器(SQL 节点)传输的查询所使用的范围内的记录。当所有绑定的索引属性都包含分区键中的所有属性时,将并行扫描所有分区。
使用 NdbTransaction::getNdbOperation() 或 NdbTransaction::getNdbIndexOperation() 创建操作后,它在以下三个步骤中定义:
使用 指定标准操作类型
NdbOperation::readTuple()
。使用 指定搜索条件
NdbOperation::equal()
。使用 指定属性操作
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 示例:在扫描中使用二级索引”中找到。
我们现在更详细地讨论创建和使用同步事务所涉及的每个步骤。
-
定义单行操作类型。 支持以下操作类型:
NdbOperation::insertTuple()
: 插入一个不存在的元组。NdbOperation::writeTuple()
: 如果一个元组存在则更新它,否则插入一个新的元组。NdbOperation::updateTuple()
:更新现有的元组。NdbOperation::deleteTuple()
:删除现有的元组。NdbOperation::readTuple()
:使用指定的锁定模式读取现有的元组。
所有这些操作都对唯一的元组键进行操作。当
NdbIndexOperation
使用时,这些操作中的每一个都对定义的唯一哈希索引进行操作。笔记如果要在同一事务中定义多个操作,则需要为每个操作调用
NdbTransaction::getNdbOperation()
或NdbTransaction::getNdbIndexOperation()
。 指定搜索条件。 搜索条件用于选择元组。使用 设置搜索条件
NdbOperation::equal()
。-
指定属性操作。 接下来,有必要确定应该读取或更新哪些属性。重要的是要记住:
删除既不能读取也不能设置值,而只能删除它们。
读取只能读取值。
更新只能设置值。通常属性由名称标识,但也可以使用属性的标识来确定属性。
NdbOperation::getValue()
返回NdbRecAttr
包含读取值的对象。要获得实际值,可以使用以下两种方法之一;该应用程序可以使用它自己的内存(通过指针传递
aValue
)到NdbOperation::getValue()
,或NdbRecAttr
在 NDB API 分配 的对象中接收属性值 。
调用
NdbRecAttr
时释放对象 。Ndb::closeTransaction()
因此,应用程序无法在对 的任何后续调用之后引用此对象Ndb::closeTransaction()
。NdbRecAttr
在调用之前 尝试从对象读取数据会NdbTransaction::execute()
产生未定义的结果。
扫描大致相当于 SQL 游标,提供了一种执行高速行处理的方法。可以对表(使用
NdbScanOperation
)或有序索引(通过 )
执行扫描NdbIndexScanOperation
。
扫描操作具有以下特点:
它们可以执行共享、独占或脏读操作。
他们可能会处理多行。
它们可用于更新或删除多行。
它们可以在多个节点上并行运行。
使用
NdbTransaction::getNdbScanOperation()
or
创建操作后NdbTransaction::getNdbIndexScanOperation()
,执行如下:
-
定义标准操作类型,使用
NdbScanOperation::readTuples()
.笔记有关死锁的更多信息,请参见NdbScanOperation::readTuples(),这些死锁可能在使用独占锁执行同时、相同的扫描时发生。
NdbScanFilter
使用、NdbIndexScanOperation::setBound()
或两者 指定搜索条件 。使用 指定属性操作
NdbOperation::getValue()
。使用 执行交易
NdbTransaction::execute()
。通过连续调用 遍历结果集
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);
第二个示例使用 an
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);
执行扫描所需的每个步骤的一些额外讨论如下:
-
定义扫描操作类型。
NdbScanOperation::readTuples()
请务必记住,每个扫描操作 (或 ) 仅支持单个操作NdbIndexScanOperation::readTuples()
。笔记如果你想在同一个事务中定义多个扫描操作,那么你需要 为每个操作 单独调用
NdbTransaction::getNdbScanOperation()
或 。NdbTransaction::getNdbIndexScanOperation()
-
指定搜索条件。 搜索条件用于选择元组。如果没有指定搜索条件,扫描将返回表中的所有行。搜索条件可以是一个
NdbScanFilter
(可以同时用于NdbScanOperation
和NdbIndexScanOperation
)或边界(只能用于索引扫描 - 请参阅NdbIndexScanOperation::setBound()
)。索引扫描可以同时使用NdbScanFilter
和 边界。笔记使用 NdbScanFilter 时,检查每一行,无论它是否实际返回。但是,当使用边界时,只会检查边界内的行。
指定属性操作。 接下来,有必要定义应该读取哪些属性。与事务属性一样,扫描属性由名称定义,但也可以使用属性的身份来定义属性。正如本文档其他地方所讨论的(参见 第 1.4.2.2 节“同步事务”
NdbOperation::getValue()
),读取的值 作为NdbRecAttr
对象 由方法返回 。
扫描也可用于更新或删除行。这是按如下方式执行的:
使用排他锁扫描
NdbOperation::LM_Exclusive
。(当遍历结果集时:)对于每一行,可选择调用
NdbScanOperation::updateCurrentTuple()
orNdbScanOperation::deleteCurrentTuple()
。(如果执行
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
参数传递的任何值都将被忽略,取而代之的是使用最大并行度。换句话说,在这种情况下,可以同时并并行地扫描所有可能扫描的片段。
对表或索引执行扫描有可能返回大量记录;但是,Ndb 一次只锁定每个片段的预定行数。每个片段锁定的行数由传递给 的批处理参数控制
NdbScanOperation::readTuples()
。
为了使应用程序能够处理锁的释放方式,
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 基本扫描示例”。
在定义构成事务的操作时或在实际执行事务时都可能发生错误。捕获和处理任何一种错误都需要测试 返回的值
NdbTransaction::execute()
,然后,如果指示错误(即,如果此值等于-1
),则使用以下两种方法来识别错误的类型和位置:
NdbTransaction::getNdbErrorOperation()
返回对导致最近错误的操作的引用。NdbTransaction::getNdbErrorLine()
产生操作中错误方法的方法编号,以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();
}
这里errorLine
是3
,因为错误发生在调用
NdbOperation
对象的第三个方法中(在本例中为theOperation
)。如果 的结果
NdbTransaction::getNdbErrorLine()
是0
,则在执行操作时发生错误。在此示例中,
errorOperation
是指向对象的指针
theOperation
。该
NdbTransaction::getNdbError()
方法返回一个NdbError
提供有关错误信息的对象。
发生错误时,
事务不会自动关闭。您必须致电
Ndb::closeTransaction()
或
NdbTransaction::close()
关闭交易。
一种处理事务失败(即报告错误时)的推荐方法如下所示:
-
NdbTransaction::execute()
通过使用参数的特殊ExecType
值 调用来回滚事务type
。有关如何完成此操作的更多信息, 请参见NdbTransaction::execute()和 NdbTransaction::ExecType 。
通过调用关闭事务
NdbTransaction::close()
。如果错误是暂时的,请尝试重新启动事务。
当一个事务包含同时执行的多个操作时,可能会发生多个错误。在这种情况下,应用程序必须遍历所有操作并查询它们的每个
NdbError
对象以找出真正发生的事情。
即使提交被报告为成功,也可能会发生错误。为了处理这种情况,NDB API 提供了一种额外的
NdbTransaction::commitStatus()
方法来检查事务的提交状态。