文档中心MogDBMogDB StackUqbar
v1.1

文档:v1.1

支持的版本:

其他版本:

MOT并发控制机制

通过大量研究,我们找到了最佳的并发控制机制,结论为:基于SILO的OCC算法是MOT中最符合ACID特性的OCC算法。SILO为满足MOT的挑战性需求提供了最好的基础。

img 说明: MOT完全符合原子性、一致性、隔离性、持久性(ACID)特性,如MOT简介所述。

下面介绍MOT的并发控制机制。


MOT本地内存和全局内存

SILO管理本地内存和全局内存,如所示。

  • 全局内存是所有核共享的长期内存,主要用于存储所有的表数据和索引。
  • 本地内存是短期内存,主要由会话使用,用于处理事务及将数据更改存储到事务内存中,直到提交阶段。

当事务需要更改时,SILO将该事务的所有数据从全局内存复制到本地内存。使用OCC方法,全局内存中放置的是最小的锁,因此争用时间极短。事务更改完成后,该数据从本地内存回推到全局内存中。

本地内存与SILO增强并发控制的基本交互式事务流如下所示:

图 1 私有(本地)内存(每个事务)和全局内存(所有核的所有事务)

私有(本地)内存(每个事务)和全局内存(所有核的所有事务)

具体请参见Industrial-Strength OLTP Using Main Memory and Many-cores[对比:磁盘与MOT]。


MOT SILO增强特性

SILO凭借其基本算法流程,优于我们在研究实验中测试的许多其他符合ACID的OCC算法。然而,为了使SILO成为产品级机制,我们必须用许多在最初设计中缺失的基本功能来增强它,例如:

  • 新增对交互式事务的支持,其中事务的SQL运行在客户端实现,而不是作为服务器端的单个步骤运行。
  • 新增乐观插入
  • 新增对非唯一索引的支持
  • 新增对事务中写后读校验(RAW)的支持,使用户能够在提交之前查看更改
  • 新增对无锁协同垃圾回收的支持
  • 新增对无锁检查点的支持
  • 新增对快速恢复的支持
  • 新增对分布式部署两阶段提交的支持

在不破坏原始SILO的可扩展特性的前提下添加这些增强是非常具有挑战性的。


MOT隔离级别

即使MOT完全兼容ACID,MogDB 1.1.0并非支持所有的隔离级别。下表介绍了各隔离级别,以及MOT支持和不支持的内容。

表 1 隔离级别

隔离级别 说明
READ UNCOMMITTED MOT不支持
READ COMMITTED MOT支持
READ COMMITTED(读已提交)隔离级别保证任何正在读取的数据在上一次读取时都已提交。它只是限制读者看到任何中间数据、未提交数据,或脏读。数据被读取后可以自由更改,因此,读已提交隔离级别并不保证事务再次读取时能找到相同的数据。
SNAPSHOT MOT不支持
SNAPSHOT(快照)隔离级别提供与SERIALIZABLE(可序列化)相同的保证,同时支持并发事务修改数据。相反,它迫使每个读者看到自己的世界版本(自己的快照)。不阻止并发更新使得编程非常容易,且可扩展性很强。然而,在许多实现中,这种隔离级别需要更高的服务器资源。
REPEATABLE READ MOT支持
REPEATABLE READ(可重复读)是一个更高的隔离级别,除了READ COMMITTED隔离级别的保证之外,它还保证任何读取的数据都不能更改。如果一个事务再次读取相同的数据,它将找出该数据,不做更改,并且保证它可读取。乐观模型使得并发事务能更新该事务读取的行。在提交时,该事务将验证REPEATABLE READ隔离级别是否被违反。若违反,则回滚该事务,必须重试。
SERIALIZABLE MOT不支持
SERIALIZABLE(可序列化)隔离提供了更强的保证。除了REPEATABLE READ隔离级别保证的所有内容外,它还保证后续读取不会看到新数据。它之所以被命名为SERIALIZABLE,是因为隔离非常严格,几乎有点像事务串行运行,而不是并行运行。

下表显示了不同隔离级别启用的并发副作用。

表 2 隔离级别启用的并发副作用

隔离级别 说明 不可重复读 幻影
READ UNCOMMITTED
READ COMMITTED
REPEATABLE READ
SNAPSHOT
SERIALIZABLE

在不久后将发布的版本中,MogDB MOT还将支持SNAPSHOT和SERIALIZABLE隔离级别。


MOT乐观并发控制

并发控制模块(简称CC模块)提供了主内存引擎的所有事务性需求。CC模块的主要目标是为主内存引擎提供各种隔离级别的支持。


乐观OCC与悲观2PL

悲观2PL(2阶段锁定)和乐观并发控制(OCC)的功能差异在于对事务完整性分别采用悲观和乐观方法。

基于磁盘的表使用悲观方法,这是最常用的数据库方法。MOT引擎使用的是乐观方法。

悲观方法和乐观方法的主要功能区别在于,如果冲突发生,

  • 悲观的方法会导致客户端等待;
  • 而乐观方法会导致其中一个事务失败,使得客户端必须重试失败的事务。

乐观并发控制方法(MOT使用)

乐观并发控制(OCC)方法在冲突发生时检测冲突,并在提交时执行验证检查。

乐观方法开销较小,而且通常效率更高,原因之一是事务冲突在大多数应用程序中并不常见。

当强制执行REPEATABLE READ隔离级别时,乐观方法与悲观方法之间的函数差异更大,而当强制执行SERIALIZABLE隔离级别时,函数差异最大。

悲观方法(MOT未使用)

悲观并发控制(2PL,或称2阶段锁定)方法使用锁阻止在潜在冲突的发生。执行语句时应用锁,提交事务时释放锁。基于磁盘的行存储使用这种方法,并且添加了多版本并发控制(Multi-version Concurrency Control,MVCC)。

在2PL算法中,当一个事务正在写入行时,其他事务不能访问该行;当一个行正在读取时,其他事务不能覆盖该行。在访问时锁定每个行,以进行读写;在提交时释放锁。这些算法需要一个处理和避免死锁的方案。死锁可以通过计算等待图中的周期来检测。死锁可以通过使用TSO保持时序或使用某种回退方案来避免。

遇时锁定(ETL)

另一种方法是遇时锁定(ETL),它以乐观的方式处理读取,但写入操作锁定它们访问的数据。因此,来自不同ETL事务的写入操作相互感知,并可以决定中止。实验证明,ETL通过两种方式提高OCC的性能:

  • 首先,ETL会在早期检测冲突,并通常能增加事务吞吐量。这是因为事务不会执行无用的操作。(通常)在提交时发现的冲突无法在不中止至少一个事务的情况下解决。
  • 其次,ETL写后读校验(RAW)运行高效,无需昂贵或复杂的机制。

结论:

OCC是大多数工作负载最快的选项。这一点我们在初步研究阶段已经发现。

其中一个原因是,当每个核执行多个线程时,锁很可能被交换线程持有,特别是在交互模式下。另一个原因是悲观算法涉及死锁检测(产生开销),并通常使用读写锁(比标准自旋锁效率低)。

我们选择Silo是因为它比其他现有选项(如TicToc)简单,同时对大多数工作负载保持相同的性能。ETL有时比OCC更快,但它引入了假中止,可能会使用户混淆,而OCC则只在提交时中止。


OCC与2PL的区别举例

下面是会话同时更新同一个表时,两种用户体验的区别:悲观(针对基于磁盘的表)和乐观(针对MOT表)。

本例中,使用如下表测试命令:

table “TEST” - create table test (x int, y int, z int, primary key(x));

本示例描述同一测试的两个方面:用户体验(本示例中的操作)和重试要求。

悲观方法示例-用于基于磁盘的表

下面是一个悲观方法例子(非MOT)。任何隔离级别都可能适用。

以下两个会话执行尝试更新单个表的事务。

WAIT LOCK操作发生,客户端体验是:会话2卡住,直到会话1完成COMMIT,会话2才能进行。

但是,使用这种方法时,两个会话都成功,并且不会发生异常中止(除非应用了SERIALIZABLE或REPEATABLE-READ隔离级别),这会导致整个事务需要重试。

表 3 悲观方法代码示例

会话1 会话2
t0 Begin Begin
t1 update test set y=200 where x=1;
t2 y=200 Update test set y=300 where x=1; - Wait on lock
t4 Commit
Unlock
Commit(in READ-COMMITTED this will succeed, in SERIALIZABLE it will fail)
y = 300

乐观方法示例-用于MOT

下面是一个乐观方法的例子。

它描述了创建一个MOT表,然后有两个并发会话同时更新同一个MOT表的情况。

create foreign table test (x int, y int, z int, primary key(x));
  • OCC的优点是,在COMMIT之前没有锁。
  • OCC的缺点是,如果另一个会话更新了相同的记录,则更新可能会失败。如果更新失败(在所有支持的隔离级别中),则必须重试整个会话#2事务。
  • 更新冲突由内核在提交时通过版本检查机制检测。
  • 会话2将不会等待其更新操作,并且由于在提交阶段检测到冲突而中止。

表 4 乐观方法代码示例-用于MOT

会话1 会话2
t0 Begin Begin
t1 update test set y=200 where x=1;
t2 y=200 Update test set y=300 where x=1;
t4 Commit y = 300
Commit
ABORT
y = 200
Copyright © 2011-2024 www.enmotech.com All rights reserved.