SQL Server 2014新功能 -- 内存中OLTP(In-Memory OLTP)
概述
内存中OLTP(项目“Hekaton”)是一个全新的、完全集成到SQL Server的数据库引擎组件。 对OLTP工作负载访问中在内存中的数据进行了优化。内存中OLTP能够帮助OLTP工作负载实现显著的性能改善,并减少处理时间。表能被视为“内存优化”,提升内存中的OLTP功能。内存优化表是完全可事务的、并可以使用Transact-SQL进行访问。Transact-SQL存储过程可以编译成为机器码,从而进一步地提高内存优化表的性能。该引擎是专为高并发而设计的,并且阻塞是最小的。
术语
基于磁盘的表:
传统的表存储方式,具有以下主要属性:
・ 映射到一个文件组,并且该文件组包含一个或多个文件。
・ 每个文件划分为 8 个页区,每页的大小为 8K 字节。
・ 可跨多个表共享一个区,但分配的页和表或索引之间存在一对一映射。 换言之,一个页面不能有来自两个或更多表或索引的行。
・ 根据需要将数据移到内存(缓冲池)中,并且修改后的或新建的页面将以异步方式写入到主要生成随机 IO 的磁盘。
内存优化表:
内存优化表的存储具有以下主要属性:
・ 所有内存优化表都映射到内存优化的文件组。 使用文件流文件组生成此文件组。
・ 不存在任何页面,并且数据以行的形式保留。
・ 对内存优化表进行的所有更改都通过追加到活动文件进行存储。 对文件进行的读取和写入操作是有顺序的。
・ 按照先删除再插入的方式实现更新。 不会立即从存储中移除已删除的行。 依据内存优化表的持久性中所述的策略,已删除的行由称作 MERGE 的后台进程移除。
・ 与基于磁盘的表不同,不对内存优化表的存储进行压缩。 在将压缩的(ROW 或 PAGE)基于磁盘的表迁移到内存优化表时,您将需要考虑大小的更改。
・ 内存优化表可以是持久的或非持久的。 您只需为持久性内存优化表配置存储。
跨容器的事务:
是指同时参考内存优化表和基于磁盘的表的事务。
互操作性:
是指引用内存优化表的解释型的Transact-SQL。
可编译的Transact-SQL:
即传统的解释性Transact-SQL。当使用互操作时,内存优化表将会访问所有的Transact- SQL区域,但您不应该指望取得使用本地编译存储过程相同的性能。当运行即席查询时,互操作是合适的选择,或在应用程序迁移到内存中的OLTP之前使用。这种使用是最佳性能的关键程序迁移过程中的一个步骤。当您需要同时访问内存优化表和基于磁盘的表时,也可以使用可编译的Transact-SQL 。
Transact-SQL使用互操作访问内存优化表时,不支持的功能仅有以下几种:
・ TRUNCATE TABLE;
・ MERGE(内存优化表的目标);
・ 动态和基于键值集的游标(这些都是自动降级到静态游标);
・ 跨数据库中的查询;
・ 跨数据库中的事务;
・ 链接服务器;
・ 锁定提示:TABLOCK、XLOCK、PAGLOCK等(NOLOCK支持);
・ 隔离级别的提示READUNCOMMITTED、READCOMMITTED READCOMMITTEDLOCK;
・ 内存优化的表类型和表中不只是由CTP1支持的变量。
本地编译的存储过程:
是指一种对象类型,这种类型被内存驻留OLTP所支持;内存驻留OLTP被编译为机器代码,并有可能进一步提高性能,甚至比只使用内存优化表更加优化。另一种替代方案是采用基于解释型的Transact-SQL存储过程,这是SQL Server中常用的、采用基础的编译的存储过程只能引用内存优化表。
包含内存中OLTP组件的SQL Server引擎
虽然内存驻留OLTP 与SQL Server关系型引擎集成,并能通过相同的接口访问,但是其内部的行为和性能却大相径庭。
无论调用本地编译的存储过程或者Transact-SQL,客户端应用程序采用与内存优化表或基于磁盘的表同样的方式连接到TDS处理器。您可以看到解释型的Transact-SQL能通过互操作能力访问内存优化表,但本地编译的存储过程只能访问内存优化表。
使用内存中OLTP
内存中OLTP引擎从2013年6月CTP版本开始,已经成为SQL Server 2014的一部分。内存中OLTP SQL Server的安装是SQL Server安装程序的一部分。内存中OLTP组件只能被安装在一个SQL Server 2014 64位版本中,而不与32位版本兼容。
准备:创建示例数据库
CREATE DATABASE imoltp GO
第一步:设置数据库支持内存中OLTP
包含内存优化表的任何数据库至少需要有一个MEMORY_OPTIMIZED_DATA文件组。这些文件组用于存储数据和增量文件,这些文件被SQL Server用来恢复内存优化表。虽然创建MEMORY_OPTIMIZED_DATA文件组与创建常规FILESTREAM文件组的语法几乎是相同的,但是还是必须要指定CONTAINS MEMORY_OPTIMIZED_DATA这一选项。
下面通过两种方式来添加MEMORY_OPTIMIZED_DATA文件组和存放FILESTREAM文件的容器。
使用T-SQL实现:
ALTER DATABASE imoltp ADD FILEGROUP imoltp_mod CONTAINS MEMORY_OPTIMIZED_DATA ALTER DATABASE imoltp ADD FILE (name='imoltp_mod1', filename='E:\SQL-DATA\imoltp_mod1') TO FILEGROUP imoltp_mod GO
使用SSMS实现:
1. 在Object Explorer中, 展开数据库节点, 右键点击数据库,单击属性。
2. 添加一个新的内存优化数据的文件组,点击文件组页面。在MEMORY OPTIMIZED DATA选项下,单击添加文件组并设置文件组的各项值。
3. 在文件组中添加文件,点击general 页面。 在Database files下,点击添加并且输入文件的各项值。File type 设置为FILESTREAM Data。
第二步:创建内存优化表
创建优化内存表的语法与创建基于磁盘的表的语法几乎相同,只有一些限制以及需要的扩展。指定表是内存优化表是用MEMORY_OPTIMIZED= ON语句实现的。内存优化表只可以有以下这些可支持的数据类型的列:
・ 字节;
・ 所有整数类型:tinyint、smallint、int、bigint;
・ 所有money类型:money、smallmoney;
・ 所有浮点类型:float、real;
・ 所有日期/时间类型:datetime、smalldatetime、datetime2、date、time;
・ 数字和浮点数类型;
・ 所有非LOB字符类型:char(n)、varchar(n)、nchar(n)、nvarchar(n)、sysname;
・ 非LOB二进制类型:binary(n)、varbinary(n);
・ 独特的识别符。
请注意的是LOB数据类型是不可用的;不能有XML类型、CLR、或者max数据类型的列,并且所有行的长度都限制在8060个字节之内,而且没有行外数据。事实上,8060字节的限制是在表创建的时候执行的,所以不像基于磁盘的表,内存优化表不能创建两个varchar(5000)的列。
内存优化表可以通过两种DURABILITY值定义:SCHEMA_AND_DATA或SCHEMA_ONLY,前者是默认值。内存优化表由DURABILITY=SCHEMA_ONLY定义,这意味着表中数据的变化没有被记录下来,并且表中的数据并没有保存在磁盘上。但是,架构会被作为数据库元数据的一部分保存下来,所以在一个SQL Server重启的过程中恢复数据库之后,这样的空表是可用的。
如前所述,内存优化表必须至少有一个索引,但这一要求能通过索引自动创建来满足,从而支持主键约束。除了那些用schema_only选项所创建的表以外的其他表都必须有一个声明的主键。至少有一个索引必须声明支持一个主键约束。下面的示例显示了一个主键索引创建成为一个分区索引,其中的统计指标也必须被指定。
当创建内存优化表时,除已列的对数据类型的限制之外,只有少数的其他限制:
・ 没有DML触发器;
・ 没有FOREIGN KEY或CHECK约束;
・ 没有IDENTITY列;
・ 除了主键没有唯一索引;
・ 最多8个索引,包括支持主键的索引。
此外,一旦一个表被创建后,架构是不可改变的。您将需要删除并重新创建表,而不是使用ALTER TABLE。此外,并没有具体的索引DDL命令(比如创建索引、修改索引、删除索引)。索引总是作为表创建的一部分而创建。
目前我们有两张表,‘ShoppingCart’和‘UserSession’。‘ShoppingCart’是一个持久化的表(默认值),这意味着表中的内容是存储在磁盘上的,不会因为服务器崩溃而丢失。‘UserSession’是一个非持久化的表(DURABILITY=SCHEMA_ONLY),这意味着表中内容仅存储在内存中,服务器重启即会丢失。
注:SQL 2014 内存优化的表支持非聚集哈希索引(hash index)和非聚集索引(rang index)。hash索引的Bucket_cout建议值是表中能找到的唯一索引键值个数的4到8倍。
下面通过两种方式来创建内存优化表。
使用T-SQL实现:
USE imoltp GO -- durable table (持久化的表) CREATE TABLE dbo.ShoppingCart ( ShoppingCartId int not null primary key nonclustered hash with (bucket_count=2000000), UserId int not null index ix_UserId nonclustered hash with (bucket_count=1000000), CreatedDate datetime2 not null, TotalPrice money ) WITH (MEMORY_OPTIMIZED=ON) GO -- non-durable table (非持久化的表) CREATE TABLE dbo.UserSession ( SessionId int not null primary key nonclustered hash with (bucket_count=400000), UserId int not null, CreatedDate datetime2 not null, ShoppingCartId int, index ix_UserId nonclustered hash (UserId) with (bucket_count=400000) ) WITH(MEMORY_OPTIMIZED=ON, DURABILITY=SCHEMA_ONLY) GO
使用SSMS实现:
1. 在Object Explorer,右键单击您数据库的Tables节点,点击new,然后点击Memory Optimized Table。然后可以看到创建内存优化表的模板。
2. 替换模板中的参数,在query菜单中点击Specify Values for Template Parameters。快捷键是Ctrl-Shift-M。
3. 执行完毕后,确认表已创建。
第三步:加载数据
可以通过多种方式把数据加载到表中,包括 INSERT .. SELECT from一个已经存在的存储在磁盘上的表和BCP。
-- insert a few rows INSERT dbo.UserSession VALUES (1,342,GETUTCDATE(),4) INSERT dbo.UserSession VALUES (2,65,GETUTCDATE(),NULL) INSERT dbo.UserSession VALUES (3,8798,GETUTCDATE(),1) INSERT dbo.UserSession VALUES (4,80,GETUTCDATE(),NULL) INSERT dbo.UserSession VALUES (5,4321,GETUTCDATE(),NULL) INSERT dbo.UserSession VALUES (6,8578,GETUTCDATE(),NULL) INSERT dbo.ShoppingCart VALUES (1,8798,GETUTCDATE(),NULL) INSERT dbo.ShoppingCart VALUES (2,23,GETUTCDATE(),45.4) INSERT dbo.ShoppingCart VALUES (3,80,GETUTCDATE(),NULL) INSERT dbo.ShoppingCart VALUES (4,342,GETUTCDATE(),65.4) GO -- verify table contents SELECT * FROM dbo.UserSession SELECT * FROM dbo.ShoppingCart GO
第四步:更新统计信息
内存优化的表不支持auto_update_statistics,因此统计信息需要手动进行更新。您可以使用UPDATE STATISTICS来更新单个表的统计信息或者sp_updatestats来更新数据库中的所有表的统计信息。
-- update statistics on memory optimized tables UPDATE STATISTICS dbo.UserSession WITH FULLSCAN, NORECOMPUTE UPDATE STATISTICS dbo.ShoppingCart WITH FULLSCAN, NORECOMPUTE GO
第五步:执行查询
优化内存表可以以两种不同的方式访问:要么通过可编译的Transact-SQL互操作,或通过本地编译的存储过程。
现在您已经做好了执行查询的准备。因为查询需要访问内存优化的表,他们将会受益于不加锁的数据结构,从而提高了数据访问的效率。以下是一些例子:
-- in an explicit transaction, assign a cart to a session and update the total price. -- note that the isolation level hint is required for memory-optimized tables with -- SELECT/UPDATE/DELETEstatements in explicit transactions BEGIN TRAN UPDATE dbo.UserSession WITH (SNAPSHOT) SET ShoppingCartId=3 WHERE SessionId=4 UPDATE dbo.ShoppingCart WITH (SNAPSHOT) SET TotalPrice=65.84 WHERE ShoppingCartId=3 COMMIT GO -- verify table contents SELECT * FROM dbo.UserSession u JOIN dbo.ShoppingCart s on u.ShoppingCartId=s.ShoppingCartId WHERE u.SessionId=4 GO
第六步:创建本地编译的存储过程
为了进一步优化内存优化表的访问以及优化您的业务逻辑的执行,您可以选择创建本地编译的存储过程。这些存储过程是使用Transact-SQL创建的,但并不支持完整的Transaction-SQL环境。具体细节可参考联机丛书。
以下是一个访问之前我们创建的表的本地编译的存储过程的例子。
-- natively compiled stored procedure for assigning a shopping cart to a session CREATE PROCEDURE dbo.usp_AssignCart @SessionId int WITH NATIVE_COMPILATION, SCHEMABINDING, EXECUTE AS OWNER AS BEGIN ATOMIC WITH (TRANSACTION ISOLATION LEVEL = SNAPSHOT, LANGUAGE = N'us_english') DECLARE @UserId int, @ShoppingCartId int SELECT @UserId=UserId, @ShoppingCartId=ShoppingCartId FROM dbo.UserSession WHERE SessionId=@SessionId IF @UserId IS NULL THROW 51000, 'The session or shopping cart does not exist.', 1 UPDATE dbo.UserSession SET ShoppingCartId=@ShoppingCartId WHERE SessionId=@SessionId END GO EXEC usp_AssignCart 1 GO
下面的存储过程通过向内存优化表中插入大量数据行来测试本地编译的存储过程的性能。该脚本插入了1,000,000行数据。
需要注意的是如果写事物日志文件变成应用的性能瓶颈,SQL Server允许您采用非持久化的表(DURABILITY=SCHEMA_ONLY)来完全除去写事务日志。
-- natively compiled stored procedure for inserting a large number of rows -- this demonstrates the performance of native procs CREATE PROCEDURE dbo.usp_InsertSampleCarts @StartId int, @InsertCount int WITH NATIVE_COMPILATION, SCHEMABINDING, EXECUTE AS OWNER AS BEGIN ATOMIC WITH (TRANSACTION ISOLATION LEVEL = SNAPSHOT, LANGUAGE = N'us_english') DECLARE @ShoppingCartId int = @StartId WHILE @ShoppingCartId < @StartId + @InsertCount BEGIN INSERT INTO dbo.ShoppingCart VALUES (@ShoppingCartId, 1, '2013-01-01T00:00:00', NULL) SET @ShoppingCartId += 1 END END GO -- insert 1,000,000 rows DECLARE @StartId int = (SELECT MAX(ShoppingCartId)+1 FROM dbo.ShoppingCart) EXEC usp_InsertSampleCarts @StartId, 1000000 GO -- verify the rows have been inserted SELECT COUNT(*) FROM dbo.ShoppingCart GO
使用SSMS创建本地编译的存储过程:
1. 在 Object Explorer中,右键单击您的数据库中的Stored Procedures 节点,点击New,然后点击 Natively Compiled Stored Procedure。创建本地编译的存储过程的模板将会显示在界面上。
2. 替换模板中的参数,点击Query 菜单下的 Specify Values for Template Parameters。快捷键是Ctrl-Shift-M。
SQL Server功能支持
很多SQL Server功能所支持的内存驻留OLTP和数据库包含内存优化表。例如,AlwaysOn的组件,日志传送和数据库备份和恢复被完全支持。但是,数据库镜像和复制不被支持。您可以使用SQL Server Management Studio以支持内存优化表和SSIS工作。
对于支持和不支持的功能的完整列表,请参阅到SQL Server 内存驻留OLTP文档。
总结
SQL Server内存驻留OLTP提供了可创造和运行内存优化的极其高效的管理,实现了对OLTP工作负载的性能优化。真正实现了多版本的积极并发控制,且无需在访问过程中锁定。所有内存驻留OLTP内存优化表必须至少拥有一个索引,然后所有对这类表的访问都需要通过这些索引进行。除少量限制情况下外,内存驻留OLTP内存优化表可以作为基于磁盘表中的相同引用的事务。本地编译的存储过程以最快的方式来使用您的内存优化表和性能业务逻辑计算。