加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 站长学院 > MsSql教程 > 正文

SQLSERVER2000中数据库物理优化

发布时间:2020-12-12 15:47:55 所属栏目:MsSql教程 来源:网络整理
导读:SQLSERVER数据表的记录重排物理优化方案 ??? 该方案产生于2005年,当时我接手了某产业库的设计.我们在开发这个产品时,在数据库中引入了100万条记录作为开发版数据库.开发过程非常顺利,前台程序的测试速度也非常好.但我们在后来将当前的5亿条数据库追加到开发

SQLSERVER数据表的记录重排物理优化方案

??? 该方案产生于2005年,当时我接手了某产业库的设计.我们在开发这个产品时,在数据库中引入了100万条记录作为开发版数据库.开发过程非常顺利,前台程序的测试速度也非常好.但我们在后来将当前的5亿条数据库追加到开发库进行测试时,发现无论什么情况,都无法从数据库中取出记录,发现在读取数据库时,磁盘灯一直在亮,而性能性能监视器里的CPU使用率很底.此时的SQLSERVER服务器会呈的假死状态.
?? 后来我想我们的数据库为优化而预留的空间被填满后,数据会被追加到数据库文件的最后页.会不会是因为这个原因,导至了磁盘的磁头寻道,读取的效率下降?而造成了一个高I/O的假象?基于这个想法,我再次到查询分析器中运行了前台取值存储过程,然后用WIDNOWS的性能监视器观察,发现数据读取的值真的是很低.
?? 基于这个原因,所以,我认为如果将数据库的表按聚集索引的方向重排一次再回写,一定会大大减少磁盘的寻道,读取时间.从而大大提高数据库的读取效率.下面的过程就是基于这个原因而写的.
?? 首先说明,下面这个存储过程具有一定的危险性,而且,我们已经对以年度为序,纵向分表.对年度数据表,名称都是有规则的,如这里的是 MC_CP_DATA_####(四位的年).在使用这个过程时,一定要结合你当前的实际情况.

--------------------------------------
-- 数据表的数据重组物理优化方案.
--???????????????? MSTOP 2005/5/2
--------------------------------------

-----------------------------------------------
--表优化记录.
-----------------------------------------------

--分段截取函数
IF EXISTS(SELECT [NAME] FROM SYSOBJECTS WHERE [NAME]='Fun_SplitStr') BEGIN
?DROP FUNCTION Fun_SplitStr
END
GO

CREATE FUNCTION DBO.Fun_SplitStr(
?@S VARCHAR(8000),????? --包含多个数据项的字符串
?@POS INT,????????????? --要获取的数据项的位置
?@SPLIT VARCHAR(10)???? --数据分隔符
)RETURNS VARCHAR(512)
AS
BEGIN
?IF @S IS NULL RETURN(NULL)
?DECLARE @SPLITLEN INT
?SELECT @SPLITLEN=LEN(@SPLIT+'A')-2
?WHILE @POS>1 AND CHARINDEX(@SPLIT,@S+@SPLIT)>0
? SELECT @POS=@POS-1,
?? @S=STUFF(@S,1,CHARINDEX(@SPLIT,@S+@SPLIT)+@SPLITLEN,'')
?RETURN? LEFT(@S,@S+@SPLIT)-1)
END

GO
-----------------------------------------
-- 文件路径.
-----------------------------------------
IF EXISTS(SELECT [NAME] FROM SYSOBJECTS WHERE [NAME]='Prc_FilePath') BEGIN
?DROP PROC Prc_FilePath
END

GO

CREATE Proc Prc_FilePath(
?@Var_Path VarChar(4000),
?@Var_String VarChar(4000) OutPut
)? As Begin
?Declare @Tab_Tmp Table (Int_ID Int,Var_Str VarChar(128) )
?Declare @Var_S1 Varchar(4000)
?Declare @Int_ID Int

?SET @Var_Path=REPLACE(@Var_Path,'//','/')
?SET @Int_ID=1
?SET @Var_S1=dbo.FUN_SplitStr(@Var_Path,@Int_ID,'/')
?WHILE LEN(@Var_S1)>0 BEGIN
? INSERT INTO @Tab_Tmp VALUES(@Int_ID,@Var_S1)
? SET @Int_ID=@Int_ID+1
? SET @Var_S1=dbo.FUN_SplitStr(@Var_Path,'/')
?END
?DELETE FROM @Tab_Tmp WHERE Int_ID = (SELECT MAX(Int_ID) FROM @Tab_Tmp)
?SET @Var_String=''
?SELECT @Var_String=@Var_String + Var_Str + '/' From @Tab_Tmp Order By Int_ID
?IF RIGHT(@Var_String,1)<>'/' AND LEN(@Var_String)>0 BEGIN
? SET @Var_String=@Var_String + '/'
?END
End

GO

--------------------------------------
--记录需优化表的表信息.
--------------------------------------

IF EXISTS(SELECT [NAME] FROM SYSOBJECTS WHERE [NAME]='SY_Table_Optimize') BEGIN
?DROP TABLE SY_Table_Optimize
END

GO

CREATE TABLE SY_Table_Optimize (
?NVR_DBNAME? NVARCHAR(128)? NOT NULL,
?NVR_TABLENAME? NVARCHAR(128)? NOT NULL,
?NVR_TABLEGROUPNAME NVARCHAR(128)? NOT NULL,
?NVR_TABLEGROUPPATH NVARCHAR(256)? NULL,
?INT_UpRowCount? INT?? NULL,
?DAT_UpDate? DATETIME? Null
CONSTRAINT [PK_SY_Table_Optimize] PRIMARY KEY? CLUSTERED ([NVR_DBNAME],[NVR_TABLENAME]))

GO

-----------------------------------------------------------------------------------
-- 初始化 SY_Table_Optimize?,
-- 组名和文件名必须是同名.如:文件名是 ABC.NDF,则组名是: ABC,并且,一个文件一个组一个表.
-----------------------------------------------------------------------------------
INSERT INTO SY_Table_Optimize
SELECT DB_NAME(),A.[NAME],C.groupname,D.[FILENAME],B.[ROWS],GETDATE()
FROM SYSOBJECTS AS A inner join SYSINDEXES AS B?
ON? A.ID=B.ID AND A.XTYPE='U' AND B.INDID<2 AND A.[NAME] LIKE 'MC_CP_DATA%'
INNER JOIN sysfilegroups AS C ON B.GROUPID=C.GROUPID
INNER JOIN sysfiles AS D ON C.GROUPID=D.GROUPID
?
GO

---------------------------------
-- 物理优化所有的表.
-- 在作业里加一个作业:
-- EXECUTE dt_optimize_all_table
---------------------------------
IF EXISTS(SELECT [NAME] FROM SYSOBJECTS WHERE [NAME]='dt_optimize_all_table') BEGIN
?DROP PROC dt_optimize_all_table
END

GO

CREATE PROC dt_optimize_all_table(
?@INT_VALVE INT=50000? --数据库优化阀值.
) WITH ENCRYPTION AS BEGIN

?DECLARE @NVR_DBNAME? NVARCHAR(64) --数据库名.
?DECLARE @NVR_DBPATH? NVARCHAR(256) --数据库所在路径.
?DECLARE @INT_ROWCOUNT? INT? --当前表的总行数.
?DECLARE @INT_NEWROWCOUNT INT
?DECLARE @NVR_TABLENAME? NVARCHAR(256) --表名.
?DECLARE @NVR_OLEGROUPNAME NVARCHAR(256)
?DECLARE @NVR_NEWGOUPNAME NVARCHAR(256)
?DECLARE @NVR_CMD? NVARCHAR(4000)
?DECLARE @INT_ROW? INT
?DECLARE @NVR_TABLEGROUPPATH NVARCHAR(4000)

?SET @NVR_DBNAME=DB_NAME();
?SELECT @INT_ROW=MAX(ABS(T2.[ROWS]-T1.INT_UpRowCount)) FROM
? (?
?? SELECT NVR_TABLENAME,INT_UpRowCount
?? FROM SY_Table_Optimize WHERE NVR_DBNAME=@NVR_DBNAME
? ) AS T1 INNER JOIN
? (
?? SELECT A.[NAME],B.[ROWS]
?? FROM SYSOBJECTS AS A,SYSINDEXES AS B?
?? WHERE A.ID=B.ID AND A.XTYPE='U' AND B.INDID<2
? ) AS T2 ON T1.NVR_TABLENAME=T2.[NAME]

?IF @INT_ROW>=@INT_VALVE BEGIN
? --------------------------------------------
? --先清理一次日志.因为处理要需要大量的磁盘空间.
? --------------------------------------------
? SET @NVR_CMD='DUMP TRANSACTION ' + @NVR_DBNAME +? ' WITH NO_LOG'
? EXECUTE(@NVR_CMD)
?
? SET @NVR_CMD='DBCC SHRINKFILE(2,0)'
? EXECUTE(@NVR_CMD)
?
? --断开所有相关连接.要一个个断开.
? DECLARE @INT_SPID INT
? SET @NVR_CMD=''
? SELECT @INT_SPID=MIN(SPID) FROM MASTER.DBO.SYSPROCESSES WHERE DBID=DB_ID() AND SPID<>@@spid
? SET @INT_SPID=ISNULL(@INT_SPID,-1)
? WHILE @INT_SPID>0 BEGIN
?? SET @NVR_CMD=N' KILL ' + RTRIM(@INT_SPID) + ';'?
?? EXECUTE SP_EXECUTESQL @NVR_CMD
?? SELECT @INT_SPID=MIN(SPID) FROM MASTER.DBO.SYSPROCESSES WHERE DBID=DB_ID() AND SPID>@INT_SPID? AND SPID<>@@spid
?? SET @INT_SPID=ISNULL(@INT_SPID,-1)
? END
? -----------------------------------------

? SELECT @NVR_TABLENAME=MIN(NVR_TABLENAME) FROM SY_Table_Optimize WHERE NVR_DBNAME=@NVR_DBNAME;
? WHILE LEN(@NVR_TABLENAME)>0 BEGIN
??
?? -----------------------------------------------------

?? SELECT? @NVR_OLEGROUPNAME=NVR_TABLEGROUPNAME,
??? @INT_ROWCOUNT=INT_UpRowCount,
??? @NVR_TABLEGROUPPATH=REPLACE(NVR_TABLEGROUPPATH,'/')
?? FROM SY_Table_Optimize? WHERE NVR_DBNAME=@NVR_DBNAME AND? NVR_TABLENAME=@NVR_TABLENAME;

??? --路径要从系统表来.
?? SELECT? @NVR_TABLEGROUPPATH=REPLACE(D.[FILENAME],'/')?
?? FROM SYSOBJECTS AS A inner join SYSINDEXES AS B?
?? ON? A.ID=B.ID AND A.XTYPE='U' AND B.INDID<2 AND A.[NAME]=@NVR_TABLENAME
?? INNER JOIN sysfilegroups AS C ON B.GROUPID=C.GROUPID
?? INNER JOIN sysfiles AS D ON C.GROUPID=D.GROUPID

?? -----------------------------------------------------

?? EXECUTE Prc_FilePath @NVR_TABLEGROUPPATH,@NVR_DBPATH OUTPUT

?? SELECT @INT_NEWROWCOUNT=B.[ROWS]
?? FROM SYSOBJECTS AS A,SYSINDEXES AS B?
?? WHERE A.ID=B.ID AND A.XTYPE='U' AND B.INDID<2 AND A.[NAME]=@NVR_TABLENAME;
?
?? --如果当前行数改变大于某个值,则优化.
?? IF ABS(@INT_NEWROWCOUNT-@INT_ROWCOUNT)>=@INT_VALVE BEGIN
??? EXECUTE dt_optimize_table @NVR_DBNAME,@NVR_DBPATH,@NVR_TABLENAME,@NVR_OLEGROUPNAME,@NVR_NEWGOUPNAME OUTPUT;
??? --更新优化记录.
??? UPDATE SY_Table_Optimize SET NVR_TABLEGROUPNAME=@NVR_NEWGOUPNAME,DAT_UpDate=GETDATE() WHERE NVR_DBNAME=@NVR_DBNAME AND? NVR_TABLENAME=@NVR_TABLENAME;
?? END
?? SELECT @NVR_TABLENAME=MIN([NVR_TABLENAME]) FROM SY_Table_Optimize WHERE? NVR_DBNAME=@NVR_DBNAME AND NVR_TABLENAME>@NVR_TABLENAME;
?? SET @NVR_TABLENAME=ISNULL(@NVR_TABLENAME,'');
? END
?END

?--查看,是否有新的表..
?INSERT INTO SY_Table_Optimize
?SELECT @NVR_DBNAME,GETDATE()
?FROM SYSOBJECTS AS A inner join SYSINDEXES AS B?
?ON? A.[ID]=B.[ID] AND A.XTYPE='U' AND B.INDID<2 AND A.[NAME] LIKE 'MC_CP_DATA%' AND A.[NAME] NOT IN ( SELECT NVR_TABLENAME FROM SY_Table_Optimize )
?INNER JOIN sysfilegroups AS C ON B.GROUPID=C.GROUPID
?INNER JOIN sysfiles AS D ON C.GROUPID=D.GROUPID

END

GO

---------------------------------
-- 对指定的表进行物理优化.
---------------------------------
IF EXISTS(SELECT [NAME] FROM SYSOBJECTS WHERE [NAME]='dt_optimize_table') BEGIN
?DROP PROC dt_optimize_table
END

GO

CREATE PROC dt_optimize_table (
?@NVR_DBNAME? NVARCHAR(64),? --数据库名.
?@NVR_DBPATH? NVARCHAR(256),--数据库所在路径.
?@NVR_TABLENAME? NVARCHAR(256),--表名.
?@NVR_OLEGROUPNAME NVARCHAR(256),--上一个文件组的名称.
?@NVR_NEWGOUPNAME NVARCHAR(256) OUTPUT --新的组文件名.
)? WITH ENCRYPTION? AS BEGIN
?
?DECLARE @NVR_NEWID? NVARCHAR(16)? --新的表文件编号.
?DECLARE @NVR_CMD? NVARCHAR(4000)--命令
?DECLARE @NVR_TMPTABLENAME NVARCHAR(256) --暂时表名
?DECLARE @INT_TRANSACTION INT

?SET @NVR_NEWID= LEFT(REPLACE(NEWID(),'-',''),16)
?SET @NVR_TMPTABLENAME=@NVR_TABLENAME + '_' + @NVR_NEWID
?SET @NVR_NEWGOUPNAME=@NVR_TMPTABLENAME

?--添加一个文件组.
?SET @NVR_CMD='ALTER DATABASE [' + @NVR_DBNAME + '] ADD FILEGROUP [' + @NVR_TMPTABLENAME + ']'
?EXECUTE(@NVR_CMD)

?--向文件组中添加一个文件
?SET @NVR_CMD='
? ALTER DATABASE [' + @NVR_DBNAME + '] ADD FILE(
?? NAME = N''' + @NVR_TMPTABLENAME + ''',
?? FILENAME = N''' + @NVR_DBPATH + @NVR_TMPTABLENAME + '.NDF'',
?? SIZE = 3,
?? FILEGROWTH = 10%)
? TO FILEGROUP [' +@NVR_TMPTABLENAME + ']'
?EXECUTE(@NVR_CMD)

?--------------------------------------------
?--事务段
?--------------------------------------------
?SET @INT_TRANSACTION=1
?BEGIN TRANSACTION
?--------------------------------------------
?--在该文件中添加表. **这里要手工修改**.
?--------------------------------------------
?SET @NVR_CMD='
CREATE TABLE [' + @NVR_TMPTABLENAME + '] (
?[VAR_GOODSID] [varchar](8) NOT NULL,
?[VAR_AREAID] [varchar](8) NOT NULL,
?[BIG_DATE] [int]? NOT NULL,
?[INT_INDEXID] [int]? NOT NULL,
?[FLO_VALUE] [float]? NULL,
?CONSTRAINT [PK_' + @NVR_TMPTABLENAME + '] PRIMARY KEY?
?(
? [VAR_GOODSID],
? [VAR_AREAID],
? [BIG_DATE],
? [INT_INDEXID]
?)? ON [' + @NVR_TMPTABLENAME + ']
) ON [' + @NVR_TMPTABLENAME + ']'
?? EXECUTE(@NVR_CMD)

?IF @@ERROR<>0 BEGIN
? SET @INT_TRANSACTION=-1
? GOTO RollTRANSACTION
?END

?--将数据移到新的表.
?SET @NVR_CMD='INSERT INTO ' + @NVR_TMPTABLENAME
? + ' SELECT * FROM ' + @NVR_TABLENAME +? ' ORDER BY VAR_GOODSID,VAR_AREAID,BIG_DATE,INT_INDEXID'
?EXECUTE(@NVR_CMD)

--? --在表上建立一个日期的索引.
--? SET @NVR_CMD='CREATE INDEX IX_' + @NVR_TMPTABLENAME + '_DATE ON ' + @NVR_TMPTABLENAME + ' (BIG_DATE)'
--? EXECUTE(@NVR_CMD)

?IF @@ERROR<>0 BEGIN
? SET @INT_TRANSACTION=-1
? GOTO RollTRANSACTION
?END

?--删除原表.
?SET @NVR_CMD='DROP TABLE ' + @NVR_TABLENAME
?EXECUTE(@NVR_CMD)

?IF @@ERROR<>0 BEGIN
? SET @INT_TRANSACTION=-1
? GOTO RollTRANSACTION
?END
?--提交事务.
?COMMIT TRANSACTION
?SET @INT_TRANSACTION=0
?--------------------------------------------
?--事务段
?--------------------------------------------

?--将表名改为原来表名.
?SET @NVR_CMD='exec sp_rename ''' + @NVR_TMPTABLENAME + ''',''' + @NVR_TABLENAME + ''''
?EXECUTE(@NVR_CMD)

?--回滚事务.
RollTRANSACTION:
?IF @INT_TRANSACTION=-1 BEGIN
? ROLLBACK TRANSACTION
?END ELSE BEGIN
? --删除原来的表文件.
? SET @NVR_CMD='
? ALTER DATABASE [' + @NVR_DBNAME + ']
? REMOVE FILE ' + @NVR_OLEGROUPNAME
? EXECUTE(@NVR_CMD)

? SET @NVR_CMD='
? ALTER DATABASE [' + @NVR_DBNAME + ']
? REMOVE FILEGROUP ' + @NVR_OLEGROUPNAME
? EXECUTE(@NVR_CMD)
?END

?--------------------------------------------
?--清理日志
?--------------------------------------------
?SET @NVR_CMD='DUMP TRANSACTION ' + @NVR_DBNAME + ' WITH NO_LOG'
?EXECUTE(@NVR_CMD)
?
?SET @NVR_CMD='DBCC SHRINKFILE(2,0)'
?EXECUTE(@NVR_CMD)

END

GO

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读