cra*_*ank 13 sql sql-server performance
我目前想知道VARCHAR/NVARCHAR上的一些性能差异,特别是在使用复杂LIKE查询(以_或%开头)时.
我在Microsoft SQL Server 2014上有一个testsetup.我有2个表.两者都有一个ID字段(标识(1,1)和一个值字段(VARCHAR(450)或NVARCHAR(450)).两者都有相同的1'000'000随机生成的条目.
这些表名为tblVarCharNoIndex和tblNVarCharNoIndex(因此,没有索引.如果我使用索引,行为几乎相同).
现在,我执行以下查询测试持续时间(一次在VARCHAR上;一次在NVARCHAR上)
SELECT * FROM tblVarcharNoIndex WHERE Value LIKE '%ab%'
SELECT * FROM tblNVarcharNoIndex WHERE Value LIKE '%ab%'
Run Code Online (Sandbox Code Playgroud)
执行时间大不相同.在VARCHAR表上需要1540ms,在NVARCHAR表上需要8630 ms,因此使用NVARCHAR需要花费超过5倍的时间.
据我所知,NVARCHAR具有性能影响,因为它需要2个字节来存储,这完全有意义.但我无法解释性能下降500%,这对我来说毫无意义.
根据请求,这里有更多的数据.
查询表创建
CREATE TABLE [dbo].[tblVarcharNoIndex](
[Id] [int] IDENTITY(1,1) NOT NULL,
[Value] [varchar](450) NOT NULL,
CONSTRAINT [PK_tblVarcharNoIndex] PRIMARY KEY CLUSTERED
(
[Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
CREATE TABLE [dbo].[tblNVarcharNoIndex](
[Id] [int] IDENTITY(1,1) NOT NULL,
[Value] [nvarchar](450) NOT NULL,
CONSTRAINT [PK_tblNVarcharNoIndex] PRIMARY KEY CLUSTERED
(
[Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
Run Code Online (Sandbox Code Playgroud)
查询值生成
DECLARE @cnt INT = 0;
DECLARE @entries INT = 1000000 --1'000'000;
DECLARE @maxLength INT = 450;
DECLARE @minLength INT = 50;
DECLARE @value VARCHAR(450)
DECLARE @length INT
WHILE @cnt < @entries
BEGIN
SELECT @value = ''
SET @length = @minLength + CAST(RAND() * (@maxLength - @minLength) as INT)
WHILE @length <> 0
BEGIN
SELECT @value = @value + CHAR(CAST(RAND() * 96 + 32 as INT))
SET @length = @length - 1
END
INSERT INTO tblBase(Value, NValue) VALUES (@value, @value)
SET @cnt = @cnt + 1;
END;
Run Code Online (Sandbox Code Playgroud)
(稍后从tblBase复制值)
LIKE查询有问题
DECLARE @start DATETIME
DECLARE @end DATETIME
DECLARE @testname NVARCHAR(100) = 'INSERT FROM other table'
--VARCHAR No Index
PRINT 'starting ''' + @testname + ''' on VARCHAR (No Index)'
SET @start = GETDATE()
SELECT * FROM tblVarcharNoIndex WHERE Value LIKE '%ab%' --This takes 1540ms
SET @end = GETDATE()
PRINT '-- finished ''' + @testname + ''' on VARCHAR (No Index)'
PRINT '-- Duration ' + CAST(DATEDIFF(mcs, @start, @end) AS VARCHAR(100)) + ' microseconds'
--NVARCHAR No Index
PRINT 'starting ''' + @testname + ''' on NVARCHAR (No Index)'
SET @start = GETDATE()
SELECT * FROM tblNVarcharNoIndex WHERE Value LIKE '%ab%' --This takes 8630ms
SET @end = GETDATE()
PRINT '-- finished ''' + @testname + ''' on NVARCHAR (No Index)'
PRINT '-- Duration ' + CAST(DATEDIFF(mcs, @start, @end) AS VARCHAR(100)) + ' microseconds'
Run Code Online (Sandbox Code Playgroud)
执行计划 两个查询的执行计划看起来完全一样(我现在无处上传图像,但它非常简单):
SELECT(0%)<---并行(收集流)(3%)<---主键上的聚簇索引扫描(97%)
不过这个理论是合理的。LIKE是将每个值与字符串的一部分进行比较的运算符。如果操作员确实基于正确的并且SQL Server不知道值的一部分相对于另一部分的优势,则SQL Server必须运行如下所示的算法(示例中C#):
for (; foundValue == false && Start < (length - 2); Start += 1)
{
searchValue = x.Substring(Start, 2);
if (searchValue == compareValue)
foundValue = true;
}
Run Code Online (Sandbox Code Playgroud)
只是 中的字符数量是原来的两倍NVARCHAR。
根据我自己的测试,我注意到以下几点:
表“tblVarcharNoIndex”。扫描计数 1、逻辑读取 97、物理读取 0、预读读取 0、lob 逻辑读取 0、lob 物理读取 0、lob 预读读取 0。
表“tblNVarcharNoIndex”。扫描计数 1、逻辑读取 189、物理读取 0、预读读取 0、lob 逻辑读取 0、lob 物理读取 0、lob 预读读取 0。
逻辑读取意味着 SQL 为比较存储了多少数据,我们注意到这个数字略高于 2 倍。我认为,当查看实际执行计划并注意到估计行数为 56 与 73 时就可以看到答案,即使最终返回的行数相同。
然而,查看客户端统计数据会显示您可能注意到的内容:
NVAR VAR AVERAGE
Query Profile Statistics
Number of INSERT, DELETE and UPDATE statements 0 0 0.0000
Rows affected by INSERT, DELETE, or UPDATE statements 0 0 0.0000
Number of SELECT statements 2 2 2.0000
Rows returned by SELECT statements 306 306 306.0000
Number of transactions 0 0 0.0000
Network Statistics
Number of server roundtrips 1 1 1.0000
TDS packets sent from client 1 1 1.0000
TDS packets received from server 45 23 34.0000
Bytes sent from client 146 144 145.0000
Bytes received from server 180799 91692 136245.5000
Time Statistics
Client processing time 286 94 190.0000
Total execution time 317 156 236.5000
Wait time on server replies 31 62 46.5000
Run Code Online (Sandbox Code Playgroud)
请注意,从服务器收到的 TDS 数据包不同(回想一下,行的估计是不同的),这不仅占用更多字节,而且还占用更多处理时间。执行时间大约是原来的 2 倍,但处理时间却是原来的 3 倍。
其中有多少与您的处理器和 SQL Server 协议相关?可能是其中的一些或很多(此查询是在一台古老的 EDU Lenovo 笔记本电脑上运行的,配有 Windows 10、DuoCore 1.64Ghz、16GB DDR3)。虽然具体情况我没有资格回答。
尽管如此,我们仍然可以得出一件事:SQL Server 的行估计对客户端和发送/接收的数据有影响。