LK存储过程的执行信息

近来遇到一个比较困扰的优化问题,存储过程的主体逻辑如下:

ALTER PROC [dbo].[DBA_TroubleShooting]
AS
BEGIN
    DECLARE @StartTime DATETIME
    DECLARE @EndTime DATETIME
    SELECT @StartTime=CONVERT(VARCHAR,GETDATE()-1,112),@EndTime=CONVERT(VARCHAR,GETDATE(),112)
    DECLARE @Spreader VARCHAR(32)
    DECLARE loop_cursor CURSOR FOR
    SELECT spreadAccount FROM dbo.SpreadStatisticsAccount ssa WITH(NOLOCK)
    --WHERE spreadAccount=‘LG0537‘ --分别用LG0537、LG0540测试
    OPEN loop_cursor;
    FETCH NEXT FROM loop_cursor INTO @Spreader

    WHILE @@FETCH_STATUS = 0
    BEGIN

        -- 日登录用户
        DECLARE @DayLoginUserNum INT
        SELECT @DayLoginUserNum = COUNT(DISTINCT ais.UserId)
        FROM dbo.AccountsInfoSimple ais WITH(NOLOCK)
        INNER JOIN dbo.UserLoginRecord t WITH(NOLOCK) ON ais.userID = t.userID
        WHERE ais.spreaderAccount = @Spreader AND ais.userType = 0
        AND t.loginDate BETWEEN @StartTime AND @EndTime AND t.typeCode = ‘02‘;

        SELECT @DayLoginUserNum
        --注册、登录、充值、活跃数、游戏时长...
        --INSERT INTO dbo.SpreadAccountStatisticsNew(...)VALUES(...,@Spreader,@DayLoginUserNum,...)

        FETCH NEXT FROM loop_cursor INTO @Spreader
    END
    CLOSE loop_cursor;
    DEALLOCATE loop_cursor;
END

游标统计各推广号对应的注册、登录、充值、活跃数、游戏时长等数据,并将结果插入到统计表。这里变量是在存储过程里面定义的,它的值是在存储过程的语句执行的过程中得到的。对于这种本地变量,SQL Server在编译的时候不知道它的值是多少。
我们使用代码中提供的两个推广号分别测试存储过程的执行情况,spreaderAccount=‘LG0537‘



spreaderAccount=‘LG0540‘



它们的执行计划相同,并不是重用了执行计划。每次都是先修改存储过程(对应缓存被清除),然后再执行存储过程。从执行计划可以看出,对于@Spreader变量的两个不同值,它对AccountsInfoSimple表的估计行数(EstimateRows)都是2978.927。但实际上LG0537只有1条记录,LG0540却有955760条记录。它们记录数直接影响UserLoginRecord的执行次数(Executes)。
一般来说,使用本地变量作出来的执行计划是一种比较"中庸"的方法,不是最快的,也不是最慢的。它对语句性能的影响,一般不会有parameter sniffing那么严重。很多时候,它还是解决parameter sniffing的一个候选方案。本例中的@Spreader是本地变量,但是它选择的执行计划并不太好。尤其是@Spreader在AccountsInfoSimple表返回记录数很多时,嵌套循环简直就是一个噩梦。
修改推广号语句where spreadAccount in(‘LG0537‘,‘LG0540‘),实际执行计划如下:


针对每一个推广号都可以看到计算@DayLoginUserNum的执行计划,并且会有每个操作的实际行数、执行次数信息。如果从sys.dm_exec_query_plan中取对应的执行计划,只有估计信息,不能一眼看出选择的操作是否合适:

查看过程中语句执行情况:

存储过程执行一次(ProcExCount),@DayLoginUserNum语句执行两次(SQLExCount),从逻辑读、CPU时间、总时间可知,语句第二次执行占大部分开销。这里第二次的@DayLoginUserNum重用第一次的语句缓存计划。
如果在日登录用户语句的末尾加上option (recompile),再次执行后获取过程语句执行情况:

@DayLoginUserNum语句显示只执行一次,这是由于语句重编译只保留最后一次的缓存。指定语句层次的Recompile,存储过程级别的计划重用还是会有的。只是在运行到那句话的时候,才会发生重编译。下图中执行两次带option (recompile)的存储过程:

注意加上option (recompile),执行计划与之前不同,因此逻辑读、CPU时间、总时间有所差异:


嵌套循环的外部表与内部表进行调换,使用的索引也有所改变。单独从逻辑读来看,语句加有option (recompile)貌似比不加所读取的页面要少。但,真的是这样吗?如果99%的推广号在AccountsInfoSimple表中只有少量匹配的记录,option (recompile)还合适吗?从前面的执行计划来看并不一定。我希望的是当推广号在AccountsInfoSimple表匹配记录很少时,它选择AccountsInfoSimple(外部表,IDX_spreaderAccount_userType)嵌套循环UserLoginRecord(内部表,IX_Userid);当推广号在AccountsInfoSimple表匹配记录很多时,它使用UserLoginRecord(外部表,logonDate)嵌套循环AccountsInfoSimple(内部表,IX_AccountsInfoSimple_UserID),或者两表哈希匹配得出结果。
不太明白的是,加有option (recompile),每次执行语句为什么不能选择"聪明"点的执行计划?
发现存储过程只统计了一个推广号数据,用时却比以往400+推广号还多!这个推广号对应在AccountsInfoSimple表有千万级的数据量,可是之前这个推广号也包含在内,等待核查!!!
预留
预留
附上05版本查看存储过程的执行情况的语句

--存储过程的执行情况(b汇总值会小于a的实际值)
SELECT top 50 CASE when c.dbid = 32767
            then ‘Resource‘
            else DB_NAME(c.dbid) end DbName
      --,OBJECT_SCHEMA_NAME(c.objectid,c.dbid) AS SchName
      ,OBJECT_NAME(c.objectid,c.dbid) AS ObjName
      ,MIN(b.creation_time) AS creation_time --有时候多个计划存在于同一储存过程的缓存中
      ,MIN(b.last_execution_time) AS last_execution_time
      ,SUM(a.usecounts) AS UseCount
      ,SUM(b.total_logical_reads) / SUM(a.usecounts) AS avg_logical_reads
      ,SUM(b.total_logical_writes) / SUM(a.usecounts) AS avg_logical_writes
      ,SUM(b.total_worker_time) / SUM(a.usecounts) AS avg_worker_time
      ,SUM(b.total_elapsed_time) / SUM(a.usecounts) AS avg_elapsed_time
FROM sys.dm_exec_cached_plans a with(nolock)
   INNER JOIN
   (SELECT MIN(creation_time) AS creation_time
          ,MIN(last_execution_time) AS last_execution_time
          ,SUM(total_logical_reads) AS total_logical_reads
          ,SUM(total_logical_writes) AS total_logical_writes
          ,SUM(total_worker_time) AS total_worker_time
          ,SUM(total_elapsed_time) AS total_elapsed_time
          ,plan_handle
      FROM sys.dm_exec_query_stats with(nolock)
      GROUP BY plan_handle) b
    ON a.plan_handle = b.plan_handle
   CROSS APPLY sys.dm_exec_query_plan(a.plan_handle) c
   --CROSS APPLY sys.dm_exec_sql_text(a.plan_handle) c --用于筛选text信息
WHERE a.objtype = ‘Proc‘
  --AND c.text like ‘Proc‘
GROUP BY c.dbid,c.objectid
ORDER BY SUM(b.total_logical_reads) / SUM(a.usecounts) DESC;

--SQLCmd in Proc or Batch
SELECT top 500 CASE when c.dbid = 32767
            then ‘Resource‘
            else DB_NAME(c.dbid) end DbName
      --,OBJECT_SCHEMA_NAME(c.objectid,c.dbid) AS SchName
      ,OBJECT_NAME(c.objectid,c.dbid) AS ObjName
      ,a.usecounts AS ProcExCount
      --,a.plan_handle
      , SUBSTRING (c.text,(b.statement_start_offset/2) + 1,
      ((CASE WHEN b.statement_end_offset = -1 THEN LEN(CONVERT(NVARCHAR(MAX), c.text)) * 2
      ELSE b.statement_end_offset END - b.statement_start_offset)/2) + 1) RunSQL
      ,b.creation_time --编译计划的时间
      ,b.last_execution_time --上次开始执行计划的时间
      ,b.execution_count AS SQLExCount--计划自上次编译以来所执行的次数
      ,b.last_logical_reads --上次执行计划时所执行的逻辑读取次数
      ,b.total_logical_reads/b.execution_count avg_logical_reads
      ,b.last_worker_time --上次执行计划所用的 CPU 时间(微秒)
      ,b.last_elapsed_time --最近一次完成执行此计划所用的时间(微秒)
      ,b.total_elapsed_time/b.execution_count avg_elapsed_time --上次完成执行此计划所用的总时间
      --,b.*
FROM sys.dm_exec_cached_plans a with(nolock)
   INNER JOIN sys.dm_exec_query_stats b with(nolock)
      ON a.plan_handle = b.plan_handle
   CROSS APPLY sys.dm_exec_sql_text(b.sql_handle) c
WHERE a.objtype = ‘Proc‘
and OBJECT_NAME(c.objectid,c.dbid)=‘DBA_TroubleShooting‘
order by a.plan_handle,b.sql_handle;

--Execution Plan
SELECT top 50 CASE when b.dbid = 32767
            then ‘Resource‘
            else DB_NAME(b.dbid) end DbName
      ,OBJECT_SCHEMA_NAME(b.objectid,b.dbid) AS SchName
      ,OBJECT_NAME(b.objectid,b.dbid) AS ObjName
      ,a.usecounts
      ,a.plan_handle
      ,b.query_plan
FROM sys.dm_exec_cached_plans a with(nolock)
   CROSS APPLY sys.dm_exec_query_plan(a.plan_handle) b
WHERE a.objtype = ‘Proc‘
and OBJECT_NAME(b.objectid,b.dbid)=‘DBA_TroubleShooting‘;

预留
预留
存储过程里面:游标+查询语句;游标+查询语句抽离成存储过程;哪种更好?第二种会有parameter sniffing现象吗?
预留
预留
预留

时间: 2024-09-29 01:32:53

LK存储过程的执行信息的相关文章

DBA工具——DMV——通过sys.dm_exec_procedure_stats查看存储过程执行信息

原文:DBA工具--DMV--通过sys.dm_exec_procedure_stats查看存储过程执行信息 对于DBA来说,经常要手机存储过程的某些信息: 执行了多少次 执行的执行计划如何 执行的平均读写如何 执行平均需要多少时间 列名 数据类型 说明 database_id int 存储过程所在的数据库 ID. object_id int 存储过程的对象标识号. type char(2) 对象的类型: P = SQL 存储过程 PC = 程序集 (CLR) 存储过程 X = 扩展存储过程 t

dblink 的源数据表结构修改后在存储过程里执行报错

原情况:A服务器表A服务器B也有一张表A服务器B上有一个存储过程要把本地的head表向A服务器表里插入数据.变更后:在A服务器表里增加了一个字段inserttime,服务器B存储过程本地表向A服务器插入时,记录插入的时间.问题修改语句如下:insert into [email protected]  select t.*,sysdate from A;这个语句单独执行没有问题.但在存储过程里执行一直报错,报值过多. 解决: 猜想可能是dblink的问题,把原来的dblink删除,重新新建一个db

存储过程中执行动态Sql语句

存储过程中执行动态Sql语句 MSSQL为我们提供了两种动态执行SQL语句的命令,分别是EXEC和sp_executesql;通常,sp_executesql则更具有优势,它提供了输入输出接口,而EXEC没有.还有一个最大的好处就是利用sp_executesql,能够重用执行计划,这就大大提供了执行性能,还可以编写更安全的代码.EXEC在某些情况下会更灵活.除非您有令人信服的理由使用EXEC,否侧尽量使用sp_executesql. 1.EXEC的使用 EXEC命令有两种用法,一种是执行一个存储

用C/C++实现对STORM的执行信息查看和控制

近期公司有个需求.须要在后端应用server上实时获取STORM集群的执行信息和topology相关的提交和控制,经过几天对STORM UI和CMD源代码的分析,得出能够通过其thrift接口调用实现这些功能.先下载一个thrift库进行编码和安装.关于thrift能够參见这个地方. 安装完毕后,从STORM源代码中将storm.thrift复制到thrift文件夹下. 输入: hrift -gen cpp storm.thrift 会得到一个gen-cpp文件夹,里面就是thrift先关脚本的

mysql存储过程动态执行SQL

CREATE PROCEDURE feeMonth(in fmark varchar(200),in fuser char(32),in ftime BIGINT,in fmonth char(6)) BEGIN #定义SQL变量 declare create_sql varchar(100); declare sel_sql varchar(100); declare del_sql varchar(100); declare fmon varchar(100); #定义表名变量 declar

如何在存储过程中执行set命令  我来答

1.EXEC使用EXEC命令两种用种执行存储程另种执行态批处理所讲都第二种用 面先使用EXEC演示例,代码1DECLARE @TableName VARCHAR(50),@Sql NVARCHAR (MAX),@OrderID INT;SET @TableName = 'Orders';SET @OrderID = 10251;SET @sql = 'SELECT * FROM '+QUOTENAME(@TableName) +'WHERE OrderID = '+ CAST(@OrderID

LK按作业执行时长优化

新到一家公司,需要折腾点认可出来.然后开始苦逼的优化工作.暂时不吐槽权限问题!!!优化效果优化前,作业历史记录(前30)P图优化前,CPU使用情况有几个作业平均时长2.5~3.5小时,还有很多时长在半小时以上的作业,基本要到11-12点才能完成作业统计.CPU每天7:00-11:00一直维持在比较高的数值.优化后,作业历史记录(前30)P图最近三天的CPU情况最近一天的CPU情况优化后,作业执行时长明显降低,从最近3天执行情况看,9:00前统计作业能够执行完成.服务器CPU高峰时段由之前的7:0

CSharp 调用存储过程来执行增、删、改操作

对表进行增,删,改数据时,每次都需要访问一次数据库,这样会影响性能:如果把查询的数据拼接成XML形式,作为一个参数整体传给存储过程来处理,这只访问数据库一次,执行速度会快很多. 1.CSharp 代码如下: public bool CreateUpdateDeleteHelpCategory(HelpCategoryInfo helpCategory, DataProviderAction action) { bool result; if (null == helpCategory) { re

查找存储过程的执行频率和时间

由于公司最近的数据库服务器CPU非常不稳定. 于是乎下手查找问题的来源.想了下,只能从存储过程的执行状态中下手. 查了下资料,发现MSSQL中的系统表sys.dm_exec_procedure_stats会记录存储过程的执行状态数据.字段含义就不累述了.开始干活: 1.将数据插入一张新表记录 select convert(nvarchar(10),getdate(),121) as countdate,d.* into procedure_stats_daily  from SYS.proced