SQL Server In-Memory OLTP Internals for SQL Server 2016

本文涉及的产品
云数据库 RDS SQL Server,独享型 2核4GB
简介: SQL Server In-Memory OLTP Internals for SQL Server 2016这份白皮书是在上一份《SQL Server In-Memory OLTP Internals Overview》基础上的,很多东西都是一样的不再介绍,只介绍不相同的部分。
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的内存优化表有聚集列存储索引,那么就有2varheap,一个用于压缩行组,另外一个用来保存新行,可以让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表中,和磁盘表的聚集列存储索引。当行多的时候查询会很没有效率。这种情况下重组列存储索引并没有什么用,除非删除并且重建索引。一旦rowgroup90%的行被删除,剩下的10%会自动被插入到未压缩的varheap,在内存优化表的Delta rowgroup中。Rowgroup的存储会被进行垃圾回收。

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

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

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行。有一个内存消费者用于压缩rowgroupHKCS_COMPRESSED消费者),2个用于range index1个用于hash index2个用于表的行存储(rowstore)(这个和白皮书中说的不同),行存储中其中一个是为了表中的行,第二个是delta rowgroup。每个有列存储索引的表都有4个内部表,xtp_object_id都不相同。每个内部表为了访问方便至少有一个索引用于数据访问。四个内部表:ROW_GROUP_INFO_TABLE(+hash索引)SEGMENTS_TABLE(+2hash索引)DICTIONARIES_TABLE(+hash 索引),DELETED_ROW_TABLE+hash索引)。(这些内部表的细节白皮书没有介绍)

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

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。因为OPENrowgroup是不压缩的,因此为null,若为STATS_MISMATCH表示行太少,若为SPILLOVER表示有移除导致。

相关实践学习
使用SQL语句管理索引
本次实验主要介绍如何在RDS-SQLServer数据库中,使用SQL语句管理索引。
SQL Server on Linux入门教程
SQL Server数据库一直只提供Windows下的版本。2016年微软宣布推出可运行在Linux系统下的SQL Server数据库,该版本目前还是早期预览版本。本课程主要介绍SQLServer On Linux的基本知识。 相关的阿里云产品:云数据库RDS&nbsp;SQL Server版 RDS SQL Server不仅拥有高可用架构和任意时间点的数据恢复功能,强力支撑各种企业应用,同时也包含了微软的License费用,减少额外支出。 了解产品详情:&nbsp;https://www.aliyun.com/product/rds/sqlserver
目录
相关文章
|
23天前
|
SQL 人工智能 算法
【SQL server】玩转SQL server数据库:第二章 关系数据库
【SQL server】玩转SQL server数据库:第二章 关系数据库
61 10
|
23天前
|
SQL 数据库 数据库管理
【SQL server】玩转SQL server数据库:第三章 关系数据库标准语言SQL(一)模式、表、索引与视图
【SQL server】玩转SQL server数据库:第三章 关系数据库标准语言SQL(一)模式、表、索引与视图
55 11
|
23天前
|
SQL 算法 数据库
【SQL server】玩转SQL server数据库:第三章 关系数据库标准语言SQL(二)数据查询
【SQL server】玩转SQL server数据库:第三章 关系数据库标准语言SQL(二)数据查询
97 6
|
18天前
|
SQL 安全 网络安全
IDEA DataGrip连接sqlserver 提示驱动程序无法通过使用安全套接字层(SSL)加密与 SQL Server 建立安全连接的解决方法
IDEA DataGrip连接sqlserver 提示驱动程序无法通过使用安全套接字层(SSL)加密与 SQL Server 建立安全连接的解决方法
38 0
|
3月前
|
SQL 算法 数据库
【数据库SQL server】关系数据库标准语言SQL之数据更新
【数据库SQL server】关系数据库标准语言SQL之数据更新
33 0
|
SQL 数据库 索引
Sqlserver与access数据库sql语法十大差异
ACCESS结构简单容易处理,而且也能满足多数的网站程序要求,也是初学者的试牛刀。ACCESS是小型数据库,既然是小型就有他根本的局限性: 1)、数据库过大,一般ACCESS数据库达到50M左右的时候性能会急剧下降! 2)、网站访问频繁,经常超过100人的在线时,处理速度会有影响! 3)、记录数过多,一般记录数达到10万条左右的时候性能就会急剧下降!微软公司为了与ACCESS高低搭配的一种高端方案:改用了Sqlserver,但语法会有一些差异。
938 0
|
23天前
|
SQL 安全 算法
【SQL server】玩转SQL server数据库:第四章 数据库安全性
【SQL server】玩转SQL server数据库:第四章 数据库安全性
65 12
|
23天前
|
SQL 存储 算法
【SQL server】玩转SQL server数据库:第一章 绪论
【SQL server】玩转SQL server数据库:第一章 绪论
42 5
|
6天前
|
SQL 数据可视化 算法
SQL Server聚类数据挖掘信用卡客户可视化分析
SQL Server聚类数据挖掘信用卡客户可视化分析
15 2
|
3天前
|
SQL 机器学习/深度学习 数据采集
数据分享|SQL Server、Visual Studio、tableau对信贷风险数据ETL分析、数据立方体构建可视化
数据分享|SQL Server、Visual Studio、tableau对信贷风险数据ETL分析、数据立方体构建可视化
14 0