我有一段 SQL 似乎在环境 A 中运行得非常快,但完全相同的查询在环境 B 中运行得非常慢!
环境应该是相同的,所以我应该做什么和/或我应该在哪里查看查询为什么不执行相同?
我们有一个包含超过 1TB 数据的 mariadb 表(故事),定期运行一个查询来获取最近添加的行以在其他地方建立索引。
innodb_version: 5.6.36-82.1
version : 10.1.26-MariaDB-0+deb9u1
Run Code Online (Sandbox Code Playgroud)
当查询优化器决定使用二级索引进行范围遍历(以 1000 为单位)时,查询工作正常
explain extended SELECT stories.item_guid
FROM `stories`
WHERE (updated_at >= '2018-09-21 15:00:00')
AND (updated_at <= '2018-09-22 05:30:00')
ORDER BY `stories`.`id` ASC
LIMIT 1000;
+------+-------------+---------+-------+-----------------------------+-----------------------------+---------+------+--------+----------+---------------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+------+-------------+---------+-------+-----------------------------+-----------------------------+---------+------+--------+----------+---------------------------------------+
| 1 | SIMPLE | stories | range | index_stories_on_updated_at | index_stories_on_updated_at | 5 | NULL | 192912 | 100.00 …Run Code Online (Sandbox Code Playgroud) 我知道与使用如下谓词编写的存储过程相关的参数嗅探问题:
CREATE PROCEDURE [dbo].[Get] @Parameter INT = NULL AS BEGIN;
SELECT [Field] FROM [dbo].[Table]
WHERE [Field] = @Parameter
OR @Parameter IS NULL;
END;
Run Code Online (Sandbox Code Playgroud)
根据参数的值,第一次执行时是标量还是 NULL,一个计划被缓存,对于相反的值可能是次优的。
假设 [Field] 是标量,并且是表上的聚簇索引。以下编写存储过程以支持查询的方法的优缺点是什么:
同一个存储过程中的条件选择
CREATE PROCEDURE [dbo].[Get] @Parameter INT = NULL AS BEGIN;
IF(@Parameter IS NOT NULL) BEGIN;
SELECT [Field]
FROM [dbo].[Table]
WHERE [Field] = @Parameter;
END;
ELSE BEGIN;
SELECT [Field]
FROM [dbo].[Table];
END;
END;
Run Code Online (Sandbox Code Playgroud)
存储过程中的动态 SQL
CREATE PROCEDURE [dbo].[Get] @Parameter INT = NULL AS BEGIN;
DECLARE @sql NVARCHAR(MAX) = N'';
SET @sql += N'SELECT …Run Code Online (Sandbox Code Playgroud) 我们有一个针对单项查询优化的视图(200 毫秒无并行性):
select *
from OptimizedForSingleObjectIdView e2i
where ObjectId = 3374700
Run Code Online (Sandbox Code Playgroud)
它也适用于一小组静态 ID(~5)。
select *
from OptimizedForSingleObjectIdView e2i
where ObjectId in (3374700, 3374710, 3374720, 3374730, 3374740);
Run Code Online (Sandbox Code Playgroud)
但是,如果对象来自外部源,那么它会生成一个缓慢的计划。执行计划显示视图部分的执行分支忽略了 ObjectId 上的谓词,而在原始情况下,它使用它们来执行索引查找。
select v.*
from
(
select top 1 ObjectId from Objects
where ObjectId % 10 = 0
order by ObjectId
) o
join OptimizedForSingleObjectIdView v -- (also tried inner loop join)
on v.ObjectId = o.ObjectId;
Run Code Online (Sandbox Code Playgroud)
我们不希望投资于“双重”优化非奇异情况的视图。相反,我们“寻求”的解决方案是对每个对象重复调用一次视图,而无需求助于 SP。
大多数情况下,以下解决方案会逐行调用视图。但是这次不是,甚至不是只有 1 个对象:
select v.*
from
(
select top 1 ObjectId
from Objects …Run Code Online (Sandbox Code Playgroud) 我正在审查一个性能不佳的查询,如下所示:
WHERE manymany.Active = -1
AND manymany.Check1 = -1
AND manymany.WebsiteID = @P1
AND CURRENT_TIMESTAMP BETWEEN ISNULL(manymany.FromDate, '1950-01-01') AND ISNULL(manymany.UptoDate, '2050-01-01')
AND main.Active = -1
AND main.StatusID = 1
AND CURRENT_TIMESTAMP BETWEEN main.FromDate AND ISNULL(main.UptoDate, '2050-01-01')
AND (main.TextCol1 IS NOT NULL OR main.TextCol2 IS NOT NULL)
ORDER BY aux.SortCode
Run Code Online (Sandbox Code Playgroud)
我不小心在这个查询上使用了 SSMS 查询设计器,它重新编写了查询,如下所示:
WHERE manymany.Active = -1
AND manymany.Check1 = -1
AND manymany.WebsiteID = @P2
AND CURRENT_TIMESTAMP BETWEEN ISNULL(manymany.FromDate, '1950-01-01') AND ISNULL(manymany.UptoDate, '2050-01-01')
AND main.Active = -1
AND main.StatusID = 1 …Run Code Online (Sandbox Code Playgroud) 我们支持大量对查询性能不太了解或根本不关心的开发人员和分析师。我们每天都会有数百个构思不佳的查询(也由于设计不佳的数据库架构),每个查询都会从我们的服务器中吸取数十 GB 的 RAM,并将执行计划存储在 SQL 缓存中的 MB 范围内。
我们想限制某些用户使用 SORT 和 HASH MATCH,特别是 ORDER BY、SELECT DISTINCT 和任何类型的 JOIN。我们的目的是让用户提取他们的数据,但只在他们这边工作。
这可以在 SQL Server 中实现吗?有没有支持它的数据库引擎?您对此有何想法?
我知道这会违反关系数据库的原则,但我们正在寻找某种方法来强制正确使用引擎而不是未受过教育的用户滥用。
我们已经在考虑实现 Resource Governor 为这些坏查询分配一定数量的机器资源。
我不确定我从哪里开始,但是有没有办法查看优化器为查询生成查询计划花费了多长时间?它是否存储在任何 DMV 或某个统计数据的一部分中?或者,如果我包含实时统计数据或实际执行计划,我可以以某种方式计算它吗?也许在查询存储中?
sql-server optimization statistics execution-plan sql-server-2016
我目前正在调查一个应用程序,该应用程序似乎针对它正在查询的数据库生成 99% 的即席查询计划。我可以通过运行以下语句来检索查询计划缓存中的对象摘要来验证这一点:
抱歉无法在 SE 编辑器中输入代码,因此截图
参考:计划缓存和优化临时工作负载(SQLSkills.com / K. Tripp) 稍作修改
上述查询的结果如下:
CacheType Total Plans Total MBs Avg Use Count Total MBs - USE Count 1 Total Plans - USE Count 1
-------------------- -------------------- --------------------------------------- ------------- --------------------------------------- -------------------------
Adhoc 158997 5749.520042 2 2936.355979 126087
Prepared 1028 97.875000 695 46.187500 576
Proc 90 69.523437 39659 21.187500 21
View 522 75.921875 99 0.453125 3
Rule 4 0.093750 22 0.000000 0
Trigger 1 0.070312 12 0.000000 0
Run Code Online (Sandbox Code Playgroud)
在计划缓存中的 158'997 个即席查询中,126'087 个查询只执行了一次。 …
当执行查询时,SQL Server将产生一个查询计划列表,并启发式地选择成本较低的计划。
所选择的计划将存储在计划缓存中,以供后续看到相同查询时使用。
当表的某些属性发生变化或者重建索引时,它会再次产生一个查询计划列表,并启发式地选择一个成本较低的查询计划,并将其存储在计划缓存中。
但是,MSDN 似乎表明估计计划存储在计划缓存中,请参见下面的屏幕截图。那是对的吗?
使用 StackOverflow2010 数据库,我可以在 users 表上创建索引,如下所示:
CREATE INDEX IX_DisplayName ON dbo.Users
(
DisplayName,
UpVotes
)
Run Code Online (Sandbox Code Playgroud)
然后对索引的键运行不等式搜索:
SELECT DisplayName,
UpVotes
FROM Users
WHERE DisplayName <> N'Alex'
Run Code Online (Sandbox Code Playgroud)
我在这里得到计划
我正在尝试弄清楚 SQL Server 如何获取此查询的结果。
该计划从一些持续扫描开始,但输出列表是空白的,因此我不清楚它们的用途。
然后,每个恒定扫描进入一个计算标量,每个计算标量输出
Compute Scalar Node6
Expr1002 = 10
Expr1003 = NULL
Expr1004 = N'Alex'
Compute Scalar Node9
Expr1005 = 6
Expr1006 = N'Alex'
Expr1007 = NULL
Run Code Online (Sandbox Code Playgroud)
然后,连接运算符似乎连接了上面的一些输出:
Expr1010 = Expr1008,Expr1006
Expr1011 = Expr1004,Expr1009
Expr1012 = Expr1002,Expr1005
Run Code Online (Sandbox Code Playgroud)
但它有我在计划中看不到的输入(Expr 1008 和 Expr1009)
我也不知道为什么需要TOP N排序
索引搜索是有意义的 - 它正在寻找 > Expr1011 和 < Expr1012。我 …
execution-plan ×10
sql-server ×9
optimization ×2
performance ×2
plan-cache ×2
cross-apply ×1
innodb ×1
mariadb ×1
statistics ×1
view ×1