如何解讀SQL Server日誌(2/3)

来源:http://www.cnblogs.com/Joe-T/archive/2016/08/19/5788672.html
-Advertisement-
Play Games

接下來說說返回的RowLogo Content列,例子中返回了三個列。這些列包含了數據操作的“有效工作負載(Playload)”記錄。根據不同操作類型有效負載的內容也是不同的,但是它必須包含足夠的信息,能讓相應的數據操作在恢復時能被REDO和UNDO。對於每一個INSERT而言,它包含了插入行的所有 ...


接下來說說返回的RowLogo Content列,例子中返回了三個列。這些列包含了數據操作的“有效工作負載(Playload)”記錄。根據不同操作類型有效負載的內容也是不同的,但是它必須包含足夠的信息,能讓相應的數據操作在恢復時能被REDO和UNDO。對於每一個INSERT而言,它包含了插入行的所有數據。我們來驗證一下,先看看RowLogo Contents 0的內容:

0x10002400010000007374616E64616C6F6E6520786163742020202020797D6F0064A60000030000

然後再看DBCC PAGE中Slot 0的內容:

0000000000000000:   10002400 01000000 7374616e 64616c6f 6e652078  ..$.....standalone x
0000000000000014:   61637420 20202020 797d6f00 64a60000 030000    act     y}o.d......

DBCC PAGE輸出行的內容被分成了左、中、右三個部分:

  • 左:表示對應內容在行內的字元位置(或者起始偏移量)
  • 中:表示存儲在頁上實際數據,由5段8位的16進位數組成
  • 右:實際數據的ASCII表示形式,只有字元數據能夠被辨認出來。
    BY Joe .TJ

對比RowLogo Contents 0和DBCC PAGE輸出Slot 0的中段數據,發現兩者數據是一樣的。同時證明瞭LOP_INSERT_ROWS操作的RowLogo Contents 0日誌內容包含了完整的被插入數據的內容。然而,通過分析RowLogo Contents的內容去找出某一特定的行,這個是非常困難的事情。如果你熟悉行結構,RowLogo Contents 0 列的內容可以分解為:

  • 1000 (列頭,2Bytes狀態位)
  • 2400 (2Bytes 列數量值的Offset量,也表示列數量值前有多少位元組被使用)
  • 01000000 (ID列,INT)
  • 7374616E64616C6F6E6520786163742020202020 (data列,char(20))
  • 797D6F0064A60000 (created_at列,datetime)
  • 0300 (列數量,2Bytes)
  • 00 (NULL點陣圖=Ceiling(NULL列數量/8))

    關於行結構的更多內容,參考《SQL Server 2012 Internals》第六章'Table Storage'中的'The structure of data row'部分。行結構圖:
    image

我用幾個例子來說明如何使用這些知識幫助我們解決問題。先修改表中的數據:

insert into demotable (data) values ('junk'), ('important'), ('do not change!');
update demotable set data='changed' where data = 'do not change!';
update demotable set created_at = getutcdate() where data = 'changed';
delete from demotable where data='important'

經過上面的修改,新插入的三行中只有('junk')沒有改變。('do not changed')被改成了('change'),創建時間也被改成現在的UTC時間。important行被刪除了。我們能否從日誌找到這些修改操作對應的日誌呢?先從較簡單的問題開始

  • 找到'junk'相關的日誌

因為'junk'還存在於表中,我們通過找到它的lockres,然後根據lockres去搜索日誌中的[Lock Information],再從匹配的行中[Transaction ID]欄位得到INSERT的事務ID,再通過事務ID去找出與這個事務相關的所有日誌記錄。

--get lockres value of 'junk'
declare @lockres nchar(14);
select top(1) @lockres=%%lockres%%
from dbo.demotable
where data='junk';

--Using lockres to find Transaction ID
declare @xactid nvarchar(14);
select top(1) @xactid=[Transaction ID] from sys.fn_dblog(null,null)
where CHARINDEX(@lockres,[Lock Information] )>0

--get all log record of the found Transaction ID
select [Current LSN], [Operation], [Transaction ID], 
    [Transaction SID], [Begin Time], [End Time]  
from sys.fn_dblog(null,null)
where [Transaction ID]=@xactid;

Current LSN             Operation             Transaction ID  Transaction SID      Begin Time               End Time
----------------------- ---------------- -------------------- ------------------ ----------------------- ---------
00000023:0000007b:0001  LOP_BEGIN_XACT       0000:00000363     0x01                 2016/08/19 14:50:27:917  NULL
00000023:0000007b:0002  LOP_INSERT_ROWS      0000:00000363     NULL                 NULL                     NULL
00000023:0000007b:0003  LOP_INSERT_ROWS      0000:00000363     NULL                 NULL                     NULL
00000023:0000007b:0004  LOP_INSERT_ROWS      0000:00000363     NULL                 NULL                     NULL
00000023:0000007b:0005  LOP_COMMIT_XACT      0000:00000363     NULL                 NULL            2016/08/19 14:50:27:920

使用上面的方法時,我假設了幾個前提:

  • lockres是唯一的,沒有HASH碰撞
  • 鎖定行的第一個事務就是我想找的事務

在現實中情況不會這麼簡單:HASH碰撞的問題,可能有很多事務曾鎖定過這一行等等。

  • 找出把'do not change!'修改成'changed!'的事務

跟之前的思路差不多,先從表中存的'changed'找到lockres,然後根據lockres找到所有事務ID,然後再找出所有的日誌記錄。

--get lockres value of the updated 'changed'
declare @lockres nchar(14);
select @lockres=%%lockres%%
from dbo.demotable
where data='changed';

--Using lockres to find ALL relevant Transaction IDs
declare @xactid table (xid nvarchar(14));
insert into @xactid
select [Transaction ID] 
from sys.fn_dblog(null,null)
where CHARINDEX(@lockres,[Lock Information] )>0

--get all log records of the found Transaction ID
select [Current LSN], [Operation], [Transaction ID], 
    [Transaction SID], [Begin Time], [End Time],
    [Num Elements], [RowLog Contents 0], [RowLog Contents 1], 
    [RowLog Contents 2],
    [RowLog Contents 3], [RowLog Contents 4], [RowLog Contents 5]
from sys.fn_dblog(null,null)as a
join @xactid b 
on a.[Transaction ID]=b.xid

Changed

從結果可以看到,有2個LOP_MODIFY_ROW操作的日誌,怎麼知道哪一條日誌是我想要找的?在查詢時,我增了[Num Elements]列,因為[Num Elements]=6,表示這個操作有6個有效工作負載,所以我增加了和[RowLog Contents 0]到[RowLog Contents 5]這6個欄位。這個6個欄位中,包含所有的效工作負載。我們之前說過,日誌必須包含足夠多的信息,才能支持恢復時的REDO和UNDO。也諒是說這個6個有效工作負載包含了修改前後的數據。從LSN=00000023:0000007d:0002的LOP_MODIFY_ROW的[RowLog Contents 0]和[RowLog Contents 1]的有效負載內容,可以看出是ASCII。於是:

select cast(0x646F206E6F74206368616E676521 as char(20)),
cast (0x6368616E67656420202020202020 as char(20))
-------------------- --------------------
do not change!       changed   

現在我們可以確定事務0000:00000364諒是我們要找的UPDATE的日誌。然後通過[Begin Time]和[Transaction SID]可以誰什麼時候修改了這條數據。

  • 找出刪除'important'的日誌

'important'被刪除了,所以沒有辦法使用lockres來尋找。如果我們知道它的ID值,我們可以插入一條相同ID的記錄,它會生成一樣lockres,可惜我們也不知道ID值。我們現知道的只有data列的值為'important',如果'important'在data列中的篩選度足夠高的話,可以嘗試通過[Log Record]去找到一些匹配的日誌記錄。

select [Current LSN],Operation,Context,[Transaction ID]
from fn_dblog(null, null)
where charindex(cast('important' as varbinary(20)), [Log Record]) > 0;

Current LSN             Operation        Context           Transaction ID
----------------------- ---------------- ----------------- --------------
00000023:0000007b:0003  LOP_INSERT_ROWS  LCX_CLUSTERED     0000:00000363
00000023:0000007f:0002  LOP_DELETE_ROWS  LCX_MARK_AS_GHOST 0000:00000366

select [Current LSN], [Operation], [AllocUnitName], [Transaction Name]
    from fn_dblog(null, null)
    where [Transaction ID] = '0000:00000366';

從找到的結果,可以看出LOP_INSERT_ROWS是的插入時的操作,LOP_DELETE_ROWS是一個插入操作。我們可以試著用LOP_DELETE_ROWS 的事務0000:00000366去找到相關的日誌記錄。例子中這種複雜粗暴找到日誌的方式在現實中可能會非常困難。例子用的一個簡單的ASCII的字元串,如果是其它類型的,你需要知道值在SQL Server中的內部表示形式(如numeric,decimal),還要用寫出正確的Intel平臺的LSB值 (如int,datetime)。這個是非常非常困難的


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 一、項目簡介 貪吃蛇是一個很經典的游戲,也很適合用來學習。本教程將和大家一起做一個Android版的貪吃蛇游戲。我已經將做好的案例上傳到了應用寶,大家可以下載下來把玩一下。為了和其它的貪吃蛇區別開來,我取名叫“快樂貪吃蛇”。應用寶鏈接:http://sj.qq.com/myapp/detail.ht ...
  • 下標腳本可以定義在類(Class)、結構體(Struct)、枚舉(enumeration)這些目標中,可以認為是訪問集合,列表或序列的快捷方式,使用下標腳本的索引設置和獲取值,不需要再調用實例的特定的賦值和訪問方法。對於同一個目標可以定義多個下標腳本,通過索引值類型的不同來進行重載,下標腳本不限於單 ...
  • if (iOS8) { //iOS8以上包含iOS8 if ([[UIApplication sharedApplication] currentUserNotificationSettings].types == UIUserNotificationTypeNone) { NSLog(@"1111 ...
  • Message: 定義: public final class Message implements Parcelable Message類是個final類,就是說不能被繼承,同時Message類實現了Parcelable介面,我們知道android提供了一種新的類型:Parcel。本類被用作封裝數 ...
  • if (iOS8) { NSURL *url = [NSURL URLWithString:UIApplicationOpenSettingsURLString]; if ([[UIApplication sharedApplication] canOpenURL:url]) { [[UIAppli ...
  • 下麵直接貼代碼 1. 將GB2312轉化為中文,如BAFAC2DCB2B7→胡蘿蔔,兩個位元組合成一個文字 2.將中文轉化為GB2312,並且結果以byte[]形式返回,如胡蘿蔔→new byte[]{BA FA C2 DC B2 B7},一個字被分為兩個位元組 3.將十六進位的byte[]原封不動的轉 ...
  • 我們在平常開發過程中,在設計數據的時候,經常碰到數據類型選擇的問題,為了更快,更合適地選擇正確的數據類型,所以在這裡做個總結。 轉自:http://www.cnblogs.com/mcgrady/p/5776255.html ...
  • fvdwtfv18yy0m 士大夫士大夫 撒發順豐 select name,DATATYPE_STRING,VALUE_STRING from v$sql_bind_capture where sql_id='fvdwtfv18yy0m'; SELECT TABLE_NAME,COLUMN_NAME ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...