emp*_*mpi 6 sql-server performance memory-optimized-tables
我正在尝试使用经典临时表对 Microsoft SQL Server 2016 中的内存优化表进行基准测试。
SQL Server 版本:
Microsoft SQL Server 2016 (SP2) (KB4052908) - 13.0.5026.0 (X64) Mar 18 2018 09:11:49
Copyright (c) Microsoft Corporation
Developer Edition (64-bit) on Windows 10 Enterprise 10.0 <X64> (Build 17134: ) (Hypervisor)
Run Code Online (Sandbox Code Playgroud)
我正在遵循此处描述的步骤:https : //docs.microsoft.com/en-us/sql/relational-databases/in-memory-oltp/faster-temp-table-and-table-variable-by-using-内存优化?view=sql-server-ver15。
CrudTest_TempTable 1000, 100, 100
go 1000
Run Code Online (Sandbox Code Playgroud)
相对
CrudTest_memopt_hash 1000, 100, 100
go 1000
Run Code Online (Sandbox Code Playgroud)
这个测试有什么作用?
并且这样重复了 1000 次。
第一个使用经典临时表的存储过程大约需要 6 秒才能运行。
第二个存储过程至少需要 15 秒,并且通常会出错:
开始执行循环
消息 3998,级别 16,状态 1,第 3
行在批处理结束时检测到不可提交的事务。事务回滚。消息 701,级别 17,状态 103,过程 CrudTest_memopt_hash,第 16 行 [批处理开始行 2]
资源池“默认”中的系统内存不足,无法运行此查询。
我做了以下优化(在更糟之前):
哈希索引包括 Col1 和 SpidFilter
在单个事务中完成所有操作使其运行速度更快(但是如果没有它运行会很好)
我正在生成随机 ID - 没有它,每次迭代的记录都在同一个桶中
我还没有创建本地编译的 SP,因为我的结果很糟糕。
我的机器上有足够的可用内存,SQL Server 可以使用它——在不同的场景中它分配了很多内存,但在这个测试用例中它只是出错了。
对我来说,这些结果意味着内存优化表不能替换临时表。你有类似的结果还是我做错了什么?
使用临时表的代码是:
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
DROP PROCEDURE IF EXISTS CrudTest_TempTable;
GO
CREATE PROCEDURE CrudTest_TempTable
@InsertsCount INT, @UpdatesCount INT, @DeletesCount INT
AS
BEGIN
SET NOCOUNT ON;
BEGIN TRAN;
CREATE TABLE #tempTable
(
Col1 INT NOT NULL PRIMARY KEY CLUSTERED,
Col2 NVARCHAR(4000),
Col3 NVARCHAR(4000),
Col4 DATETIME2,
Col5 INT NOT NULL
);
DECLARE @cnt INT = 0;
DECLARE @currDate DATETIME2 = GETDATE();
WHILE @cnt < @InsertsCount
BEGIN
INSERT INTO #tempTable (Col1, Col2, Col3, Col4, Col5)
VALUES (@cnt,
'sdkfjsdjfksjvnvsanlknc kcsmksmk ms mvskldamvks mv kv al kvmsdklmsdkl mal mklasdmf kamfksam kfmasdk mfksamdfksafeowa fpmsad lak',
'msfkjweojfijm skmcksamepi eisjfi ojsona npsejfeji a piejfijsidjfai spfdjsidjfkjskdja kfjsdp fiejfisjd pfjsdiafjisdjfipjsdi s dfipjaiesjfijeasifjdskjksjdja sidjf pajfiaj pfsdj pidfe',
@currDate, 100);
SET @cnt = @cnt + 1;
END
SET @cnt = 0;
WHILE @cnt < @UpdatesCount
BEGIN
UPDATE #tempTable SET Col5 = 101 WHERE Col1 = cast ((rand() * @InsertsCount) as int);
SET @cnt = @cnt + 1;
END
SET @cnt = 0;
WHILE @cnt < @DeletesCount
BEGIN
DELETE FROM #tempTable WHERE Col1 = cast ((rand() * @InsertsCount) as int);
SET @cnt = @cnt + 1;
END
COMMIT;
END
GO
Run Code Online (Sandbox Code Playgroud)
内存测试中使用的对象是:
DROP PROCEDURE IF EXISTS CrudTest_memopt_hash;
GO
DROP SECURITY POLICY IF EXISTS tempTable_memopt_hash_SpidFilter_Policy;
GO
DROP TABLE IF EXISTS tempTable_memopt_hash;
GO
DROP FUNCTION IF EXISTS fn_SpidFilter;
GO
CREATE FUNCTION fn_SpidFilter(@SpidFilter smallint)
RETURNS TABLE
WITH SCHEMABINDING , NATIVE_COMPILATION
AS
RETURN
SELECT 1 AS fn_SpidFilter
WHERE @SpidFilter = @@spid;
GO
CREATE TABLE tempTable_memopt_hash
(
Col1 INT NOT NULL,
Col2 NVARCHAR(4000),
Col3 NVARCHAR(4000),
Col4 DATETIME2,
Col5 INT NOT NULL,
SpidFilter SMALLINT NOT NULL DEFAULT (@@spid),
INDEX ix_SpidFiler NONCLUSTERED (SpidFilter),
INDEX ix_hash HASH (Col1, SpidFilter) WITH (BUCKET_COUNT=100000),
CONSTRAINT CHK_SpidFilter CHECK ( SpidFilter = @@spid )
) WITH (MEMORY_OPTIMIZED = ON, DURABILITY = SCHEMA_ONLY);
GO
CREATE SECURITY POLICY tempTable_memopt_hash_SpidFilter_Policy
ADD FILTER PREDICATE dbo.fn_SpidFilter(SpidFilter)
ON dbo.tempTable_memopt_hash
WITH (STATE = ON);
GO
Run Code Online (Sandbox Code Playgroud)
使用它们的存储过程是:
CREATE PROCEDURE CrudTest_memopt_hash
@InsertsCount INT, @UpdatesCount INT, @DeletesCount int
AS
BEGIN
SET NOCOUNT ON;
BEGIN TRAN;
DECLARE @cnt INT = 0;
DECLARE @currDate DATETIME2 = GETDATE();
DECLARE @IdxStart INT = CAST ((rand() * 1000) AS INT);
WHILE @cnt < @InsertsCount
BEGIN
INSERT INTO tempTable_memopt_hash(Col1, Col2, Col3, Col4, Col5)
VALUES (@IdxStart + @cnt,
'sdkfjsdjfksjvnvsanlknc kcsmksmk ms mvskldamvks mv kv al kvmsdklmsdkl mal mklasdmf kamfksam kfmasdk mfksamdfksafeowa fpmsad lak',
'msfkjweojfijm skmcksamepi eisjfi ojsona npsejfeji a piejfijsidjfai spfdjsidjfkjskdja kfjsdp fiejfisjd pfjsdiafjisdjfipjsdi s dfipjaiesjfijeasifjdskjksjdja sidjf pajfiaj pfsdj pidfe',
@currDate, 100);
SET @cnt = @cnt + 1;
END
SET @cnt = 0;
WHILE @cnt < @UpdatesCount
BEGIN
UPDATE tempTable_memopt_hash
SET Col5 = 101
WHERE Col1 = @IdxStart + cast ((rand() * @InsertsCount) as int);
SET @cnt = @cnt + 1;
END
SET @cnt = 0;
WHILE @cnt < @DeletesCount
BEGIN
DELETE FROM tempTable_memopt_hash
WHERE Col1 = @IdxStart + cast ((rand() * @InsertsCount) as int);
SET @cnt = @cnt + 1;
END
DELETE FROM tempTable_memopt_hash;
COMMIT;
END
GO
Run Code Online (Sandbox Code Playgroud)
指数统计:
table index total_bucket_count empty_bucket_count empty_bucket_percent avg_chain_length max_chain_length
[dbo].[tempTable_memopt_hash] PK__tempTabl__3ED0478731BB5AF0 131072 130076 99 1 3
Run Code Online (Sandbox Code Playgroud)
更新
我包括我的最终测试用例和用于创建过程、表等的 sql 代码。我已经对空数据库进行了测试。
SQL 代码:https : //pastebin.com/9K6SgAqZ
测试用例:https : //pastebin.com/ckSTnVqA
我的最后一次运行看起来像这样(临时表是最快的表,但我能够使用内存优化表变量实现最快的时间):
Start CrudTest_TempTable 2019-11-18 10:45:02.983
Beginning execution loop
Batch execution completed 1000 times.
Finish CrudTest_TempTable 2019-11-18 10:45:09.537
Start CrudTest_SpidFilter_memopt_hash 2019-11-18 10:45:09.537
Beginning execution loop
Batch execution completed 1000 times.
Finish CrudTest_SpidFilter_memopt_hash 2019-11-18 10:45:27.747
Start CrudTest_memopt_hash 2019-11-18 10:45:27.747
Beginning execution loop
Batch execution completed 1000 times.
Finish CrudTest_memopt_hash 2019-11-18 10:45:46.100
Start CrudTest_tableVar 2019-11-18 10:45:46.100
Beginning execution loop
Batch execution completed 1000 times.
Finish CrudTest_tableVar 2019-11-18 10:45:47.497
Run Code Online (Sandbox Code Playgroud)
恕我直言,在OP测试不能显示的优点memory-optimized表,因为这些表的最大优势是,他们是lock-and-latch free,这意味着你update/ insert/delete不采取locks在所有允许这些并发修改tables。
但是所做的测试根本不包括并发更改,显示的代码将所有更改合二为一session。
另一个观察结果:hash index在 上定义table是错误的,因为您只搜索one column并且哈希索引是在 上定义的two columns。两列上的哈希索引意味着hash function应用于两个参数,但您仅搜索一列,因此hash index无法使用。
你认为通过使用 mem opt 表我可以得到比临时表的性能改进还是只是为了限制临时表上的 IO?
Memory-optimized tables不应该替代temporary tables,正如已经提到的,您将在高度并发的 OLTP环境中看到利润,而您猜测temporary table仅对您的会话可见,根本没有并发。
消除闩锁和锁。所有内存中 OLTP 内部数据结构都是 无锁无锁的。内存中 OLTP 使用新的多版本并发控制 (MVCC) 来提供事务一致性。从用户的角度来看,它的行为方式类似于常规 SNAPSHOT 事务隔离级别;但是,它不使用引擎盖下的锁定。这种模式允许多个会话处理相同的数据,而不会相互锁定和阻塞,并提高了系统的可扩展性,允许充分利用现代多 CPU/多核硬件。
被引书籍:Dmitri Korotkevitch 的 Pro SQL Server Internals
您如何看待标题“通过使用内存优化更快的临时表和表变量”
我打开这篇文章,看到了这些例子(按照它们在文章中的顺序)
A. 我table variables只在它们包含很少行的情况下使用。为什么我还要关心这几行?
B. 更换global tempdb ##table。我只是根本不使用它们。
C. 更换session tempdb #table。如前所述,session tempdb #table任何其他会话都看不到,那么收益是什么?数据不去磁盘?tempdb如果你真的有问题,你应该考虑一下最快的 SSD 磁盘tempdb吗?2014年的tempdb的对象开始不一定去disk,甚至在情况下bulk inserts,在任何情况下,我甚至RCSI对我的数据库启用和有没有问题tempdb。