NuoDB
by NuoDB

MVCC.方面的隔离级别:第2部分 - 索引和延迟

再次,无所畏惧的读者。这篇文章将继续讨论开始 这里。我将在孤立级别作为SQL构造的情况下,我将继续将它们的分布式MVCC数据库(如Nuodb等分布式MVCC数据库)。 NB:此帖子,如前一个将使用并排时间表的图形表示。第一列是“Transaction1”,它与与三个其他列相同的隔离级别运行。每个剩余列正在展示Nuodb中三个支持的隔离级别的行为。阅读这些时间表的方法是从顶部到底部的时间流到左下方,从左到右(即,在给定的水平切片中,事务1中的动作发生在事务2之前)。所有这些示例都是针对初始化的样本DB执行:

SQL> create table t1 (f1 integer);
SQL> insert into t1 values (1), (3), (5), (7), (9);
SQL> select * from t1;
 F1  
 --- 
  1  
  3  
  5  
  7  
  9  

更新暗盒和索引更新

 没有进一步的ADO,让我们进入这个例子。

示例1:选择更新

选择更新是一个有趣的SQL构造。它看起来好像是读取(毕竟,它包含'select'),它返回结果集。但是,选择更新实际上是一种写​​入形式。基本上,选择for update每行匹配where子句的每一行都写道,但覆盖它之前的确切值。然后它将覆盖的行作为结果集返回。因为标准要求在事务提交之前需要保持行的有效“锁定”,因为用户需要执行自己的两阶段锁定时,选择更新是有用的。让我们来看看在操作中选择的一个例子:

选择更新示例

在这里,我们看到所有三个隔离级别都将阻止选择更新,但它们都有不同的行为。

read_commited. 正在阻止,因为它无法决定在TXN 1完成之前要写入哪个记录版本

Adantent_read. 正在阻止,因为它已检测到并发更新,并且不知道是否在TXN 1完成之前是否发生故障或更新

write_commited. 是阻止的,因为它只是等待在TXN 1的记录版本上安装新的记录版本

这就是理由的局面 write_commited. 可以直观。 write_commited. 允许串行语义 换向写作 比其他隔离级别更有效地执行。这里的推理与序列存在背后的原因类似。如果我作为应用程序开发人员,知道更新(或更新集)与更新的更新,其他事务将在同一表上执行,然后 write_commited. 是唯一的隔离级别,可以让所有交易的更新同时堆积。复杂因素是严格的SQL语义要求更新(或删除)无法返回,直到其更改一致。因此,性能改进将处于单一语句级别。这目前还适用于存储过程,但是在那里有更多的语义灵活性。

示例2:索引更新

有些人可能已经注意到已经对没有索引的表进行了所有示例。这意味着记录选择本质上是不精确的。即使更新可能需要更改单行,事务尚未选择,但要扫描表中的每一行以查找所有匹配项。当然,这正是索引应该帮助的索引。如果我们在表上粘贴索引,让我们看看(如果有的话)更改:

> CREATE INDEX idx1 ON t1(f1);

 使用索引更新

在此示例中,事务2不必阻止事务1来提交,因为索引允许两个更新查询选择完全匹配的记录。由于TXN 1和TXN 2更新的记录集不重叠,因此没有写入冲突,因此两个事务都可以继续。重要的是要注意的是,无阻塞和阻塞行为都与定义一致 read_commited.。不同之处在于,非阻塞行为是索引启用的性能改进。如果事务1和2都尝试更新相同的记录版本(无论写入设置选择的精度如何),则仍将有更新冲突。

这是什么意思? Adantent_read. (快照隔离),通过其定义强制实施精度,因此索引不会加速写入那里。 但如果您的申请正在使用 read_commited. 或者 write_commited.,您可以通过添加增加写入设置精度的索引来减少争用。在诊断性能问题时,这是一项很好的拇指。

性能和延迟考虑因素

NuodB是分布式数据库。这很酷,因为这意味着Nuodb可以以传统的单节点解决方案不能缩放和调整。但它也意味着系统设计人员和应用程序开发人员现在也必须在思考其应用程序的性能特征时进行延迟。

Adantent_read.write_commited. 对正常选择语句进行快照版本控制。因此,这些隔离级别没有潜伏敏感性。也就是说,无论链接在数据库的两个半部之间的速度慢,链路两侧的读卡器都不会由于延迟而遭受性能下降。

这种行为的直觉是它是分布式系统的相对论逻辑的直接后果。在NOODB中,事务控制是单个节点的本地。因此,当交易在TE上开始时,该节点上的“发生在”事务集中的“发生”是完全的。正是在那一刻在该节点上提交的一组交易。因此,“全局”排序是什么并不重要(因此,不需要原子钟),那么那么未知已经已知要提交的任何事务被认为是同时执行的,并且适当地进行可见性计算。

read_commited. 是一个不同的故事。因为孤立级别的定义是只读取最近承诺版本的记录, read_commited.读取行为取决于到达TE处理TE的提交消息的时间 read_commited. 交易。当然,没有人写作 read_commited. 应用程序可以预期同一SELEC的任何两个执行才能返回相同的值(根据SQL标准)。根据应用的语义, read_commited. 可以将本地事务的完成时间与远程事务的完成时间绑定。任何给定的选择将始终返回最近提交的版本,但如果本地事务正在旋转等待传播的远程提交,则会遭受延迟。这是数据位置肯定会改善交易延迟的情况。

把握它, read_commited. 允许应用程序从严格的隔离中逃脱,并在发布时查看已提交的更改,但如果应用程序将写入交易放在世界的一侧和另一方的读者身上,则读者将不得不等待作者的更改在世界各地传播。在这一声明中没有什么可激进的,这是对这些应用程序开发人员的提醒,第一次将其分布式水域浸入分布式水域中。 始终尊重应用程序的位置。

写道

在这里讨论(简要)Nuodb执行更新的方式是合适的。更新在乐观地完成异步协调。给定表将被分解为多个“原子”,这是独立分布式对象。对于每个记录,它存在它所存在的原子。对于每个原子,我们有一个杰出的节点,我们称之为“主席”。当多个节点正在尝试更新完全相同的记录版本时,主席存在打破联系。当节点更新记录版本时:

  1. 该事务乐观地使用更新的值安装新版本
  2. 该事务将更新广播到所有对等体(异步)
  3. Recort Atom董事长仔细检查此更新冲突
  4. 在冲突上,将拒绝发送到更新节点
  5. 成功,将确认发送到更新节点

即使在等待记录主席的确认时,更新节点也将继续处理更新的记录集。以这种方式,批量更新以最有效的方式交错。这种乐观的方法意味着在常见的情况下(成功更新),不需要完成进一步的工作,并且更新版本已经将网络流过到所有对等体。结果是在更新失败时,更新节点必须执行一些额外的工作并将返回消息发送到“撤消”失败的更新。

read_commited.write_commited. 交易将阻止对表中发生的任何更新冲突(一个或多个冲突行更新)。这是为了防止幻影和其他丑陋的写异常。但是,这意味着如果某些记录正在全局更新, read_commited.write_commited. 交易将强制一种全球2PL(两阶段锁定)。如果该行被位于高延迟链路的另一端的事务更新,这意味着这些更新将具有与该延迟一起成长的执行时间。如上所述,如果每个更新与其他事务的重叠概率没有或非常低,则使用索引使得尽可能特定的写入集可以减少或消除由于虚假冲突而消除阻塞。

Adantent_read. Snapshot隔离,可以利用,以便只有两个事务正在更新完全相同的记录版本,它们将必须彼此等待。由于更新的方式通过主席协调,因此可以使用单行更新等待更新消息的往返。但是,批量更新利用异步并异步地发送所有更新请求,以便N-Row更新不需要N串行往返行程,并且与主席的消息传递协调将成为具有更新版本的流程的一部分无论如何传播到所有同龄人。

在这里,性能推荐直观。拥有全球访问的表是完全没事的。但是,避免定期全局更新特定行。一般来说,全球变异的全球州是一种狡猾的命题(分布式系统中的双重)。无论如何,大多数事情都最终有一种自然的地方,这可以防止这一点是一个问题。例如,人们可以想象一个包含数十亿个条目的全球“客户表”。但是,似乎对任何现实的应用似乎都是同时更新欧洲,亚洲和N.美国的同一客户排。即使有一些长时间运行的分析查询,如果他们在整个桌子上搅动,那么 contement_read, 他们永远不会在任何隔离级别上持有更新。每一个,然后,可能有导致全局更新一行,但是在任何分布式上下文中,不断且高频的应用程序会很差。要进一步举行示例,因为只要应用程序演示了一些位置,所以每个TE都不断地从其存储器中删除未使用的原子副本,因此NOODB将自动启动缓存表数据以反映该位置。这意味着应用程序开发人员不必在创建自定义缓存层上令人震惊,应用程序的自然地区将反映在Nuodb本身的缓存行为中(有关更多详细信息,请参阅: 诺博德布缓存介绍, memcached vs nuodb.)。

希望这有助于填补上一篇文章中的一些差距,并赋予了我忠实的读者一些关于如何理解诺博德布的隔离级别的直觉。我希望你已经走出了一些拇指的规则,这将有助于您推理数据库应用程序,因为它在Nuodb上运行。