Sae*_*ati 5 performance sql-server datatypes t-sql
考虑这个表:
create table Books
(
Id bigint not null primary key identity(1, 1),
UniqueToken varchar(100) not null,
[Text] nvarchar(max) not null
)
Run Code Online (Sandbox Code Playgroud)
假设这张表中有超过 100,000 本书。
现在我们有 10,000 本书数据要插入到这个表中,其中一些是重复的。所以我们需要先过滤重复,然后再插入新书。
检查重复项的一种方法是这样的:
select UniqueToken
from Books
where UniqueToken in
(
'first unique token',
'second unique token'
-- 10,000 items here
)
Run Code Online (Sandbox Code Playgroud)
Text
列的存在是否会影响此查询的性能?如果是这样,我们如何优化它?
PS 我有相同的结构,对于其他一些数据。而且它的表现并不好。一个朋友告诉我,我应该把我的桌子分成两张桌子,如下所示:
create table BookUniqueTokens
(
Id bigint not null primary key identity(1, 1),
UniqueToken varchar(100)
)
create table Books
(
Id bigint not null primary key,
[Text] nvarchar(max)
)
Run Code Online (Sandbox Code Playgroud)
而且我必须只在第一个表上执行重复查找算法,然后将数据插入到它们中。他声称通过这种方式,性能会变得更好,因为表在物理上是分开的。他声称该[Text]
列会影响对该列的任何select
查询UniqueToken
。
考虑在IN
10K 记录数据集的子句中使用 8 个过滤谓词的查询。
select UniqueToken
from Books
where UniqueToken in
(
'Unique token 1',
'Unique token 2',
'Unique token 3',
'Unique token 4',
'Unique token 5',
'Unique token 6',
'Unique token 9999',
'Unique token 5000'
-- 10,000 items here
);
Run Code Online (Sandbox Code Playgroud)
使用聚集索引扫描,此测试表上没有其他索引
数据大小为216 字节。
您还应该注意,即使有 8 条记录,OR
过滤器也在堆积。
发生在该表上的读取:
当您Text
在查询的选择部分中包含该列时,实际数据大小会发生巨大变化:
select UniqueToken,Text
from Books
where UniqueToken in
(
'Unique token 1',
'Unique token 2',
'Unique token 3',
'Unique token 4',
'Unique token 5',
'Unique token 6',
'Unique token 9999',
'Unique token 5000'
-- 10,000 items here
);
Run Code Online (Sandbox Code Playgroud)
再次,带有残差谓词的聚集索引扫描:
但是数据集为32KB。
由于有近 1000 个 lob 逻辑读取:
现在,当我们创建有问题的两个表,并用相同的 10k 记录填充它们时
在没有Text
. 请记住,使用Books
表时我们有 99 次逻辑读取。
select UniqueToken
from BookUniqueTokens
where UniqueToken in
(
'Unique token 1',
'Unique token 2',
'Unique token 3',
'Unique token 4',
'Unique token 5',
'Unique token 6',
'Unique token 9999',
'Unique token 5000'
-- 10,000 items here
)
Run Code Online (Sandbox Code Playgroud)
读数BookUniqueTokens
较低,为 67 而不是 99。
我们可以将其追溯到原始Books
表中的页面和新表中没有Text
.
原Books
表:
新BookUniqueTokens
表
因此,所有页面 +(2 个开销页面?)都是从聚集索引中读取的。
为什么有差别,为什么差别不大?毕竟数据量差异很大(Lob数据<>没有Lob数据)
Books
数据空间
BooksWithText 数据空间
原因是ROW_OVERFLOW_DATA。
当数据大于 8kb 时,数据将作为 ROW_OVERFLOW_DATA 存储在不同的页面上。
好吧,如果lob数据存储在不同的页面上,为什么这两个聚集索引的页面大小不一样?
由于将 24 字节指针添加到聚集索引以跟踪这些页面中的每一个。毕竟sql server需要知道哪里可以找到lob数据。
他声称 [Text] 列会影响 UniqueToken 列上的任何选择查询。
和
Text 列的存在是否会影响此查询的性能?如果是这样,我们如何优化它?
如果存储的数据实际上是Lob Data,则使用答案中提供的Query
由于 24 字节的指针,它确实带来了一些开销。
根据执行次数/分钟不是疯狂的高,我会说这是可以忽略不计的,即使有 100K 记录。
请记住,这种开销仅在包含以下内容的索引时发生 Text
(例如聚集索引)时。
但是,如果使用聚集索引扫描,并且lob数据不超过8kb呢?
如果数据不超过 8kb,并且您没有索引UniqueToken
,则开销可能会更大。即使没有选择Text
列。
当 Text 只有 137 个字符长(所有记录)时,对 10k 条记录进行逻辑读取:
表'Books2'。扫描计数 1,逻辑读取 419
由于所有这些额外的数据都在聚集索引页上。
再次,一个索引UniqueToken
(不包括Text
列)将解决此问题。
正如@David Browne - Microsoft 所指出的,您还可以将数据存储在行外,以便在不选择此文本列时不会在聚集索引上添加此开销。
此外,如果您确实希望将文本存储在行外,则可以在不使用单独表的情况下强制执行该操作。只需使用 sp_tableoption 设置“行外大值类型”选项即可。docs.microsoft.com/en-us/sql/relational-databases
TL; 博士
根据给出的查询,UniqueToken
不包含索引的索引TEXT
应该可以解决您的问题。此外,我会使用临时表或表类型来进行过滤而不是IN
语句。
编辑:
是的,UniqueToken 上有一个非聚集索引
您的示例查询未触及 Text
列,根据查询,这应该是一个覆盖索引。
如果我们在之前使用的三个表上进行测试(UniqueToken
+ Lob 数据、Solely UniqueToken
、UniqueToken
nvarchar(max) 列中的 + 137 个字符数据)
CREATE INDEX [IX_Books_UniqueToken] ON Books(UniqueToken);
CREATE INDEX [IX_BookUniqueTokens_UniqueToken] ON BookUniqueTokens(UniqueToken);
CREATE INDEX [IX_Books2_UniqueToken] ON Books2(UniqueToken);
Run Code Online (Sandbox Code Playgroud)
这三个表的读取保持不变,因为使用了非聚集索引。
Table 'Books'. Scan count 8, logical reads 16, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'BookUniqueTokens'. Scan count 8, logical reads 16, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'Books2'. Scan count 8, logical reads 16, physical reads 5, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Run Code Online (Sandbox Code Playgroud)
额外细节
作者:@David Browne - 微软
此外,如果您确实希望将文本存储在行外,则可以在不使用单独表的情况下强制执行该操作。只需使用 sp_tableoption 设置“行外大值类型”选项即可。docs.microsoft.com/en-us/sql/relational-databases/
请记住,您必须重建索引才能使已填充的数据生效。
通过@Erik Darling
在
过滤 Lob 数据很糟糕。
当使用更大的数据类型时,您的内存授权可能会达到顶峰,从而影响性能。
归档时间: |
|
查看次数: |
2649 次 |
最近记录: |