SQL 服务器内存优化 Table - 与临时内存相比性能较差 table
SQL Server Memory Optimized Table - poor performance compared to temporary table
我正在尝试使用经典临时 tables 在 Microsoft SQL Server 2016 中对内存优化 tables 进行基准测试。
SQL 服务器版本:
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)
CrudTest_TempTable 1000, 100, 100
go 1000
对比
CrudTest_memopt_hash 1000, 100, 100
go 1000
这个测试是做什么的?
- 1000 次插入
- 100 次随机更新
- 100 次随机删除
重复 1000 次。
第一个使用经典临时 tables 的存储过程需要大约 6 秒才能 运行。
第二个存储过程至少需要 15 秒并且通常会出错:
Beginning execution loop
Msg 3998, Level 16, State 1, Line 3
Uncommittable transaction is detected at the end of the batch. The transaction is rolled back.
Msg 701, Level 17, State 103, Procedure CrudTest_memopt_hash, Line 16 [Batch Start Line 2]
There is insufficient system memory in resource pool 'default' to run this query.
我做了以下优化(在它变得更糟之前):
哈希索引包括 Col1 和 SpidFilter
在单个事务中完成所有操作使其运行速度更快(但是 运行 没有它会很好)
我正在生成随机 ID - 没有它,每次迭代的记录最终都在相同的桶中
我还没有创建本地编译的 SP,因为我的结果很糟糕。
我的盒子上有足够的空闲 RAM,SQL 服务器可以使用它 - 在不同的情况下它会分配很多内存,但在这个测试用例中它只会出错。
对我而言,这些结果意味着内存优化 tables 无法替代临时 tables。你有类似的结果还是我做错了什么?
使用临时 tables 的代码是:
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
内存测试中使用的对象是:
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
使用它们的存储过程是:
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
指数统计:
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
更新
我包括我的最终测试用例和 sql 用于创建过程的代码,tables 等。我在空数据库上执行了测试。
SQL代码:https://pastebin.com/9K6SgAqZ
测试用例:https://pastebin.com/ckSTnVqA
我的最后一个 运行 看起来像这样(温度 table 是最快的 tables,但我能够使用内存优化实现最快的时间 table变量):
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
恕我直言,OP中的测试无法显示memory-optimized
tables的优势
因为这些table的最大优点是它们是lock-and-latch free
,这意味着你的update
/insert
/delete
不带locks
完全允许对这些 tables
.
进行并发更改
但是所做的测试根本不包括并发更改,显示的代码将所有更改集中在一个 session
。
另一个观察结果:table
上定义的 hash index
是错误的,因为您仅在 one column
上搜索,而散列索引是在 two columns
上定义的。两列上的哈希索引意味着 hash function
应用于两个参数,但您仅在一列上搜索,因此 hash index
无法使用。
Do you think by using mem opt tables I can get performance
improvements over temp tables or is it just for limiting IO on tempdb?
Memory-optimized tables
不应该替代temporary tables
,前面已经说过,你会在高并发OLTP
环境下看到收益,虽然正如您猜测 temporary table
仅对您的会话可见,但根本没有并发性。
Eliminate latches and locks. All In-Memory OLTP internal data structures are latch- and lock-free. In-Memory OLTP uses a new
multi-version concurrency control (MVCC) to provide transaction
consistency. From a user standpoint, it behaves in a way similar to
the regular SNAPSHOT transaction isolation level; however, it does
not use locking under the hood. This schema allows multiple sessions
to work with the same data without locking and blocking each other
and improves the scalability of the system allowing fully utilize
modern multi-CPU/multi-core hardware.
引用书籍:Pro SQL 服务器内部,作者 Dmitri Korotkevitch
What do you think about the title "Faster temp table and table
variable by using memory optimization"
我打开这篇文章并看到了这些示例(按照它们在文章中的顺序)
- 一个。内存优化基础 table 变量
- 乙。场景:替换全局tempdb ##table
- C。场景:替换session tempdb #table
一个。我只在它们包含很少行的情况下才使用 table variables
。我为什么要关心这几行?
乙。替换 global tempdb ##table
。我只是根本不使用它们。
摄氏度。替换 session tempdb #table
。如前所述,session tempdb #table
对任何其他会话都不可见,那么有什么好处呢?数据不去磁盘?如果 tempdb
确实有问题,您是否应该为 tempdb
考虑最快的 SSD 磁盘?从 2014 年开始,tempdb 对象不一定会转到 disk
,即使在 bulk inserts
的情况下,无论如何我什至在我的数据库上启用了 RCSI
并且 [=32= 没有问题].
可能不会看到性能提升,只有在非常特殊的应用程序中才会看到。 SQL 过去曾涉足 'pin table' 之类的东西,但优化器根据实际 activity 选择内存中的页面,可能在几乎所有情况下都表现出色。几十年来,这一直在进行性能调整。我认为 'in memory' 比任何实际用途更像是一个营销接触点。请证明我错了。
我正在尝试使用经典临时 tables 在 Microsoft SQL Server 2016 中对内存优化 tables 进行基准测试。
SQL 服务器版本:
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)
CrudTest_TempTable 1000, 100, 100
go 1000
对比
CrudTest_memopt_hash 1000, 100, 100
go 1000
这个测试是做什么的?
- 1000 次插入
- 100 次随机更新
- 100 次随机删除
重复 1000 次。
第一个使用经典临时 tables 的存储过程需要大约 6 秒才能 运行。
第二个存储过程至少需要 15 秒并且通常会出错:
Beginning execution loop
Msg 3998, Level 16, State 1, Line 3
Uncommittable transaction is detected at the end of the batch. The transaction is rolled back.Msg 701, Level 17, State 103, Procedure CrudTest_memopt_hash, Line 16 [Batch Start Line 2]
There is insufficient system memory in resource pool 'default' to run this query.
我做了以下优化(在它变得更糟之前):
哈希索引包括 Col1 和 SpidFilter
在单个事务中完成所有操作使其运行速度更快(但是 运行 没有它会很好)
我正在生成随机 ID - 没有它,每次迭代的记录最终都在相同的桶中
我还没有创建本地编译的 SP,因为我的结果很糟糕。
我的盒子上有足够的空闲 RAM,SQL 服务器可以使用它 - 在不同的情况下它会分配很多内存,但在这个测试用例中它只会出错。
对我而言,这些结果意味着内存优化 tables 无法替代临时 tables。你有类似的结果还是我做错了什么?
使用临时 tables 的代码是:
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
内存测试中使用的对象是:
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
使用它们的存储过程是:
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
指数统计:
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
更新
我包括我的最终测试用例和 sql 用于创建过程的代码,tables 等。我在空数据库上执行了测试。
SQL代码:https://pastebin.com/9K6SgAqZ
测试用例:https://pastebin.com/ckSTnVqA
我的最后一个 运行 看起来像这样(温度 table 是最快的 tables,但我能够使用内存优化实现最快的时间 table变量):
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
恕我直言,OP中的测试无法显示memory-optimized
tables的优势
因为这些table的最大优点是它们是lock-and-latch free
,这意味着你的update
/insert
/delete
不带locks
完全允许对这些 tables
.
但是所做的测试根本不包括并发更改,显示的代码将所有更改集中在一个 session
。
另一个观察结果:table
上定义的 hash index
是错误的,因为您仅在 one column
上搜索,而散列索引是在 two columns
上定义的。两列上的哈希索引意味着 hash function
应用于两个参数,但您仅在一列上搜索,因此 hash index
无法使用。
Do you think by using mem opt tables I can get performance improvements over temp tables or is it just for limiting IO on tempdb?
Memory-optimized tables
不应该替代temporary tables
,前面已经说过,你会在高并发OLTP
环境下看到收益,虽然正如您猜测 temporary table
仅对您的会话可见,但根本没有并发性。
Eliminate latches and locks. All In-Memory OLTP internal data structures are latch- and lock-free. In-Memory OLTP uses a new multi-version concurrency control (MVCC) to provide transaction consistency. From a user standpoint, it behaves in a way similar to the regular SNAPSHOT transaction isolation level; however, it does not use locking under the hood. This schema allows multiple sessions to work with the same data without locking and blocking each other and improves the scalability of the system allowing fully utilize modern multi-CPU/multi-core hardware.
引用书籍:Pro SQL 服务器内部,作者 Dmitri Korotkevitch
What do you think about the title "Faster temp table and table variable by using memory optimization"
我打开这篇文章并看到了这些示例(按照它们在文章中的顺序)
- 一个。内存优化基础 table 变量
- 乙。场景:替换全局tempdb ##table
- C。场景:替换session tempdb #table
一个。我只在它们包含很少行的情况下才使用 table variables
。我为什么要关心这几行?
乙。替换 global tempdb ##table
。我只是根本不使用它们。
摄氏度。替换 session tempdb #table
。如前所述,session tempdb #table
对任何其他会话都不可见,那么有什么好处呢?数据不去磁盘?如果 tempdb
确实有问题,您是否应该为 tempdb
考虑最快的 SSD 磁盘?从 2014 年开始,tempdb 对象不一定会转到 disk
,即使在 bulk inserts
的情况下,无论如何我什至在我的数据库上启用了 RCSI
并且 [=32= 没有问题].
可能不会看到性能提升,只有在非常特殊的应用程序中才会看到。 SQL 过去曾涉足 'pin table' 之类的东西,但优化器根据实际 activity 选择内存中的页面,可能在几乎所有情况下都表现出色。几十年来,这一直在进行性能调整。我认为 'in memory' 比任何实际用途更像是一个营销接触点。请证明我错了。