在逝去的2016后半年,由于项目需要支持数据的快速更新和多用户的高并发,我试水SQL Server 2016的In-Memory OLTP,创建内存数据库实现项目的需求,现在项目接近尾声,系统运行稳定,写一篇博客,记录一下使用内存数据库的经验。
从SQL Server 2016开始支持In-Memory OLTP,通俗地讲,是内存数据库,使用内存优化表(Memory-Optimized Table,简称MOT)来实现,MOT驻留在内存中。在查询MOT时,只从内存中读取数据行,不会产生Disk IO;在更新MOT时,数据的更新直接写入到内存中。内存优化表能够在硬盘上维护一个数据副本,该副本只用于持久化数据,不用于数据读写操作。只有在数据库恢复时,数据库引擎才会从该副本中读取数据。
在内存数据库中,不是所有的数据都需要存储在内存中,有些数据仍然能够存储在Disk上,硬盘表(Disk-Based Table,简称DBT)是传统的表存储结构,每个Page是8KB,在查询和更新DBT时,产生Disk IO操作,将数据从Disk读取到内存,或者将数据更新异步写入到Disk中。
内存数据库将原本存储在Disk上的数据,存储在内存中,利用内存的高速访问优势实现数据的快速查询和更新,但是,内存数据库,不仅仅是存储空间的变化,内存数据库引擎实现本地编译模块(Natively compiled)、交叉事务(Cross-Container Transaction)和查询互操作(Query Interop):
- 本地编译模块:如果代码模块只访问MOT,那么可以将该模块定义为本地编译模块,SQL Server直接将TSQL脚本编译成机器代码;SQL Server 2016支持本地编译的模式有:存储过程(SP),触发器(Trigger),标量值函数(Scalar Function)或内嵌多语句函数(Inline Multi-Statement Function)。相比于解释性(Interpreted)TSQL 模块,机器代码直接使用内存地址,性能更高。
- 交叉事务:在解释性TSQL模块中,一个事务既能访问硬盘表,也能访问内存优化表;实际上,SQL Server创建了两个事务,一个事务用于访问硬盘表,一个事务用于访问内存优化表,在DMV中,分别使用transaction_id 和 xtp_transaction_id 来标识。
- 查询互操作:解释性TSQL脚本能够访问内存优化表和硬盘表,本地编译模块只能访问内存优化表。
内存数据被整合到SQL Server关系引擎中,使用内存数据库时,客户端应用程序甚至感受不到任何变化,DAL接口也不需要做任何修改。由于Query Interop的存在,任何解释性TSQL脚本都能透明地访问MOT,只是性能没有本地编译TSQL脚本性能高。在使用分布式事务访问MOT时,必须设置合适的事务隔离级别,推荐使用Read Committed,如果发生MSSQLSERVER_41333 错误,说明产生交叉事务隔离错误(CROSS_CONTAINER_ISOLATION_FAILURE),原因是当前事务的隔离级别太高。
一,创建内存数据库
内存优化表的数据必须存储在包含Memory_Optimized_Data的File Group中,该FileGroup可以有多个File,每个File实际上是Folder,一个DB只能创建一个包含Memory_Optimized_Data的File Group。
step1,创建一个数据库,创建的Data File的数量最好和CPU内核数量保持一致,存放在不同的物理磁盘上;
use master go --Create database create database Test_MemboryDB on Primary ( name=Test_MemoryDB_1, filename='D:\Program Files\Microsoft SQL Server\Test_MemoryDB_1.mdf', size=5GB, FileGrowth=1GB ), ( name=Test_MemoryDB_2, filename='D:\Program Files\Microsoft SQL Server\Test_MemoryDB_2.ndf', size=5GB, FileGrowth=1GB ), ( name=Test_MemoryDB_3, filename='D:\Program Files\Microsoft SQL Server\Test_MemoryDB_3.ndf', size=5GB, FileGrowth=1GB ), ( name=Test_MemoryDB_4, filename='D:\Program Files\Microsoft SQL Server\Test_MemoryDB_4.ndf', size=5GB, FileGrowth=1GB ), ( name=Test_MemoryDB_5, filename='D:\Program Files\Microsoft SQL Server\Test_MemoryDB_5.ndf', size=5GB, FileGrowth=1GB ), ( name=Test_MemoryDB_6, filename='D:\Program Files\Microsoft SQL Server\Test_MemoryDB_6.ndf', size=5GB, FileGrowth=1GB ), ( name=Test_MemoryDB_7, filename='D:\Program Files\Microsoft SQL Server\Test_MemoryDB_7.ndf', size=5GB, FileGrowth=1GB ), ( name=Test_MemoryDB_8, filename='D:\Program Files\Microsoft SQL Server\Test_MemoryDB_8.ndf', size=5GB, FileGrowth=1GB ), ( name=Test_MemoryDB_9, filename='D:\Program Files\Microsoft SQL Server\Test_MemoryDB_9.ndf', size=5GB, FileGrowth=1GB ), ( name=Test_MemoryDB_10, filename='D:\Program Files\Microsoft SQL Server\Test_MemoryDB_10.ndf', size=5GB, FileGrowth=1GB ), ( name=Test_MemoryDB_11, filename='D:\Program Files\Microsoft SQL Server\Test_MemoryDB_11.ndf', size=5GB, FileGrowth=1GB ), ( name=Test_MemoryDB_12, filename='D:\Program Files\Microsoft SQL Server\Test_MemoryDB_12.ndf', size=5GB, FileGrowth=1GB ), ( name=Test_MemoryDB_13, filename='D:\Program Files\Microsoft SQL Server\Test_MemoryDB_13.ndf', size=5GB, FileGrowth=1GB ), ( name=Test_MemoryDB_14, filename='D:\Program Files\Microsoft SQL Server\Test_MemoryDB_14.ndf', size=5GB, FileGrowth=1GB ), ( name=Test_MemoryDB_15, filename='D:\Program Files\Microsoft SQL Server\Test_MemoryDB_15.ndf', size=5GB, FileGrowth=1GB ), ( name=Test_MemoryDB_16, filename='D:\Program Files\Microsoft SQL Server\Test_MemoryDB_16.ndf', size=5GB, FileGrowth=1GB ), ( name=Test_MemoryDB_17, filename='D:\Program Files\Microsoft SQL Server\Test_MemoryDB_17.ndf', size=5GB, FileGrowth=1GB ), ( name=Test_MemoryDB_18, filename='D:\Program Files\Microsoft SQL Server\Test_MemoryDB_18.ndf', size=5GB, FileGrowth=1GB ), ( name=Test_MemoryDB_19, filename='D:\Program Files\Microsoft SQL Server\Test_MemoryDB_19.ndf', size=5GB, FileGrowth=1GB ), ( name=Test_MemoryDB_20, filename='D:\Program Files\Microsoft SQL Server\Test_MemoryDB_20.ndf', size=5GB, FileGrowth=1GB ) LOG ON ( name = N'Test_MemboryDB_log', filename = N'D:\Program Files\Microsoft SQL Server\Test_MemboryDB_log.ldf' , size = 10GB , filegrowth = 1GB ) GO