SQL Server In-Memory OLTP Internals for SQL Server 2016

这份白皮书是在上一份《SQL Server In-Memory OLTP Internals Overview》基础上的,很多东西都是一样的不再介绍,只介绍不相同的部分。

行和索引存储

Range索引

Range索引在2014的时候还是不支持的。Range index 使用bwtree数据结构。Bwtree和btree一样有叶子结点和中间节点。最重要的不通点是,bwtree page指针是一个逻辑的page id,而不是物理的page no。PID表示mapping table 上的位置,mapping table把PID和物理内存地址关联。Bwtree的index page是从来不更新的,而是增加一个新的,然后让mapping table的相同PID指向一个不同的物理内存地址。

具体的bwtree的算法可以看:http://www.cnblogs.com/Amaranthus/p/4375331.html

列存储索引

列存储索引基本结构

SQL Server 2016内存优化表支持聚集的列存储索引。列存储索引是高复合的索引,并不是由行来组织,而是用列来组织的。行被分为多个组,一个组最多可以有2^20行,然后把某一列的数据放入行组中,不会去管剩下的行。

每个行组,SQL Server都会使用Vertipaq压缩算法,重新编码和排列行组中的顺序来打到最有的压缩效果。每个行组中的列都是独立保存的,这个结构称之为段(segment),每个段都是一个LOB,保存在LOB的分配段元中。段是数据读写的基本单元,如图,表示吧一组多个索引列转化为几个段

上图中,表被分为3个行组,每个行组有4个段,一共有12个段。

为了支持聚集行存储索引的更新,有2个额外的结构。一个独立的内部表(deleted rows table DRT)。顾名思义是用来做被删除行的bitmap,用来保存所有已经删除的行的rowid。新行加入会被保存在一个堆中,Delta Store。当行数达到一定行数(通常是2^20或者10万行),SQL Server会吧这些行转化为新的压缩的行组。

内存优化表中聚集列存储索引和内存优化表的非聚集索引是分开保存的,是数据的一个副本。实际上,内存优化表的聚集列存储索引你可以理解为,保存了所有列的非聚集列存储索引。因为数据是高效压缩的,因此开销比较少。因为类存储索引可以压缩到原始数据的10%,因此开销也只有10%。

所有的类存储索引段都是在内存中的。为了恢复的目的,每个行组在内存优化文件组中都保存成一个独立的文件类型为LARGE DATA,在文件中对于某个行组,所有的段都是存放在一起的。SQL Server也维护了一个指针,指向每个段并且可以访问这个段,特别是访问了部分列的时候。这个部分会在下面CHECKPOINT FILES的时候介绍。新的行会被以列存储索引保存,但是并不会马上加入到压缩行组中,新的行只能使用内存优化表的其他索引来访问。如图,新的行和整个表分开维护的。你可以认为这些行是“delta rowgroup”和磁盘表的Delta Store类似,但是这些行是内存优化表的一部分,但是不是技术上的列存储索引的一部分。实际上是课件的delta rowgroup

内存优化表中的列存储索引只能在interop模式下由优化器进行选择。查询使用类存储索引可以并发并且对于高性能有很多好处。原生编译过程是不会使用列存储索引的,并且所有的查询都不会并发执行。若一个SQL Server 2016的内存优化表有聚集列存储索引,那么就有2个varheap,一个用于压缩行组,另外一个用来保存新行,可以让SQL Server快速识别哪些行还没有进入压缩段,这些行也在可见的delta rowgroup中。

有2个后台线程每2分钟执行一次,用来检查delta rowgroup中的行。注意这些行包含最新插入的,和update的,在内存优化表update就是delete+insert。如果这些行数超过10万那么就有下面2个操作:

  1. 行会被复制到一个或者多个行组,每个段都会被压缩转化变成聚集列存储索引的一部分。
  2. 行会从特定的内存分配器移到常规的内存存储。

SQL Server并不会是实际统计行数,而是使用评估。没有行组的行数可以超过1048576.如果超过有10万行,那么就会创建另外一个行组。如果小于10万行那么这些行还是会被留在原来的地方。

因为最新插入的行会被频繁更新,或者会被删除,想要延迟对最新行的压缩,可以设置一个等待量。当内存优化表有聚集列存储索引,那么就可以增加一个COMPRESSION_DELAY的参数,指定新行必须在delta rowgroup中呆多久。只有超过参数的行数超过10万才会被压缩到常规的列存储索引行组中。

当行被转换到压缩rowgroup之后,所有删除的行都会被放到Delete Rows表中,和磁盘表的聚集列存储索引。当行多的时候查询会很没有效率。这种情况下重组列存储索引并没有什么用,除非删除并且重建索引。一旦rowgroup中90%的行被删除,剩下的10%会自动被插入到未压缩的varheap,在内存优化表的Delta rowgroup中。Rowgroup的存储会被进行垃圾回收。

Note:
前面提到的,如果内存优化表有任何LOB或者溢出列,列存储索引不能在上面被创建。因为最大的行不能超过8060字节。另外一旦内存优化表有一个列存储索引,就不能使用alter table操作。需要先删除列存储索引,alter,然后再创建列存储索引。

以下是创建内存优化表的脚本,有2个索引,一个range索引一个列存储索引,然后查询内存消费。并且设置COMPRESSION_DELAY为60分钟。

USE master;

GO

SET NOCOUNT ON;

GO

DROP DATABASE IF EXISTS IMDB;

GO

CREATE DATABASE IMDB;

GO

ALTER DATABASE IMDB

ADD FILEGROUP IMDB_mod_FG

CONTAINS MEMORY_OPTIMIZED_DATA;

GO

ALTER DATABASE IMDB

ADD FILE (   NAME = ‘IMDB_mod‘ ,

FILENAME = ‘c:\HKData\IMDB_mod‘

)

TO FILEGROUP IMDB_mod_FG;

GO

USE IMDB;

GO

DROP TABLE IF EXISTS dbo.OrderDetailsBig;

GO

CREATE TABLE dbo.OrderDetailsBig

(

OrderID INT NOT NULL ,

ProductID INT NOT NULL ,

UnitPrice MONEY NOT NULL ,

Quantity SMALLINT NOT NULL ,

Discount REAL NOT NULL INDEX IX_OrderID NONCLUSTERED HASH ( OrderID )

WITH (BUCKET_COUNT = 20000000) ,

INDEX IX_ProductID NONCLUSTERED ( ProductID ) ,

CONSTRAINT PK_Order_Details

PRIMARY KEY NONCLUSTERED

(

OrderID ,

ProductID

) ,

INDEX clcsi_OrderDetailsBig CLUSTERED COLUMNSTORE

WITH (COMPRESSION_DELAY = 60)

)

WITH ( MEMORY_OPTIMIZED = ON, DURABILITY = SCHEMA_AND_DATA );

GO

SELECT OBJECT_NAME(c.object_id) AS table_name ,

a.xtp_object_id ,

a.type_desc ,

minor_id ,

memory_consumer_id AS consumer_id ,

memory_consumer_type_desc AS consumer_type_desc ,

memory_consumer_desc AS consumer_desc ,

CONVERT(NUMERIC(10, 2), allocated_bytes / 1024. / 1024) AS allocated_MB ,

CONVERT(NUMERIC(10, 2), used_bytes / 1024. / 1024) AS used_MB

FROM   sys.memory_optimized_tables_internal_attributes a

JOIN sys.dm_db_xtp_memory_consumers c ON a.object_id = c.object_id

AND a.xtp_object_id = c.xtp_object_id

LEFT JOIN sys.indexes i ON c.object_id = i.object_id

AND c.index_id = i.index_id;

返回的结果:

上图,显示表自己有6行。有一个内存消费者用于压缩rowgroup(HKCS_COMPRESSED消费者),2个用于range index,1个用于hash index,2个用于表的行存储(rowstore)(这个和白皮书中说的不同),行存储中其中一个是为了表中的行,第二个是delta rowgroup。每个有列存储索引的表都有4个内部表,xtp_object_id都不相同。每个内部表为了访问方便至少有一个索引用于数据访问。四个内部表:ROW_GROUP_INFO_TABLE(+hash索引),SEGMENTS_TABLE(+2个hash索引),DICTIONARIES_TABLE(+hash 索引),DELETED_ROW_TABLE(+hash索引)。(这些内部表的细节白皮书没有介绍)

除了看内存消费者之外,另外一个要检查的DMV是sys.dm_db_column_store_row_group_ physical_stats,这个视图不单单是显示了每个COMPRESSED并且OPEN的rowgroup的行数。你可以用一下脚本查看:

BEGIN TRAN;

DECLARE @i INT = 0;

WHILE ( @i < 10000000 )

BEGIN

INSERT INTO dbo.OrderDetailsBig

VALUES ( @i, @i % 1000000, @i % 57, @i % 10, 0.5 );

SET @i = @i + 1;

IF ( @i % 264 = 0 )

BEGIN

COMMIT TRAN;

BEGIN TRAN;

END;

END;

COMMIT TRAN;

SELECT   row_group_id ,

state_desc ,

total_rows ,

trim_reason_desc

FROM     sys.dm_db_column_store_row_group_physical_stats

WHERE    object_id = OBJECT_ID(‘dbo.OrderDetailsBig‘)

ORDER BY row_group_id;

GO

可以通过time_reason_desc字段可以查看为什么rowgroup的行会少于1048576行。如果没有小于1048576那么就显示NO_TRIM。因为OPEN的rowgroup是不压缩的,因此为null,若为STATS_MISMATCH表示行太少,若为SPILLOVER表示有移除导致。

时间: 2024-10-16 05:14:06

SQL Server In-Memory OLTP Internals for SQL Server 2016的相关文章

SQL Server 内存中OLTP内部机制概述(一)

----------------------------我是分割线------------------------------- 本文翻译自微软白皮书<SQL Server In-Memory OLTP Internals Overview>:http://technet.microsoft.com/en-us/library/dn720242.aspx ----------------------------我是分割线------------------------------- SQL S

SQL Server 内存中OLTP内部机制概述(二)

----------------------------我是分割线------------------------------- 本文翻译自微软白皮书<SQL Server In-Memory OLTP Internals Overview>:http://technet.microsoft.com/en-us/library/dn720242.aspx 译者水平有限,如有翻译不当之处,欢迎指正. ----------------------------我是分割线---------------

SQL Server 内存中OLTP内部机制概述(三)

----------------------------我是分割线------------------------------- 本文翻译自微软白皮书<SQL Server In-Memory OLTP Internals Overview>:http://technet.microsoft.com/en-us/library/dn720242.aspx 译者水平有限,如有翻译不当之处,欢迎指正. ----------------------------我是分割线---------------

SQL Server 内存中OLTP内部机制概述(四)

----------------------------我是分割线------------------------------- 本文翻译自微软白皮书<SQL Server In-Memory OLTP Internals Overview>:http://technet.microsoft.com/en-us/library/dn720242.aspx 译者水平有限,如有翻译不当之处,欢迎指正. ----------------------------我是分割线---------------

Sql Server 优化 SQL 查询:如何写出高性能SQL语句

1. 首先要搞明白什么叫执行计划? 执行计划是数据库根据SQL语句和相关表的统计信息作出的一个查询方案,这个方案是由查询优化器自动分析产生的,比如一条SQL语句如果用来从一个 10万条记录的表中查1条记录,那查询优化器会选择“索引查找”方式,如果该表进行了归档,当前只剩下5000条记录了,那查询优化器就会改变方案,采用 “全表扫描”方式. 可见,执行计划并不是固定的,它是“个性化的”.产生一个正确的“执行计划”有两点很重要: (1)    SQL语句是否清晰地告诉查询优化器它想干什么? (2)

SQL Server on Ubuntu——Ubuntu上的SQL Server(全截图)

本文从零开始一步一步介绍如何在Ubuntu上搭建SQL Server 2017,包括安装系统.安装SQL等相关步骤和方法(仅供测试学习之用,基础篇). 一.   创建Ubuntu系统(Create Ubuntu System) 1.      前提准备 由于本文主要研究SQL Server 2017在Linux上的搭建方法,从Install SQL Server on Linux中得知当前SQL Server 2017 CTP 2.0对于Ubuntu系统支持16.04和16.10,同时可以直接下

SQL Server 2012笔记分享-33:SQL Server profiler跟踪

需求 使用SQL server profiler跟踪当前实例的所有常见活动,并将结果保存到文件中. ================================================================ 开始程序中选择SQL server profiler,如图. 选择文件--新建跟踪,如图. 连接到当前的SQL server实例,如图. 在跟踪属性里面配置跟踪名称.保存到的文件地址.保存到SQL报表.启用跟踪停止时间等,如图. 下图是一个跟踪启动后实时的跟踪情况,如图

SQL Server 2012笔记分享-1:SQL Server版本的演变

SQL是英文Structured Query Language的缩写,意思为结构化查询语言.SQL语言的主要功能就是同各种数据库建立联系,进行沟通.按照ANSI(美国国家标准协会)的规定,SQL被作为关系型数据库管理系统的标准语言.SQL语句可以用来执行各种各样的操作,例如更新数据库中的数据,从数据库中提取数据等.目前,绝大多数流行的关系型数据库管理系统,如Oracle, Sybase, Microsoft SQL Server, Access等都采用了SQL语言标准. SQL server版本

转:SQL SERVER 2014 安装图解(含 SQL SERVER 2014 安装程序共享)

开篇介绍 2015年1月1日,新的一年开始之际,本来应该好好做点有意义的事情来跨个年的.结果,老习惯 - 睡觉之前一定要折腾一下电脑,说干就干,给新到的 DELL 电脑装虚机,下载 SQL SERVER 2014,安装 SQL SERVER 2014. SQL SERVER 2014 安装完成以后,不像 SQL SERVER 2008 R2 会提供一个 BIDS 开发工具,也不像 SQL SERVER 2012 会提供一个 SSDT 开发工具,也就是说 BI 的开发工具(SSIS, SSRS,