亚洲免费在线-亚洲免费在线播放-亚洲免费在线观看-亚洲免费在线观看视频-亚洲免费在线看-亚洲免费在线视频

SQL Server 存儲引擎-剖析Forwarded Records

系統 1795 0
原文: SQL Server 存儲引擎-剖析Forwarded Records

我們都知道數據在存儲引擎中是以頁的形式組織的,但數據頁在不同的組織形式中其中對應的數據行存儲是不盡相同的,這里通過實例為大家介紹下堆表的中特有的一種情形Forwared Records及處理方式.

概念

堆表中,當對其中的記錄進行更新時,如果當前數據頁無法滿足更新行的容量,此時這行記錄將會轉移到新的數據頁中,而原數據頁中將會留下指針(文件號,頁號,槽號)鏈接到新的數據頁中.

Code 創建測試數據

      
        create
      
      
        database
      
      
         testpage


      
      
        go
      
      
        use
      
      
         testpage


      
      
        go
      
      
        create
      
      
        table
      
      
         testtb

(

id 
      
      
        int
      
      
        identity
      
      (
      
        1
      
      ,
      
        1
      
      
        ),

str1 
      
      
        char
      
      (
      
        100
      
      ) 
      
        default
      
      
        replicate
      
      (
      
        '
      
      
        a
      
      
        '
      
      ,
      
        100
      
      
        ),

str2 
      
      
        varchar
      
      (
      
        2000
      
      ) 
      
        default
      
      
        replicate
      
      (
      
        '
      
      
        b
      
      
        '
      
      ,
      
        500
      
      
        ),

str3 
      
      
        varchar
      
      (
      
        2000
      
      ) 
      
        default
      
      
        replicate
      
      (
      
        '
      
      
        c
      
      
        '
      
      ,
      
        1000
      
      
        )

)


      
      
        go
      
      
        insert
      
      
        into
      
       testtb 
      
        default
      
      
        values
      
      
        go
      
      
        20
      
    

Code 查看相關數據頁 如圖1-1

      
        DBCC
      
       TRACEON(
      
        3604
      
      
        )




      
      
        GO
      
      
        DBCC
      
       IND(testpage,
      
        '
      
      
        testtb
      
      
        '
      
      , 
      
        1
      
      )
      
        --
      
      
        -find the data page
      
      
        GO
      
      
        DBCC
      
       PAGE(
      
        '
      
      
        testpage
      
      
        '
      
      , 
      
        1
      
      , 
      
        79
      
      , 
      
        3
      
      )
      
        --
      
      
        ---view data page find slot 2(ID=3)
      
      
        GO
      
    

?????????? ????????????????????????

??????????? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 圖1-1

現在我們來更新ID=3的數據使當前數據頁(79)無法容納此行數據,然后觀察數據頁,

Code

update dbo.testtb set str2=replicate('t', 1000) where ID=3--update ID=3

GO

DBCC PAGE('testpage', 1, 79, 3)

GO

繼續找到slot 2槽位(ID=3)觀察 如圖1-2所示,此時slot2數據的Record Type = FORWARDING_STUB,也就是此時槽位2只留下RID記錄,數據轉到其他數據頁中了(Forwarding to? =? file 1 page 94 slot 0? )

?

??????????????????????????????????????????????? 圖1-2

?

這里稍微深入的講下RID的存儲內容,實例中根據dbcc page已經給我們展示RID的內容,實際上存儲是16進制的如圖1-2中的黑色部分(045e0000 00010000 00

).具體對應RID內容如圖1-3

??????????????????????????????????????????? 圖1-3

?

我們在找到實際存儲ID=3的數據頁看下數據內容(1:94:0) 如圖1-4

圖中我省去了數據內容

code

DBCC PAGE('testpage', 1, 94, 3)

GO

???????????????????????????????????????? 圖1-4

?

接下來我們繼續更新ID=3讓新的數據頁也無法容納它,然后觀察相應的數據頁如圖1-5(三個dbcc page 合成圖)

(此時ID=3的原始頁94,槽號2指向了新的數據頁位置184)如圖所示1-5所示

code

      
        insert
      
      
        into
      
       testtb 
      
        default
      
      
        values
      
      
        go
      
      
        20
      
      
        --
      
      
        --先插入一些數據
      
      
        update
      
       dbo. Testtb 
      
        set
      
       str2
      
        =
      
      
        replicate
      
      (
      
        '
      
      
        t
      
      
        '
      
      , 
      
        2000
      
      ),str3
      
        =
      
      
        replicate
      
      (
      
        '
      
      
        t
      
      
        '
      
      , 
      
        2000
      
      ) 
      
        where
      
       ID
      
        =
      
      
        3
      
      
        GO
      
      
        --
      
      
        -繼續更新ID=3
      
      
        DBCC
      
       PAGE(
      
        '
      
      
        testpage
      
      
        '
      
      , 
      
        1
      
      , 
      
        79
      
      , 
      
        3
      
      )
      
        --
      
      
        ------源ID=3,現在執行(1:184:2)
      
      
        GO
      
      
        DBCC
      
       PAGE(
      
        '
      
      
        testpage
      
      
        '
      
      , 
      
        1
      
      , 
      
        94
      
      , 
      
        3
      
      )
      
        --
      
      
        ------第一次修改時ID=3存儲位置(1:94:0),現在slot 0沒有了
      
      
        GO
      
      
        DBCC
      
       PAGE(
      
        '
      
      
        testpage
      
      
        '
      
      , 
      
        1
      
      , 
      
        184
      
      , 
      
        3
      
      )
      
        --
      
      
        ----目前id=3的數據存儲位置
      
      
        GO
      
    

?

?????????????????????????????????????????????? 圖1-5

?

可以看出id=3的原始頁(1:79:2)的數據再次變更后的由(1:94:0)挪到了(1:184:2)中,

而頁號94槽號0就不存在了.

堆表中的非聚集索引.

當堆表中有非聚集索引存在時,非聚集索引RID指向的原始頁位置

我們通過實例看下

注:關于heap rid我就不做詳細介紹了,實例中通過查詢轉換可以算出10進制對應的RID

Code

      
        CREATE
      
      
        UNIQUE
      
      
        NONCLUSTERED
      
      
        INDEX
      
       inx_1 
      
        ON
      
      
         testtb (id )




      
      
        DBCC
      
       IND(testpage,
      
        '
      
      
        testtb
      
      
        '
      
      , 
      
        -
      
      
        1
      
      )
      
        --
      
      
        --find the index page (page type 2)115
      
      
        GO
      
      
        DBCC
      
       PAGE(
      
        '
      
      
        testpage
      
      
        '
      
      , 
      
        1
      
      , 
      
        115
      
      , 
      
        3
      
      )
      
        --
      
      
        -find the heap rid where id=3 heap rid =0x4F00000001000200
      
      
        DECLARE
      
      
        @HeapRid
      
      
        BINARY
      
      (
      
        8
      
      
        )


      
      
        SET
      
      
        @HeapRid
      
      
        =
      
      
        0x4F00000001000200
      
      
        SELECT
      
      
        CONVERT
      
       (
      
        VARCHAR
      
      (
      
        5
      
      
        ),

                    
      
      
        CONVERT
      
      (
      
        INT
      
      , 
      
        SUBSTRING
      
      (
      
        @HeapRid
      
      , 
      
        6
      
      , 
      
        1
      
      
        )

                               
      
      
        +
      
      
        SUBSTRING
      
      (
      
        @HeapRid
      
      , 
      
        5
      
      , 
      
        1
      
      
        )))

     
      
      
        +
      
      
        '
      
      
        :
      
      
        '
      
      
        +
      
      
        CONVERT
      
      (
      
        VARCHAR
      
      (
      
        10
      
      
        ),

                    
      
      
        CONVERT
      
      (
      
        INT
      
      , 
      
        SUBSTRING
      
      (
      
        @HeapRid
      
      , 
      
        4
      
      , 
      
        1
      
      
        )

                               
      
      
        +
      
      
        SUBSTRING
      
      (
      
        @HeapRid
      
      , 
      
        3
      
      , 
      
        1
      
      
        )

                               
      
      
        +
      
      
        SUBSTRING
      
      (
      
        @HeapRid
      
      , 
      
        2
      
      , 
      
        1
      
      
        )

                               
      
      
        +
      
      
        SUBSTRING
      
      (
      
        @HeapRid
      
      , 
      
        1
      
      , 
      
        1
      
      
        )))

     
      
      
        +
      
      
        '
      
      
        :
      
      
        '
      
      
        +
      
      
        CONVERT
      
      (
      
        VARCHAR
      
      (
      
        5
      
      
        ),

                    
      
      
        CONVERT
      
      (
      
        INT
      
      , 
      
        SUBSTRING
      
      (
      
        @HeapRid
      
      , 
      
        8
      
      , 
      
        1
      
      
        )

                               
      
      
        +
      
      
        SUBSTRING
      
      (
      
        @HeapRid
      
      , 
      
        7
      
      , 
      
        1
      
      
        )))

                               
      
      
        AS
      
      
        '
      
      
        Fileid:Pageid:slot
      
      
        '
      
    

可以看到select 的輸出正好是(1:79:2)我們原始的id=3的位置

?

關于性能

由于forwarded record的存在,當訪問到這種數據行時,會消耗額外的隨機IO,從而影響性能.更有甚者,由于額外的數據頁被放入內存中,造成BP的污染,致使性能下降.

(研發要求對一個頻繁訪問的大堆表更新擴充欄位,執行完了性能依舊下降有木有?)

?

我們通過簡單實例來看下

訪問forwarded record會造成額外IO如圖2-1

Code

      
        set
      
      
        statistics
      
       io 
      
        on
      
      
        select
      
      
        *
      
      
        from
      
       testtb 
      
        where
      
       id
      
        =
      
      
        2
      
      
        select
      
      
        *
      
      
        from
      
       testtb 
      
        where
      
       id
      
        =
      
      
        3
      
    

??????????????????????????????????? 圖2-1

當表數據量大時,大批量更新擴充欄位會造成對緩沖池的污染

code

      
        create
      
      
        table
      
      
         testbp

(

id 
      
      
        int
      
      
        identity
      
      (
      
        1
      
      ,
      
        1
      
      
        ),

str1 
      
      
        char
      
      (
      
        100
      
      ) 
      
        default
      
      
        replicate
      
      (
      
        '
      
      
        a
      
      
        '
      
      ,
      
        100
      
      
        ),

str2 
      
      
        varchar
      
      (
      
        2000
      
      ) 
      
        default
      
      
        replicate
      
      (
      
        '
      
      
        b
      
      
        '
      
      ,
      
        500
      
      
        ),

str3 
      
      
        varchar
      
      (
      
        2000
      
      ) 
      
        default
      
      
        replicate
      
      (
      
        '
      
      
        c
      
      
        '
      
      ,
      
        1000
      
      
        )

)


      
      
        go
      
      
        insert
      
      
        into
      
       testbp 
      
        default
      
      
        values
      
      
        go
      
      
        10000
      
      
        dbcc
      
      
         dropcleanbuffers


      
      
        select
      
      
        *
      
      
        from
      
      
         testbp




      
      
        SELECT
      
      
        count
      
      (
      
        *
      
      )
      
        *
      
      
        8
      
      
        /
      
      
        1024
      
      
        AS
      
      
        '
      
      
        Cached Size (MB)
      
      
        '
      
      
        

,
      
      
        CASE
      
      
         database_id


      
      
        WHEN
      
      
        32767
      
      
        THEN
      
      
        '
      
      
        ResourceDb
      
      
        '
      
      
        ELSE
      
      
        db_name
      
      
        (database_id)


      
      
        END
      
      
        AS
      
      
        '
      
      
        Database
      
      
        '
      
      
        FROM
      
       sys.dm_os_buffer_descriptors 
      
        with
      
      
        (nolock)


      
      
        where
      
      
        db_name
      
      (database_id)
      
        =
      
      
        '
      
      
        testpage
      
      
        '
      
      
        GROUP
      
      
        BY
      
      
        db_name
      
      
        (database_id) ,database_id


      
      
        --
      
      
        ---buffer pool 15MB
      
      
        update
      
       dbo. testbp 
      
        set
      
       str2
      
        =
      
      
        replicate
      
      (
      
        '
      
      
        t
      
      
        '
      
      , 
      
        1000
      
      )
      
        --
      
      
        -make forwarded recordes
      
      
        dbcc
      
      
         dropcleanbuffers




      
      
        select
      
      
        *
      
      
        from
      
      
         testbp






      
      
        SELECT
      
      
        count
      
      (
      
        *
      
      )
      
        *
      
      
        8
      
      
        /
      
      
        1024
      
      
        AS
      
      
        '
      
      
        Cached Size (MB)
      
      
        '
      
      
        

,
      
      
        CASE
      
      
         database_id


      
      
        WHEN
      
      
        32767
      
      
        THEN
      
      
        '
      
      
        ResourceDb
      
      
        '
      
      
        ELSE
      
      
        db_name
      
      
        (database_id)


      
      
        END
      
      
        AS
      
      
        '
      
      
        Database
      
      
        '
      
      
        FROM
      
       sys.dm_os_buffer_descriptors 
      
        with
      
      
        (nolock)


      
      
        where
      
      
        db_name
      
      (database_id)
      
        =
      
      
        '
      
      
        testpage
      
      
        '
      
      
        GROUP
      
      
        BY
      
      
        db_name
      
      
        (database_id) ,database_id


      
      
        --
      
      
        ----31MB
      
    

順序執行代碼時可以看出,testpage表更改前后占Buffer Pool的大小分別為15M,31M,對BP影響明顯.

監控/發現

實際生產環境中我們需要監控一些性能指標用來輔助DBA解決問題,保證運維效率,針對這里,我們監控性能計數器中SQL Server Access Methods對象中的forwarded records/sec,如果你設定的了性能Baseline,這個值如果有異常變化,則需要我們關注.

同時我們也可以根據系統的DMF找出特定對象的forwarded records信息.代碼如下

      
        select
      
      
        object_name
      
      (
      
        object_id
      
      ) 
      
        as
      
      
         objectName

    ,index_type_desc

    ,forwarded_record_count

 
      
      
        from
      
      
         

    sys.dm_db_index_physical_stats(
      
      
        db_id
      
      (),
      
        null
      
      ,
      
        null
      
      ,
      
        null
      
      , 
      
        '
      
      
        detailed
      
      
        '
      
      
        )

    
      
      
        where
      
      
        object_name
      
      (
      
        object_id
      
      )
      
        =
      
      
        '
      
      
        testbp
      
      
        '
      
      
        --
      
      
        ----view the forwarded records info
      
    

注:可以通過簡單的Batch檢索整個庫甚至實例中的堆表的相關信息,有興趣的朋友自己寫下.

處理

如果發現了因為forwarded Recordes引起的性能問題,我們可以選擇表中創建聚集索引改變數據組織結構(forwarded Recordes只在堆表中存在).如果無法添加聚集索引,也可以選擇重組堆表( alter table heap rebuild )操作時應注意時間窗口

結語

任何事物都存在因果,套用數據庫系統中,我們應該清楚自己的所作所為,以及帶來的效用/影響.合理到位的分析,評估會讓我們的工作變得從容.

SQL Server 存儲引擎-剖析Forwarded Records


更多文章、技術交流、商務合作、聯系博主

微信掃碼或搜索:z360901061

微信掃一掃加我為好友

QQ號聯系: 360901061

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。

【本文對您有幫助就好】

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描上面二維碼支持博主2元、5元、10元、自定義金額等您想捐的金額吧,站長會非常 感謝您的哦!!!

發表我的評論
最新評論 總共0條評論
主站蜘蛛池模板: 国产精品区一区二区免费 | 奇米亚洲春色 | 色偷偷亚洲第一成人综合网址 | 国产一区二区在线不卡 | 香蕉视频免费在线观看 | 欧美成人高清视频 | 日韩毛片免费观看 | 深夜在线看片 | 久久这里只有精品免费的 | 精品福利一区二区三区免费视频 | 四虎永久网址 | aaaaaaa毛片 | 国产成人无精品久久久久国语 | 我要看免费一级毛片 | 亚洲高清视频免费 | 欧日韩美香蕉在线观看 | 九九久久国产精品大片 | 一级片免费视频 | 欧美成人免费夜夜黄啪啪 | 色视频在线免费观看 | 亚洲一区二区三区欧美 | 免看一级一片一在线看 | 亚洲欧美日本国产综合在线 | 欧美日韩国产58香蕉在线视频 | 日本免费一区尤物 | 私人影院免费观看 | 国产一区二区三区久久 | 狠狠色噜噜狠狠狠狠米奇777 | 九九久久视频 | 真91视频| 黄色成人在线网站 | 国产精品日韩欧美一区二区三区 | 1000部羞羞禁止免费观看视频 | 欧美亚洲另类综合 | 四虎最新紧急入口 | 亚洲精品一区二区卡 | 被黑人做的白浆直流在线播放 | 国产日比视频 | 国产精品久久久久影院免费 | 国产福利视频深夜福利 | 男人的天堂222eee |