2018年12月7日 星期五

[SQL Server]主動通知SQL Server發生錯誤的機制

當SQL Server有特定錯誤發生時,管理人員也沒有時間經常去查看SQL Server錯誤檔,
於是有時特定錯誤可能很重要時,但卻沒有人被通知到,現在小弟實作一個主動通知的機制。
首先先新增警示,在這裡選擇想要被通知的錯誤訊息的等級或其它設定
https://ithelp.ithome.com.tw/upload/images/20181029/201074089J2h0p0y4T.jpg
https://ithelp.ithome.com.tw/upload/images/20181029/20107408O5wErzpiCM.jpg
接著在回應頁籤中,勾選執行作業->選擇作業
https://ithelp.ithome.com.tw/upload/images/20181029/201074083aJkLXa2cn.jpg
當有我們指定的錯誤層級發生時,這個作業會立刻被執行,
因此這個作業內容就是要來撰寫發送通知的相關SQL Script,
基本上排程Script內容大致上會先去抓取SQL錯誤檔的內容,
接著再透過系統SP:sp_send_dbmail來發送EMail通知管理者。

檢查資料庫中每張資料表的使用狀況

通常在系統上線後,隨著User日積月累地去操作系統的次數,
你會發現有些資料其實不需要經常被User存取到,或者是根本User也不需要這些資料了,
此時為了增進系統的效能,應該要著手進行資料搬移或刪除等動作,俗稱HouseKepping的作業。
舉例來說:
1.User有可能不需要經常的存取到上一季的資料,那麼可以寫個排程去定期將上一季的資料,
搬至另一個表格存檔封存,類似E-Mail封存的概念,而原本這張表格只需要保留本季的資料即可。
2.User可能再也不需要存取一年前的舊資料,那麼即可寫個排程去定期刪除超過一年的舊資料。
因此定期查看每張資料表的使用情況就變的重要了,此類查詢SQL指令如下:
SELECT 
    t.NAME AS TableName,
    s.Name AS SchemaName,
    t.modify_date AS ModifyDate,
    p.rows AS RowCounts,
    SUM(a.total_pages) * 8 AS TotalSpaceKB, 
    SUM(a.used_pages) * 8 AS UsedSpaceKB, 
    (SUM(a.total_pages) - SUM(a.used_pages)) * 8 AS UnusedSpaceKB    
FROM 
    sys.tables t
INNER JOIN      
    sys.indexes i ON t.OBJECT_ID = i.object_id
INNER JOIN 
    sys.partitions p ON i.object_id = p.OBJECT_ID AND i.index_id = p.index_id
INNER JOIN 
    sys.allocation_units a ON p.partition_id = a.container_id
LEFT OUTER JOIN 
    sys.schemas s ON t.schema_id = s.schema_id
WHERE 
    t.NAME NOT LIKE 'dt%' 
    AND t.is_ms_shipped = 0
    AND i.OBJECT_ID > 255 
GROUP BY 
    t.Name, s.Name, t.modify_date, p.Rows
ORDER BY 
    t.Name
https://ithelp.ithome.com.tw/upload/images/20181028/20107408cRefqE7flB.jpg
由上圖可看出那些表格擁有比較多筆資料和所佔的容量,以及他的表格中資料最後的修改日期,
那麼就可以先針對最近修改日期而且有數萬筆以上資料的表格,進行舊資料的搬移或刪除。

log檔和tempdb檔案大小一直增加時,該如何縮小?

在管理SQL Server經常會遇到此類的問題,如果這兩類檔案長的太大的話,
也會造成SQL Server緩慢,因此要控管這兩類檔案的大小,首先來針對log檔的縮小進行說明。
因為資料庫運行時會有大量的新增、刪除、修改等交易,導致log檔必須是記錄這些動作,
在log檔持續長大的情況下,此時要縮小log檔有兩種方式:
1.寫個數小時備份log檔的job排程
2.直接下指令DBCC SHRINKDATABASE
如果這樣子都縮小不下來,那麼就必須查看是否有大量交易正在進行中佔住Log檔案。
接下來是如何縮小tempdb檔案大小,因為此類檔案是主要供Store Procedure建立的暫存物件(#,##開頭的)所使用的
,因此要先下CHECKPOINT指令將在記憶體的資料回寫到資料檔裡,接著再下以指令
--釋放佔用的記憶體
DBCC FREESYSTEMCACHE ('ALL')
DBCC FREEPROCCACHE
DBCC DROPCLEANBUFFERS
DBCC FREESESSIONCACHE
GO
特別注意以上這幾行指令只能在系統的離峰時間進行
因為有許多SQL指令的執行計劃快取都存在記憶體裡,一旦清空將導致CPU需要重新編譯執行計劃,有可能造成CPU使用率拉高並持續一段時間,進而使系統緩慢。
以上指令都做好後,接著再DBCC SHRINKDATABASE(N'tempdb')即可。
通常會讓tempdb暴增的原因是在Store Procedure中大量使用暫存表格和物件,使用後也沒有立刻刪除(drop),隨著SP執行次數的增加,暫存物件會持續增加,進而使tempdb檔案愈來愈大。
所以養成良好的撰寫Store Procedure的習慣是很重要的!

使用T-SQL分析IIS Log

相信有不少攻城獅都有管理IIS站台的經驗,有時會遇到IIS突然無法連線等狀況發生,
大概大部份的人會在Command視窗下iisreset /restart指令,讓IIS重啓看看,真的再不行就重開機,
也有人會針對消耗資源多的應用程式集區,在User的離峰時間去設定自動回收重啓
以上都是治標不治本的方法...
因為系統有了問題要去釐清可能的問題點,而不是妄加猜測是資料庫、應用程式、網路所導致的,
小弟建議可先從IIS本身提供的Log來進行分析,
開啟"Internet Information Services(IIS)管理員"->"記錄",查看記錄檔所存放的目錄。
https://ithelp.ithome.com.tw/upload/images/20181023/20107408qfUCsKo9PE.jpg
點"選取欄位",可勾選要紀錄的資料欄位
https://ithelp.ithome.com.tw/upload/images/20181023/20107408vfTubEwniI.jpg
由於IIS Log檔中的資料是很繁雜的,僅用記事本來查看要分析出有用的資訊是有難度的,
因此小弟介紹使用BULK INSERT的功能將IIS Log大量匯入至表格中再來找尋有用的資料,
這樣子會便捷許多。
IIS Log檔的內容如下
https://ithelp.ithome.com.tw/upload/images/20181023/20107408fHIvutpEkh.jpg
首先,先依據Log檔裡資料的標頭來建立張資料表供匯入資料使用
CREATE TABLE IIS_Log(
[date] date null,
....
....
)
接著使用以下script將IIS Log檔匯入剛建的表格裡
BULK INSERT IIS_Log FROM 'C:\inetpub\logs\Logfiles\W3SVC1\u_ex18023.log'
WITH (
        FIELDTERMINATOR = ' ',
        ROWTERMINATOR = '\n'
     }
順利匯入後,即可使用SQL來分析各網頁的歷程紀錄。
通常在網站開發時測不出來的問題,可能在網站上線後一段時間後爆發出來,
此時善用Log檔可以有助於了解整個網站的運作情況以及問題點的釐清。

[SQL Server] Performance DashBoard Report的介紹

今天小弟介紹一個實用的SQL Server管理報表,由於他是透過DMV搜集資料的,
所以不用安裝Reporting Service,
來下載使用,那麼現在話不多說就先來安裝,
安裝完後接著去安裝的目錄裡(C:\Program Files (x86)\Microsoft SQL Server\110\Tools\Performance Dashboard)
開啓setup.sql檔執行安裝sql物件。
https://ithelp.ithome.com.tw/upload/images/20181022/20107408X2LEPRENxD.jpg
接著去新增自訂報表
https://ithelp.ithome.com.tw/upload/images/20181022/201074083yVteQahwS.jpg
開啓C:\Program Files (x86)\Microsoft SQL Server\110\Tools\Performance Dashboard\performance_dashboard_main.rdl
接著就可以看到DashBoard報表的主畫面,其中有藍色的連結點入都是各式不同資訊的報表
https://ithelp.ithome.com.tw/upload/images/20181022/20107408YTWoWxIreM.jpg
例如:當下的查詢指令
https://ithelp.ithome.com.tw/upload/images/20181022/20107408Oe1sqtseVU.jpg
各式Wait的累積數量
https://ithelp.ithome.com.tw/upload/images/20181022/20107408TPoRTBJRgv.jpg
目前在背景跑的Trace詳細資料
https://ithelp.ithome.com.tw/upload/images/20181022/20107408waR0W5d7lo.jpg
相信有了這些資訊,對於SQL Server調教效能或問題解決時,應該有不錯的益處。

[Windows]如何建立重複使用Hyper-V VM的虛擬硬碟檔

有時侯在建Lab環境時,需要一次建立數台VM Guest,
此時為了避免重複地去安裝數台VM Guest的OS,
因此只要裝好一台VM後,把這個當成基礎的VM虛擬硬碟檔,以後有新VM即可複製使用。
在VM灌好OS後,立即進入命令視窗畫面輸入以下指令
cd c:\Windows\System32\Sysprep
sysprep /generalize /oobe /mode:vm /shutdown
https://ithelp.ithome.com.tw/upload/images/20181014/20107408MttjnDxmUi.jpg
執行完sysprep後將會自動關機,接著至存放磁碟檔案的目錄(ex:d:\VM Files\Win2016_Host01),
直接複製到另一個地方並改目錄名稱(ex:d:\VM Files\Win2016_Host02),
接著在建新的VM時把磁碟檔案路徑指到新的目錄即可。

2018年10月17日 星期三

[SQL Server]Index管理和調教

曾經提到如何使用DMV找出Top 10 IO/CPU Query的SQL指令,
通常會針對前幾名消耗硬體資源較大的SQL指令進行索引調教,
首先先介紹查詢Index使用情況的DMV:sys.dm_db_index_usage_stats
--使用語法如下
SELECT OBJECT_NAME(S.[OBJECT_ID]) AS [OBJECT NAME], 
       I.[NAME] AS [INDEX NAME], 
       USER_SEEKS, 
       USER_SCANS, 
       USER_LOOKUPS, 
       USER_UPDATES 
FROM   SYS.DM_DB_INDEX_USAGE_STATS AS S 
       INNER JOIN SYS.INDEXES AS I ON I.[OBJECT_ID] = S.[OBJECT_ID] AND I.INDEX_ID = S.INDEX_ID 
WHERE  OBJECTPROPERTY(S.[OBJECT_ID],'IsUserTable') = 1
       AND S.database_id = DB_ID()
https://ithelp.ithome.com.tw/upload/images/20181016/20107408l799GFCeYJ.jpg
由以上的查詢結果得知,每各Index被讀取(USER_SEEKS+USER_SCANS+USER_LOOKUPS)的次數以及
被更新(USER_UPDATES)的次數,因為Index如果沒有經常被讀取,那代表他是比較沒有效益的,
另外無用的索引建太多,會影響Insert/Update/Delete的效率,因此索引不是建的愈多愈好
例如:在OLTP系統經常有交易在進行的情況下,反而容易造成鎖定的產生,進而拉長應用程式等待交易的時間!!

[SQL Server]SQL Profiler和SQL Trace的介紹

曾經有人問過我,要如何錄製他的程式對SQL Server所下的SQL指令,
筆者回他用SQL Profiler就可以錄到了,但要切記一點:必須要主動過濾錄製的範圍
因為如果是正式環境,上面會有大量的指令在進行中,一旦沒有去設定自己要錄的條件,
會錄到許多自己不想要的內容。
在實務上通常會以HostName(主機名稱)、LoginName(登入帳號)、TextData(對SQL Server下的指令),
這三個欄位來進行過濾,實作步驟如下:
進入SQL Profiler->檔案->新增追蹤
因為只要錄製特定帳號User1下的指令,Events只要選擇TSQL即可
https://ithelp.ithome.com.tw/upload/images/20181015/201074080mwslLqYUO.jpg
接著再點選資料行篩選,在左邊清單找到LoginName,在右邊類似欄位中輸入%user1%後,
便可以開始執行錄製了!
https://ithelp.ithome.com.tw/upload/images/20181015/20107408ejox4AzC3u.jpg
在SSMS下用user1帳號登入後,輸入以下指令進行查詢
https://ithelp.ithome.com.tw/upload/images/20181015/20107408W0VwEced8I.jpg
執行後,立刻去SQL Profiler查看,果然己經錄製下來!
https://ithelp.ithome.com.tw/upload/images/20181015/20107408KO4EIXk43r.jpg

[SQL Server]SQL Server 2016支援的資料遮罩

以前在做資料遮罩時,幾乎都是要自己寫些程式才能實作出來,
現在SQL Server 2016就有提供遮罩的功能供大家使用,以下為用實例來為大家介紹用法:
首先先建立範例資料:
CREATE TABLE Cust(
  ID varchar(11) primary key,
        Name nvarchar(10),
  Birth date,
  Mail varchar(50),  
        Credit varchar(19))
GO

INSERT INTO Cust 
 VALUES ('A123456789', N'陳一', '19700101', 'aa@gmail.com', '1111-2222-3333-4444')
      , ('D234567890', N'蔡三', '19790202', 'bb@gmail.com', '5555-6666-7777-8888')
      , ('H987654321', N'老王', '19810303', 'cb@gmail.com', '9999-1111-2222-3333')
https://ithelp.ithome.com.tw/upload/images/20181012/20107408vphZuFEM0h.jpg
建立測試資料後,先針對生日欄位設定預設的遮罩功能
ALTER TABLE Cust ALTER COLUMN Birthday ADD MASKED WITH(FUNCTION='default()')
接著再建立使用者masktester,讓他有select的權限,再用他來查詢資料
CREATE USER masktester WITHOUT LOGIN
GRANT SELECT ON Cust TO masktester

EXEC AS USER = 'masktester'
SELECT * FROM Cust
查詢結果如下,原本的生日都變成1900-01-01了!
https://ithelp.ithome.com.tw/upload/images/20181012/201074084IIS50XEdd.jpg
以上是基本的遮罩應用。
現在我們來做些進階應用,例如Email信用卡卡號等重要資訊,
可用partial,email()等參數來進行客製化遮罩。
ALTER TABLE Cust ALTER COLUMN CreditCard ADD MASKED WITH (FUNCTION = 'partial(4,"-XXXX-XXXX-",4)')
ALTER TABLE Cust ALTER COLUMN Email ADD MASKED WITH (FUNCTION = 'email()')

EXEC AS USER = 'masktester'
SELECT * FROM Cust
REVERT
遮罩效果如下:
https://ithelp.ithome.com.tw/upload/images/20181013/201074080MyklGXY06.jpg
SQL Server 2016提供的遮罩方式是By User來進行控管的,
因此在實際應用上要針對不同的User來設定是否需要開啓或關閉遮罩。
--關閉遮罩
GRANT UNMASK TO masktester
--恢復遮罩
REVOKE UNMASK TO masktester

[SQL Server]使用DMV找出最消耗CPU/IO的SQL指令

在實務上經常會定期找出消耗CPU/IO成本比較大的Top 10 Query來進行語法調教,
以增進DB的效能和AP查詢回應的速度,以下介紹常用到的二種DMV:
--查出前十名總累積IO數最高的的Query
SELECT TOP 10
total_logical_reads+total_logical_writes AS [總IO],
execution_count AS [執行次數],
total_logical_reads AS [讀取IO數],
total_logical_writes AS [寫入IO數],
st.text AS [SQL指令]
FROM sys.dm_exec_query_stats qs
CROSS APPLY sys.dm_exec_sql_text(sql_handle) st
WHERE total_logical_reads+total_logical_writes> 0
ORDER BY [總IO] DESC
查詢結果如下:
https://ithelp.ithome.com.tw/upload/images/20181009/20107408uXje0ADm8y.jpg
--查出前十名總使用CPU時間最高的的Query
SELECT  TOP 10 
qs.total_worker_time AS [總執行時間],
qs.execution_count AS [執行次數],
qs.total_worker_time / qs.execution_count AS [平均執行時間],
SUBSTRING(qt.text, qs.statement_start_offset / 2, 
(CASE WHEN qs.statement_end_offset = -1 THEN len(CONVERT (NVARCHAR (MAX), qt.text)) * 2
ELSE qs.statement_end_offset END - qs.statement_start_offset) / 2) AS [SQL指令]
FROM     sys.dm_exec_query_stats AS qs
CROSS APPLY sys.dm_exec_sql_text (qs.sql_handle) AS qt
ORDER BY [總執行時間] DESC;
查詢結果如下:
https://ithelp.ithome.com.tw/upload/images/20181009/20107408WPBJM1wD8Q.jpg

SQL指令調教的方法不外乎是新增缺乏的索引定期維護索引
再者可以針對擁有大量資料量的表格進行資料定期刪除,
又或者可將少被使用的歷史資料(例如:半年前或上一季)切到其它張表封存
等到有需要查詢再進行存取,這作法也是可以有效的減少查詢回應的時間。

2018年9月14日 星期五

[Windows]清除過期檔案的好工具,forfile指令的應用

最近在下電腦d槽裡有許多過期的檔案分散在各個子目錄中,因為想空出多些空間想刪除這些無用的檔案,因為分散在各個子目錄總不能一個個點進去刪除吧?!
IT人應該是要想辧法將一些固定作業的SOP實作成自動化排程,因此曾經看過有forfile指令似乎還不錯用。

使用範例如下:

刪除D槽TempFiles目錄底下(含子目錄),所有100天之前的檔案

forfiles /p d:\TempFiles /s /m *.* /d -100 /c "cmd /c del @file"

參數說明:
    /P    pathname 表示要開始搜尋的路徑。預設資料夾是目前的目錄 (.)。
    /M   searchmask 根據 searchmask 搜尋檔案。預設的 searchmask 是 '*'。
    /S    指示 forfiles 遞迴搜尋子目錄。例如 "DIR /S"。
    /C    command 表示每個檔案要執行的命令。命令字串應該包含在雙括號之間。
                             預設的命令是 "cmd /c echo @file"。
    /D    date          選取上次修改日期大於或
                        等於 (+),或小於或等於 (-)
                        指定日期 (使用
                        "yyyy/MM/dd" 格式指定) 的檔案; 或選取
                        上次修改日期大於或等於 (+)
                        目前日期加上 "dd" 天,或
                        小於或等於 (-) 目前日期減去
                        "dd" 天的檔案。有效的 "dd" 可以
                        是介於 0 - 32768 之間的數字。
                        如果未指定,"+" 將被視為預設符號。

2018年9月1日 星期六

[SQL]把找到的資料轉成HTML格式

在SQL實務應用上,相信不少人都會把抓出來的特定資料再透過email寄出來吧?
此時如果能轉成HTML格式會更方便閱讀,以下將分享使用FOR XML方式來達成此需求。

--建立測試資料
IF OBJECT_ID('#TT','U') IS NOT NULL
 DROP TABLE #TT
CREATE TABLE #TT 
(
   id INT,
   name VARCHAR(20),
   sex char(1),
   memo VARCHAR(50),
   cr_date datetime default(getdate())
)

INSERT INTO #TT 
(id,name,sex,memo)
VALUES
(1,N'John',N'M',N'test11111'),
(2,N'Lee',N'F',N'test22222'),
(3,N'Penny',N'F',N'test33333'),
(4,N'Wade',N'M',N'test444444'),
(5,N'James',N'M',N'test55555')

DECLARE @Content VARCHAR(MAX)
--將所有欄位名稱設成TD,再以''分隔
SET @Content = CAST((SELECT name AS 'TD','',
CASE sex WHEN 'M' THEN '男性' WHEN 'F' THEN '女性' END as 'TD','',
memo AS 'TD',''
FROM #TT
FOR XML PATH('tr'),TYPE) AS VARCHAR(MAX))

DECLARE @HTML VARCHAR(MAX)
SET @HTML='<HTML><BODY><TABLE border=1><TH>姓名</TH><TH>性別</TH><TH>MEMO</TH>'+@Content+'</TABLE>'

SELECT @HTML

[SQL Server][HA]鏡像應用Q&A

在上一篇文章己經把鏡像同步建立好了,
接著分享在資料庫維運時可能遇到的鏡像操作問題。
問題一、原本的主機發生問題(如硬碟故障或其它硬體有問題時),如何切換到備援機?
可以連至主機進行鏡像手動容錯移轉(manual failover)至備援機
USE [master]
ALTER DATABASE [APPSYS] SET PARTNER FAILOVER;
如果仍無法移轉,在容許資料遺失的情況下可執行以下指令
USE [master]
ALTER DATABASE MyDB SET PARTNER FORCE_SERVICE_ALLOW_DATA_LOSS 
或者至備援機斷開鏡像。
--連至備援端DB移除資料庫鏡像
USE [master]
--中斷鏡像同步後,如主體端DB沒有做過Log備份,都可以再次設定鏡像同步
ALTER DATABASE [APPSYS] SET PARTNER OFF;
--將資料庫恢復成RECOVERY模式,可進行存取的狀態
RESTORE DATABASE [APPSYS] WITH RECOVERY;

問題二、如何監控鏡像同步的情況?
可查詢sys.database_mirroring查詢當下鏡像的情況:
select db_name(database_id) as '資料庫名稱',mirroring_state_desc as '鏡像狀態' from sys.database_mirroring
where mirroring_state is not null
鏡像狀態可能的狀態有:
Unknown、正在同步處理、已同步處理、已暫停、已中斷連接
只要透過排程(Job)來定期執行以上語法,再判斷回傳鏡像狀態為己暫停或是己中斷連接時,
即發送通知給相關人員即可。

問題三、鏡像機制中斷後重建時要注意什麼?
在鏡像中斷後,只要主體端無任何備份(導致交易紀錄截斷)
仍可以直接進行鏡像同步,無需在備援機再次還原DB。
但假如主體端己經過做備份,則是仍須要到主體端備份DB後再至備援機進行DB還原才可以進行鏡像同步。

問題四、假設己容錯移轉至備援機,如何讓前端系統程式能使用相同的登入帳號(Login)?
在主體端可以使用微軟(KB提供的語法建立二支Store Procedure:sp_hexadecimal、sp_help_revlogin,
再執行exec sp_help_revlogin可顯示全部Login的建立Script,再拿去備援機執行即可複製Login。
exec sp_help_revlogin
--顯示Login Create Script
--CREATE LOGIN [tt01] WITH PASSWORD = 0x01005B429E7F53752B8B2B8DE7D5F97512738BA25CC527490C78 HASHED, SID = 0x51236A2E950F2A4680B087495907AFE3, DEFAULT_DATABASE = [APPSYS], CHECK_POLICY = ON, CHECK_EXPIRATION = OFF
問題五、如果遇到主機需要進行硬體維修或網路中斷時,可透過鏡像暫停和繼續來避開作業時間。
--暫停鏡像,以利後續作業
ALTER DATABASE APPSYS SET PARTNER SUSPEND;
--恢復鏡像
ALTER DATABASE APPSYS SET PARTNER RESUME;
--需等待後續的資料同步完成

參考資料:

[SQL Server]主動通知SQL Server發生錯誤的機制

當SQL Server有特定錯誤發生時,管理人員也沒有時間經常去查看SQL Server錯誤檔, 於是有時特定錯誤可能很重要時,但卻沒有人被通知到,現在小弟實作一個主動通知的機制。 首先先新增警示,在這裡選擇想要被通知的錯誤訊息的等級或其它設定 接著在回應頁籤中,勾選執...