内存中 OLTP

9/1/2015来源:SQL技巧人气:1229

内存中 OLTP - 常见的工作负荷模式和迁移注意事项(二)

----------------------------我是分割线-------------------------------

本文翻译自微软白皮书《In-Memory OLTP – Common Workload Patterns and Migration Considerations》:http://technet.microsoft.com/en-us/library/dn673538.aspx

译者水平有限,如有翻译不当之处,欢迎指正。

----------------------------我是分割线-------------------------------

内存中OLTP常见的实施场景

以下章节讨论了一些常见的经历了瓶颈的应用程序实施和前面所述的场景。所有这些场景都从实施内存中OLTP中受益良多。

以下汇总表提供了每个场景的模式特点,挑战和内存中OLTP带来的主要收益。这将帮助你了解符合你的需求的特定的实施。请注意,某些场景下并不互相排斥,并且一些实施的细节可适用于多种场景。

实施方案

模式特点和挑战

内存中OLTP的主要收益

高速率的数据插入

•主要只追加的方式存储

•消除争用

•写入的工作负荷下无法插入

•尽可能减少日志记录的I/O

读取性能和扩展

•高性能的读取操作

•消除争用

•无法满足纵向扩展的需求

•高效的数据检索

•尽可能减少代码执行时间

•为了扩展的CPU效率

繁重的数据处理计算

•插入/更新/删除的工作负荷

•消除争用

数据库内繁重的计算

•尽可能减少代码执行时间

•读取和写入争用

•高效的数据处理

低延迟

•要求典型的数据库解决方案无法实现的低延迟的业务事务

•消除争用

•高并发会加剧延迟

•尽可能减少代码执行时间

•高效的数据检索

会话状态管理

•繁重的插入,更新和点查找

•消除争用

•在来自于多个无状态的Web服务器的负荷下进行用户扩展

•高效的数据检索

•可以选择减少或者去除I/O

对于每一个场景,将介绍常见业务和应用程序的挑战和瓶颈。我们还将介绍有助于改善这些工作负荷性能的内存中OLTP架构或者设计的注意事项。客户采用的参考方案将在之后的内容中提供。

高速率的数据插入

一些高性能应用程序已经试图解决的关键挑战之一是极高速率的数据加载或插入。接收高度波动的数据流可能会压垮传统型RDBMS的性能。在这种场景中,主要模式是插入。通常,应用程序尝试每秒将一定数量的行或者业务事务输入到系统中,以满足所需的事务吞吐量。智能计量和遥测系统的数据插入是这个场景的两个例子。

典型瓶颈

在数据库中,瓶颈通常出现在“非常热”(频繁访问)的数据集区域中表里的锁和闩锁。大量线程访问在标准B树中的同一数据页可能会导致最新数据页的争用。为了提高查询性能而增加的索引可能也会增加插入执行的额外开销。在许多情况下,需要在写入吞吐量的优化和为了读取添加索引之间进行权衡。在这种场景下,可能包括I/O事务延迟等在内的其他潜在的瓶颈或者性能阻塞者。

应用内存中OLTP — “冲击减缓器”

在这种场景下,“冲击减缓器”是指为了“减缓”输入速率而以内存优化而创建的表。输入速率对于系统是一种“冲击”。

图1 冲击减缓器

这种情况下关于内存优化表的优化包括:

  • 消除锁和闩锁争用。
  • 如果工作负荷主要由插入组成,这些插入所需的日志记录的数量要小于基于磁盘的对象。这主要是因为系统不会记录内存优化表的索引分配。
  • 通过DURABILITY =SCHEMA_ONLY创建内存优化表来消除I/O。只有在内存优化表中的数据能够丢失(比如,服务故障)的情况下可以考虑。在这种故障的情况下,数据将需要被重新创建或者重新加载到表中。
  • 通过利用延迟的持续性,在潜在的性能收益和丢失一些数据之间权衡。

这些优化能够使系统实现更大的吞吐量。

内存中OLTP设计的注意事项

在许多情况下,这种场景的实施包括创建内存优化表来存储一部分特定的数据集。作为一个选择,你可以通过Transact-SQL将数据移动到基于磁盘的目标表中。其中需要考虑的一个主要的设计点,是需要驻留在内存优化表中数据集的预期用途和需求。

内存优化表可能只存储一个固定时间周期(几分钟,几小时,几天或更多)的记录,而基于磁盘的SQL Server表则存放“稍微更冷一些”的,不经常访问的数据。由于内存中OLTP集成到SQL Server中,在同一个数据库同时拥有内存优化表和基于磁盘的表是相当普遍的。很典型的是,基于磁盘的表拥有与内存优化表相同的架构。表上的插入(或者“追加”)操作是DML主要的工作负荷。

在内存优化表和基于磁盘的表之间迁移数据可以使用解释型Transact-SQL来完成,选择来自内存优化表数据将其插入到基于磁盘的表里,并(很有可能)从内存优化表中删除该数据。这样的数据迁移通常被计划为一个可以及时执行的“后台”或SQL代理进程。

一个典型的数据迁移实施通常需要一个已知的键值或参照值,以便确定用于迁移的数据。通常已被成功实施的例子包括:

  • 日期。
  • 可以归于某种类型 “时间戳”的一个序列值。
  • 一个范围的值。

内存优化表和基于磁盘的表的这种分区是通常被称为“手动分区”。SQL Server中对于基于磁盘的表没有类似于表和索引分区操作这样的特定系统功能。联机丛书中“用于对内存优化表进行分区的应用程序模式”章节提供了这方面的一个例子。

应用程序部署的其他属性将会影响哪些数据被存储在内存优化表中以及多久将数据迁移到基于磁盘的表中。其中,这些注意事项可能包括:

  • 可以被分配给内存优化表的内存数量。输入数据将会持续增长,而分配给SQL Server的内存是有限的。将数据迁移到基于磁盘的表可以释放一些分配的内存。在大多数情况下,由于异步的垃圾收集机制,这不会立即生效。如果应用程序有存储大量数据的需求,它可能不需要将整个数据集都保存在内存优化表中。请注意,基于磁盘的表上的查询可能也需要分配给SQL Server缓冲池中页的内存。
  • 对于数据所需的某些属性,内存优化表并不支持。这可能包括数据加密,特殊的索引类型,或者内存优化表不支持的其他选项。
  • 考虑查询类型和使用内存中OLTP之外的表和索引得到最好解决的某些查询的性能需求。对于点查找查询,内存优化表提供的哈希索引很有效率。然而,工作负荷模式有可能是OLTP和数据仓库类型的工作负荷的混合。这些数据仓库类型的工作负荷可能会受益于像数据仓库的内存中列存储这样的解决方案。当涉及到内存优化表时,无论是这种索引还是并行执行计划都不能被使用。在这些情况下,在一段时间后将这些数据迁移到基于磁盘的表中或许被证明是最佳的解决方案。

作为内存优化表和基于磁盘的表之间的一个抽象层,也可以用视图来访问这两种表。在这种情况下,需要用解释型Transact-SQL访问视图。

另一种可以实施内存中OLTP并需要考虑和快速数据加载相同注意事项的场景,是暂存表,通常用于将初始数据加载到数据仓库中。内存中OLTP优化的目标场景,OLTP工作负荷在这个场景中也可以实现。系统全面支持使用bcp.exe,BULK INSERT或者SSIS包将数据导入到内存优化表。但是可以为快速数据加载实施的TABLOCK提示或其它的锁行为,与内存中OLTP并不兼容,应该删除。

暂存表通常用作加载数据的中间保存区。一旦数据被处理并迁移到它的最终目的地,暂存表中的数据则不再需要,可能能够被丢弃。此外,如果数据在加载过程中丢失,可以被重新创建并重新加载。因此,在许多情况下,可以使用DURABILITY =SCHEMA_ONLY创建内存优化表,以避免产生所有的日志记录和I/O。

也需要考虑加载,删除或截断暂存表。在使用内存优化表的情况下,你需要删除数据,因为不支持截断。从内存优化表删除数据应该是非常快的。但与从基于磁盘的表中删除数据不同,在垃圾收集进程清理数据行之前,行版本将仍将使用内存。了解了这个机制就能够理解这将暂时增加对内存的需求。当删除大量的数据行时,这可能会有不良的影响。因此,另一种办法是删除并重建暂存表。

最后,需要考虑涉及数据迁移的表存放的位置。内存中OLTP引擎不支持与多个数据库进行交互并涉及内存优化表的事务。因此,暂存表的数据源必须来自于外部的客户端连接,比如SSIS,或者来自同个数据库内的一张表。同样,目标表必须与暂存表在同一个数据库中。

在许多情况下,这样的配置已经证明对于大量的插入和工作负荷的“峰值”是成功的。这个配置还提供了一个成功的整体数据层设计的解决方案来处理这些要求苛刻的工作负荷。采用这种模式客户的成功和测试结果也表明,数据加载并将数据迁移回到基于磁盘的表要更快。也就是说,与直接从基于磁盘的表插入和查询相比要有更少的整体延迟。

客户采用方案

微软内部一个名为事件报告的应用程序,被用于提供准实时的信息,包括来自于大约7500台生成数据的机器的性能数据和事件记录。数据流通过一个Web API。在输入速率的高峰期,数据库无法插入所有数据。通过利用“冲击减缓器”场景中的内存中OLTP,事件报告应用程序增加了六倍的数据插入率。每秒事务数从3500增加到23,000以上。这解决了限制应用程序获得所需吞吐量的数据插入峰值的问题。

读取性能和扩展

在一些情况下,工作负荷的性能关键部分集中在读取的执行。很多时候整体数据集的一小块被大量地查询。它可能是只读的或者只有一些不频繁的更新或者修改。总体而言,在这个模式中,有向大量用户提供服务的需求。通常情况下,他们采用一个明确的(通常是非即席型)查询工作负荷来访问大多为读取的数据中的一个小集合。

有几种类型的架构实施场景可以有利于大量的读取操作。第一种方法是为应用程序使用中间层缓存解决方案来服务于快速读取。第二种方法,在很多情况下,是为了性能,横向扩展读取的工作负荷。这种方法包括拥有查询关键数据的多个副本。多个副本能够尽可能减少客户端之间的数据延迟,并可以成为高可用性架构的一部分。通常,这些解决方案通过使用SQL Server AlwaysOn可用性组的可读辅助副本或者SQL Server的事务复制来实现。横向扩展的解决方案通常运行良好,但要承担高昂的采购和运营成本。

这个工作负荷模式和架构普遍存在于,包括大型零售商,社交网络浏览,应用程序,显示排行榜或者最佳推荐引擎的应用程序等场景中。

典型的瓶颈

在这种场景中,有几种类型的瓶颈可能具有挑战性。即使所有数据页都加载到内存中,并且索引也已经优化, Transact-SQL调用的执行仍然可能很慢。在某些情况下,解析和编译查询的开销也会增加额外的执行时间。在大规模下,并发读写场景的闩锁和锁也会延迟查询执行。

硬件瓶颈,例如CPU利用率,可能会限制扩展,并且需要使用许多只读服务器来分担用户负荷。

中间层缓存的解决方案在管理多个层时会增加额外的复杂度和开销。在层之间数据迁移的性能开销也会增加将数据放到缓存中的总时间。

应用内存中OLTP

内存中OLTP的内存优化表和本地编译的存储过程可以做为一种“缓存”解决方案进行实施。需要对数据的小子集进行极其快速且只读访问的应用程序可以使用这种“缓存”。特别是,本地编译的存储过程可以减少目标查询的执行时间,从而提供了更快的总体查询执行时间。如果工作负荷包含单个数据行的查找,使用非聚集哈希索引将提供额外的性能提升。

对于需要更好读取性能的已有解决方案实施内存中OLTP,通常对于应用程序或数据库代码只需要极少的修改。将数据迁移到内存中OLTP可以尽可能减少与高速缓存管理和分配元数据检索相关的延迟。这将提供更快的数据传输。然而,这种情况的主要收益,通常可以通过将访问这些数据集的数据库代码迁移到本地编译的存储过程来实现。这将通过消除解析,优化,编译,执行计划缓存等方面尽可能减少执行时间。这并不会影响固有延迟的某些其他方面,例如通信协议,网络开销,登录时间等等。

内存中OLTP还提供了对AlwaysOn可用性组和充当事务复制订阅的内存优化表的集成支持。这两种场景能够很好地集成到横向扩展的读取解决方案中。它们可以为可读副本/订阅提供进一步的可扩展性和性能提升。在这些情况下,内存中OLTP的集成也潜在地为服务器整合提供了一种通过提高CPU效率或者只读扩展来实现的机制。这一改进使每台只读服务器能够处理比之前更多的负荷。

图2 读取缓存

内存中OLTP设计的注意事项

在一些情况下,内存优化表可以是读取和写入的主表。如果是这样,主要的收益是在锁和闩锁方面的可伸缩性以及来自于内存优化表潜在的性能收益。由于该表保存了数据的唯一副本,你通常要将内存优化表创建为持久(SCHEMA_AND_DATA)表。这为读取提供了与高速缓存类似的性能,却拥有持续性和“关系型”的特性。

其他情况通常会利用一个中间层的高速缓存。在这种情况下,数据可能驻留在后端的一台数据库服务器中,而数据的一个子集按照一定的时间间隔推送来更新中间层的高速缓存。在这种情况下,应用内存中OLTP也有一些注意事项。首先,在许多情况下,应配置内存优化表为模拟或替换中间层的高速缓存。主要的源表可能拥有持续性的特性(甚至可能不是内存优化表)。然而, “缓存”表可以被创建为SCHEMA_ONLY,如果需要的话,你可以从源中恢复数据集。如果更新不频繁并且I/O没有问题,采用SCHEMA_AND_DATA创建对象在失败时可以通过引擎进行“自动”恢复。

关于使用内存中OLTP替代其他缓存系统的一些更令人信服的理由包括:

  • 内存中OLTP提供了一个熟悉的开发和管理的体验。所有对数据的访问都是通过Transact-SQL。不需要其他的编程接口。
  • 所有可用性和管理功能都包含在单个源中(数据库和SQL Server)。对于一个跨层和应用程序的高可用性或者灾备策略,这需要的开销更少。在这种情况下,对于每个应用程序的实施和管理是唯一的。
  • 创建数据库中的“关系型缓存”需要中间层和服务器数据存储之间更少的网络往返。
  • 这将尽可能减少维护环境所需的服务器数量。用户能够在一个环境内,整合和购买更多内存来供内存优化表使用,也可以将其用于数据层的其他资源。
  • 性能是非常