RDS SQL Server - 专题分享 - 巧用执行计划缓存之Table Scan

本文涉及的产品
RDS SQL Server Serverless,2-4RCU 50GB 3个月
推荐场景:
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
简介: # 背景引入 执行计划中的Table Scan或者是Clustered Index Scan会导致非常低下的查询性能,尤其是对于大表或者超大表。执行计划缓存是SQL Server内存管理中非常重要的特性,这篇系列文章我们探讨如何从执行计划缓存的角度来发现RDS SQL数据库引擎中的Table Scan行为,以及与之相应SQL查询语句详细信息。 # 问题分析 其实,我们大家都知道,Table

背景引入

执行计划中的Table Scan或者是Clustered Index Scan会导致非常低下的查询性能,尤其是对于大表或者超大表。执行计划缓存是SQL Server内存管理中非常重要的特性,这篇系列文章我们探讨如何从执行计划缓存的角度来发现RDS SQL数据库引擎中的Table Scan行为,以及与之相应SQL查询语句详细信息。

问题分析

其实,我们大家都知道,Table Scan或者Clustered Index Scan是关系型数据库查询性能很差的一种表扫描查询方式,如果在数据库引擎中存在大量的Table Scan行为的话,一般数据库性能都不会好到哪里去。在阿里云RDS SQL产品中,我们可以很简单的查看发生在RDS SQL引擎中的Table Scan频率,方法是:在阿里云控制台 云数据库RDS版 => 实例列表 => 打开对应的RDS SQL实例(在此以RDS SQL 2008R2版本为例)=> 监控与报警 => 引擎监控
01.png

往下拉动滚动条,会看到性能指标“平均每秒全表扫描次数”,表示发生在RDS SQL 2008R2数据库引擎中平均每秒表扫描的次数。
02.png

在很多次性能优化的案例中,我们告诉客人:“您的RDS 实例中,存在很多Table Scan行为,导致查询效率低下”,这时候客户会立马反问:那您知道我的哪些查询语句导致了Table Scan操作吗?今天这篇文章就是分享这个问题的解法:我们从执行计划缓存的角度来获取哪些查询语句导致了RDS SQL数据库引擎的Table Scan行为。

测试脚本

为了模拟出RDS SQL数据库引擎执行查询语句使用Table Scan的行为,我们创建一张没有任何Index,没有主键的表,接下来初始化10000条数据,然后循环执行查询语句和存储过程。测试的代码如下所示:

USE TestDb
GO

-- create demo table TestTableScan
IF OBJECT_ID('dbo.TestTableScan', 'U') IS NOT NULL
BEGIN
    DROP TABLE dbo.TestTableScan
END
GO

CREATE TABLE dbo.TestTableScan
(
    RowID INT IDENTITY(1,1) NOT NULL
    , OrderID UNIQUEIDENTIFIER NOT NULL
    , ItemID INT NOT NULL
);

-- data init for 10000 records.
;WITH a 
AS (
    SELECT * 
    FROM (VALUES(1),(2),(3),(4),(5),(6),(7),(8),(9),(10)) AS a(a)
), RoundData
AS(
SELECT TOP(10000)
    OrderID = NEWID()
    ,ItemIDRound = abs(checksum(newid()))
    ,Price = a.a * b.a * 10
    ,OrderQty = a.a + b.a + c.a + d.a + e.a + f.a + g.a + h.a
FROM a, a AS b, a AS c, a AS d, a AS e, a AS f, a AS g, a AS h
)
INSERT INTO dbo.TestTableScan(OrderID, ItemID)
SELECT 
    OrderID
    ,ItemID = cast(ROUND((1300 * (ItemIDRound*1./cast(replace(ItemIDRound, ItemIDRound, '1' + replicate('0', len(ItemIDRound))) as bigint)) + 101), 0) as int)
FROM RoundData

GO


--Test ADHOC
DECLARE
    @do INT = 0
    ,@loop INT = 5;
WHILE @do < @loop
BEGIN
    SELECT * FROM dbo.TestTableScan AS A
        INNER JOIN dbo.TestTableScan AS B
        ON A.ItemID = B.ItemID

    SET @do = @do + 1;
END
GO

--Call Store Procedure Testing
CREATE PROC dbo.UP_TableScanTesting
AS
BEGIN
    SET NOCOUNT ON
    SELECT * FROM dbo.TestTableScan AS A
        INNER JOIN dbo.TestTableScan AS B
        ON A.ItemID = B.ItemID

    SELECT TOP 1 * FROM sys.objects
    where object_id = @@PROCID
END
GO

EXEC dbo.UP_TableScanTesting
GO

解决方法

首先,我们可以动态视图sys.dm_exec_query_stats中根据每一个查询的plan_handle来统计该查询的执行次数、总共耗时、总的逻辑读、写、总共CPU时间消耗等信息;然后通过执行计划缓存函数sys.dm_exec_query_plan来分析相应的查询语句的详细信息。详情脚本参加如下代码:

-- Generate all query SQL text with "table scan" in cached query plan 
;WITH  XMLNAMESPACES 
    (DEFAULT N'http://schemas.microsoft.com/sqlserver/2004/07/showplan'   
            ,N'http://schemas.microsoft.com/sqlserver/2004/07/showplan' AS ShowPlan)
,EQS 
AS (
    SELECT EQS.plan_handle 
           ,SUM(EQS.execution_count) AS ExecutionCount 
           ,SUM(EQS.total_worker_time) AS TotalWorkTime 
           ,SUM(EQS.total_logical_reads) AS TotalLogicalReads 
           ,SUM(EQS.total_logical_writes) AS TotalLogicalWrites 
           ,SUM(EQS.total_elapsed_time) AS TotalElapsedTime 
           ,MAX(EQS.last_execution_time) AS LastExecutionTime 
     FROM sys.dm_exec_query_stats AS EQS 
     GROUP BY EQS.plan_handle
), info
AS(
    SELECT DISTINCT
        EQS.[ExecutionCount] 
        ,EQS.[TotalWorkTime] 
        ,EQS.[TotalLogicalReads] 
        ,EQS.[TotalLogicalWrites] 
        ,EQS.[TotalElapsedTime] 
        ,EQS.[LastExecutionTime] 
        ,ScanObject = StmtSimple.Node.value('(QueryPlan/RelOp//RelOp//TableScan/Object/@Database)[1]','sysname') + '.' +
                    StmtSimple.Node.value('(QueryPlan/RelOp//RelOp//TableScan/Object/@Schema)[1]','sysname') + '.' +
                    StmtSimple.Node.value('(QueryPlan/RelOp//RelOp//TableScan/Object/@Table)[1]','sysname')
        ,Statement = StmtSimple.Node.value('(@StatementText)[1]', 'nvarchar(max)')
        ,EST.text
        ,ECP.[objtype] AS [ObjectType] 
        ,ECP.[cacheobjtype] AS [CacheObjectType]
        ,EQS.plan_handle
    FROM sys.dm_exec_cached_plans AS ECP
        INNER JOIN EQS
        ON ECP.plan_handle = EQS.plan_handle
        CROSS APPLY sys.dm_exec_sql_text(ECP.[plan_handle]) AS EST
        CROSS APPLY sys.dm_exec_query_plan(ECP.[plan_handle]) AS EQP
        CROSS APPLY EQP.query_plan.nodes('/ShowPlanXML/BatchSequence/Batch/Statements/StmtSimple') AS StmtSimple(Node)
    WHERE StmtSimple.Node.exist('data(QueryPlan/RelOp//RelOp[@PhysicalOp="Table Scan"])') = 1
)
SELECT A.*, EQP.query_plan
FROM info AS A
    CROSS APPLY sys.dm_exec_query_plan(A.[plan_handle]) AS EQP
ORDER BY A.TotalElapsedTime DESC, A.ExecutionCount

执行上面的查询,结果展示如下图所示:
03.png

从分析结果的截图来看,我们可以得到很多有用的信息,比如:
每个查询语句的执行次数、总执行耗时、逻辑读写、对应Table Scan的查询Statement和详细的查询Batch语句,以及详细的执行计划等有用信息。

最后总结

这篇文章讨论了如何从执行计划缓存中找到Table Scan查询语句的详情,利用这个方法可以很轻松的找到RDS SQL客户需要的答案。

相关实践学习
如何在云端创建MySQL数据库
开始实验后,系统会自动创建一台自建MySQL的 源数据库 ECS 实例和一台 目标数据库 RDS。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
目录
相关文章
|
18天前
|
SQL 缓存 关系型数据库
美团面试:Mysql 有几级缓存? 每一级缓存,具体是什么?
在40岁老架构师尼恩的读者交流群中,近期有小伙伴因未能系统梳理MySQL缓存机制而在美团面试中失利。为此,尼恩对MySQL的缓存机制进行了系统化梳理,包括一级缓存(InnoDB缓存)和二级缓存(查询缓存)。同时,他还将这些知识点整理进《尼恩Java面试宝典PDF》V175版本,帮助大家提升技术水平,顺利通过面试。更多技术资料请关注公号【技术自由圈】。
美团面试:Mysql 有几级缓存? 每一级缓存,具体是什么?
|
16天前
|
缓存 NoSQL 关系型数据库
mysql和缓存一致性问题
本文介绍了五种常见的MySQL与Redis数据同步方法:1. 双写一致性,2. 延迟双删策略,3. 订阅发布模式(使用消息队列),4. 基于事件的缓存更新,5. 缓存预热。每种方法的实现步骤、优缺点均有详细说明。
|
2月前
|
SQL 关系型数据库 MySQL
创建包含MySQL和SQLServer数据库所有字段类型的表的方法
创建一个既包含MySQL又包含SQL Server所有字段类型的表是一个复杂的任务,需要仔细地比较和转换数据类型。通过上述方法,可以在两个数据库系统之间建立起相互兼容的数据结构,为数据迁移和同步提供便利。这一过程不仅要考虑数据类型的直接对应,还要注意特定数据类型在不同系统中的表现差异,确保数据的一致性和完整性。
32 4
|
2月前
|
缓存 NoSQL 关系型数据库
MySQL与Redis缓存一致性的实现与挑战
在现代软件开发中,MySQL作为关系型数据库管理系统,广泛应用于数据存储;而Redis则以其高性能的内存数据结构存储特性,常被用作缓存层来提升数据访问速度。然而,当MySQL与Redis结合使用时,确保两者之间的数据一致性成为了一个重要且复杂的挑战。本文将从技术角度分享MySQL与Redis缓存一致性的实现方法及其面临的挑战。
144 2
|
2月前
|
关系型数据库 MySQL 网络安全
5-10Can't connect to MySQL server on 'sh-cynosl-grp-fcs50xoa.sql.tencentcdb.com' (110)")
5-10Can't connect to MySQL server on 'sh-cynosl-grp-fcs50xoa.sql.tencentcdb.com' (110)")
|
2月前
|
关系型数据库 MySQL 数据库
docker启动mysql多实例连接报错Can’t connect to local MySQL server through socket ‘/var/run/mysqld/mysqld.sock’
docker启动mysql多实例连接报错Can’t connect to local MySQL server through socket ‘/var/run/mysqld/mysqld.sock’
178 0
|
3月前
|
缓存 NoSQL 算法
【Azure Redis 缓存】Redis性能指标之Server Load
【Azure Redis 缓存】Redis性能指标之Server Load
|
3月前
|
缓存 NoSQL Redis
一天五道Java面试题----第九天(简述MySQL中索引类型对数据库的性能的影响--------->缓存雪崩、缓存穿透、缓存击穿)
这篇文章是关于Java面试中可能会遇到的五个问题,包括MySQL索引类型及其对数据库性能的影响、Redis的RDB和AOF持久化机制、Redis的过期键删除策略、Redis的单线程模型为何高效,以及缓存雪崩、缓存穿透和缓存击穿的概念及其解决方案。
|
3月前
|
数据采集 关系型数据库 MySQL
大数据-业务数据采集-FlinkCDC The MySQL server is not configured to use a ROW binlog_format
大数据-业务数据采集-FlinkCDC The MySQL server is not configured to use a ROW binlog_format
41 1
|
3月前
|
缓存 NoSQL 关系型数据库
(八)漫谈分布式之缓存篇:唠唠老生常谈的MySQL与Redis数据一致性问题!
本文来聊一个跟实际工作挂钩的老生常谈的问题:分布式系统中的缓存一致性。
153 11