SQL Mon 介绍

本文涉及的产品
云数据库 RDS SQL Server,独享型 2核4GB
简介: 原文:SQL Mon 介绍这是一个相当高级的SQL Server监控工具,全面监控SQL Server的活动与性能,分析性能瓶颈,给出优化建议。 red-gate有一个在线的数据库监控工具,不过那个商业的东西价钱不便宜。
原文: SQL Mon 介绍

这是一个相当高级的SQL Server监控工具,全面监控SQL Server的活动与性能,分析性能瓶颈,给出优化建议。

 

red-gate有一个在线的数据库监控工具,不过那个商业的东西价钱不便宜。我写的这个平民版,开源,功能上有颇多的差异(各有长短)。

 

项目在Codeplex上开源:http://sqlmon.codeplex.com/

 

在Codeproject上有英文介绍:http://www.codeproject.com/KB/database/sqlmonitor.aspx

 

介绍

是否想过:“SQL Server为什么那么慢?”,“为什么CPU占用那么高?”,“到底哪里死锁了?”,“为什么数据库那么大?”,“怎样才可以查看我的存储过程和函数的历史版本?”,“可以让我的SQL Server跑得更快吗?”。

 

你的答案就在这里;-)

 

到底能干嘛

  • 监控SQL Server的活动:进程、任务,详细查看当前执行的语句与实际变量值,终止进程
  • IO/CPU/网络等性能趋势图
  • 函数/存储过程等的版本控制,这在商业软件中也没有(如果你知道,告诉我)
  • 对象浏览器:服务器、数据库、表、视图、函数、存储过程等
  • 数据库管理:收缩、日志清除、备份、恢复等
  • 在整个数据库中搜索对象/脚本内容,这在SQL Server 2012中也无法做到
  • 自动显示所有对象的脚本,如表、视图、函数、存储过程等

 

概览

 

 

在上图中,我们可以看见表的create脚本。如果你选择其它对象,如函数、存储过程等,一样会显示相应的脚本。

 

在对象列表中,如果是数据表,显示表的占用空间(包括索引)、记录数等。

 

这些在SQL Server 2012中都没有。

 

获取数据库信息

 

 

View Code
SELECT  DB_NAME(database_id)  AS DatabaseName, Name  AS Logical_Name, Physical_Name,  CAST(size  AS  decimal( 30, 0)) * 8  AS Size, state  FROM sys.master_files  WHERE  DB_NAME(database_id)  =  ' YOUR_DATABASE_NAME ' 

 

对象/脚本搜索

 

 

View Code
-- search in script
Select s.name, s.create_date  AS CreateDate, s.modify_date  AS ModifyDate, s.type, c. text  from syscomments c  left  join sys.objects s  on c.id  = s. object_id  where  [ Text ]  like  ' %YOUR_QUERY_HERE% '

-- search in jobs
SELECT job_id, name, date_created  AS CreateDate, date_modified  AS ModifyDate,  ' Job '  AS type  FROM msdb.dbo.sysjobs

 

 

获取表结构

 

 

-- To get table names and records
SELECT 
     [ TableName ]  = so.name, 
     [ RowCount ]  =  MAX(si.rows) 
FROM 
    sysobjects so, 
    sysindexes si 
WHERE 
    so.xtype  =  ' U ' 
     AND 
    si.id  =  OBJECT_ID(so.name) 
GROUP  BY 
    so.name
-- To get table used space
EXEC sp_spaceused  ' TABLE_NAME '
-- To get table script
declare  @Id  int@i  int@i2  int, @Sql  varchar( max), @Sql2  varchar( max),  @f1  varchar( 5),  @f2  varchar( 5),  @f3  varchar( 5),  @f4  varchar( 5),  @T  varchar( 5)
     select  @Id = object_id( ' YOUR_TABLE_NAME_HERE '),  @f1  =  char( 13+  char( 10),  @f2  =  '      '@f3 = @f1 + @f2@f4 = ' , '  +  @f3
    
     if  not( @Id  is  null)
     BEGIN
     declare  @Data  table(Id  int  identity  primary  key, D  varchar( maxnot  null, ic  int  null, re  int  null, o  int  not  null);
    
     --  Columns
     with c  as(
         select c.column_id, Nr  = row_number()  over( order  by c.column_id), Clr = count( *over(),
            D  =  quotename(c.name)  +  '  '  +
                 case  when s.name  =  ' sys '  or c.is_computed = 1  then  ''  else  quotename(s.name)  +  ' . '  end  +
                 case  when c.is_computed = 1  then  ''  when s.name  =  ' sys '  then t.Name  else  quotename(t.name)  end  +
                 case  when c.user_type_id !=c.system_type_id  or c.is_computed = 1  then  ''
                     when t.Name  in ( ' xml '' uniqueidentifier '' tinyint '' timestamp '' time '' text '' sysname '' sql_variant '' smallmoney '' smallint '' smalldatetime '' ntext '' money ',
                                     ' int '' image '' hierarchyid '' geometry '' geography '' float '' datetimeoffset '' datetime2 '' datetime '' date '' bigint '' bit 'then  ''
                     when t.Name  in( ' varchar ', ' varbinary '' real '' numeric '' decimal '' char '' binary ')
                         then  ' ( '  +  isnull( convert( varchar, nullif(c.max_length, - 1)),  ' max '+  isnull( ' , ' + convert( varchar, nullif(c.scale,  0)),  ''+  ' ) '
                     when t.Name  in( ' nvarchar ', ' nchar ')
                         then  ' ( '  +  isnull( convert( varchar, nullif(c.max_length, - 1/  2),  ' max '+  isnull( ' , ' + convert( varchar, nullif(c.scale,  0)),  ''+  ' ) '
                     else  ' ?? '
                     end  + 
                 case  when ic. object_id  is  not  null  then  '  identity( '  +  convert( varchar,ic.seed_value)  +  ' , '  +  convert( varchar,ic.increment_value)  +  ' ) '  else  ''  end  +
                 case  when c.is_computed = 1  then  ' as '  + cc.definition  when c.is_nullable  =  1  then  '  null '  else  '  not null '  end  +
                 case c.is_rowguidcol  when  1  then  '  rowguidcol '  else  ''  end  +
                 case  when d. object_id  is  not  null  then  '  default  '  + d.definition  else   ''  end
         from sys.columns c
         inner  join sys.types t
         on t.user_type_id  = c.user_type_id
         inner  join sys.schemas s
         on s.schema_id =t.schema_id
         left  outer  join sys.computed_columns cc
         on cc. object_id =c. object_id  and cc.column_id =c.column_id
         left  outer  join sys.default_constraints d
         on d.parent_object_id = @id  and d.parent_column_id =c.column_id
         left  outer  join sys.identity_columns ic
         on ic. object_id =c. object_id  and ic.column_id =c.column_id
         where c. object_id = @Id
        
    )
         insert  into  @Data(D, o)
         select  '      '  + D  +  case Nr  when Clr  then  ''  else  ' , '  +  @f1  end0
         from c  where  NOT D  IS  NULL 
         order  by column_id
    
     --  SubObjects
     set  @i = 0
     while  1 = 1
         begin
         select  top  1  @i =c. object_id@T  = c.type,  @i2 =i.index_id
         from sys.objects c 
         left  outer  join sys.indexes i
         on i. object_id = @Id  and i.name =c.name
         where parent_object_id = @Id  and c. object_id > @i  and c.type  not  in( ' D ')
         order  by c. object_id
         if  @@rowcount = 0  break
         if  @T  =  ' C ' 
             insert  into  @Data 
             select  @f4  +  ' check  '  +  case is_not_for_replication  when  1  then  ' not for replication  '  else  ''  end  + definition,  nullnull10
             from sys.check_constraints  where  object_id = @i
         else  if  @T  =  ' Pk '
             insert  into  @Data 
             select  @f4  +  ' primary key '  +  isnull( '  '  +  nullif( lower(i.type_desc), ' clustered '),  ''),  @i2null20
             from sys.indexes i
             where i. object_id = @Id  and i.index_id = @i2
         else  if  @T  =  ' uq '
             insert  into  @Data  values( @f4  +  ' unique '@i2null30)
         else  if  @T  =  ' f '
             begin
             insert  into  @Data 
             select  @f4  +  ' foreign key '- 1@i40
             from sys.foreign_keys f
             where f. object_id = @i
            
             insert  into  @Data 
             select  '  references  '  +  quotename(s.name)  +  ' . '  +  quotename(o.name),  - 2@i41
             from sys.foreign_keys f
             inner  join sys.objects o
             on o. object_id =f.referenced_object_id
             inner  join sys.schemas s
             on s.schema_id =o.schema_id
             where f. object_id = @i
            
             insert  into  @Data 
             select  '  not for replication '- 3@i42
             from sys.foreign_keys f
             inner  join sys.objects o
             on o. object_id =f.referenced_object_id
             inner  join sys.schemas s
             on s.schema_id =o.schema_id
             where f. object_id = @i  and f.is_not_for_replication = 1
             end
         else
             insert  into  @Data  values( @f4  +  ' Unknow SubObject [ '  +  @T  +  ' ] 'nullnull99)
         end

     insert  into  @Data  values( @f1 + ' ) 'nullnull100)
    
     --  Indexes
     insert  into  @Data
     select  @f1  +  ' create  '  +  case is_unique  when  1  then  ' unique  '  else  ''  end  +  lower(s.type_desc)  +  '  index  '  +  ' i '  +  convert( varchar, row_number()  over( order  by index_id))  +  '  on  '  +  quotename(sc.Name)  +  ' . '  +  quotename(o.name), index_id,  null1000
     from sys.indexes s
     inner  join sys.objects o
     on o. object_id =s. object_id
     inner  join sys.schemas sc
     on sc.schema_id =o.schema_id
     where s. object_id = @Id  and is_unique_constraint = 0  and is_primary_key = 0  and s.type_desc  !=  ' heap '
    
     --  columns
     set  @i = 0
     while  1 = 1
         begin
         select  top  1  @i =ic  from  @Data  where ic > @i  order  by ic 
         if  @@rowcount = 0  break
         select  @i2 = 0@Sql = null@Sql2 = null
         while  1 = 1
             begin
             select  @i2 =index_column_id, 
                 @Sql  =  case c.is_included_column  when  1  then  @Sql  else  isnull( @Sql  +  ' '' ( '+ cc.Name  +  case c.is_descending_key  when  1   then  '  desc '  else  ''  end  end,
                 @Sql2  =  case c.is_included_column  when  0  then  @Sql2  else  isnull( @Sql2  +  ' '' ( '+ cc.Name  +  case c.is_descending_key  when  1   then  '  desc '  else  ''  end  end
             from sys.index_columns c
             inner  join sys.columns cc
             on c.column_id =cc.column_id  and cc. object_id =c. object_id
             where c. object_id = @Id  and index_id = @i  and index_column_id > @i2
             order  by index_column_id
             if  @@rowcount = 0  break
             end
         update  @Data  set D =D + @Sql  + ' ) '  +  isnull( '  include '  +  @Sql2  +  ' ) '''where ic = @i
         end
        
     --  references
     set  @i = 0
     while  1 = 1
         begin
         select  top  1  @i =re  from  @Data  where re > @i  order  by re
         if  @@rowcount = 0  break
        
         select  @i2 = 0@Sql = null@Sql2 = null
         while  1 = 1
             begin
             select  @i2 =f.constraint_column_id, 
                 @Sql  =  isnull( @Sql  +  ' '' ( '+ c1.Name,
                 @Sql2  =  isnull( @Sql2  +  ' '' ( '+ c2.Name
             from sys.foreign_key_columns f
             inner  join sys.columns c1
             on c1.column_id =f.parent_column_id  and c1. object_id =f.parent_object_id
             inner  join sys.columns c2
             on c2.column_id =f.referenced_column_id  and c2. object_id =f.referenced_object_id
             where f.constraint_object_id = @i  and f.constraint_column_id > @i2
             order  by f.constraint_column_id
             if  @@rowcount = 0  break
             end
         update  @Data  set D  = D  +  @Sql  +  ' ) '   where re = @i  and ic =- 1
         update  @Data  set D  = D  +  @Sql2  +  ' ) '   where re = @i  and ic =- 2
         end;
    
     --  Render
     with x  as(
         select id =d.id - 1, D =d.D  +  isnull(d2.D, '')
         from  @Data d
         left  outer  join  @Data d2
         on d.re =d2.re  and d2.o = 42
         where d.o = 41
        
    )
     update  @Data
         set D =d.D +x.D
     from  @Data d
     inner  join x
     on x.id =d.id
    
     delete  @Data  where o  in( 4142)
    
     select  @Sql  =  ' create table  '  +  quotename(s.name)  +  ' . '  +  quotename(o.name)  +  ' ( '  +  @f1
     from sys.objects o
     inner  join sys.schemas s
     on o.schema_id  = s.schema_id
     where o. object_id = @Id
    
     set  @i = 0
     while  1 = 1
         begin
         select  top  1  @I =Id,  @Sql  =  @Sql  + D  from  @Data  order  by o,  case  when o = 0  then  right( ' 0000 '  +  convert( varchar,id), 5)   else D  end, id
         if  @@rowcount = 0  break
         delete  @Data  where id = @i
         end
     END
     SELECT  @Sql

 

 

性能趋势图

 

 

在上图中,我们可以看见SQL Server历史/当前的IO/CPU/网络信息都在趋势图中显示。

 

这些数据来自几个系统变量:

 

  • @@cpu_busy
  • @@io_busy
  • @@idle
  • @@pack_received
  • @@pack_sent
  • @@connections
  • @@packet_errors
  • @@total_read
  • @@total_write
  • @@total_errors

 

 以下是相应的SQL:

 

declare  @now          datetime
declare  @cpu_busy      int
declare  @io_busy     int
declare  @idle         int
declare  @pack_received     int
declare  @pack_sent     int
declare  @pack_errors     int
declare  @connections     int
declare  @total_read     int
declare  @total_write     int
declare  @total_errors     int

declare  @oldcpu_busy      int     /*  used to see if DataServer has been rebooted  */
declare  @interval     int
declare  @mspertick     int     /*  milliseconds per tick  */


/*
**  Set @mspertick.  This is just used to make the numbers easier to handle
**  and avoid overflow.
*/
select  @mspertick  =  convert( int@@timeticks  /  1000.0)

/*
**  Get current monitor values.
*/
select
     @now  =  getdate(),
     @cpu_busy  =  @@cpu_busy,
     @io_busy  =  @@io_busy,
     @idle  =  @@idle,
     @pack_received  =  @@pack_received,
     @pack_sent  =  @@pack_sent,
     @connections  =  @@connections,
     @pack_errors  =  @@packet_errors,
     @total_read  =  @@total_read,
     @total_write  =  @@total_write,
     @total_errors  =  @@total_errors

/*
**  Check to see if DataServer has been rebooted.  If it has then the
**  value of @@cpu_busy will be less than the value of spt_monitor.cpu_busy.
**  If it has update spt_monitor.
*/
select  @oldcpu_busy  = cpu_busy
     from master.dbo.spt_monitor
if  @oldcpu_busy  >  @cpu_busy
begin
     update master.dbo.spt_monitor
         set
            lastrun  =  @now,
            cpu_busy  =  @cpu_busy,
            io_busy  =  @io_busy,
            idle  =  @idle,
            pack_received  =  @pack_received,
            pack_sent  =  @pack_sent,
            connections  =  @connections,
            pack_errors  =  @pack_errors,
            total_read  =  @total_read,
            total_write  =  @total_write,
            total_errors  =  @total_errors
end

/*
**  Now print out old and new monitor values.
*/
set nocount  on
select  @interval  =  datediff(ss, lastrun,  @now)
     from master.dbo.spt_monitor
/*  To prevent a divide by zero error when run for the first
** time after boot up
*/
if  @interval  =  0
     select  @interval  =  1
select last_run  = lastrun, current_run  =  @now, seconds  =  @interval,
    cpu_busy_total  =  convert( int, (( @cpu_busy  *  @mspertick/  1000)),
    cpu_busy_current  =  convert( int, ((( @cpu_busy  - cpu_busy)
         *  @mspertick/  1000)),
    cpu_busy_percentage  =  convert( int, (((( @cpu_busy  - cpu_busy)
         *  @mspertick/  1000*  100/  @interval),
    io_busy_total  =  convert( int, (( @io_busy  *  @mspertick/  1000)),
    io_busy_current  =  convert( int, ((( @io_busy  - io_busy)
         *  @mspertick/  1000)),
    io_busy_percentage  =  convert( int, (((( @io_busy  - io_busy)
         *  @mspertick/  1000*  100/  @interval),
    idle_total  =  convert( int, (( convert( bigint, @idle*  @mspertick/  1000)),
    idle_current  =  convert( int, ((( @idle  - idle)
         *  @mspertick/  1000)),
    idle_percentage  =  convert( int, (((( @idle  - idle)
         *  @mspertick/  1000*  100/  @interval),
    packets_received_total  =  @pack_received,
    packets_received_current  =  @pack_received  - pack_received,
    packets_sent_total  =  @pack_sent,
    packets_sent_current  =  @pack_sent  - pack_sent,
    packet_errors_total  =  @pack_errors,
    packet_errors_current  =  @pack_errors  - pack_errors,
    total_read  =  @total_read,
    current_read  =  @total_read  - total_read,
    total_write  =  @total_write,
    current_write  =     @total_write  - total_write,
    total_errors  =  @total_errors,
    current_errors  =  @total_errors  - total_errors,
    connections_total  =  @connections,
    connections_current  =  @connections  - connections
from master.dbo.spt_monitor

/*
**  Now update spt_monitor
*/
update master.dbo.spt_monitor
     set
        lastrun  =  @now,
        cpu_busy  =  @cpu_busy,
        io_busy  =  @io_busy,
        idle  =  @idle,
        pack_received  =  @pack_received,
        pack_sent  =  @pack_sent,
        connections  =  @connections,
        pack_errors  =  @pack_errors,
        total_read  =  @total_read,
        total_write  =  @total_write,
        total_errors  =  @total_errors

 

 

版本控制

数据库开发人员总在想,每次修改了函数/存储过程,我们都得自己做备份,用以历史参考,当发现错误的时候,可以回滚。在SQL Monitor里面,这个是全自动的。

 

版本控制的思想来自这里:http://www.sqlteam.com/article/using-ddl-triggers-in-sql-server-2005-to-capture-schema-changes

 

原理就是用数据库DDL触发器记录每个DDL操作,自增版本,并存储到一个表中。

 

 

关键代码 

 

 

SET ANSI_NULLS  OFF
GO
SET QUOTED_IDENTIFIER  OFF
GO
SET ANSI_NULLS  ON
GO
SET QUOTED_IDENTIFIER  ON
GO
IF  NOT  EXISTS ( SELECT  *  FROM sys.objects  WHERE  object_id  =  OBJECT_ID(N ' [dbo].[VERSION_CONTROL_TABLE] 'AND type  in (N ' U '))
BEGIN
CREATE  TABLE  [ dbo ]. [ {0} ](
     [ ID ]  [ bigint ]  IDENTITY( 1, 1NOT  NULL,
     [ databasename ]  [ varchar ]( 256NULL,
     [ eventtype ]  [ varchar ]( 50NULL,
     [ objectname ]  [ varchar ]( 256NULL,
     [ objecttype ]  [ varchar ]( 25NULL,
     [ sqlcommand ]  [ nvarchar ]( maxNULL,
     [ loginname ]  [ varchar ]( 256NULL,
     [ hostname ]  [ varchar ]( 256NULL,
     [ PostTime ]  [ datetime ]  NULL,
     [ Version ]  [ int ]  NOT  NULL,
  CONSTRAINT  [ PK_VERSION_CONTROL_TABLE ]  PRIMARY  KEY  CLUSTERED 
(
     [ ID ]  ASC
) WITH (IGNORE_DUP_KEY  =  OFFON  [ PRIMARY ]
ON  [ PRIMARY ]
END

GO

SET ANSI_NULLS  ON
GO
SET QUOTED_IDENTIFIER  ON
GO

CREATE  TRIGGER  [ TRG_VERSION_CONTROL_TABLE} ]
ON  DATABASE
FOR CREATE_PROCEDURE, ALTER_PROCEDURE, DROP_PROCEDURE,
CREATE_TABLE, ALTER_TABLE, DROP_TABLE,
CREATE_FUNCTION, ALTER_FUNCTION, DROP_FUNCTION,
CREATE_TRIGGER, ALTER_TRIGGER, DROP_TRIGGER,
CREATE_VIEW, ALTER_VIEW, DROP_VIEW,
CREATE_INDEX, ALTER_INDEX, DROP_INDEX

AS

SET NOCOUNT  ON

DECLARE  @CurrentVersion  int
DECLARE  @CurrentID  int
DECLARE  @DatabaseName  varchar( 256)
DECLARE  @ObjectName  varchar( 256)
DECLARE  @data XML

SET  @data  = EVENTDATA()

INSERT  INTO dbo.VERSION_CONTROL_TABLE(databasename, eventtype,objectname, objecttype, sqlcommand, loginname,Hostname,PostTime, Version)
VALUES(
@data.value( ' (/EVENT_INSTANCE/DatabaseName)[1] '' varchar(256) '),
@data.value( ' (/EVENT_INSTANCE/EventType)[1] '' varchar(50) '),   --  value is case-sensitive
@data.value( ' (/EVENT_INSTANCE/ObjectName)[1] '' varchar(256) '), 
@data.value( ' (/EVENT_INSTANCE/ObjectType)[1] '' varchar(25) '), 
@data.value( ' (/EVENT_INSTANCE/TSQLCommand)[1] '' varchar(max) '), 
@data.value( ' (/EVENT_INSTANCE/LoginName)[1] '' varchar(256) '),
HOST_NAME(),
GETDATE(),
0
)

SET  @CurrentID  = IDENT_CURRENT( ' VERSION_CONTROL_TABLE ')
SELECT  @DatabaseName  = databasename,  @ObjectName  = objectname  FROM VERSION_CONTROL_TABLE  WHERE ID  =  @CurrentID
IF ( @DatabaseName  IS  NOT  NULL  AND  @ObjectName  IS  NOT  NULL)
BEGIN
     SELECT  @CurrentVersion  =  MAX(Version)  FROM VERSION_CONTROL_TABLE  WHERE databasename  =  @DatabaseName  AND objectname  =  @ObjectName
     UPDATE VERSION_CONTROL_TABLE  SET Version  =  ISNULL( @CurrentVersion0+  1  WHERE ID  =  @CurrentID
END
GO
ENABLE  TRIGGER  [ TRG_VERSION_CONTROL_TABLE ]  ON  DATABASE

 

 

SQL Monitor会全自动给你的所有脚本修改做版本记录。你可以随时查看在什么时候哪个机器用什么身份修改了哪个对象的脚本。

 

 

SQL Monitor内置版本比较,你可以清楚知道不同的版本的差异。 

 

 

活动监控

 

 

 

上图清晰显示所有系统的活动进程,每个进程当前执行什么语句。

 

获取进程列表

 

View Code
-- To get processes
SELECT s.session_id  AS spid, s.login_time, s. host_name  AS hostname, s.host_process_id  AS hostprocess, s.login_name  AS loginname, s.logical_reads  AS physical_io, s.cpu_time  AS cpu, s.program_name,  0  AS dbid, s.last_request_start_time  AS last_batch_begin,  CASE  WHEN status  =  ' running '  THEN  GETDATE()  ELSE  dateadd(ms, s.cpu_time, s.last_request_end_time)  END  AS last_batch_end, s.status  FROM sys.dm_exec_sessions s  JOIN sys.dm_exec_connections c  ON s.session_id  = c.session_id

 

 

获取任务列表

View Code
-- To get jobs
SELECT job_id  AS spid, name  AS program_name,  0  AS dbid,  0  AS cpu,  0  AS physical_io,  NULL  AS login_time,  NULL  AS last_batch_begin,  NULL  AS last_batch_end,  NULL  AS status,  NULL  AS hostname,  NULL  AS hostprocess,  NULL  AS cmd,  NULL  AS loginname  FROM msdb.dbo.sysjobs

 

 

分析

这是商业级的数据与性能分析,SQL Monitor自动给你的系统、数据库、数据表、索引等进行分析。

 

 

 

基本原理是首先利用master.sys.xp_fixeddrives获取磁盘的剩余空间,然后:

 

 

// database  &  disk free  space
         var databases  = GetDatabasesInfo();
         var files  = new List <tuple <bool,  />>();
        databases.AsEnumerable().ForEach(d  =>
        {
             var  database  = GetDatabaseInfo(d [ "name" ].ToString());
             database.AsEnumerable().ForEach(f  =>
            {
                files. Add(new Tuple <bool,  />( Convert.ToInt32(f [ "type" ]==  1, f [ "physical_name" ].ToString(),  Convert.ToInt64( Convert.ToDecimal(f [ "Size" ]/ Size1K)));
            }
            );
        });
         var spaces  = new Dictionary <string,  />>();
         //MB free
         var driveSpaces  = Query(" EXEC master.sys.xp_fixeddrives");
        driveSpaces.AsEnumerable().ForEach(s  =>
        {
             //could  not  use name but rather  index, because the  column name will change according  to locale
            spaces. Add(s [ 0 ].ToString(), new KeyValue < long/>( Convert.ToInt64(s [ 1 ]),  0));
        });
        files.ForEach(f  =>
        {
             //maybe  some access issues
            try
            {
                 var drive  = f.Item2. Substring( 01);
                 if (spaces.ContainsKey(drive))
                {
                    spaces [ drive ].Value  += f.Item3;
                }
            }
            catch (Exception)
            {
                 //mmmm.....what can we do, mate?
            }
        });
        spaces.ForEach(s  =>
        {
             if (s.Value. Key  < s.Value.Value  /  100  * Settings.Instance.DatabaseDiskFreeSpaceRatio)
            {
                analysisResult. Add(new AnalysisResult { ResultType  = AnalysisResultTypes.DiskFreeSpace, ObjectName  = s. Key, ReferenceValue  = s.Value. Key, CurrentValue  = s.Value.Value, Factor  = Settings.Instance.DatabaseDiskFreeSpaceRatio  + SizePercentage });
            }
        });

         // database data  file  &  log  file  space
        databases.AsEnumerable().ForEach(d  =>
        {
             var name  = d [ "name" ].ToString();
             if (!systemDatabases. Contains(name))
            {
                 var  database  = GetDatabaseInfo(name);
                 var databaseSpace  = new Dictionary <databasefiletypes,  /> { { DatabaseFileTypes.Data,  0 }, { DatabaseFileTypes. Log0 } };
                 database.AsEnumerable().ForEach(f  =>
                {
                     var  key  = (DatabaseFileTypes) Convert.ToInt32(f [ "type" ]);
                    databaseSpace [ key ]  +=  Convert.ToInt64( Convert.ToDecimal(f [ "Size" ]/ Size1K);
                }
                );
                bool? shrink  =  null;
                 if (databaseSpace [ DatabaseFileTypes.Log ]  > databaseSpace [ DatabaseFileTypes.Data ]  /  100  * Settings.Instance.DatabaseDataLogSpaceRatio)
                    shrink  = false;
                 else
                {
                     var logSpaces  = SQLHelper.Query(" DBCC SQLPERF(LOGSPACE)", GetServerInfo(name));
                     var logSpace  = logSpaces. Select(string.Format(" [ Database Name ]  =  ' {0} '", name));
                     if (logSpace.Length  >  0)
                    {
                         var logSpacedUsed  =  Convert.ToDouble(logSpace [ 0 ][ "Log Space Used (%)" ]);
                         if (logSpacedUsed  < Settings.Instance.DatabaseDataLogSpaceRatio)
                            shrink  = true;
                    }
                }
                 if (shrink  !=  null)
                    analysisResult. Add(new AnalysisResult { ResultType  = AnalysisResultTypes.DatabaseLogSpace, ObjectName  = name, ReferenceValue  = databaseSpace [ DatabaseFileTypes.Log ], CurrentValue  = databaseSpace [ DatabaseFileTypes.Data ], Factor  = Settings.Instance.DatabaseDataLogSpaceRatio  + SizePercentage,  Key  = (bool)shrink ?  1 :  0 });
            }
        });

 

对于表空间,使用了sp_spaceused,关键代码:

 

var tables  = GetObjects(KeyTables);
tables.AsEnumerable().ForEach(t  =>
    {
         var name  = t [ KeyName ].ToString();
         var  space  = Query(string.Format(" EXEC sp_spaceused  ' {0} '", name), CurrentServerInfo);
         if ( space.Rows. Count  >  0)
        {
             var row  =  space.Rows [ 0 ];
             var dataSize  = ToKB(row [ "data" ]/ Size1K;
             var indexSize  = ToKB(row [ "index_size" ]/ Size1K;
             if (indexSize  > dataSize  /  100  * Settings.Instance.TableDataIndexSpaceRatio)
                analysisResult. Add(new AnalysisResult { ResultType  = AnalysisResultTypes.TableIndexSpace, ObjectName  = name, ReferenceValue  = dataSize, CurrentValue  = indexSize, Factor  = Settings.Instance.DatabaseDataLogSpaceRatio  + SizePercentage,  Key  = ( int)TableIndexSpaceRules.DataIndexSpaceRatio });
        }
    });

 

 

 

 

 

最新版本

 

http://sqlmon.codeplex.com/releases/view/77943

 

转载:http://www.cnblogs.com/unruledboy 

相关实践学习
使用SQL语句管理索引
本次实验主要介绍如何在RDS-SQLServer数据库中,使用SQL语句管理索引。
SQL Server on Linux入门教程
SQL Server数据库一直只提供Windows下的版本。2016年微软宣布推出可运行在Linux系统下的SQL Server数据库,该版本目前还是早期预览版本。本课程主要介绍SQLServer On Linux的基本知识。 相关的阿里云产品:云数据库RDS&nbsp;SQL Server版 RDS SQL Server不仅拥有高可用架构和任意时间点的数据恢复功能,强力支撑各种企业应用,同时也包含了微软的License费用,减少额外支出。 了解产品详情:&nbsp;https://www.aliyun.com/product/rds/sqlserver
目录
相关文章
|
12天前
|
SQL 人工智能 算法
【SQL server】玩转SQL server数据库:第二章 关系数据库
【SQL server】玩转SQL server数据库:第二章 关系数据库
51 10
|
1月前
|
SQL 数据库 数据安全/隐私保护
Sql Server数据库Sa密码如何修改
Sql Server数据库Sa密码如何修改
|
2月前
|
SQL 算法 数据库
【数据库SQL server】关系数据库标准语言SQL之数据查询
【数据库SQL server】关系数据库标准语言SQL之数据查询
96 0
|
2月前
|
SQL 算法 数据库
【数据库SQL server】关系数据库标准语言SQL之视图
【数据库SQL server】关系数据库标准语言SQL之视图
77 0
|
22天前
|
SQL
启动mysq异常The server quit without updating PID file [FAILED]sql/data/***.pi根本解决方案
启动mysq异常The server quit without updating PID file [FAILED]sql/data/***.pi根本解决方案
16 0
|
12天前
|
SQL 算法 数据库
【SQL server】玩转SQL server数据库:第三章 关系数据库标准语言SQL(二)数据查询
【SQL server】玩转SQL server数据库:第三章 关系数据库标准语言SQL(二)数据查询
74 6
|
7天前
|
SQL 安全 网络安全
IDEA DataGrip连接sqlserver 提示驱动程序无法通过使用安全套接字层(SSL)加密与 SQL Server 建立安全连接的解决方法
IDEA DataGrip连接sqlserver 提示驱动程序无法通过使用安全套接字层(SSL)加密与 SQL Server 建立安全连接的解决方法
17 0
|
12天前
|
SQL 存储 数据挖掘
数据库数据恢复—RAID5上层Sql Server数据库数据恢复案例
服务器数据恢复环境: 一台安装windows server操作系统的服务器。一组由8块硬盘组建的RAID5,划分LUN供这台服务器使用。 在windows服务器内装有SqlServer数据库。存储空间LUN划分了两个逻辑分区。 服务器故障&初检: 由于未知原因,Sql Server数据库文件丢失,丢失数据涉及到3个库,表的数量有3000左右。数据库文件丢失原因还没有查清楚,也不能确定数据存储位置。 数据库文件丢失后服务器仍处于开机状态,所幸没有大量数据写入。 将raid5中所有磁盘编号后取出,经过硬件工程师检测,没有发现明显的硬件故障。以只读方式将所有磁盘进行扇区级的全盘镜像,镜像完成后将所
数据库数据恢复—RAID5上层Sql Server数据库数据恢复案例
|
16天前
|
SQL 数据安全/隐私保护
SQL Server 2016安装教程
SQL Server 2016安装教程
17 1
|
16天前
|
SQL 安全 Java
SQL server 2017安装教程
SQL server 2017安装教程
15 1

相关课程

更多