内存表的性能比基于磁盘的表差


10

我在SQL Server 2014中有一个表,如下所示:

CREATE TABLE dbo.MyTable
(
[id1] [bigint] NOT NULL,
[id2] [bigint] NOT NULL,
[col1] [int] NOT NULL default(0),
[col2] [int] NOT NULL default(0)
)

(id1,id2)是PK。基本上,id1是将一组结果(id2,col1,col2)分组的标识符,其pk为id2。

我正在尝试使用内存表来摆脱现有的基于磁盘的表,这是我的瓶颈。

  • 表中的数据被写入->读取->删除一次。
  • 每个id1值都有数千个id2(成百上千)。
  • 数据在表中存储的时间非常短,例如20秒。

在此表上执行的查询如下:

-- INSERT (can vary from 10s to 10,000s of records):
INSERT INTO MyTable
  SELECT @fixedValue, id2, col1, col2 FROM AnotherTable

-- READ:
SELECT id2, col1
FROM MyTable INNER JOIN OtherTbl ON MyTable.id2 = OtherTbl.pk
WHERE id1 = @value
ORDER BY col1

-- DELETE:
DELETE FROM MyTable WHERE id1 = @value

这是我用于表格的当前定义:

CREATE TABLE dbo.SearchItems
(
  [id1] [bigint] NOT NULL,
  [id2] [bigint] NOT NULL,
  [col1] [int] NOT NULL default(0),
  [col2] [int] NOT NULL default(0)

  CONSTRAINT PK_Mem PRIMARY KEY NONCLUSTERED (id1,id2),
  INDEX idx_Mem HASH (id1,id2) WITH (BUCKET_COUNT = 131072)
) WITH (MEMORY_OPTIMIZED = ON, DURABILITY = SCHEMA_ONLY)

不幸的是,与基于磁盘的表的先前情况相比,此定义导致性能下降。数量级大约高出10%(在某些情况下达到100%,因此是两倍时间)。

最重要的是,考虑到Microsoft宣传的无锁体系结构,我期望在高并发方案中获得超级优势。相反,最糟糕的性能恰好是当有多个并发用户在表上运行多个查询时。

问题:

  • 正确设置的BUCKET_COUNT是什么?
  • 我应该使用哪种索引?
  • 为什么性能要比基于磁盘的表差?

sys.dm_db_xtp_hash_index_stats的查询返回:

total_bucket_count = 131072
empty_bucket_count = 0
avg_chain_len = 873
max_chain_length = 1009

我更改了存储区计数,因此sys.dm_db_xtp_hash_index_stats的输出为:

total_bucket_count = 134217728
empty_bucket_count = 131664087
avg_chain_len = 1
max_chain_length = 3

即便如此,结果仍然几乎相同。


您确定没有遇到参数嗅探吗?您是否尝试过使用来运行查询OPTION(OPTIMIZE FOR UNKNOWN)(请参阅表提示)?
TT。

我的猜测是您遇到了行链问题。你能给我们输出select * from sys.dm_db_xtp_hash_index_stats 吗?:另外,此链接应该回答大多数/所有的问题msdn.microsoft.com/en-us/library/...
肖恩Gallardy

4
哈希索引仅对包含在两个包含列上的谓词有用。您是否尝试过在表上没有哈希索引?
Mikael Eriksson

我发现只有使用本机编译的存储过程才能实现内存技术的最佳性能改进。
Daniel Hutmacher '16

@DanielHutmacher FWIW我已经看到了反例,其中的所有好处都是从消除闩锁和添加本机编译的过程中获得的收益为零或可忽略不计。我认为没有足够的空间发表笼统的声明(尽管在这种情况下您可能是正确的,但我什至没有查看细节)。
亚伦·伯特兰

Answers:


7

虽然由于缺少信息而无法完整回答该帖子,但它应该能够为您指明正确的方向,或者获得其他见识,您以后可以与社区分享。

不幸的是,与基于磁盘的表的先前情况相比,此定义导致性能下降。数量级大约高出10%(在某些情况下达到100%,因此是两倍时间)。

最重要的是,考虑到Microsoft宣传的无锁体系结构,我期望在高并发方案中获得超级优势。相反,最糟糕的性能恰好是当有多个并发用户在表上运行多个查询时。

这令人不安,因为绝对不是这种情况。某些工作负载不适用于内存表(SQL 2014),而某些工作负载则适合于此工作负载。在大多数情况下,仅通过迁移和选择适当的索引就可以将性能降低到最小。

本来我只是在狭窄地考虑您对此的疑问:

问题:

  • 正确设置的BUCKET_COUNT是什么?
  • 我应该使用哪种索引?
  • 为什么性能要比基于磁盘的表差?

最初,我认为实际的内存表和索引不是最佳状态存在问题。尽管内存优化的哈希索引定义存在一些问题,但我认为真正的问题与所使用的查询有关。

-- INSERT (can vary from 10s to 10,000s of records):
INSERT INTO MyTable
  SELECT @fixedValue, id2, col1, col2 FROM AnotherTable

如果仅涉及内存表,则此插入应该非常快。但是,它也涉及基于磁盘的表,并且受到与此表相关的所有锁定和阻塞。因此,这里的实时浪费在基于磁盘的表上。

将数据加载到内存后,我对基于磁盘的表中的100,000行插入进行了快速测试时,响应时间不到一秒。但是,大多数数据只保留很短的时间,少于20秒。这并没有给它太多时间来真正驻留在缓存中。另外,我不确定AnotherTable真正的大小,也不知道是否正在从磁盘读取值。我们必须依靠您来提供这些答案。

使用“选择”查询:

SELECT id2, col1
FROM MyTable INNER JOIN OtherTbl ON MyTable.id2 = OtherTbl.pk
WHERE id1 = @value
ORDER BY col1

同样,我们受制于基于互操作+磁盘的表性能。此外,在HASH索引上排序并不便宜,因此应使用非聚集索引。我在评论中链接的索引指南中对此进行了说明。

为了给出一些基于实际研究的事实,我SearchItems在内存表中加载了1000万行和AnotherTable100,000,因为我不知道它的实际大小或统计信息。然后,我使用上面的选择查询执行。另外,我在wait_completed上创建了一个扩展事件会话,并将其放入环形缓冲区。每次运行后都要清洗。我还DBCC DROPCLEANBUFFERS模拟了一个环境,其中所有数据可能都不驻留在内存中。

在真空中观察时,结果并不惊人。由于我正在测试的笔记本电脑使用的是更高级别的SSD,因此我人为地降低了所用VM的基于磁盘的性能。

仅在基于内存的表上运行了5次查询后,结果中没有等待信息(删除联接且没有子查询)。这几乎与预期的一样。

但是,当使用原始查询时,我确实有等待。在这种情况下,正是PAGEIOLATCH_SH在从磁盘读取数据时才有意义。由于我是该系统中的唯一用户,并且没有花费时间为联接表创建用于插入,更新,删除的大型测试环境,因此我预计不会发生任何锁定或阻塞。

在这种情况下,再次,很大一部分时间花在了基于磁盘的表上。

最后删除查询。使用has索引仅基于ID1查找行并不是非常有效。虽然相等谓词确实适用于哈希索引,但数据所属的存储区基于整个哈希列。因此,id1,id2(其中id1 = 1,id2 = 2和id1 = 1,id2 = 3)将散列到不同的存储桶中,因为散列将跨越(1,2)和(1,3)。这将不是简单的B树范围扫描,因为哈希索引的结构方式不同。然后,我希望它不是此操作的理想指标,但是我不希望它花费比经验更长的数量级。我将对此感兴趣。

最重要的是,考虑到Microsoft宣传的无锁体系结构,我期望在高并发方案中获得超级优势。相反,最糟糕的性能恰好是当有多个并发用户在表上运行多个查询时。

虽然确实可以使用锁来实现逻辑一致性,但操作仍必须是原子的。这是通过一个特殊的基于CPU的比较运算符完成的(这就是为什么In-Memory仅与某些[尽管过去4年中几乎所有的CPU都可以使用]处理器一起工作)的原因。因此,我们不能免费获得所有内容,仍然会有一些时间来完成这些操作。

提出的另一点是,在几乎所有查询中,使用的接口都是T-SQL(而不是本机编译的SPROC),它们都接触至少一个基于磁盘的表。这就是为什么我相信,最终,我们实际上并没有提高性能,因为我们仍然受限于基于磁盘的表的性能。

跟进:

  1. 为wait_completed创建扩展事件会话,并指定您已知的SPID。运行查询并提供给我们输出或在内部使用它。

  2. 给我们更新#1的输出。

  3. 没有任何魔术数字可以确定哈希索引的存储桶数。基本上,只要铲斗没有完全装满并且行链保持在3或4以下,性能就应该可以接受。这有点像在问:“我应该将日志文件设置为什么?” -它取决于每个进程,每个数据库,每个使用类型。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.