我注意到在运行 SQL Server 2016 SP1 CU6 的服务器上,扩展事件会话有时会显示 SELECT 查询导致写入。例如:
执行计划没有显示写入的明显原因,例如可能溢出到 TempDB 的哈希表、假脱机或排序:
对 MAX 类型的变量分配或自动统计更新也可能导致这种情况,但在这种情况下也不是写入的原因。
还有什么可以写的?
使用 查询系统版本控制的时态表FOR system_time ALL
似乎会忽略开始时间和结束时间相等的行。
例如:
/* Create a simple system-versioned table */
CREATE TABLE [dbo].[svt_test](
[string] [VARCHAR](30) NOT NULL,
[validFrom] [DATETIME2](7) GENERATED ALWAYS AS ROW START NOT NULL,
[validTo] [DATETIME2](7) GENERATED ALWAYS AS ROW END NOT NULL,
CONSTRAINT [PK_svt_test] PRIMARY KEY CLUSTERED
(
[string] ASC
),
PERIOD FOR SYSTEM_TIME ([validFrom], [validTo])
)
WITH
(
SYSTEM_VERSIONING = ON ( HISTORY_TABLE = [dbo].[svt_test_history] )
);
/*
Insert and immediately delete a row.
May have to try multiple times to get a row with the same start and end
timestamp.
*/
INSERT INTO dbo.svt_test (string) VALUES ('string');
DELETE dbo.svt_test WHERE string = 'string';
/* 0 rows */
SELECT * FROM dbo.svt_test;
/* 1 row */
SELECT * FROM dbo.svt_test_history;
/*
0 rows.
Should be the "union of rows that belong to the current and the history table"
per https://msdn.microsoft.com/en-us/library/dn935015.aspx
*/
SELECT * FROM dbo.svt_test FOR system_time ALL;
/*
CONTAINED IN does not find the row either, even though MSDN says the
interval is defined as SysStartTime >= start_date_time AND SysEndTime <=
end_date_time
*/
DECLARE @Start DATETIME2(7);
SELECT @Start = validFrom FROM [dbo].[svt_test_history] WHERE string = 'string';
DECLARE @End DATETIME2(7);
SELECT @End = validTo FROM [dbo].[svt_test_history] WHERE string = 'string';
SELECT * FROM dbo.svt_test FOR system_time CONTAINED IN (@Start, @End);
/* Clean up */
ALTER TABLE dbo.svt_test SET (SYSTEM_VERSIONING = OFF);
DROP TABLE dbo.svt_test;
DROP TABLE dbo.svt_test_history;
我已经在 2016 SP1 CU1 (12.0.4416) 和 2016 CU3 (13.0.2186) 上对此进行了验证。我用datetime2(0)
而不是得到了相同的结果datetime2(7)
。
有人会碰巧知道这可能是错误还是设计使然?或者,也许我误解了什么?
fn_dblog()
以这种格式显示交易 ID:
0000:00049d43
并sys.dm_tran_database_transactions
以这种格式显示交易 ID:
9811233
我相信上面的例子以不同的格式显示了相同的交易 ID。
有没有办法在这两种格式之间进行转换?或者这实际上是两个不同的实体——在这种情况下,有没有办法匹配fn_dblog()
与交易相关的 DMV?
是否有关于 SQL Server 2016 中关于如何为包含 SUBSTRING() 或其他字符串函数的谓词估计基数的更改的任何文档或研究?
我问的原因是我正在查看一个查询,该查询在兼容模式 130 下性能下降,原因与与包含对 SUBSTRING() 调用的 WHERE 子句匹配的行数估计值的变化有关。我通过查询重写纠正了这个问题,但我想知道是否有人知道有关 SQL Server 2016 中此区域更改的任何文档。
演示代码如下。在此测试用例中,估计值非常接近,但准确性因数据而异。
在测试用例中,在兼容级别 120 中,SQL Server 似乎使用直方图进行估计,而在兼容级别 130 中,SQL Server 似乎假设固定的 10% 的表匹配。
CREATE DATABASE MyStringTestDB;
GO
USE MyStringTestDB;
GO
DROP TABLE IF EXISTS dbo.StringTest;
CREATE TABLE dbo.StringTest ( [TheString] varchar(15) );
GO
INSERT INTO dbo.StringTest
VALUES
( 'Y5_CLV' );
INSERT INTO dbo.StringTest
VALUES
( 'Y5_EG3' );
INSERT INTO dbo.StringTest
VALUES
( 'ZY_NE' );
INSERT INTO dbo.StringTest
VALUES
( 'ZY_PQT' );
INSERT INTO dbo.StringTest
VALUES
( 'ZY_T2V' );
INSERT INTO dbo.StringTest
VALUES
( 'ZY_TT4' );
INSERT INTO dbo.StringTest
VALUES
( 'ZY_ZKK' );
INSERT INTO dbo.StringTest
VALUES
( 'ZZ_LW6' );
INSERT INTO dbo.StringTest
VALUES
( 'ZZ_QO3' );
INSERT INTO dbo.StringTest
VALUES
( 'ZZ_TZ7' );
INSERT INTO dbo.StringTest
VALUES
( 'ZZ_UZZ' );
CREATE CLUSTERED INDEX IX_Clustered ON dbo.StringTest (TheString);
/*
Uses fixed % for estimate; 1.1 rows estimated in this case.
Plan for computation:
CSelCalcFixedFilter (0.1) <----
Selectivity: 0.1
*/
ALTER DATABASE MyStringTestDB SET compatibility_level = 130;
GO
SELECT *
FROM dbo.StringTest
WHERE SUBSTRING(TheString, 1, CHARINDEX('_',TheString) - 1) = 'ZZ'
OPTION (QUERYTRACEON 2363, QUERYTRACEON 3604);
/*
Uses histogram to get estimate of 1
CSelCalcPointPredsFreqBased <----
Distinct value calculation:
CDVCPlanLeaf
0 Multi-Column Stats, 1 Single-Column Stats, 0 Guesses
Individual selectivity calculations:
(none)
Loaded histogram for column QCOL: [DBA].[dbo].[StringTest].TheString from stats with id 1
*/
ALTER DATABASE MyStringTestDB SET compatibility_level = 120;
GO
SELECT *
FROM dbo.StringTest
WHERE SUBSTRING(TheString, 1, CHARINDEX('_',TheString) - 1) = 'ZZ'
OPTION (QUERYTRACEON 2363, QUERYTRACEON 3604);
/*
-- Simpler rewrite; works fine in both compat levels and gets better estimate.
SELECT *
FROM dbo.StringTest
WHERE TheString LIKE 'ZZ[_]%'
OPTION (QUERYTRACEON 2363, QUERYTRACEON 3604);
*/
是否可以通过修改查询或影响优化器的策略,通过一次查找或扫描检索与以下相同的数据?
与此类似的代码和架构目前在 SQL Server 2014 上。
复制脚本。设置:
USE tempdb;
GO
IF OBJECT_ID('dbo.TestUpload', 'U') IS NOT NULL
DROP TABLE dbo.TestUpload;
CREATE TABLE dbo.TestUpload(
JobRunId bigint NOT NULL,
ThingAName nvarchar(255) NOT NULL,
ThingAType nvarchar(255) NOT NULL,
ThingAGranularity nvarchar(255) NOT NULL,
ThingBName nvarchar(255) NOT NULL,
ThingBType nvarchar(255) NOT NULL,
ThingBGranularity nvarchar(255) NOT NULL
);
CREATE CLUSTERED INDEX IX_JobRunId ON dbo.TestUpload (JobRunId);
GO
INSERT INTO dbo.TestUpload (JobRunId, ThingAName, ThingAType, ThingAGranularity, ThingBName, ThingBType, ThingBGranularity)
VALUES (1, 'A', 'B', 'C', 'D', 'E', 'F');
GO 10
INSERT INTO dbo.TestUpload (JobRunId, ThingAName, ThingAType, ThingAGranularity, ThingBName, ThingBType, ThingBGranularity)
VALUES (1, 'D', 'E', 'F', 'A', 'B', 'C');
GO 10
询问:
DECLARE @JobRunID bigint = 1;
SELECT JobRunId,
ThingAName AS Name,
ThingAType AS [Type],
ThingAGranularity AS Granularity
FROM dbo.TestUpload
WHERE JobRunId = @JobRunID
UNION
SELECT JobRunId,
ThingBName AS Name,
ThingBType AS [Type],
ThingBGranularity AS Granularity
FROM dbo.TestUpload
WHERE JobRunId = @JobRunID;
拆除:
IF OBJECT_ID('dbo.TestUpload', 'U') IS NOT NULL
DROP TABLE dbo.TestUpload;
我认为这可能不是理想的建模。我试图从开发人员那里获得更多关于如何选择架构的信息,但我很好奇是否有我忽略的 TSQL 技巧,因为更改查询比更改架构更容易。
我正在测试从聚集列存储索引中删除数据。
我注意到执行计划中有一个很大的 eager spool operator:
这完成了以下特征:
如果我欺骗估算器使其低估,我会得到一个避免使用 TempDB 的更快计划:
估计扫描成本:56.901
(这是一个估计的计划,但评论中的数字是正确的。)
有趣的是,如果我通过运行以下命令刷新增量存储,则假脱机再次消失:
ALTER INDEX IX_Clustered ON Fact.RecordedMetricsDetail REORGANIZE WITH (COMPRESS_ALL_ROW_GROUPS = ON);
似乎只有当增量存储中的页面超过某个阈值时才会引入假脱机。
为了检查增量存储的大小,我正在运行以下查询来检查表的行内页面:
SELECT
SUM([in_row_used_page_count]) AS in_row_used_pages,
SUM(in_row_data_page_count) AS in_row_data_pages
FROM sys.[dm_db_partition_stats] as pstats
JOIN sys.partitions AS p
ON pstats.partition_id = p.partition_id
WHERE p.[object_id] = OBJECT_ID('Fact.RecordedMetricsDetail');
第一个计划中的假脱机迭代器有什么合理的好处吗?我不得不假设它是为了提高性能而不是为了万圣节保护,因为它的存在并不一致。
我在 2016 CTP 3.1 上对此进行了测试,但我在 2014 SP1 CU3 上看到了相同的行为。
我已经发布了一个生成模式和数据的脚本,并在此处引导您演示问题。
这个问题主要是出于对优化器此时行为的好奇,因为我有一个解决方法来解决提示问题的问题(一个大的假脱机填充了 TempDB)。我现在正在通过使用分区切换来删除。
我想在完成需要不同时间的 SSIS 包运行的一些 ETL 工作后执行 SSRS 订阅。我正在考虑使用这种方法,它基本上由 SSIS 调用与订阅的 SQL Server 代理作业调用以执行订阅相同的存储过程组成。
但是,我不确定管理执行订阅的权限的最佳方式是什么。例如,我不愿意将用户添加到 中,RsExecRole
因为 Microsoft 在某些文档中不鼓励这样做,并且我不愿意授予对象级权限来执行该ReportServer.ado.AddEvent
过程,因为它们可能会在任何迁移中被忽略。(编辑:我想我们可以通过执行代理作业而不是调用存储过程来解决这个问题。)
我还希望避免在其中一个 GUID 更改时执行订阅中断的代码出现问题。
SSRS 专业人员如何管理此类请求的权限?或者,是否有更简单的方法可以绕过这些问题?
假设我有一个Foo
包含列的表ID1, ID2
和一个在 上定义的复合主键ID2, ID1
。(我目前正在使用一个 System Center 产品,该产品有几个以这种方式定义的表,其中主键列的排列顺序与它们在表定义中出现的顺序相反。)
CREATE TABLE dbo.Foo(
ID1 int NOT NULL,
ID2 int NOT NULL,
CONSTRAINT [PK_Foo] PRIMARY KEY CLUSTERED (ID2, ID1)
);
GO
-- Add a row and update stats so that histogram isn't empty
INSERT INTO Foo (ID1, ID2) VALUES (1,2);
UPDATE STATISTICS dbo.Foo;
中的key_ordinal
列sys.index_columns
显示索引列的顺序与它们在复合主键中声明的顺序相同:
SELECT t.name, i.name, c.column_id, c.name, ic.index_column_id, ic.key_ordinal
FROM sys.tables AS t
JOIN sys.indexes AS i
ON t.[object_id] = i.[object_id]
JOIN sys.index_columns AS ic
ON ic.[object_id] = i.[object_id]
AND ic.index_id = i.index_id
JOIN sys.columns AS c
ON ic.column_id = c.column_id
AND ic.[object_id] = c.[object_id]
WHERE t.name = 'Foo';
直方图还以相同的顺序显示统计数据:
DBCC SHOW_STATISTICS ('Foo',PK_Foo);
但是,sys.stats_columns
显示以相反顺序列出的列 ( ID1, ID2
)。
SELECT s.name, sc.stats_column_id, c.name
FROM sys.stats AS s
JOIN sys.stats_columns AS sc
ON s.stats_id = sc.stats_id
AND s.[object_id] = sc.[object_id]
JOIN sys.columns AS c
ON c.[object_id] = s.[object_id]
AND c.column_id = sc.column_id
JOIN sys.objects AS o
ON o.[object_id] = c.[object_id]
WHERE o.name = 'Foo'
AND s.name = 'PK_Foo';
联机丛书说stats_column_id
是“一组统计列中基于 1 的序数”,因此我希望值 1 指向统计对象中的第一列。
这是我的错误sys.stats_columns
还是我的误解?
我已验证此行为发生在 SQL Server 2005、2008、2008 R2、2012 和 2014 的当前版本上。
sys.stats_columns
似乎反映了其他情况下统计对象内的顺序,例如:
CREATE TABLE dbo.Foo2(
ID1 int NOT NULL,
ID2 int NOT NULL,
ID3 int NULL,
String VARCHAR(10) NULL,
CONSTRAINT [PK_Foo2] PRIMARY KEY CLUSTERED (ID2, ID1)
);
GO
INSERT INTO Foo2 (ID1, ID2, ID3, String) VALUES (1,2,3,'String');
CREATE STATISTICS ST_Test ON Foo2 (ID3, String);
CREATE STATISTICS ST_Test2 ON Foo2 (String, ID3);
DBCC SHOW_STATISTICS ('Foo2',ST_Test);
DBCC SHOW_STATISTICS ('Foo2',ST_Test2);
SELECT s.name, sc.stats_column_id, c.name
FROM sys.stats AS s
JOIN sys.stats_columns AS sc
ON s.stats_id = sc.stats_id
AND s.[object_id] = sc.[object_id]
JOIN sys.columns AS c
ON c.[object_id] = s.[object_id]
AND c.column_id = sc.column_id
JOIN sys.objects AS o
ON o.[object_id] = c.[object_id]
WHERE o.name = 'Foo2'
AND s.name LIKE 'ST_Test%';
这是另一个sys.stats_columns
似乎返回正确数据的示例,这次是针对索引的统计信息:
--drop table dbo.Foo3
CREATE TABLE dbo.Foo3(
ID1 int NOT NULL,
ID2 int NOT NULL,
ID3 int NULL,
String VARCHAR(10) NULL,
CONSTRAINT [PK_Foo3] PRIMARY KEY CLUSTERED (ID2, ID1)
);
GO
INSERT INTO Foo3 (ID1, ID2, ID3, String) VALUES (1,2,3,'String');
UPDATE STATISTICS Foo3;
CREATE INDEX IX_Test ON Foo3 (ID3, String);
CREATE INDEX IX_Test2 ON Foo3 (String, ID3);
DBCC SHOW_STATISTICS ('Foo3',IX_Test);
DBCC SHOW_STATISTICS ('Foo3',IX_Test2);
SELECT s.name, sc.stats_column_id, c.name
FROM sys.stats AS s
JOIN sys.stats_columns AS sc
ON s.stats_id = sc.stats_id
AND s.[object_id] = sc.[object_id]
JOIN sys.columns AS c
ON c.[object_id] = s.[object_id]
AND c.column_id = sc.column_id
JOIN sys.objects AS o
ON o.[object_id] = c.[object_id]
WHERE o.name = 'Foo3'
AND s.name LIKE 'IX_Test%';
我正在与一位正在努力阻止以下错误的开发人员合作:
服务器将断开连接,因为客户端驱动程序在会话处于单用户模式时发送了多个请求。当会话中仍有批处理正在运行时,客户端发送重置连接的请求,或者当会话正在重置连接时客户端发送请求时,会发生此错误。请联系客户端驱动程序供应商。
发生此错误时,典型的结果是会话持有锁,但没有运行 SQL(sys.dm_exec_requests
session_id 没有行),并导致阻塞,直到它被杀死。阻塞的原因很简单,但如何阻止错误的发生却不是。
关于这个问题的一些事实:
sys.dm_exec_connections
为 .Net SqlClient 数据提供程序net_transport
所示。sys.dm_exec_connections
Session
如果在连接字符串上设置 Async=true 应该可以防止这个问题。如果您使用 MARS 并在多个并发线程上使用相同的连接,则可能发生的情况是重置连接的调用可能会稍微延迟并触发此错误。如果您设置 Async=true,我们会在客户端驱动程序中执行额外的锁定以防止这种情况发生。
关于此错误的更多信息很少。它似乎表明客户端的程序sp_reset_connection
在批处理仍在运行时正在调用。我可以设置跟踪来确认这一点,但我必须记录很多不相关的活动,而且问题每隔几天才会发生一次。此外,我不确定开发人员是否能够利用这些证据来解决问题。
作为 DBA 或系统管理员,我是否可以使用任何其他技术来进一步解决此问题,或者我可以向开发人员建议任何可能为他提供有用信息或减少问题发生的可能性?
给定以下常量:
给定这些常量,SQL Server 是否会始终为给定查询生成相同的计划?
如果没有,是否有其他考虑?是否还要考虑不确定性因素?
我正在做一个项目,我需要查找与发出 HTTP 请求的记录的 IP 地址关联的主机名。目前,查找是日常 ETL 工作的一部分。当前的方法是使用标量 CLR 函数(与此类似的代码在网络上的许多地方发布,下面发布了我的修改;我不确定原作者是谁):
using System.Data.SqlTypes;
using System.Net;
using System.Security;
using System.Text.RegularExpressions;
using Microsoft.SqlServer.Server;
public partial class udfn_GetHostName
{
[Microsoft.SqlServer.Server.SqlFunction]
public static string udfn_GetHostname(string IPAddr)
{
try
{
/*
Using deprecated method intentionally.
GetHostEntry() is now recommended.
But it does some irritating things like returning an error if a PTR
record points to a name that doesn't have an A record.
*/
IPHostEntry IpEntry = Dns.GetHostByAddress(IPAddr);
// Test whether the record returned has at least one alphabetic character
// If it does, then it's a name
// Otherwise the DNS server might have returned the IP address
Match match = Regex.Match(IpEntry.HostName.ToString(), @"[a-zA-Z]+");
if (match.Success)
{
return IpEntry.HostName.ToString();
}
else
{
return "None";
}
}
catch(Exception ex)
{
return "Failed";
//return ex.Message.ToString();
}
}
}
我不是 C# 开发人员,因此 CLR 代码的质量可能不是很好。
然后我在将新行加载到维度后调用这样的函数:
-- Update only rows that we just inserted
UPDATE DIM.Network_Addresses
SET reverse_dns = dbo.[udfn_GetHostname](client_ip)
WHERE reverse_dns IS NULL
AND is_current = 1
AND created_date = (SELECT MAX(created_date) FROM DIM.API_Network_Address);
这种方法有效但速度很慢,至少有几个原因。
1) 使用标量函数使 SQL Server 使用新的 SQL 上下文对需要更新的每一行调用一次 CLR 函数。
2) 由于 GetHostname() 和其他 CLR 名称解析函数的工作方式,函数调用本身非常慢:长时间超时,有时多次往返网络,如果 DNS 服务器不响应或没有 PTR,则所有超时记录等
可以推荐一种设计模式来提高查找反向 DNS 记录和更新表的性能吗?
我正在考虑一些不同的事情:
1) 将这项工作移到数据库之外,并使用诸如 dig 之类的工具并行进行查找。
2)尝试找到一些方法来并行调用函数或将其转换为内联函数(在这方面没有取得太大进展!)
但是,欢迎任何想法。
尝试使用特定 SQL Server 群集的网络名称将 SSMS 连接到 Integration Services 时收到以下错误:
连接到计算机“FooDB”上的集成服务服务失败,出现以下错误:“访问被拒绝。”
如果计算机尚未配置为允许通过 DCOM 进行远程连接,或者用户确实有权通过 DCOM 访问 SQL Server Integration Services 服务,则会出现此错误。
这是一个有据可查的解决方案的常规问题。例如,请参阅此处和此处的解决方案。
但是,我已经尝试了所有我知道的解决方案,但问题仍然存在。
更详细地说,我做了以下事情:
验证连接的用户是否具有上面链接到 MsDtsServer100 上的文章中列出的 DCOM 权限:
启动和激活权限:允许本地启动、允许远程启动、本地激活、远程激活
访问权限:允许本地访问,允许远程访问
配置权限:允许读取
通过数据包嗅探器确认与连接相关的所有流量都已成功通过防火墙。在 TCP 连接断开之前显示的最后一个数据包是来自服务器的回复,其中包含 MSRPC 标头内的“拒绝访问”的 Windows 状态代码。
测试将用户添加到“分布式 COM 用户”组和/或本地管理员组,然后重新启动服务器。这允许用户使用本地节点名称(FooDBN1、FooDBN2)从 SSMS 连接到 SSIS,但是在连接到集群网络名称(FooDB)时,他们仍然会收到“访问被拒绝”错误,这是他们习惯的使用,以及在我们的其他集群上有效的方法。
此外,我还没有发现在其他集群上需要更改这些组的成员身份。
在我检查过的其他集群上,我可以使用集群名称将 SSMS 连接到 SSIS,而无需任何非默认配置。
我意识到这可能更适合 ServerFault 并且可以根据需要迁移问题,但这也是 SQL Server 问题,我认为这里的用户以前可能更有可能处理过它。
平台详情:
谁能建议我接下来应该看什么?
更新:这神秘地今天才开始工作,但仅适用于本地管理员组的成员。据我所知,一切都没有改变。
我见过几个人SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED
在阅读系统 DMV 之前打电话。假设您没有在同一事务中混合调用 DMV 和表格,是否有任何理由这样做?
我从客户端收到的一个常见请求是帮助找出哪些查询或客户端对 SQL Server 使以太网链路饱和的贡献最大。
理想情况下,我会查看一些计数器或 DMV,显示按查询、客户端、应用程序、数据库等聚合的网络 I/O。
由于似乎没有直接的方法,作为替代,我通常查看 sys.dm_exec_query_stats 中的 *_rows 列(我认为这些列仅存在于 2008 R2+ 中)并查找似乎涉及 BLOB。
有没有更好的方法来解决这个问题?
有没有办法通过登录 SQL Server 来获取资源使用情况统计信息?
理想情况下,我希望有一个版本sys.dm_exec_sessions
可以跟踪所有会话的使用情况,而不仅仅是当前连接的会话。然后我可以运行这样的东西:
select login_name, sum(reads) as reads,
sum(writes) as writes,
sum(cpu_time) as cpu_time,
count (login_name) as sessions
from sys.dm_Exec_Sessions
where login_name is not null
group by login_name
显然,这是当前运行的,但仅显示当前连接会话的统计信息。
我的雇主正在考虑部署使用 Windows MoveFile API 对打开的文件进行碎片整理的 NTFS 碎片整理软件。这将部署到数千台运行 2005-2012 年 SQL 版本和 2003 年和 2008 R2 年 Windows 版本的 SQL Server 服务器。如果重要的话,我所说的产品是 PerfectDisk,但我相信有类似的程序可以以相同的方式工作。
到目前为止,除了偶尔出现的 I/O 性能问题外,测试没有发现很多问题,这并不奇怪,可以通过重新安排和调整碎片整理来解决。但是,我更担心数据损坏的风险。
这里有人有在数据库服务器上运行此类软件的经验吗?您遇到过任何数据损坏吗?
尽管无法找到任何确凿的证据表明它会带来问题,但我对此感到相当不安。
感谢您的回复。
编辑添加:谢天谢地,这个可怕的想法被遗忘了,部分原因可能是我给出的一些警告。