數據表的物理優化方案(1)

    面對一個總量過億的數據庫,如何優化?雖然優化了主鍵,建立了索引,優化了查詢,可是,它爲什麼還是那麼慢呢? 更惡劣的情況是,在月結,轉帳,統計彙總時,它總是超時.
    這的確是讓很多人頭痛的事.更多人提出的解決方案是:換硬件吧,換硬件真的那麼有效嗎?就算快一倍,從60秒變成了30秒,你的用戶一樣是無法忍受.
    兩三年前,我也遇到過這個問題,也被這個問題拆磨了好幾天,後來,在優化數據庫的過程中.我發現,對大型超大型的數據庫來說,軟優化是遠遠不夠的.就算換硬件,好象效果也不

怎麼好.在經過一系列動作之後,你算是用盡了全身解數了.速度還沒有質的改變,抓狂嗎? 我爲這個問題抓狂過.
    哈哈,說了一通廢話.開始我今天的真正目的:
    經過了必要的軟優化,在同樣的硬件條件下,如何讓你的數據庫快起來.下面的過程,就是實現這個目的,對你的表進行物理優化.
    過程不長,但收到的效果可以說是立竿見影的,如果你嘗試過一切都無效的話,那我建議你試試它.
    優化的原理很簡單,打個比方吧,數據庫中的記錄,類似一個結構體,一個數據表,類似一個鏈表.主鍵,雖然是B樹,本質也是一個指針.無論你對數據庫怎樣操作,最後增加的記錄都

是記錄在數據庫文件的未尾.唯一改變的,就是記錄的指針.雖然,數據文件設置了主鍵,但它在磁盤上的實體卻是無序的.當查找某條記錄時,它只能按指針的指向去跳,這要磁盤去移

動,去尋道.我們的效率就消耗在這裏.我們要做的,就是重新讓數據實體按主鍵的方向排序.讓磁頭能迅速地找到我們所要的數據.  :)
    下面基本是這個解決方案的全部,也是從實質的庫中修改過來的.當然,由於每個人的習慣不一樣,可能有些出入,但總體的原理,還是一樣的.修改一下,即可使用.
    使用方法,建立一個作業,每週的週六晚上執行 EXECUTE dt_optimize_all_table 即可.過程 dt_optimize_all_table 有一個閥值,如果某表的改變的記錄超過了某個值(默認

是5萬),那它就執行整理.所以,如果你如果是按日期來表的話,你的歷史表是不會被整理的.再詳細的情況就不說了,過程裏的說明都有.

    注:這個過程其實是有點危險的,在正式應用前,一定要經過幾次的測試.另,如果你將本過程用於你的生產中,所產生的任何問題,與我無關.

--------------------------------------
--數據表的物理優化方案.
--              MSTOP
--------------------------------------

-----------------------------------------------
--表優化記錄.
-----------------------------------------------
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 'BL_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

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_DBPATH=LEFT(NVR_TABLEGROUPPATH, CHARINDEX('/' + NVR_TABLEGROUPNAME ,NVR_TABLEGROUPPATH)-1 ) ---這裏要特別留意
FROM SY_Table_Optimize  WHERE NVR_DBNAME=@NVR_DBNAME AND  NVR_TABLENAME=@NVR_TABLENAME;

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,INT_UpRowCount=@INT_NEWROWCOUNT,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
END

GO

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章