----------------------------我是分割线-------------------------------
本文翻译自微软白皮书《In-Memory OLTP – Common Workload Patterns and Migration Considerations》:http://technet.microsoft.com/en-us/library/dn673538.aspx
译者水平有限,如有翻译不当之处,欢迎指正。
----------------------------我是分割线-------------------------------
摘要:内存中OLTP是集成到SQL Server2014中并针对现代硬件趋势进行设计的一个高性能的内存优化引擎。内存中OLTP允许用户在将数据保留在传统的基于磁盘的表结构中的同时,将数据迁移到常驻内存的表中。对于那些对性能要求很高的工作负荷,用户还可以将Transact-SQL代码迁移到本地编译的存储过程。这种方式能够提供额外的性能提升。本文旨在帮助读者了解内存中OLTP可提供显著受益的一些常见的架构模式。本文还讨论了将应用程序迁移到内存中OLTP的注意事项。
简介
20世纪80年代初,架构注意事项驱动了关系数据库管理系统最初的设计,这些注意事项很大程度上是基于可用的硬件资源和那个时代的业务需求。在如今的现代数据库引擎中,大部分的设计模式仍然占主导地位。然而自从那个时代以来,业务需求和可用的硬件资源已经发生了巨大的变化。因此,为了满足当今日益增长的需求,一些模式必须进行修订。
组织机构经常会遇到极具挑战性的场景,这些场景要求大量扩展应用程序来处理不断增加的数据量和并发用户数。这种趋势在包括零售,在线银行,在线游戏等在内的所有行业中都很明显。与此同时,许多现代业务的工作负荷都需要在这样规模下保持较低的延迟。对于只有计算机系统(比如,不需要对人响应)产生和处理数据的场景尤其如此。这种“机器产生数据”的例子包括金融交易,传感器和智能测量,制造,遥测,安全监控/分析和其他许多产业。
硬件趋势也改变了一个典型数据库服务器的特征。虽然多核处理器的数量在持续增加,但它们的时钟速度近年来并没有显著的变化。不过,现在内存的约束要少了很多。现在普通级别的服务器可以以更低的价格点容纳更多的内存。这使得应用程序能够使用更多的主内存而不会产生磁盘IO。过去那种数据大小远远大于可用内存的模式已经不再适用。此外,在RDBMS中必须实现以支持高效的内存利用率的分页复杂逻辑也不再重要。
即使在IO方面,更快的接口和固态设备的加入提供了更好的IO性能,并尽可能减少了IO瓶颈。多核系统的发展可以执行更多的并发线程。 IO的改进也提供了更快的响应时间。在许多情况下,这些发展一起将经典关系型数据库架构内的争用点推到它的极致。
正是这些趋势,以及对于这些环境中典型的OLTP行为集中在几GB到几百GB大小的“热点数据集”中的认识,要求对当前关系型数据库的架构进行根本的重新评估。内存中OLTP正是这种新型的架构,旨在缓解当前数据库的瓶颈,集成到著名的SQL Server关系型引擎中的同时,以全新的方式充分利用现代的硬件。
SQL Server内存中OLTP概述
内存中OLTP是集成到SQL Server中的一个专业的内存优化的关系型数据管理引擎和存储过程的本地编译器。微软设计了内存中OLTP来处理要求最苛刻的OLTP工作负荷。为了实现这一点,内存中OLTP引入了两个全新的概念:内存优化表和本地编译的存储过程。内存优化表中的数据驻留在内存中,同时为了恢复,事务被记录在日志中,并且没有像传统的基于磁盘的表那样的磁盘上的分页。内存优化表使用哈希和非聚集的顺序索引来提供高度优化的数据访问结构。这些索引的内部结构与传统的B树不同,提供了访问内存中数据的一种新的高性能的方式。数据访问和事务隔离都是通过提供了乐观的无阻塞实施方案的一个多版本并发控制机制来进行处理。虽然与传统的RDBMS实现方式不同,但内存中OLTP仍然符合ACID的规定。
引擎内对性能的严格要求不仅需要物理存储架构上的变化,而且需要相关数据访问编程界面的优化。本地编译的存储过程为访问内存中的数据提供了高度优化的查询机制。与传统的解释型Transact-SQL不同,本地编译的存储过程进行了优化,在创建时编译成机器语言。这能够提供更短的代码执行路径,这样反过来又显著地降低了CPU利用率和整体延迟。
内存中OLTP引擎完全集成在SQL Server中。从而为将传统的基于磁盘的表和解释型存储过程迁移成内存优化表和本地编译的存储过程提供了一种简单的方法。这些对象驻留在内存中OLTP引擎和传统的基于磁盘的表和存储过程中。这样的集成方式允许使用标准的Transact-SQL(解释型T-SQL)调用访问存储在内存优化表和基于磁盘的表中的数据。这样的集成方式有助于转换到内存中OLTP时尽可能减少应用程序的更改。
当迁移SQL Server应用程序到内存中OLTP引擎时,可以将对于性能很重要的数据移到内存优化表中,其他数据保留在基于磁盘的表中。与此类似,你可以将对于性能很重要的与内存优化表交互的那些Transact-SQL代码迁移为本地编译的存储过程。即席Transact-SQL或者非本地编译的存储过程仍可以与内存优化表进行交互。为了简化管理,在SQL Server Management Studio中集成了备份/恢复,AlwaysOn故障转移群集实例和可用性组等功能。
具备以下一个或一个以上条件的公司都应该认真考虑迁移到内存中OLTP可能带来的收益:
- 已有需要性能和可扩展性收益的SQL Server(或其它关系数据库)应用程序。
- 一个遇到数据库的瓶颈的RDBMS—比如最普遍的锁/闩锁或者代码执行。
- 由于已知的性能开销而在关键性能路径中不使用关系型数据库的环境。
OLTP工作负荷的挑战
OLTP工作负荷是微软开发内存中OLTP的目标场景。 典型的OLTP工作负荷具有以下属性的特征:
- 需要快速响应时间的短事务。
- 查询只与几张表相关,并且访问较小的数据集。
- 具有高并发的需求。
这些工作负荷通常通过以下方面定义它们的性能需求:
- 事务吞吐量。
- 并发用户数。
- 事务延迟(完成一段特定的执行代码的时间)。
在许多情况下,本文档也将使用术语“业务事务”来描述定义应用程序性能的一个衡量单位。这个术语是从应用程序或业务线的角度所描述的一个工作单元。它不是指某一层中的某物。它也不是指应用程序的一部分,比如衡量数据库事务的性能计数器。
某些工作负荷的特征会根据具体的实施而有所不同。然而,在本文中所有的场景中,将会展示将数据库逼近到性能瓶颈的类似业务和应用的需求。这对于管理关键业务工作负荷和对性能级别要求极高的应用程序而言是特别重要的。
高速率的数据插入
一个非常常见的应用程序模式的特征是需要对数据库以非常高的输入速率插入数据。对于输入数据数量波动,有多种多样的模式和需求,比如数据源的数量,数据被输入(爆发式的或者稳定的数据流)的时间段。然而,共同的特征是,需要处理大幅波动的或者恒定速率的输入工作负荷,这样的工作负荷可能压垮传统型的RDBMS。
也有许多应用程序已经展现出“ETL型”的行为。在这种场景中,数据可能不只是被插入,而且还被更新,删除,或者作为过程的一部分进行转化。在这种情况下,有两种与数据存储相关的常见模式:
- 具有处理初始负荷的中间表和数据以某种形式移动到目标表。
- 直接加载到最终的目标表和扩展写入和读取的并发性。
数据插入的整体性能问题仍然是相同的。为了为这种环境建立测量的基线,典型的度量是以事务吞吐量或者每秒装载的数据行数为特征。
读取/写入争用
许多OLTP工作负荷特有的另一个典型挑战是许多进程同时以一小部分数据作为目标。一些进程需要读取数据,同时其他进程对其修改。虽然通常这些数据集都比较小,但事实上,多并发用户会话对这些数据集进行高频率的读写请求,从而导致争用。这一争用成为扩展的显著障碍。表明这个瓶颈的一些常见模式为:
- 具有非常高的修改频率的小表。
- 被频繁访问的大表中的一小部分数据。例如,在订单表中,插入和更新新的订单,同时通常同样的这些订单是进行最多查询的。
在许多情况下,这些操作与用户交互是同步的,任何延迟都会对用户体验产生立即和线性的影响。总而言之,为了满足对性能严格要求的应用程序的需求,争用明显增加了挑战性。
低延迟
许多应用程序对于一个特定工作单元的执行都有特定的持续时间的要求。它们对于构成业务事务的具体步骤的端对端执行时间也可能有要求。应用程序会根据孤立地或在负载下执行一些工作所花费的时间来衡量它们的性能。衡量的单位可以是一个特定的查询执行,业务事务或者处理过程。衡量由客户端应用程序将数据处理到终端消费者中间所有步骤所花费的时间所组成。在许多情况下,对这些更细粒度的工作执行的衡量,被称为延迟。典型的延迟瓶颈可以归结为某种形式的数据库执行,包括:
- 通信协议栈。
- 解析,优化,并编译一个语句。
- 执行一个事务的总体时间,是基于每个操作的CPU指令数,处于较低的水平。
- 将一个持续的事务保存到事务日志的写入时间。
从业务的角度来看,这些瓶颈通常被认定需要更快地执行业务事务。在许多情况下,等待时间的单位是毫秒级别甚至更低。延迟的改善,也可以表现为更大的(业务)事务吞吐量,更大的规模或者并发,或者对数据更高效的处理。
典型瓶颈
我们所讨论的,在性能要求严格的环境中的工作负荷模式,通常在OLTP数据库应用程序和架构中表现出相似的瓶颈。在本节中,我们将简要讨论高性能OLTP SQL Server和数据库部署会遇到的主要瓶颈。
锁,闩锁和自旋锁争用
OLTP应用程序可能会忍受引擎级别的并发瓶颈,比如对于给定资源的不兼容的锁,闩锁或者自旋锁。所有场景都可能会导致并发的瓶颈,这些瓶颈会使得应用程序的性能和规模遭受挑战。锁是使用悲观并发模型的关系数据库中的一个行之有效的构造。不兼容的类型会导致阻塞或者等待特定的对象或数据。闩锁是SQL Server引擎使用的轻量级同步原语,用来保证支持索引,数据页和内部B树的页结构的一致性。但闩锁也会抑制并发。最后,自旋锁与闩锁类似,对数据结构进行串行访问。 SQL Server内部使用自旋锁来保护对共享的系统数据结构的并发访问。
一种特别具有挑战性的场景是其中的物理访问模式集中在数据的一小块区域中,比如一个索引的最新页。这个索引支持一个不断增加的键值。最主要的例子是在IDENTITY,SEQUENCE或其他连续值列上的一个聚集索引。读取进程和写入进程请求相互冲突的锁和闩锁,这些锁通常集中在最近的数据上。这造成锁和闩锁阻塞的 延迟。克服这种争用被证明是极具挑战性的,并且通常需要对应用程序的架构或者逻辑进行大量的修改。一些常见的弥补的尝试方案包括使用反向索引和非连续的聚集索引。在某些情况下,这些解决方案可以提供部分缓解,但往往会引入新的挑战,仍然会对扩展产生主要的障碍。
在一些极端的情况下,工作负荷可以实例化同时对一个或多个表产生大量修改的多个线程。这些可能会引入更多对元数据操作的竞争瓶颈,包括页的分配,区段,以及内部维护操作,比如更新PFS页。
关于闩锁和自旋锁争用的详细讨论,请参阅“诊断和解决SQL Server中的闩锁争用”和“诊断和解决SQL Server中的自旋锁争用”。
事务日志I/O
SQL Server在数据库事务日志中存储数据和索引的修改来保持持续性。事务日志记录必须在事务提交之前保存到磁盘。因此,OLTP工作负荷将经历非常频繁的,较小的日志缓冲区刷新,这会导致显著的I/O开销。此外,系统连续地写入日志记录,而只有一个日志写入器线程服务于整个数据库。这使得事务日志记录的开销成为高修改率工作负荷的主要原因。通常,底层子系统的性能,就IOPS和I/O延迟来说,是试图减少这种性能开销时首要的注意事项。事务日志所在磁盘的慢I/O会使问题变得更糟。然而,即使是高性能的磁盘子系统,在写入日志缓冲区时多个事务也会在自旋锁争用上造成瓶颈。有关日志管理器限制的详细信息,请参阅“诊断事务日志性能问题和日志管理器的限制”。
硬件资源
在许多情况下,硬件组件可能成为系统可扩展性和性能的主要障碍。一些硬件相关的常见OLTP瓶颈包括:
- 在繁忙的OLTP工作负荷下CPU使用率可能成为瓶颈。很多时候,应用程序将利用横向扩展的场景将工作负荷分散在多个服务器上。
- I/O子系统的性能通常会成为一个瓶颈。正如前面提到的,事务日志I/O可能成为事务执行时的一个瓶颈。同样地,吞吐量,作为IOPS的度量,可能成为数据/索引页分配和日志记录的瓶颈。吞吐量也可能是SQL Server数据库检查点行为的瓶颈。
很多时候,当瓶颈出现时,公司尝试使用“更快”或更有效的硬件来解决这些瓶颈。这在有限的情况下是成功的,比如I/O子系统的瓶颈。但是,使用硬件升级,你不能解决很多典型的OLTP的瓶颈,例如锁或闩锁争用。
利用内存中OLTP克服挑战
内存中OLTP技术提供了方法来解决前面提到的挑战。
锁和闩锁
对于内核的闩锁,内存优化表不使用需要闩锁的基于页的结构。内存中OLTP实现不使用锁的乐观并发控制。它利用行版本和时间戳提供了无锁和闩锁的数据修改来实现无阻塞的并发并解决了争用的瓶颈。为了克服分配争用的瓶颈,内存中OLTP采用了利用只追加的写入访问模式的数据文件和增量文件。 SQL Server使用FILESTREAM基础结构来管理这些文件。
I/O和事务日志记录
内存优化表中的事务日志记录有助于尽可能减少I/O瓶颈。由于索引完全存在于内存中,内存优化索引的修改不会记录在事务日志中。同时,在事务日志中,插入和删除事务比基于磁盘的事务需要更少的空间。最后,日志记录最多可以被组合到24K以最大限度地减少日志写入的次数。此外,系统只在提交时写入日志记录。这会限制一个事务写入日志缓冲区所需的次数。尽可能减少访问日志能够减少试图同时访问日志缓冲区的事务之间的争用。
内存中OLTP还提供了一些配置选项来帮助尽可能减少I/O瓶颈。相比于为了恢复完整记录事务,最极端的选项是使用DURABILITY = SCHEMA_ONLY语法创建内存优化表。这个选项可以恢复架构,但不能恢复数据。因此,对于这些表上的修改,系统不需要写入到事务日志。数据只是暂时性的场景可能能够从这个选项中大为受益。可以利用这个选项的几个例子将在后文中讨论。
在数据库或事务级别配置的另一个配置选项,是延迟的持续性。这个功能并不特定于内存中OLTP,因为你也可以在SQL Server2014将其作为通用的配置进行配置,系统在提交时不会将日志记录保留到磁盘上。相反,系统在一个设定的时间(而不是在每一次提交)或者在日志缓冲区填满时将日志记录刷新到磁盘上。通过这种方式,更少的,更有效和更大的I/O替代了每个事务许多小的日志的刷新。此外,系统可以在将日志记录写入到磁盘之前提交事务。这尽可能减少了事务处理对物理I/O的依赖。
应用程序的设计应该考虑到,DURABILITY = SCHEMA_ONLY和延迟的持续性选项不保证一旦服务器发生故障时所有正在运行事务的恢复。相关的详细信息,请参阅联机丛书“控制事务持续性”一节。
延迟和扩展
内存中OLTP可以显著地改善CPU利用率。通过利用高度优化的代码路径,作为本地编译的存储过程在内存中OLTP引擎中执行查询,从而提供了这些改进。相比于基于磁盘的B树,使用非聚集哈希索引能够进行更高效的点查找,因此某些查询可能会看到额外的性能提升。此外,将表迁移到内存中OLTP消除了争用问题,并允许通过内存中的索引快速访问数据。内存中OLTP也不需要获取和释放锁。有了这些功能,应用程序可以获得高得多的CPU总利用率。这可以增加吞吐量和事务速率,而CPU周期用于线程队列管理,需要更多的上下文切换。
事实上,很多目前试图解决这些瓶颈问题的解决方法都相当具有挑战性。它们可能包括更改应用程序,或在某些情况下,需要大量重新设计应用程序。在许多情况下,这些模式将从迁移到内存中OLTP中受益良多。
---------------------------我是分割线-------------------------------
内存中 OLTP - 常见的工作负荷模式和迁移注意事项(一)
内存中 OLTP - 常见的工作负荷模式和迁移注意事项(二)