内存优化表简介

适用于:SQL ServerAzure SQL 数据库Azure SQL 托管实例

内存优化表是使用 CREATE TABLE (Transact-SQL) 创建的。

默认情况下,内存优化表具有完全持久性。与(传统)基于磁盘的表上的事务一样,内存优化表上的事务具有完全原子性、一致性、隔离性和持久性 (ACID)。 内存优化表和本机编译的存储过程仅支持一部分 Transact-SQL 功能。

从 SQL Server 2016 开始以及在 Azure SQL 数据库中,对于 In-Memory OLTP 特有的 排序规则或代码页 不再有任何限制。

内存优化表的主要存储位置是主内存。 表中的行会从内存中读取并写入内存。 表数据的另一个副本维护在磁盘上,但仅用于持续性目的。 有关持久表的详细信息,请参阅 创建和管理用于内存优化的对象的存储 。 内存优化表中的数据仅在数据库恢复期间从磁盘读取(例如,在服务器重启后)。

为了获得更大的性能提升,内存中 OLTP 支持事务持续性延迟的持久表。 延迟的持久事务在提交事务并将控制权归还客户端后不久即保存到磁盘。 为了换取性能的提高,未保存到磁盘的已提交事务会在服务器崩溃或故障转移时丢失。

除了默认持久内存优化表外,SQL Server 还支持未记录的非持久内存优化表,并且其数据不会保留在磁盘上。 这意味着,这些表上的事务不需要任何磁盘 IO,但如果发生服务器故障或故障转移,则数据会丢失。

内存中 OLTP 与 SQL Server 集成,以便在所有方面(如开发、部署、可管理性和可支持性)提供无缝体验。 数据库可包含内存中对象以及基于磁盘的对象。

内存优化表中的行是版本化的。 这意味着表中的每行都可能有多个版本。 所有行版本均维护在同一个表数据结构中。 行版本控制用于实现对同一行的并发读取和写入。 有关对同一行的并发读写的详细信息,请参阅 使用内存优化表的事务

下图展示了多版本机制。 该图显示了一个包含三行的表,其中,每行都有不同的版本。

多版本控制。

该表有三行:r1、r2 和 r3。 r1 有三个版本,r2 有两个版本,r3 有四个版本。 同一行的不同版本不一定占用连续内存位置。 不同的行版本可分散到整个表数据结构中。

可将内存优化的表数据结构视为一个行版本集合。 基于磁盘的表中的行以页和区形式组织,各个行借助页码和页偏移量进行寻址,而内存优化表中的行版本则借助 8 字节的内存指针进行寻址。

可通过以下两种方式访问内存优化表中的数据:

  • 通过本机编译型存储过程。

  • 通过本机编译的存储过程之外的解释型 Transact-SQL。 这些 语句可位于解释型存储过程内,也可以是临时 Transact-SQL 语句。

访问内存优化表中的数据

可以最高效地从本机编译的存储过程访问内存优化表(本机编译的存储过程)。 还可以使用(传统的)解释型 Transact-SQL 访问内存优化表。 解释型 Transact-SQL 是指在不使用本机编译的存储过程的情况下访问内存优化表。 解释型 Transact-SQL 访问的一些示例包括从 DML 触发器、即席 Transact-SQL 批处理、视图和表值函数访问内存优化表。

下表总结了对各种对象的本机和解释型 Transact-SQL 访问。

功能 使用本机编译存储过程进行访问 解释型 Transact-SQL 访问 CLR 访问
内存优化表 1
内存优化表类型
本地编译的存储过程 现已支持本机编译的存储过程嵌套。 只要所引用的存储过程也是本机编译的,就可以在存储过程中使用 EXECUTE 语法。 否*

1无法从上下文连接(执行 CLR 模块时从 SQL Server 的连接)访问内存优化表或本机编译的存储过程。 但是,您可以创建并打开另一个连接,通过该连接访问内存优化表和本机编译存储过程。

内存优化表中的敏感数据可以使用 Always Encrypted 进行保护。 以下限制适用:

  • Always Encrypted 与安全 enclave 配合使用时,不支持对内存优化表中的列使用已启用 enclave 的密钥。 这意味着不能使用就地加密,初始加密是在客户端上完成的。
  • 内存优化表在本机编译模块中被引用时,表中任何列均不支持 Always Encrypted。

性能和可伸缩性

以下因素会影响可以使用 In-Memory OLTP 实现的性能提升:

通信: 与在每个存储过程中实现更多功能且调用次数较少的应用程序相比,使用许多短暂存储过程调用的应用程序可能会看到较小的性能提升。

Transact-SQL 执行:在使用本机编译的存储过程而不是解释的存储过程或查询执行时,内存中 OLTP 实现最佳性能。 从此类存储过程访问内存优化表可能有好处。

范围扫描与点查找:内存优化的非聚集索引支持范围扫描和有序扫描。 对于点查找,内存优化的哈希索引具有比内存优化的非聚集索引更好的性能。 内存优化的非聚集索引具有比基于磁盘的索引更好的性能。

  • 从 SQL Server 2016 开始,内存优化表的查询计划可以并行方式扫描表。 这提高了分析查询的性能。
    • 在 SQL Server 2016 中,哈希索引也变为可以并行方式进行扫描。
    • 在 SQL Server 2016 中,非聚集索引也变为可以并行方式进行扫描。

索引作: 索引作不会记录,它们仅存在于内存中。

并发: 对于性能受引擎级并发问题(例如闩锁争用或阻塞)影响的应用程序,迁移到内存中 OLTP 后,其性能会显著提升。

下表列出了关系数据库中常见的性能和可伸缩性问题以及内存中 OLTP 提高性能的方式。

问题 内存中OLTP影响
性能

高资源(CPU、I/O、网络或内存)使用率。
CPU
与解释的存储过程相比,本机编译的存储过程可以显著降低 CPU 使用率,因为它们需要更少的指令来执行 Transact-SQL 语句。

In-Memory OLTP 可以帮助减少横向扩展工作负荷的硬件投资,因为一台服务器可能会提供多个服务器的吞吐量。

输入/输出 (I/O)
如果在处理数据或索引页方面遇到 I/O 瓶颈,则内存中 OLTP 可缓解瓶颈现象。 此外,内存中 OLTP 对象的检查点编号是连续的,不会导致 I/O 操作突然增多。 但是,如果性能关键表的工作集无法适配内存,In-Memory OLTP 不适用,因为它需要数据驻留在内存中。 如果在日志记录方面遇到 I/O 瓶颈,则内存中 OLTP 可缓解瓶颈现象,因为它进行的日志记录操作较少。 如果将一个或多个内存优化的表配置为非持久的表,您可以消除数据的日志记录。

内存
In-Memory OLTP 不提供任何性能优势。 内存中 OLTP 可能会对内存产生额外的压力,因为这些对象需要驻留在内存中。

网络
In-Memory OLTP 不提供任何性能优势。 数据需要从数据层传输到应用层。
伸缩性

SQL Server 应用程序中的大多数扩展性问题,都是由并发问题引起的,例如锁、闩锁和自旋锁中的争用。
闩锁争用
一个典型场景是:在按键值顺序并发插入行时,会发生对索引最后一页的争用。 由于内存中 OLTP 在访问数据时不采用闩锁,因此可完全消除与闩锁争用相关的可伸缩性问题。

自旋锁争用
由于内存中 OLTP 在访问数据时不采用闩锁,因此可完全消除与旋转锁争用相关的可伸缩性问题。

与锁相关的争用
如果数据库应用程序遇到读操作与写操作之间的阻塞问题,则内存中 OLTP 可消除这些阻塞问题,因为它使用新的乐观并发控制形式来实现所有事务隔离级别。 In-Memory OLTP 不使用 TempDB 来存储行版本。

如果伸缩问题是由两个写操作之间的冲突(例如,两个并发事务尝试更新相同的行)引起的,则内存中 OLTP 会让其中一个事务成功,而让另一个事务失败。 必须显式或隐式重新提交失败的事务,从而重试该事务。 在任一情况下,您都需要对应用程序进行更改。

如果应用程序中两个写操作频繁发生冲突,那么乐观锁的价值就会降低。 应用程序不适合 In-Memory OLTP。 大多数 OLTP 应用程序不会有写冲突,除非冲突是由于锁升级导致的。

内存优化表中的行级安全性

行级安全在内存优化表中受支持。 向内存优化表应用行级安全策略实际上与基于磁盘的表一样,只是作为安全谓词使用的内联表值函数必须是本机编译函数(使用 WITH NATIVE_COMPILATION 选项创建)。 有关详细信息,请参阅 行级安全性 主题中的 跨功能兼容性

内存优化表提供了各种内置安全功能,这些功能对行级安全性至关重要。 有关详情,请参阅 本机编译的模块中的内置函数

EXECUTE AS CALLER - 所有本机模块现在默认都支持并使用 EXECUTE AS CALLER,即使未指定提示也是如此。 这是因为,预期所有行级安全谓词函数都使用 EXECUTE AS CALLER,以便该函数以及其中使用的任何内置函数都在调用用户的上下文中进行求值。
EXECUTE AS CALLER 因对调用方进行权限检查而产生小幅(约 10%)的性能损耗。 如果模块显式指定 EXECUTE AS OWNER 或 EXECUTE AS SELF,则避免这些权限检查及其关联的性能成本。 但是,由于必要的上下文切换,将任何一个选项与上述内置函数一起使用会对性能产生更大的影响。

场景

有关内存中 OLTP 可提高性能的典型方案的浅谈,请参阅《内存中 OLTP》

另请参阅

内存中OLTP(内存中优化)