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

时间:2023-09-27 13:21:02

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

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

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

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

将应用程序迁移到内存中OLTP

内存中OLTP集成到SQL Server中的同时,也具备不同于传统关系型数据库系统特别是SQL Server的一些独特功能。本节的目的是向读者提供一些这样的注意事项。本节并不提供有关迁移的综合指导,许多关于具体需求,支持功能和外围应用的深入讨论已超出了本文的范围。

为迁移评估工作负荷

如先前所讨论的,许多应用程序已经从实施内存中OLTP中受益颇丰。然而,不同的工作负荷可能会获得不同程度的改进。有些应用程序可能只需要极小的更改,而其他可能需要更大量的代码修改,例如将某些Transact-SQL转换成本地编译的存储过程。也有一些场景并不适合于内存中OLTP。因此,关键是要了解工作负荷和环境的特征。这项评估将帮助你确定一个应用程序对于内存中OLTP是否是一个可行的候选。以下章节将提出审核应用程序工作负荷的方法,并提供了有关这些内存中OLTP实施的可行性意见。

迁移方法论

在迁移到内存中OLTP之前,确保你彻底了解应用程序的需求和目标。此外,确定性能瓶颈是否可以由内存中OLTP解决。如下图表明, SQL Server引擎其中某些方面可以为内存中OLTP提供收益。其他与引擎交互的组件有可能不能从迁移到内存中OLTP获得收益。

图6  内存中OLTP的性能收益范围

如图6所示,内存中OLTP位于数据访问层(表和索引对象)和查询执行的引擎组件。如果当前的瓶颈位于这个领域,将这些数据集或者Transact-SQL迁移到内存中OLTP引擎中则可以提高性能。内存中OLTP比起基于磁盘的表产生的日志量也更少,因为不需要为索引分配或者UNDO需求写入日志记录。在任何情况下,到磁盘子系统I/O的日志延迟仍然是事务提交的一部分。比如SCHEMA_ONLY表(非持续)和延迟的持续性这样的功能能够消除或尽可能减少这方面的开销。另外,客户端连接层没有任何增强。有一些方式能够处理这个问题,但内存中OLTP并不能直接处理或解决这一瓶颈。

建立基线

确定基线是了解系统的当前性能和在做出更改后衡量改进或退化的关键。你可以以多种方式来实现这一点。许多工具和方法论在“监视和优化性能”这篇文章中都进行了讨论。很多工具可以用来帮助确定一个基线,但选择一个度量充当基线是非常重要的。基线度量的一些例子有:

  • 系统组件的利用率和性能,比如:磁盘,CPU,内存,网络。
  • SQL Server代码执行时间。
  • 事务吞吐量。
  • 什么系统正在等待,以及等待多长时间。比如sys.dm_os_wait_stats这样的动态管理视图可以帮助确定SQL Server的资源等待。

应用程序的整体性能也应该被视为基线的一部分。确定基线还要考虑以下因素:

  • 衡量业务事务吞吐量。
  • 事务往返时间。
  • 用户体验。
  • 扩展。

使用这些衡量可以帮助定义成功迁移的标准。

瓶颈分析

在某些情况下,数据库引擎之外的因素可能会导致应用程序中的瓶颈。因此,迁移到内存中OLTP可能不会改善这种状况。了解当前的性能瓶颈在整体应用架构是至关重要的。一旦你确定了数据库中的瓶颈,则为迁移重点关注这些特定的组件。

使用内存OLTP工具来分析工作负荷并帮助迁移

内存中OLTP产品提供了一些工具来帮助迁移过程。这些工具都集成到了Management Studio中。我们有时提到的工具集是指分析,迁移和报表(Analyze, Migrate, and Reporting , AMR)工具集。为了帮助瓶颈分析,可以使用新的数据收集器(事务性能收集组)。它们帮助收集性能数据和工作负荷特性。它们还能建议将频繁使用的或者有争用的表和代码迁移到内存中OLTP。

数据收集器比起运行单个查询来为内存中OLTP迁移确定一个很好的候选要更有意义得多。收集器使用管理数据仓库(MDW)对时间段内收集的数据执行数据聚合。收集器能够提供关于迁移到内存中OLTP带来的性能提升的预估。这个信息出现在SQL Server 2014 Management Studio中附带的“事务性能分析”报表中。有关配置和使用这些工具的详细讨论,请参阅网页“迁移到内存中OLTP”,其中有关于这一功能的详细介绍。

如果你不能利用数据收集和报告工具,还有其他方式来帮助理解SQL Server中的争用点。 SQLDiagPSSDiagSQL Nexus是你可以用来确定与闩锁,锁,自旋锁和存储过程的执行次数统计相关的争用问题的工具。 SQL Nexus能够确定等待资源,闩锁,锁和阻塞。如果SQL跟踪作为捕获的一部分运行,SQL Nexus还可以捕获执行次数最多的存储过程和Transact-SQL语句。

如果在一段时间内监视和收集这些信息的开销过大,你有另一个选择。你可以手动执行查询来检测经常访问的表,闩锁争用或者存储过程执行次数的统计。这些查询的执行提供了自从上次服务器重启以来位于内存中的快照值。在某些情况下,捕捉两个时间点的输出并确定它们之间的增量可能更加准确。收集这些信息的查询在稍后的章节中提供。

经常访问的表

这个查询提供了自从SQL Server实例被重置或者性能数据被清除以来数据库中访问最频繁的表的一个列表。在确定进行转换的候选时,可考虑靠前的检索数和扫描数。通过评估以下查询的singleton_lookup_count和range_scan_count,你可以对使用率有一些了解。对于迁移到内存中OLTP,可考虑将频繁访问的表或表中的部分数据迁移到内存优化对象中。

SELECT TOP (5)     b.name AS TableName,
a.database_id,
a.singleton_lookup_count,
a.range_scan_count
FROM sys.dm_db_index_operational_stats(DB_ID(), NULL, NULL, NULL) AS a
INNER JOIN sys.objects b on a.object_id = b.object_id
WHERE b.type <> 'S'
AND
(a.singleton_lookup_count > 0 OR a.range_scan_count > 0)
ORDER BY a.singleton_lookup_count DESC
GO
闩锁争用

通过审核sys.dm_db_index_operational_stats动态管理视图中的page_latch_wait 和 page_lock_wait列来评估闩锁争用。考虑将具有大量闩锁或锁争用的表迁移到内存中OLTP。以下查询将提供自从SQL Server实例被重置或者性能数据被清除以来累计的闩锁和锁等待。

SELECT TOP (5)    a.database_id,
so.object_id,
so.name AS TableName,
a.page_latch_wait_count ,
a.page_latch_wait_in_ms,
a.page_lock_wait_count,
a.page_lock_wait_in_ms
FROM sys.dm_db_index_operational_stats(DB_ID(), NULL, NULL, NULL) a
INNER JOIN sys.objects AS so
ON a.object_id = so.object_id
WHERE so.type = 'U' AND a.page_io_latch_wait_count > 0
ORDER BY a.page_latch_wait_count DESC;
存储过程执行次数的统计

内存中OLTP提供本地编译的存储过程。通过本地编译Transact-SQL成一个DLL文件并尽可能减少在执行时需要被处理的指令,从而极大的改善存储过程的执行时间。如果你无法使用Management Studio中的工具,以下这两个查询将提供类似的信息。然而,这些查询只能检索到这个调用执行时存在于计划缓存中的存储过程和Transact-SQL数据。有可能有更好的候选存储过程存在,但它们已经不在计划缓存中。可考虑在一天或一周中多次运行这些查询,以确定是否存在其他的候选。

以下查询将提供总工作时间最多的存储过程名称。如果最近性能数据已经清零,计划缓存会失效,或者如果SQL Server实例已被重置,则此刻计划缓存可能不具有足够多有价值的信息。

SELECT TOP (10)    sp.database_id,
so.name AS StoredProcName,
sp.total_worker_time,
sp.execution_count,
sp.total_logical_reads,
sp.total_logical_writes,
sp.total_logical_reads
FROM sys.dm_exec_procedure_stats AS sp
INNER JOIN sys.objects AS so
ON (sp.object_id = so.object_id)
WHERE so.type = 'P' AND sp.database_id = DB_ID()
ORDER BY sp.total_worker_time DESC;

以下查询检索出的数据在Management Studio工具中并不可用。但是,它可能有助于确定存储过程中的哪个语句是资源最多的消耗者。在决定哪些存储过程迁移到内存中OLTP时,知道哪些语句最多消耗资源是有价值的。这可能对只将一些高资源消耗的语句而不是整个存储过程迁移到本地编译的存储过程中有益。

SELECT TOP (50)    sp.database_id,
dbname= DB_NAME (qt.dbid),
so.name AS StoredProcName,
sp.total_worker_time,
sp.execution_count AS StoredProcedureExecCount,
qs.execution_count AS StatementExecCount,
SUBSTRING(qt.text,qs.statement_start_offset / 2 + 1,
(CASE WHEN qs.statement_end_offset = -1
THEN LEN(CONVERT(NVARCHAR(MAX), qt.text)) * 2
ELSE qs.statement_end_offset END - qs.statement_start_offset) / 2
) AS query_text
FROM sys.dm_exec_query_stats AS qs
CROSS APPLY
sys.dm_exec_sql_text(qs.sql_handle) AS qt
INNER JOIN sys.dm_exec_procedure_stats AS sp
ON (sp.sql_handle = qs.sql_handle)
INNER JOIN sys.objects so
ON (sp.object_id = so.object_id) and sp.database_id = DB_ID()
ORDER BY sp.total_worker_time DESC, qs.execution_count DESC

大规模下使用有代表性的工作负荷指导测试

定义了目标和瓶颈之后,考虑一个测试标准来模拟瓶颈是很重要的。这个测试也将决定你是否能达到目的。可能有一些场景,其中的一个“单元测试”或工作负荷的特定部分的单次执行,将有助于确认目标。例如,值得关注的可能是在隔离下跨单个线程的存储过程调用的延迟。然而,在许多情况下,只有在负荷下,瓶颈或者性能降低才会暴露出来,尤其是对于大规模下的争用。

如果你只在大规模的场景下观察到瓶颈,在产生争用之前,使用与不使用内存中OLTP执行的测试可能会显示相似的性能。一旦达到引发争用的规模,从这时开始,你会观察到使用内存中OLTP有显著的改善。在许多情况下,在传统的数据库系统中,吞吐量将达到一个平衡或者执行时间将增加。这时,从性能的角度上看,系统上额外的负荷或者规模很可能会造成性能不再提高甚至产生负面的影响。内存中OLTP中这一瓶颈的缓解有助于极好的维持和提高应用程序的整体性能,并跨越这个观察到的瓶颈。

图7展示了转换到内存中OLTP的AdventureWorks示例数据库的利用。图像显示了随着一个工作负荷一直运行到大规模下出现争用时引擎的性能提升。你只能通过模拟达到一个典型的RDBMS中规模影响性能的点的测试来认识这些好处。图中的圆圈表示在这个点,典型RDBMS执行明显需要花费更多的时间来执行同样数量的事务数。在这一点后,内存中OLTP工作负荷继续几乎呈线性的扩展,而基于磁盘的工作负荷则达到大规模的一个障碍。

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

图7从AdventureWorks示例数据库中执行DemoInsertSalesOrders的完成时间/线程数

最后,需要考虑的是,当你将工作负荷迁移到内存中OLTP,工作负荷的一些特征有可能改变。理想的情况是基于一个稳定的,明确的工作测量来衡量整体应用程序的性能。图7显示了基于“N”的理想值,N表示用户的线程数(针对时间的测量)。比起其范围在每次执行时可能发生变化的某个值,比如业务事务或者并发用户数这样的测量更容易在不同的测试间关联起来。

有目标的,迭代的迁移方法

我们建议你通过先着重于工作负荷中的特定领域来开始迁移,这部分领域表现出的瓶颈应该是内存中OLTP可以解决的。通常情况下,你可能只需将数据和对象的一个子集迁移到内存中OLTP就能实现显著的收益。

关键表到内存优化结构和Transact-SQL代码到编译代码的一些迁移都非常简单,只需要极少的变更。其他情况由于外围应用支持或者部署这些新数据库对象的能力,可能需要复杂的变更。

集成到SQL Server 2014 Management Studio中的两个帮助迁移对象的工具是内存优化顾问和本地编译顾问。这两种工具可以帮助你评估迁移的难度。它们评估表架构和存储过程的语法来寻找可能的迁移阻碍,比如不支持的数据类型。然后这些工具可以提供解决这些迁移阻碍的方法的信息。如果内存优化顾问未检测到迁移的问题,它的向导可以帮助创建内存优化表和数据迁移。

由于内存中OLTP集成到SQL Server中,它可以让你一起使用内存优化表和基于磁盘的表。内存中OLTP还允许解释型Transact-SQL和本地编译的存储过程来访问存储在内存优化表中的数据。如果你最终一定要迁移特定的组件,请使用以下增量迁移的策略:

  • 确定限制可扩展性的争用和瓶颈表。
  • 解决不支持的功能,并将关键数据迁移到内存优化表。
  • 执行使用解释型Transact-SQL访问内存优化表所需的最少的代码变更。

这些步骤应该能够减缓与锁和闩锁相关的大部分可扩展性的问题。如果你需要额外的性能提升,特别是与Transact-SQL执行时间相关,那么需要实施将Transact-SQL迁移到本地编译的存储过程。

  • 确定访问这些表及其相关对象的对于性能至关重要的Transact-SQL。
  • 解决不支持的语言结构并将这些存储过程迁移为本地编译的代码。

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

图8 迁移方法论

当你将特定的瓶颈领域迁移到内存中OLTP,系统的其他部分可能会成为新的瓶颈。当解决一个特定的瓶颈时,考虑采用迭代的方法来迁移,然后分析解决方案的下一个性能瓶颈可能存在的位置。

实施中需要进一步注意的事项

本节不提供内存中OLTP技术的综合列表。本节提供采用这项新技术功能的一些关键注意事项的指导。我们强调了一些技术或决策点,与传统的SQL Server实施相比,这些技术或决策点是全新的或者巨大的变革。

硬件或系统影响的注意事项

性能敏感的工作负荷依赖部署中的所有组件,包括软件和硬件。在下面的章节中,我们讨论了考虑硬件选择的一些关键因素,以及在部署内存中OLTP时,实施的一些注意事项。基于允许应用程序使用现今市场上可用的已有和商用硬件的概念,微软开发了内存中OLTP。内存中OLTP并不需要特别多的插槽/内核或者其他硬件组件。内存中OLTP是以 SQL Server中可与标准的商用硬件交互的优化作为目标。

内存

内存优化表完全驻留在内存中,并且不会对于施加于SQL Server或者SQL Server内部的内存压力进行响应。基于磁盘的表有缓冲池页,这些页可以被刷新到磁盘中并且内存池可以缩小。然而,内存优化表中的行不会被刷新到磁盘,并且已分配的表将驻留在内存中直到垃圾被收集。因此,你必须分配足够的内存给SQL Server以容纳分配给内存优化表的整个数据集。这将会限制可用于其它内存池,比如缓冲池,计划缓存,锁管理器等等的内存数量。

当为迁移评估数据和对象时,确定所需要的内存大小是至关重要的。你可以使用以下标准进行基本的计算:

  • 行数(对增长量进行评估)。
  • 数据的大小(数据类型)。
  • 保存在每行中的标题信息,包括开始和结束时间戳和索引指针。
  • 索引分配的空间。

但是,最关键和可变的注意事项之一是对于工作负荷的理解。了解在垃圾收集之前有多少行版本驻留在内存中是很重要的,因为这将需要额外的内存分配。行版本必须驻留在内存中,直到所有使用更老行版本的事务完成。此外,这些行的垃圾收集对于实际的进程来说是异步的。例如,在执行删除操作后,可能需要一段时间才会从内存中删除行版本。当然工作负荷中执行的事务和查询的类型会有不同。然而在OLTP工作负荷中,对于相当繁重的更新操作,或者遍历相同值的读/写调用,可以考虑2倍左右的值。这意味着表中存在的每一行有两个行版本。请注意,在内存压力下,会加快这个垃圾收集进程。关于确定内存优化表大小的更多信息,请参见“内存优化表中的表和行大小”。

如果你已经迁移了表,也有办法来查看内存优化表和索引占用的内存。使用动态管理视图sys.dm_db_xtp_table_memory_stats,或使用性能监视器SQL Server数据库计数器:XTP Memory Used (kb)。

总体而言,考虑如何为内存池利用资源调控器,对于为数据库中的内存优化表分配内存来说是有帮助的。有关的详细讨论,请参阅联机丛书中的“资源调控器”一节。

最后,考虑与内存中持久表相关的数据集的大小是很重要的。数据量将会影响恢复时间。此外,在磁盘上的存储与内存中的数据之间存在一些关系,磁盘上的存储确实有一些大小限制。对于数据库中的所有持久表的总大小建议的上限是250 GB。在内存中需要250 GB空间的持久表,平均来说,在内存优化文件组中需要两倍(500 GB)的存储空间。这个估计假设了插入,删除和更新的混合操作。这将会分配给这个文件组约4000个数据/增量文件对。

数据库中活动的爆发可能会导致检查点操作落后一段时间。这种滞后会增加所需文件的数量。作为这种爆发的一个缓冲,存储系统最多可支持8000个数据/增量文件对。当系统达到了极限,它会阻止数据库中的新事务,直到检查点操作赶上为止。这个缓冲确实提供了用于管理大于250GB的持久表的可能性。然而,我们并不推荐长时间在超过250 GB的情况下运行。缓冲只应存在于处理活动的爆发,以及检查点的延迟。超过推荐值会导致数据库中内存优化表上事务活动减缓的风险增加。

存储/磁盘子系统

对于SCHEMA_AND_DATA创建的内存优化表,对于磁盘布局和文件布局的持久性的注意事项是很重要的。存储的需求也与基于磁盘的SQL Server表有很大的不同。

内存优化表对数据库事务日志有I/ O执行。因此,I/ O延迟在整体事务执行时间中仍然是一个要素。对于日志的驱动器,考虑磁盘子系统的延迟特性是非常重要的。

数据和增量文件 - 检查点文件对(CFPs)

系统在数据和增量文件中存储持久表的数据,这也被称为检查点文件对(CFPs)。系统使用后台线程以只能附加的方式存储这些数据。数据文件包含插入的记录,而增量文件存储删除的记录。以这种方式存储数据消除了随机的I/ O。这些文件利用了基于FILESTREAM的存储机制。SQL Server数据库内存优化文件组中的文件存储与基于磁盘的表的标准存储有很大的不同。在磁盘上文件的总大小可能会比内存中对象所使用的内存的数量大得多。当创建内存优化表时,系统预先分配检查点文件对,以尽可能减少任何在事务执行时分配新文件的延迟。数据文件的大小为128 MB(或者在少于16 GB内存的服务器中为16 MB)。增量文件的大小对应为8MB和1 MB,但它们不包含任何数据。检查点文件对的数量依据逻辑处理器或调度的数量进行计算,最少为8个。这是拥有内存优化表的数据库中的固定存储开销。检查点文件对使用的整体存储空间由工作负荷,检查点和日志记录的截断特性的工作负荷所决定。空间的大小变化很大,然而,一个可供参考的初始点可以考虑分配的磁盘空间大约是内存中内存优化表大小的4倍。强调在完全或大容量日志恢复模型下,有一个满足业务恢复需求并解决内存优化表存储行为的日志备份计划的重要性是非常重要的。备份计划将有助于确定日志截断并影响垃圾收集,因此在磁盘上维持检查点文件的一个可接受的数量和大小是至关重要的。有关数据和增量文件和检查点的详细信息,请参阅“内存优化表的持久性”和“内存优化表的检查点操作”。

实施持久的内存优化表时,也有需要处理的性能方面的注意事项。数据文件的位置对于以下的方面是非常重要的:

  1. 离线检查点的性能。
  2. 执行检查点文件容器的合并。
  3. 在服务故障情况下的恢复。这将通过流I/O加载容器,将文件加载到内存中,并重新创建该表。

在稳定状态情况下检查点的写入,合并和文件清理操作,对于以下操作将会产生I/O:

  • 检查点的写入。
  • 对于参与合并操作的源文件的读取操作。
  • 对目标合并文件的写入操作。

拥有一个可以处理这种I/O量的磁盘子系统是非常重要的。对于恢复的性能,数据和增量容器的位置是很重要的。考虑在内存优化的文件组中创建多个容器,并将它们分布在不同的驱动器上。这样做可以为数据加载进内存提供更大的带宽。当你这么做时,需要了解系统是按照循环跨过特定文件的方式分配数据和增量文件。系统将其创建的第一个文件关联为一个数据文件,并将其创建的第二个文件关联为一个增量文件,以此类推。为了恢复时获得均衡的I/O流,可以考虑把文件对放在同一主轴/存储上。例如,文件1和文件2放在驱动器X上,文件3和文件4放在驱动器Y上。可以考虑一下数据和增量文件对都驻留在驱动器X和驱动器Y的情况。

处理器

考虑处理器的部署是至关重要的。特别是应该考虑一个内存中OLTP解决方案的内核和插槽的数量。我们为当前版本收集了来自客户和内部测试的观察结果。我们已经观察到在一些较多插中跨插槽通信的开销可能会影响可伸缩性。你应该将两个或四个插槽并且内核为60个以内的机器作为目标,从而达到最成功的实施。

多版本乐观并发的注意事项

内存优化表不需要忍受锁,闩锁或自旋锁。其原因是内存中OLTP的内核引擎采用无锁的乐观隔离,以及无闩锁和无自旋锁的内部结构的保护。将经历闩锁,锁或者自旋锁争用的表迁移到内存优化表中可以完全消除这个扩展的瓶颈。

然而,并发控制的这种新模式导致了行为的变化,并可能需要调整Transact-SQL错误处理,比如try-catch块,或者应用程序代码,以便适应冲突。使用默认的悲观隔离,试图修改同一资源的两个事务将会相互阻塞。一个事务将会被序列化,因为这个事务需要等待另一个事务释放锁。使用乐观的隔离,事务则不会相互阻塞,但由于更新或验证冲突,提交可能会失败。在这种情况下,系统必须重新提交该事务。重新提交实际上序列化了事务的顺序,并会招致回滚和重新提交的额外开销。在大多数OLTP环境中,写入和写入的冲突并不常见。有关冲突的重试逻辑的进一步讨论,请参阅“内存优化表事务重试逻辑准则”。

内存中OLTP只支持REPEATABLE READ,SERIALIZABLE和SNAPSHOT的隔离模式。许多应用程序都设计成在传统的SQL Server默认的READ COMMITTED隔离级别下工作。使用新的“MEMORY_OPTIMIZED_ELEVATE_TO_SNAPSHOT”数据库选项将所有使用READ COMMITTED的事务映射到快照隔离。使用此选项不需要修改应用程序的代码。

有关详细信息,请参阅联机丛书中一节“内存优化表事务隔离级别准则” 和“SQL Server 2014内存中OLTP内部机制概述”

索引准则

内存中OLTP有两个新的索引类型:内存优化的非聚集索引和非聚集哈希索引。如前面提到的,两个索引都完全创建并存在于内存中。

内存优化的非聚集索引在概念上与标准的B-tree索引类似,但他们是一种无锁和闩锁的方式实施。内存优化的非聚集索引支持点/单行以及范围查找的查询。由于支持排序扫描,它们是非相等的范围搜索的首选索引。它们对于需要结果集排序、分组和聚集的查询也是非常有用的。非聚集索引也支持部分键查找。非聚集索引类似于基于磁盘的表所使用的索引。相较于哈希索引,非聚集索引的创建相对需要更少的需求。因此,我们建议在创建内存优化表时,使用这些索引作为出发点。

内存优化的哈希索引对于点查找类型的查询是非常有效的,比如检索或者基于键值修改一行或一个行集合。对于哈希索引,行指针被存储在一个哈希表中。每个指针指向行的一个链,链中的行的索引键值具有相同的哈希值。在创建一个哈希索引时,必须指定一个哈希桶数。这个值将被用于构建哈希表。指定太少的哈希桶将导致多个键值共享一个哈希桶,这可能导致冗余数据的扫描。扫描冗余数据会降低性能。指定太多的哈希桶将导致内存的过度分配,因为许多哈希桶将是空的。一般情况下,创建索引列唯一键值的最多5倍数量的哈希桶不会显著地影响性能。请记住,是通过所有键值来计算出哈希值。因此,键值的一个子集或者不包含键值的过滤不能使用索引。这将会导致所有桶的扫描。

你可以在同一组列上创建非聚集哈希索引和非聚集索引,以满足不同的查询方式。这两种索引类型最适用于低密度的数据分布。对于哈希索引,哈希冲突会对性能产生负面的影响。对于非聚集的内存优化索引,高密度的数值会使索引查找的效率更低,但仍保留了排序扫描的好处。

在创建表时确定哈希桶的数量,之后你就不能再修改哈希桶的数量。想要改变一个哈希索引的哈希桶数,你必须创建一个新表,指定新的哈希桶数并决定如何迁移数据。这是一个不常见的过程,这将影响数据的可用性。

查询执行

内存优化表支持类似于基于磁盘的表中的数据分布统计信息。查询优化器使用这些统计信息来为涉及内存优化表的查询生成一个最佳的执行计划。根据正在执行的代码类型,统计信息在不同时期被引用。对于解释型的Transact-SQL,在编译的过程中优化查询计划和使用统计信息。对于语句的每次执行,统计信息可被用于优化一个执行计划。对于本地编译的存储过程,统计信息只在创建时,存储过程编译的时候使用一次。之后的统计更新只有在数据库脱机或服务重新启动时才会再次被使用。

在计划迁移到内存OLTP时,需要考虑统计信息创建和使用的以下几个方面:

  • 内存优化表不支持统计信息收集采样,并要求进行全表扫描。全表扫描对于拥有非常大量数据行的表会有显著的影响。默认的数据库设置会启用自动创建统计信息的选项。有了这个设定,执行查询时,缺少的统计信息可能会被自动创建以助于产生一个最优计划。对于需要全表扫描的内存优化表,这将导致查询更长的编译时间。对于解释型的T-SQL,这个影响将在执行该语句时出现。对于本地编译的存储过程,这会影响创建存储过程所需的时间。但是,它不会影响本地编译的存储过程的执行时间。
  • 对于本地编译的存储过程,统计信息只有在创建存储过程的时候才被使用。这就意味着,如果在表中填充了真实数据之前就创建了存储过程,执行计划可能不是最优的。查询优化器使用关于数据分布的错误假设进行工作。因此,我们建议只有在表完全被最真实的代表性数据填充和执行了统计信息更新之后,再创建存储过程。
  • 与基于磁盘的表不同,内存优化表上的统计信息不会被自动更新。统计更新将会执行数据的完整采样。需要注意执行统计信息更新的时机,并考虑对工作负荷的影响。从被插入或修改的行数量的角度,考虑正在执行中的工作负荷的类型。并考虑统计信息可能的陈旧程度,以及查询对数据分布变化的敏感程度。如果你不更新统计信息或者更新地非常不频繁,查询优化器可能会使用错误的分布统计信息,并生成不是最优的方案。另一方面,更新过于频繁则会增加不必要的开销。

正如前面提到的,需要考虑访问内存优化表的查询无法利用并行计划这一事实。所有包含内存优化表的查询将只使用串行计划。例如,使用B树或列存储索引的基于磁盘的表间的连接可以极大的受益于并行计划。将连接中的任何表迁移到内存中OLTP会阻止查询优化器使用这种优化。对于特定查询有必要使用并行计划的情况,可以考虑将服务于这些查询的数据集保留在基于磁盘的表中。这个解决方案类似于在“冲击减缓器”场景中所讨论的概念。

不太适合迁移的场景

在之前评估内存中OLTP工作负荷可行性的章节中,我们强调了引擎中内存中OLTP改进的特定组件。对于数据访问和过程执行组件有些增强。在这个版本中,没有对于网络和连接组件的改进。以下章节讨论可能阻碍SQL Server2014中内存中OLTP成功的场景,因此应该考虑通过应用程序来寻求利用这个技术所提供的性能提升优势。

缺乏更改代码或架构的能力

至少,用户必须将表创建为内存优化才能迁移到内存中OLTP引擎。有些场景中的架构不能被修改。在其它情况下,修改代码以支持内存优化表或者本地编译的存储过程的编程外围应用的代价是令人望而却步的。一些应用程序使用了能够支持登记对多个活动结果集(Multiple Active Results Sets ,MARS)的调用或者使用分布式事务的框架(比如实体框架)。这些应用程序可能需要修改代码来处理事务登记和连接字符串的设置。最后,与内存中OLTP支持的隔离级别不兼容的锁提示或者隔离级别也可能需要对应用程序进行一些修改。

内存限制

正如前面提到的,组成内存优化表的数据结构都保存在内存中,并且与传统B树对象不同, 不需要持久的存储支持。没有足够内存供存储内存优化行使用的场景可能会出现问题。在评估迁移时,确定所需内存的大小。考虑可能会产生多个行版本的工作负荷也同样重要,这需要分配额外的内存。

工作负载模式不是OLTP

内存中OLTP是针对OLTP工作负荷进行优化的。长时间运行的涉及很多表或者执行大数据量聚合的事务、查询和报表更容易引起多个行版本的驻留。这些工作负荷更容易对内存造成压力,直至与可能无法满足这些事务、查询和报表。同样,基于磁盘的表和索引对于非常大的数据集更好,并可以为这些耗时更久的查询执行提供更好的收益。替代的解决方案,比如非聚集索引或者可更新的聚集列存储索引可能更适合于这部分的工作负荷。非常动态的工作负荷和架构可能不是一个最佳的搭配,因为更改表和索引的定义将涉及到重新创建内存中OLTP的对象。

比如那些受益于全文搜索或XML解析的工作负荷类型,可以采用专门为使用这些数据结构和架构创建的特定索引和数据结构可以更好地提供服务。

依赖锁行为的应用程序

内存中OLTP的引擎中没有锁。在许多情况下,多版本乐观并发实施可以提供显著的规模收益。然而,在一些应用程序上有对锁结构的依赖以限制对数据的访问。除了应用程序的锁之外,内存中OLTP并不提供像标准SQL Server那样锁定记录的能力。如果应用程序的逻辑依赖于记录的物理锁(例如, 采用READPAST跳过锁定提示所实施的队列),这可能不是一个好的迁移候选。

与采用锁来防止对数据并发访问不同,内存优化表使用冲突检测来实现数据修改的隔离。写入操作繁重和修改单行的工作负荷可能会遇到许多这样的冲突。在某些情况下,尽可能减少这些冲突是可能的。然而,有时也有一些大量的冲突是不可避免的。在这种情况下,处理冲突和重试的开销可能会消除来自于内存中OLTP的大部分性能收益。一个有大量并发用户试图更新堆栈中第一行的“队列”模式,就是这种情况的一个例子。在争用的工作负荷下,这会遭受大量的冲突。

结论

有许多场景需要传统的关系型数据库管理系统无法提供的高性能系统。内存中OLTP可以帮助工作负荷达到并超过其所需的性能指标,同时整合了SQL Server2014的其他常见功能。

应用程序中当前瓶颈和数据库争用的理解是确定如何最好地利用内存中OLTP组件来实现应用程序整体性能目标的关键。 SQL Server提供了一套工具,包括数据收集组和报表,来帮助评估进行迁移的候选。 SQL Server还提供了顾问工具来帮助进行实际的迁移。在将表、数据和Transact-SQL代码迁移到内存中OLTP时,将应用程序的性能关键数据库部分作为目标。以迭代的方式执行迁移可以使得整体应用程序更少中断。

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

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

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

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