SQL Server获取磁盘空间使用情况

对于DBA来说,监控磁盘使用情况是必要的工作,然后没有比较简单的方法能获取到磁盘空间使用率信息,下面总结下这些年攒下的脚本:

最常用的查看磁盘剩余空间,这个属于DBA入门必记的东西:

-- 查看磁盘可用空间
EXEC master.dbo.xp_fixeddrives

xp_fixeddrives方式有点是系统自带,可直接使用,缺点是不能查看磁盘总大小和不能查看SQL Server未使用到的磁盘信息

使用sys.dm_os_volume_stats函数

--======================================================================
--查看数据库文件使用的磁盘空间使用情况
WITH T1 AS (
SELECT DISTINCT
REPLACE(vs.volume_mount_point,':\','') AS Drive_Name ,
CAST(vs.total_bytes / 1024.0 / 1024 / 1024 AS NUMERIC(18,2)) AS Total_Space_GB ,
CAST(vs.available_bytes / 1024.0 / 1024 / 1024 AS NUMERIC(18,2)) AS Free_Space_GB
FROM  sys.master_files AS f
CROSS APPLY sys.dm_os_volume_stats(f.database_id, f.file_id) AS vs
)
SELECT
Drive_Name,
Total_Space_GB,
Total_Space_GB-Free_Space_GB AS Used_Space_GB,
Free_Space_GB,
CAST(Free_Space_GB*100/Total_Space_GB AS NUMERIC(18,2)) AS Free_Space_Percent
FROM T1

查询效果:

sys.dm_os_volume_stats函数很好用,能直接查询到总空间和空闲空间,可惜只支持SQL Server 2008 R2 SP1即更高版本,另外无法查到数据库文件未使用到的磁盘

为兼容低版本,可采用xp_fixeddrives+xp_cmdshell方式来获取,我写了几个存储过程来获取磁盘信息:

USE [monitor]
GO

/****** Object: StoredProcedure [dbo].[usp_get_disk_free_size]  Script Date: 2016/5/25 18:21:11 ******/
SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

-- =============================================
-- Author:    GGA
-- Create date:  2016-2-1
-- Description:  收集磁盘剩余空间信息
-- =============================================
CREATE PROCEDURE [dbo].[usp_get_disk_free_size]
AS
BEGIN
  SET NOCOUNT ON;
  SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;

--==========================================
--创建相关表

IF OBJECT_ID('server_disk_usage') IS NULL
BEGIN
  CREATE TABLE [dbo].[server_disk_usage](
    [disk_num] [nvarchar](10) NOT NULL,
    [total_size_mb] [bigint] NOT NULL CONSTRAINT [DF_server_disk_usage_total_size_mb] DEFAULT ((0)),
    [free_siez_mb] [bigint] NOT NULL CONSTRAINT [DF_server_disk_usage_free_siez_mb] DEFAULT ((0)),
    [disk_info] [nvarchar](400) NOT NULL CONSTRAINT [DF_server_disk_usage_disk_info] DEFAULT (''),
    [check_time] [datetime] NOT NULL CONSTRAINT [DF_server_disk_usage_check_time] DEFAULT (getdate()),
     CONSTRAINT [PK_server_disk_usage] PRIMARY KEY CLUSTERED
    (
      [disk_num] ASC
    )
  ) ON [PRIMARY]
END

--==========================================
--查看所有数据库使用到的磁盘剩余空间
DECLARE @disk TABLE(
    [disk_num] VARCHAR(50),
    [free_siez_mb] INT)
INSERT INTO @disk
EXEC xp_fixeddrives

--更新当前磁盘的剩余空间信息
UPDATE M
SET M.[free_siez_mb]=D.[free_siez_mb]
FROM [dbo].[server_disk_usage] AS M
INNER JOIN @disk AS D
ON M.[disk_num]=D.[disk_num]

--插入新增磁盘的剩余空间信息
INSERT INTO [dbo].[server_disk_usage]
(
  [disk_num],
  [free_siez_mb]
)
SELECT
[disk_num],
[free_siez_mb]
FROM @disk AS D
WHERE NOT EXISTS(
  SELECT 1
  FROM [dbo].[server_disk_usage] AS M
  WHERE M.[disk_num]=D.[disk_num] )

END

GO

/****** Object: StoredProcedure [dbo].[usp_get_disk_total_size]  Script Date: 2016/5/25 18:21:11 ******/
SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

-- =============================================
-- Author:    GGA
-- Create date:  2016-2-1
-- Description:  收集磁盘总空间信息
-- =============================================
CREATE PROCEDURE [dbo].[usp_get_disk_total_size]
AS
BEGIN
  SET NOCOUNT ON;
  SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;

IF NOT EXISTS(SELECT * FROM [dbo].[server_disk_usage]
    WHERE [total_size_mb] = 0)
BEGIN
  RETURN;
END

--==========================================
--开启CMDShell
EXEC sp_configure 'show advanced options',1;

RECONFIGURE WITH OVERRIDE;

EXEC sp_configure 'xp_cmdshell',1;

RECONFIGURE WITH OVERRIDE

--========================================
--创建临时表用来存放每个盘符的数据
CREATE TABLE #tempDisks
(
  ID INT IDENTITY(1,1),
  DiskSpace NVARCHAR(200)
)
--============================================
--将需要检查的磁盘放入临时表#checkDisks
SELECT
ROW_NUMBER()OVER(ORDER BY [disk_num]) AS RID,
[disk_num]
INTO #checkDisks
FROM [dbo].[server_disk_usage]
WHERE [total_size_mb] = 0;

--============================================
--循环临时表#checkDisks检查每个磁盘的总量

DECLARE @disk_num NVARCHAR(20)
DECLARE @total_size_mb INT
DECLARE @sql NVARCHAR(200)
DECLARE @max INT
DECLARE @min INT
SELECT @max=MAX(RID),@min=MIN(RID) FROM #checkDisks

WHILE(@min<=@max)
BEGIN
SELECT @disk_num=[disk_num]
FROM #checkDisks WHERE RID=@min

SET @sql = N'EXEC sys.xp_cmdshell ''fsutil volume diskfree '+@disk_num+':'+''''
PRINT @sql

INSERT INTO #tempDisks
EXEC sys.sp_executesql @sql

SELECT @total_size_mb=CAST((RIGHT(DiskSpace,LEN(DiskSpace)
  -CHARINDEX(': ',DiskSpace)-1)) AS BIGINT)/1024/1024
FROM #tempDisks WHERE id = 2

SELECT @total_size_mb,@disk_num

UPDATE [dbo].[server_disk_usage]
SET [total_size_mb]=@total_size_mb
WHERE [disk_num]=@disk_num

--SELECT * FROM #tempDisks

TRUNCATE TABLE #tempDisks

SET @min=@min+1

END

--==========================================
--CMDShell

EXEC sp_configure 'xp_cmdshell',0;

EXEC sp_configure 'show advanced options',1;

RECONFIGURE WITH OVERRIDE;

END

GO

/****** Object: StoredProcedure [dbo].[usp_get_disk_usage]  Script Date: 2016/5/25 18:21:11 ******/
SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

-- =============================================
-- Author:    GGA
-- Create date:  2016-2-1
-- Description:  收集磁盘总空间信息
-- =============================================
CREATE PROCEDURE [dbo].[usp_get_disk_usage]
AS
BEGIN
  SET NOCOUNT ON;
  SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;

  EXEC [dbo].[usp_get_disk_free_size]
  EXEC [dbo].[usp_get_disk_total_size]

  SELECT
  [disk_num] AS Drive_Name
  ,CAST([total_size_mb]/1024.0 AS NUMERIC(18,2)) AS Total_Space_GB
  ,CAST(([total_size_mb]-[free_siez_mb])/1024.0 AS NUMERIC(18,2)) AS Used_Space_GB
  ,CAST([free_siez_mb]/1024.0 AS NUMERIC(18,2)) AS Free_Space_GB
  ,CAST([free_siez_mb]*100/[total_size_mb] AS NUMERIC(18,2)) AS Free_Space_Percent
  ,[disk_info]
  ,[check_time]
  FROM [monitor].[dbo].[server_disk_usage]
END
GO
--==================================
--查看磁盘空间使用
EXEC [dbo].[usp_get_disk_usage]

效果显示:

只有第一次收集磁盘信息或第一次收集新磁盘信息时,才会调用xp_cmdshell来获取磁盘的总大小,尽量减少xp_cmdshell开启带来的风险,可配合SQL Server Agent Job来使用,定期调用存储过程刷新磁盘信息,监控程序直接访问数据表来或许最后一次刷新时的磁盘信息。

此方式有一缺点是开启xp_cmdshell后获取磁盘总大小期间,其他进程可能关闭xp_cmdshell,造成存储过程执行失败,虽然发生概率较低,但毕竟存在。

如果想跳过存储过程+SQL Server Agent Job方式,直接通过程序来调用xp_cmdshell,当程序使用“RECONFIGURE WITH OVERRIDE”来配置时,会报如下错误:

CONFIG statement cannot be used inside a user transaction.DB-Lib error message 574

错误类似于我们在SSMS中使用事务包裹sp_configure语句,如:

BEGIN TRAN
EXEC sp_configure 'show advanced options',1;
RECONFIGURE WITH OVERRIDE;
EXEC sp_configure 'xp_cmdshell',1;
RECONFIGURE WITH OVERRIDE;
COMMIT

错误消息为:

配置选项 'show advanced options' 已从 0 更改为 1。请运行 RECONFIGURE 语句进行安装。
消息 574,级别 16,状态 0,第 3 行
在用户事务内不能使用 CONFIG 语句。
配置选项 'xp_cmdshell' 已从 0 更改为 1。请运行 RECONFIGURE 语句进行安装。
消息 574,级别 16,状态 0,第 5 行
在用户事务内不能使用 CONFIG 语句。

难道不能通过程序调用RECONFIGURE WITH OVERRIDE语句?

当然不是,google下相关错误,仅发现下面一个相关,有兴趣的可以参考下:

https://www.sqlservercentral.com/Forums/Topic1349778-146-1.aspx

粗略看了下,使用存储过程套存储过程的方式来绕过报错,本人没有具体测试,感觉太繁琐,于是采用简单粗暴的方式,既然报“在用户事务内不能使用 CONFIG 语句”,哪我是否可以先COMMIT下干掉“用户事务”呢?

基于此思路,最终测试获得下面方式:

DECLARE @sql VARCHAR(2000)
SET @sql ='
COMMIT;
EXEC sp_configure ''show advanced options'',1;
RECONFIGURE WITH OVERRIDE;
EXEC sp_configure ''xp_cmdshell'',1;
RECONFIGURE WITH OVERRIDE;
'
EXEC(@sql)

仔细的朋友发现我先执行了COMMIT, 您没看错,这样的打开方式虽然怪异但的确是一种打开方式,在SSMS中执行结果为:

消息 3902,级别 16,状态 1,第 2 行
COMMIT TRANSACTION 请求没有对应的 BEGIN TRANSACTION。
配置选项 'show advanced options' 已从 1 更改为 1。请运行 RECONFIGURE 语句进行安装。
配置选项 'xp_cmdshell' 已从 1 更改为 1。请运行 RECONFIGURE 语句进行安装。

虽然报错,但是的但是,xp_cmdshell的值已经被设置为1,即脚本执行生效啦!

将此代码移植到代码中,然后通过TRY CATCH将异常捕获并丢弃,你就可以愉快地调用xp_cmdshell啦。

使用xp_cmdshell开了头,当然相关信息也可以使用类似方式来获取啦!

比如获取磁盘的扇区信息:

--====================================
--使用xp_cmdshell来执行CMD命令
--获取磁盘扇区信息
EXEC sp_configure 'show advanced options',1
GO
RECONFIGURE
GO
sp_configure 'xp_cmdshell',1
GO
RECONFIGURE
GO
EXEC xp_cmdshell 'fsutil fsinfo ntfsinfo D: | find "每个"';
GO
sp_configure 'xp_cmdshell',0
GO
RECONFIGURE
GO
sp_configure 'show advanced options', 0
GO
RECONFIGURE
GO

运行效果为:

当然你可以使用fsutil fsinfo ntfsinfo D:来获取完整信息,但是更值得您关注的就是上面这几行。

感言:

当了这么多年的SQL Server DBA,现在找份像样的SQL SERVER DBA的工作真不容易,一方面是当前市场趋势导致,另一方面也是咱DBA自己“作死”造成的,看到很多同行包括我自己都还处在“刀耕火种”时代,有问题就在界面上点来点去,给外界一种“SQL Server很容易运维”的假象,而再看看MySQL DBA,只要你能假装“研究下源码”,立马给人一种“很牛逼”的赶脚,于是乎年薪三五十万不再是梦想!

以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,同时也希望多多支持我们!

(0)

相关推荐

  • MySQL中查询所有数据库占用磁盘空间大小和单个库中所有表的大小的sql语句

    查询所有数据库占用磁盘空间大小的SQL语句: 复制代码 代码如下: select TABLE_SCHEMA, concat(truncate(sum(data_length)/1024/1024,2),' MB') as data_size,concat(truncate(sum(index_length)/1024/1024,2),'MB') as index_sizefrom information_schema.tablesgroup by TABLE_SCHEMAorder by dat

  • lnmp下如何关闭Mysql日志保护磁盘空间

    LNMP一键包安装的Mysql默认开启Mysql日志,如果网站对数据库读写较为频繁的话可能会产生大量日志,并占用磁盘空间,还有可能导致VPS的硬盘闲置空间消耗满导致数据库无法开启. 本教程的路径适用于LNMP一键安装包环境,其它环境请根据实际情况操作. 若需要关闭,请执行以下操作: 一.修改文件配置 修改/etc/my.cnf,找到以下字符 复制代码 代码如下: log-bin=mysql-bin binlog_format=mixedbash 注释这两行,如下: 复制代码 代码如下: #log

  • Mysql存储引擎MyISAM的常见问题(表损坏、无法访问、磁盘空间不足)

    本文为大家分享了解决Mysql存储引擎MyISAM常见问题的方法,供大家参考,具体内容如下 一.处理MyISAM存储引擎的表损坏 在使用MySQL,可能会遇到过MyISAM存储引擎的表损坏的情况.如以下情况: .frm被锁定不能修改 找不到.myi文件(索引文件) 意外结束记录 文件被毁坏 从表处理器得到错误nnn 解决办法1: 使用MySQL自带的myisamchk工具进行修复 打开bin目录,可以看到该工具 命令如下 myisamchk -r tablename r代表recover 或 m

  • Mysql InnoDB删除数据后释放磁盘空间的方法

    Innodb数据库对于已经删除的数据只是标记为删除,并不真正释放所占用的磁盘空间,这就导致InnoDB数据库文件不断增长. 如果在创建数据库的时候设置innodb_file_per_table=1,这样InnoDB会对每个表创建一个数据文件,然后只需要运行OPTIMIZE TABLE 命令就可以释放所有已经删除的磁盘空间. 运行OPTIMIZE TABLE 表名后,虽然最后会报Table does not support optimize, doing recreate + analyze in

  • mssql 监控磁盘空间告警实现方法

    这几天突然有个想法:希望能够自动监控.收集数据库服务器的磁盘容量信息,当达到一个阀值后,自动发送告警邮件给DBA,将数据库磁盘详细信息告知DBA,提醒DBA做好存储规划计划,初步的想法是通过作业调用存储过程来实现(每天调用一次),这样避免了我每天每台数据库服务器都上去检查一下,尤其是手头的数据库服务器N多的情况,这样可以避免我每天浪费无谓的时间.如果大家有更好的建议和方法,欢迎指点一二,我整理.修改了三个存储过程如下: 存储过程1:SP_DiskCapacityAlert1.prc 说明:需要通

  • 几个缩减MySQL以节省磁盘空间的建议

    我们在工作中时常会遇到一些客户的TPS\QPS都不太高,但磁盘占用非常大,一旦单实例空间太大,像内存.网络.CPU以及备份都将增加相应的开销.可能仅仅是由于空间不满足使得我们不得不进行扩容,下面的方法提供给大家参考.有则改之无则加勉. 1.表结构设计上 1) 字符集是否遵循了最小化原则?(能用latin的就不用gbk.能用gbk的就不用utf8) 2) 索引上是否有滥用?(根本不使用的字段建索引.不适合建索引的字段建索引.重复建索引或者不能很好的利用前缀索引等) 3) 冗余字段是否太多?(各表中

  • SqlServer如何通过SQL语句获取处理器(CPU)、内存(Memory)、磁盘(Disk)以及操作系统相关信息

    在SQL SERVER中如何通过SQL语句获取服务器硬件和系统信息呢?下面介绍一下如何通过SQL语句获取处理器(CPU).内存(Memory).磁盘(Disk)以及操作系统相关信息.如有不足和遗漏,敬请补充.谢谢! 一:查看数据库服务器CPU的信息 ---SQL 1:获取数据库服务器的CPU型号 EXEC xp_instance_regread 'HKEY_LOCAL_MACHINE', 'HARDWARE\DESCRIPTION\System\CentralProcessor\0', 'Pro

  • ubuntu下磁盘空间不足导致mysql无法启动的解决方法

    前言 最近在数据库的一张表添加两个字段,后来提示什么磁盘空间不足什么什么的,后来数据库就断开连接了,之后就一直连接不上去后来,最后经过思考终于解决了这个问题,这一经历下来真是心惊胆战,本文作为记录一下磁盘空间不足导致的 mysql 无法启动的解决办法. 方法如下 操作系统:ubuntu,磁盘空间不足导致的 mysql 无法启动,会造成如下问题: root@iZ28z558vv0Z:/etc/mysql# mysql -u root -p Enter password: ERROR 2002 (H

  • SQL Server获取磁盘空间使用情况

    对于DBA来说,监控磁盘使用情况是必要的工作,然后没有比较简单的方法能获取到磁盘空间使用率信息,下面总结下这些年攒下的脚本: 最常用的查看磁盘剩余空间,这个属于DBA入门必记的东西: -- 查看磁盘可用空间 EXEC master.dbo.xp_fixeddrives xp_fixeddrives方式有点是系统自带,可直接使用,缺点是不能查看磁盘总大小和不能查看SQL Server未使用到的磁盘信息 使用sys.dm_os_volume_stats函数 --===================

  • C#实现获取磁盘空间大小的方法

    本文实例讲述了C#实现获取磁盘空间大小的方法.分享给大家供大家参考.具体实现方法如下: 方法一:利用System.IO.DriveInfo.GetDrives方法来获取 复制代码 代码如下: ///   /// 获取指定驱动器的空间总大小(单位为B) ///   ///  只需输入代表驱动器的字母即可 (大写) ///    public static long GetHardDiskSpace(string str_HardDiskName) {     long totalSize= new

  • Java获取磁盘空间的两种代码示例

    本文分享了两段获取磁盘空间的代码,参考下. 代码1: import java.io.File; public class DiskSpaceDetail { public static void main(String[] args) { File diskPartition = new File("C:"); long totalCapacity = diskPartition.getTotalSpace(); long freePartitionSpace = diskPartit

  • 使用SQL Server 获取插入记录后的ID(自动编号)

    最近在开发项目的过程中遇到一个问题,就是在插入一条记录的后要立即获取所在数据库中ID,而该ID是自增的,怎么做?在sql server 2005中有几种方式可以实现. 要获取此ID,最简单的方法就是在查询之后select @@indentity --SQL语句创建数据库和表 复制代码 代码如下: create database dbdemo go use dbdemo go create table tbldemo (     id int primary key identity(1,1),

  • Delphi实现获取磁盘空间大小的方法

    本文所述Delphi实例用以获取指定的磁盘空间容量大小,检测磁盘大小,从combox中选择磁盘代号等功能.点击"检测驱动器"容量信息的按钮,就可以在下边显示出该磁盘的总空间大小以及要用容量的大小.读者可根据需求添加对应的Button与label控件. 主要程序代码如下所示: unit Unit1; interface uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtr

  • SQL Server 获取服务器时间的sql语句

    SQL SERVER 2000用sql语句如何获得当前系统时间 就是用GETDATE(); Sql中的getDate() Sql Server 中一个非常强大的日期格式化函数 Select CONVERT(varchar(100), GETDATE(), 0): 05 16 2008 10:57AM Select CONVERT(varchar(100), GETDATE(), 1): 05/16/08 Select CONVERT(varchar(100), GETDATE(), 2): 08

  • sql server 获取系统时间的方法

    Sql Server 中一个非常强大的日期格式化函数: 获得当前系统时间,GETDATE(): 2008年01月08日 星期二 14:59 Select CONVERT(varchar(100), GETDATE(), 0): 05 16 2008 10:57AM Select CONVERT(varchar(100), GETDATE(), 1): 05/16/08 Select CONVERT(varchar(100), GETDATE(), 2): 08.05.16 Select CONV

  • SQL Server 监控磁盘IO错误,msdb.dbo.suspect_pages

    suspect_pages 表位于 msdb 数据库中,是在 SQL Server 2005 中引入的.用于维护有关可疑页的信息的 suspect_pages 数据库管理员负责管理表(主要通过删除旧的行实现).suspect_pages 表有大小限制,如果此表已满,则不会记录新的错误.若要防止此表填满,数据库管理员或系统管理员必须通过删除行来手动清除此表中的旧条目.因此,我们建议您定期删除或存档 event_type 为已还原或已修复的行或具有旧 last_update 值的行. 若要监视对 s

  • MS SQL Server获取十二个月份的英文缩写

    如果使用DATENAME()函数是取得月份的英文全称,但报表需要,只需显示月份名称缩写即可. 十二个月份的英文缩写,只有五月份是全称与缩写一样,其它月份的缩写仅是取前三位字母.因此Insus.NET写成一个自定义函数: 复制代码 代码如下: -- ============================================= -- Author: Insus.NET -- Create date: 2012-12-18 -- Description: Get Month abbrev

  • SQL Server中NULL的正确使用与空间占用

    我们常在SQL Server的使用或维护中遇上NULL,那么什么是NULL?如下是MSDN给出的一段简短描述(见"Null Values"): A value of NULL indicates that the value is unknown. A value of NULL is different from an empty or zero value. No two null values are equal. Comparisons between two null val

随机推荐