内存中OLTP是关于内存中的一切。但那只是对了一半。在今天的文章里我想给你展示下,当你从内存读取数据时,即使内存中OLTP也会引起磁盘活动。这里的问题是执行计划里,不正确的统计信息与排序(sort)运算符的组合。

排序(sort)运算符问题

我们都知道,排序(sort)运算符需要所谓的内存授予(Memory Grant)来作它的运行。内存区域是用来进行执行计划里到来记录的排序。内存授予的大小是基于估计行数数量。在基数计算(Cadinality Estimation)期间查询优化器估计执行计划里每个运算符的预计行数。

我在今年6月写了篇文章,展示了当估计错误时,排序(sort)运算符如何能溢出到TempDb。在内存中OLTP里同样的事情会发生:当估计行数错误时,在执行计划里有排序(sort)运算符,排序(sort)运算符会溢出到TempDb!我们来重现这个情形。

内存中OLTP溢出到TempDb

我们新建一个有内存中OLTP文件组配置的新数据库。

 -- Create new database
 CREATE DATABASE HashCollisions
 GO

 -- Add MEMORY_OPTIMIZED_DATA filegroup to the database.
 ALTER DATABASE HashCollisions
 ADD FILEGROUP InMemoryOLTPFileGroup CONTAINS MEMORY_OPTIMIZED_DATA
 GO

 USE HashCollisions
 GO

 -- Add a new file to the previously created file group
 ALTER DATABASE HashCollisions ADD FILE
 (
     NAME = N'InMemoryOLTPContainer',
     FILENAME = N'C:\Program Files\Microsoft SQL Server\MSSQL12.SQL2014\MSSQL\DATA\InMemoryOLTPContainer'
 )
 TO FILEGROUP [InMemoryOLTPFileGroup]
 GO

下一步我创建新的内存优化表:

 -- Create a test table
 CREATE TABLE Table1
 (
     Column1 INT IDENTITY,
     Column2 INT
     )
 ) WITH
 (
     MEMORY_OPTIMIZED = ON,
     DURABILITY = SCHEMA_ONLY
 )
 GO

从表定义可以看到,我在Column1列创建了1个哈希索引。因为它是个哈希索引,你也需要指定哈希表上你想拥有的哈希桶数。这里我指定了1个哈希桶,这是个非常,非常糟的做法。当你往表里插入记录时,因为只有1个哈希桶,你会得到巨大数量的哈希冲突(hash collisions)。一般来说,在你定义你的哈希索引的列上,哈希桶数应该和你列上的唯一值个数一致。下面代码往刚才创建的表里插入14001条记录。

 -- Insert 14001 records
 )

  , ) AS n INTO #Nums
 FROM
 master.dbo.syscolumns sc1

 INSERT INTO Table1 (Column2)
  FROM #nums
 DROP TABLE #nums
 GO

你可以通过DMV sys.dm_db_xtp_hash_index_stats查看哈希冲突数。从这个DMV的输出可以看到,你有14001条记录在这个且唯一的哈希索引的哈希桶上。现在我们来运行执行计划里有排序(sort)运算符的SELECT语句。

 -- The sort operator in this execution plan spills over to TempDb!
 SELECT * FROM Table1
 ORDER BY Column1

现在当你查看执行计划时,你会看到排序运算符已经溢出到TempDb。

这是因为哈希索引上不正确的统计信息才发生的。当你查看执行计划里Index Scan (NonClusteredHash) 运算符属性时,你会看到查询优化器从我们的哈希索引上估计行数为1,实际我们返回了140001行。

Index Scan (NonClusteredHash) 运算符的估计总是基于哈希表里哈希桶数。查询优化器这里做出的假设是你没有哈希冲突(hash collisions)——这在这里是不正确的。因此对排序运算符的内存授予是根据那个不正确的估计作为标准,这就会溢出到TempDb。在我的系统里这个查询运行了近80毫秒,对于内存中技术来说这个算很长时间了。

你如何修正这个问题?删除你的表,在哈希索引里仔细计划哈希桶数。欢迎来到内存中OLTP的精彩世界……

小结

当你使用内存中OLTP的哈希索引时,你要对你的哈希所用仔细设计你的哈希桶数。当它们错误时,是伤及性能。我已经在1个月前,写了篇文章描述哈希冲突(hash collisions)如何伤及内存中OLTP的性能——即使没有溢出到TempDb!

从中我们可以看出:在哈希索引有哈希冲突的话,你用内存中OLTP的话不能期望得到惊艳的快速性能,因为它们带来巨大的负担且影响基数计算。

感谢关注!

参考文章:

https://www.sqlpassion.at/archive/2015/02/05/memory-oltp-hekaton-sort-warnings/

内存中OLTP(Hekaton)的排序警告的更多相关文章

  1. 内存中OLTP(Hekaton)里的事务日志记录

    在今天的文章里,我想详细讨论下内存中OLTP里的事务日志如何写入事务日志.我们都知道,对于你的内存优化表(Memory Optimized Tables),内存中OLTP提供你2个持久性(durabi ...

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

    ----------------------------我是分割线------------------------------- 本文翻译自微软白皮书<SQL Server In-Memory ...

  3. 内存中 OLTP - 常见的工作负荷模式和迁移注意事项(三)

    ----------------------------我是分割线------------------------------- 本文翻译自微软白皮书<In-Memory OLTP – Comm ...

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

    ----------------------------我是分割线------------------------------- 本文翻译自微软白皮书<SQL Server In-Memory ...

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

    ----------------------------我是分割线------------------------------- 本文翻译自微软白皮书<SQL Server In-Memory ...

  6. 配置内存中OLTP文件组提高性能

    在今天的文章里,我想谈下使用内存中OLTP的内存优化文件组来获得持久性,还有如何配置它来获得高性能.在进入正题前,我想简单介绍下使用你数据库里这个特定文件组,内存OLTP是如何获得持久性的. 内存中O ...

  7. 内存中OLTP与内存不足

    我已经写了好几次内存中OLTP的文章和”为什么我还不推荐内存中OLTP给用户”.今天我想进一步谈下内存中OLTP背后的内存需求,还有如果你内存不够的话会发生什么. 一切都与内存有关! 我们都知道很久之 ...

  8. 为什么我还不推荐内存中OLTP给用户

    嗯,有些人在看玩这篇文章后会恨我,但我还是要说.1个月来我在内存中OLTP这个里领域里做了大量的工作,很多用户都请求使用这个惊艳的新技术.遗憾的是,关于内存中OLTP没有一个是真的令人激动的——看完你 ...

  9. 内存中 OLTP - 常见的工作负荷模式和迁移注意事项(二)

    ----------------------------我是分割线------------------------------- 本文翻译自微软白皮书<In-Memory OLTP – Comm ...

随机推荐

  1. RIFF和WAVE音频文件格式

    RIFF file format RIFF全称为资源互换文件格式(Resources Interchange File Format),是Windows下大部分多媒体文件遵循的一种文件结构.RIFF文 ...

  2. Java中的堆栈区别

    在函数中定义的一些基本类型的变量和对象的引用变量都在函数的栈内存中分配. 当在一段代码块定义一个变量时,Java就在栈中为这个变量分配内存空间,当超过变量的作用域后,Java会自动释放掉为该变量所分配 ...

  3. jQuery 教程

    jQuery 教程 W3School H-ui前端框架 html标签

  4. (Hibernate进阶)Hibernate映射——一对一双向关联映射(六)

    上一篇博客我们介绍了一对一的单向关联映射,单向是指只能从人(Person)这端加载身份证端(IdCard),但是反过来,不能从身份证端加载人得信息.如图所示: 关键原因在于对象模型具有方向性: 单向: ...

  5. ORACLE手工删除数据库

    很多人习惯用ORACLE的DBCA工具创建.删除数据库,这里总结一下手工删除数据库实验的步骤,文中大量参考了乐沙弥的手动删除ORACLE数据库这篇博客的内容,当然还有Oracle官方相关文档.此处实验 ...

  6. Python自动化之socketserver模块

    1 动态导入模块 import importlib aa = importlib.import_module("lib1.aa") //lib跟当前模块不是一个目录,aa是lib下 ...

  7. WWDC2014之iOS使用动态库 framework【转】

    from:http://www.cocoachina.com/industry/20140613/8810.html JUN 12TH, 2014 苹果的开放态度 WWDC2014上发布的Xcode6 ...

  8. vi命令示例大全

    1.      进入vi l vi filename: 打开或新建文件,并将光标置于第一行首 l vi +n filename:打开文件,并将光标置于第n行首 l vi + filename:打开文件 ...

  9. 设计模式总结篇系列:命令模式(Command)

    在程序设计中,经常会遇到一个对象需要调用另外一个对象的某个方法以达到某种目的,在此场景中,存在两个角色:请求发出者和请求接收者.发出者发出请求,接收者接收请求并进行相应处理.有时候,当需要对请求发出者 ...

  10. DML语言练习,数据增删改查,复制清空表

    Connected Connected as TEST@ORCL SQL> select * from t_hq_bm; BUMBM BUMMC DIANH ---------- ------- ...