欧美三区_成人在线免费观看视频_欧美极品少妇xxxxⅹ免费视频_a级毛片免费播放_鲁一鲁中文字幕久久_亚洲一级特黄

解剖SQLSERVER 第十三篇 Integers在行壓縮和頁

系統 1904 0
原文: 解剖SQLSERVER 第十三篇 Integers在行壓縮和頁壓縮里的存儲格式揭秘(譯)

解剖SQLSERVER 第十三篇 ? ?Integers在行壓縮和頁壓縮里的存儲格式揭秘(譯)

http://improve.dk/the-anatomy-of-row-amp-page-compressed-integers/

當解決OrcaMDF對行壓縮的支持的時候,視圖解析整數的時候遇到了一些挑戰。

和正常的未壓縮整數存儲不同的是這些都是可變長度--這意味著1個整數的值50只占用1個字節,而不是通常的4個字節。

這些不是新功能了,大家可以看一下 vardecimal 他被存儲為可變長度。然而不同的是兩者存儲在磁盤上的數據的方式。

注意雖然我只是實現行壓縮,他跟頁面壓縮中使用的行壓縮是一樣的,并沒有區別

大家可以看一下《 深入解析SQL Server 2008 筆記 》里面有行壓縮和頁壓縮的詳細解釋

?

?

Tinyint
Tinyint在壓縮后和壓縮前基本是一樣的(tinyint:從0到255的整數數據,存儲大小為 1 字節)只有一個例外情況,當數值是0的時候如果開啟了行壓縮將不占用任何字節,

如果是非壓縮存儲將會存儲0x0,并且占用一個字節。所有的整形類型(tinyint,smallint,int,bigint)對于0這個數值都是同等對待,數值由壓縮行元數據進行描述并且不存儲任何值

?

Smallint
讓我們開始通過觀察正常的未壓縮的smallint數值, 對于 -2,-1,1,2這些值的存儲,0不會存儲任何東西。注意,所有這些值會準確的存放在磁盤上,在這種情況下他們使用小字節序來存儲

      
        -
      
      
        2
      
      
        =
      
      
        0xFEFF
      
      
        -
      
      
        1
      
      
        =
      
      
        0xFFFF
      
      
        1
      
      
        =
      
      
        0x0100
      
      
        2
      
      
        =
      
      
        0x0200
      
    

Little-Endian

從1,2 這兩個值開始,他們很直接很簡單的轉換為decimal和你想要的實際數值。然而,-1有點不一樣,顯示0xFEFF 將他轉換為decimal是65.535 --我們能存儲的最大的無符號整形值是2個字節,

SQLSERVER對于一個smallint 的范圍是–32768 to 32767

?

計算實際值依賴于所使用的整數溢出。看看下面的C#代碼片段:

      
        unchecked
      
      
        

{

    Console.WriteLine(
      
      
        0
      
       + (
      
        short
      
      )
      
        32767
      
      
        );

    Console.WriteLine(
      
      
        0
      
       + (
      
        short
      
      )
      
        32768
      
      
        );

    Console.WriteLine(
      
      
        0
      
       + (
      
        short
      
      )
      
        32769
      
      
        );

    
      
      
        //
      
      
         ...
      
      

    Console.WriteLine(
      
        0
      
       + (
      
        short
      
      )
      
        65534
      
      
        );

    Console.WriteLine(
      
      
        0
      
       + (
      
        short
      
      )
      
        65535
      
      
        );

}
      
    

輸出如下:

      
        32767
      
      

-
      
        32768
      
      

-
      
        32767
      
      

-
      
        2
      
      

-
      
        1
      
    


如果我們這樣計算 0+有符號short的最大值,那么最大值就是有符號短整型 32767,很明顯負數就是-32767,

然而,如果我們這樣計算 0+32.768=32768,那么就會超出short的范圍,我們將最高位翻轉變成負數 -32768 卻不會溢出。

因為這些數都是常數,編譯器不允許溢出--除非我們將代碼封裝在uncheck {}section里面

?

你可能曾經聽過虛構的 符號位 。基本上它的最高位被用于指示一個數是正數還是負數。

從上面的例子應該很明顯的顯示符號位不是那么特別--通過查詢這個符號位決定一個給定的數的符號。看一下當溢出的時候符號位會怎樣

      
        32767
      
          =
      
            0b0111111111111111


      
      -
      
        32768
      
          =
      
            0b1000000000000000


      
      -
      
        32767
      
          =    0b1000000000000001
    

?

對于由于太大而引起溢出的數字,最高位“sign bit”需要進行設置。這不神奇,它只是用來引起溢出。

那么,我們有一些背景知識知道一個常規的非壓縮integers 是如何存儲的。現在看一下那些同樣數值的smallint 是如何存儲在行壓縮表里的

      -
      
        2
      
          =    
      
        0x7E
      
      

-
      
        1
      
          =    
      
        0x7F
      
      
        1
      
          =    
      
        0x81
      
      
        2
      
          =    
      
        0x82
      
    

讓我們嘗試將這些值轉換為decimal,我做如下轉換

      -
      
        2
      
          =    
      
        0x7E
      
          =    -
      
        128
      
       + 
      
        126
      
      

-
      
        1
      
          =    
      
        0x7F
      
          =    -
      
        128
      
       + 
      
        127
      
      
        1
      
          =    
      
        0x81
      
          =    -
      
        128
      
       + 
      
        129
      
      
        2
      
          =    
      
        0x82
      
          =    -
      
        128
      
       + 
      
        130
      
    

很明顯,這些值會以另一種方式進行存儲。最明顯的不同是我們現在只使用一個字節--由于變成了可變長度存儲。當我們解析這些值的時候,我們需要簡單的看一下這些數字的字節存儲。如果只使用一個字節,我們知道這表示0到255(對于tinyint來講) 或者對于smallint 數值是 -128到127 。當smallint 存儲的那個值范圍在-128到127 就會使用一個字節來存儲

?

如果我們使用相同的方法,我們明顯會獲得錯誤的結果 。1 <> 0 + 129 訣竅是在本例中將存儲的值作為無符號整數,然后最小值作為偏移量
而不是使用0來作為偏移,我們將使用有符號 的一個字節最小值-128 作為偏移

      -
      
        2
      
          =    
      
        0x7E
      
          =    -
      
        128
      
       + 
      
        126
      
      

-
      
        1
      
          =    
      
        0x7F
      
          =    -
      
        128
      
       + 
      
        127
      
      
        1
      
          =    
      
        0x81
      
          =    -
      
        128
      
       + 
      
        129
      
      
        2
      
          =    
      
        0x82
      
          =    -
      
        128
      
       + 
      
        130
      
    

?

這意味著一旦我們超出有符號 的1個字節的范圍 我們將需要用2個字節來存儲,對嗎?

解剖SQLSERVER 第十三篇 Integers在行壓縮和頁壓縮里的存儲格式揭秘

?

一個非常重要的區別是,非壓縮值會永遠使用小字節序來存儲,然而使用了行壓縮的整數值卻使用大字節序來存儲!
所以,他們不只使用不同的偏移值,而使用不同的字節序。但是最終的結果都是相同的,不過計算方式卻有很大的不同

?

Int 和 bigint
一旦我找到字節序的規律和行壓縮整型值的數值架構,int和bigint的實現就很簡單了。和其他類型一樣,他們也是可變長度的所以你有可能會碰到5字節長的bigint值和1字節長的int值。下面是SqlBigInt 類型的主要解析代碼

?

      
        switch
      
      
         (value.Length)

{

    
      
      
        case
      
      
        0
      
      
        :

        
      
      
        return
      
      
        0
      
      
        ;



    
      
      
        case
      
      
        1
      
      
        :

        
      
      
        return
      
       (
      
        long
      
      )(-
      
        128
      
       + value[
      
        0
      
      
        ]);



    
      
      
        case
      
      
        2
      
      
        :

        
      
      
        return
      
       (
      
        long
      
      )(-
      
        32768
      
       + BitConverter.ToUInt16(
      
        new
      
      [] { value[
      
        1
      
      ], value[
      
        0
      
      ] }, 
      
        0
      
      
        ));



    
      
      
        case
      
      
        3
      
      
        :

        
      
      
        return
      
       (
      
        long
      
      )(-
      
        8388608
      
       + BitConverter.ToUInt32(
      
        new
      
      
        byte
      
      [] { value[
      
        2
      
      ], value[
      
        1
      
      ], value[
      
        0
      
      ], 
      
        0
      
       }, 
      
        0
      
      
        ));



    
      
      
        case
      
      
        4
      
      
        :

        
      
      
        return
      
       (
      
        long
      
      )(-
      
        2147483648
      
       + BitConverter.ToUInt32(
      
        new
      
      [] { value[
      
        3
      
      ], value[
      
        2
      
      ], value[
      
        1
      
      ], value[
      
        0
      
      ] }, 
      
        0
      
      
        ));



    
      
      
        case
      
      
        5
      
      
        :

        
      
      
        return
      
       (
      
        long
      
      )(-
      
        549755813888
      
       + BitConverter.ToInt64(
      
        new
      
      
        byte
      
      [] { value[
      
        4
      
      ], value[
      
        3
      
      ], value[
      
        2
      
      ], value[
      
        1
      
      ], value[
      
        0
      
      ], 
      
        0
      
      , 
      
        0
      
      , 
      
        0
      
       }, 
      
        0
      
      
        ));



    
      
      
        case
      
      
        6
      
      
        :

        
      
      
        return
      
       (
      
        long
      
      )(-
      
        140737488355328
      
       + BitConverter.ToInt64(
      
        new
      
      
        byte
      
      [] { value[
      
        5
      
      ], value[
      
        4
      
      ], value[
      
        3
      
      ], value[
      
        2
      
      ], value[
      
        1
      
      ], value[
      
        0
      
      ], 
      
        0
      
      , 
      
        0
      
       }, 
      
        0
      
      
        ));



    
      
      
        case
      
      
        7
      
      
        :

        
      
      
        return
      
       (
      
        long
      
      )(-
      
        36028797018963968
      
       + BitConverter.ToInt64(
      
        new
      
      
        byte
      
      [] { value[
      
        6
      
      ], value[
      
        5
      
      ], value[
      
        4
      
      ], value[
      
        3
      
      ], value[
      
        2
      
      ], value[
      
        1
      
      ], value[
      
        0
      
      ], 
      
        0
      
       }, 
      
        0
      
      
        ));



    
      
      
        case
      
      
        8
      
      
        :

        
      
      
        return
      
       (
      
        long
      
      )(-
      
        9223372036854775808
      
       + BitConverter.ToInt64(
      
        new
      
      [] { value[
      
        7
      
      ], value[
      
        6
      
      ], value[
      
        5
      
      ], value[
      
        4
      
      ], value[
      
        3
      
      ], value[
      
        2
      
      ], value[
      
        1
      
      ], value[
      
        0
      
      ] }, 
      
        0
      
      
        ));



    
      
      
        default
      
      
        :

        
      
      
        throw
      
      
        new
      
       ArgumentException(
      
        "
      
      
        Invalid value length: 
      
      
        "
      
       +
      
         value.Length);

}
      
    

可變長度的值是一個包含字節數據的字節數組存儲在磁盤上。如果長度是0,沒有東西存儲因此我們知道他的值為0。

對于每一個剩余的有效長度,簡單的使用最小的顯示值作為偏移并且添加上存儲的值

對于非壓縮值我們可以使用BitConverter 類直接將輸入值使用系統字節序轉為期望值,對于大多數的英特爾和AMD系統,一般都是小字節序(意味著OrcaMDF 不會運行在一個大字節序的系統上)。然而,當壓縮值使用大字節序進行壓縮,我必須重新映射輸入的數組為小端字節格式,并且在字節尾補上0 以便匹配short,int和long的大小


對于shorts和ints 我將無符號數值讀取進來,因為這是我所感興趣的。工作原理是將int 和uint強制轉換為long值。我不能對long類型做同樣的事情因為沒有其他數據類型比long 更大了。對于long的最大值為9.223.372.036.854.775.807,在磁盤里實際存儲為0xFFFFFFFFFFFFFFFF。解析有符號long型使用BitConverter得出的結果 -1 由于會導致溢出。由于額外的負數溢出這有可能會導致出錯

      -
      
        9.223
      
      .
      
        372.036
      
      .
      
        854.775
      
      .
      
        808
      
       + 
      
        0xFFFFFFFFFFFFFF
      
       =>

-
      
        9.223
      
      .
      
        372.036
      
      .
      
        854.775
      
      .
      
        808
      
       + -
      
        1
      
       =


      
        9.223
      
      .
      
        372.036
      
      .
      
        854.775
      
      .
      
        807
      
    

?

結論
通常我有很多的有趣的嘗試通過執行一個select語句去找出數值在磁盤上以哪一個字節結束。
這不會花很長的時間去實現,技術內幕的書只是作為引導,還有很多東西需要我們深入挖掘

?

第十三篇完

解剖SQLSERVER 第十三篇 Integers在行壓縮和頁壓縮里的存儲格式揭秘


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

微信掃碼或搜索:z360901061

微信掃一掃加我為好友

QQ號聯系: 360901061

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

【本文對您有幫助就好】

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

發表我的評論
最新評論 總共0條評論
主站蜘蛛池模板: www.国产一区| 黄网站在线观看 | 一级做a爱片特黄在线观看yy | 欧美双插| 99青青草 | 国产99久久精品一区二区 | 日韩在线免费视频 | 久久久国产视频 | 亚洲精品第五页中文字幕 | 国产亚洲精品不卡在线 | www.亚洲在线 | 九九国产 | 亚洲欧美日韩中文综合v日本 | 久久99深爱久久99精品 | 无码日韩精品一区二区免费 | 日韩1页 | 久久中文字幕一区 | 日本一级高清不卡视频在线 | 欧美激烈精交gif动态图18p | 九久精品 | 亚洲精品无码成人A片在线虐 | 日韩午夜激情视频 | 国产精品成人国产乱一区 | 日本一区二区在线视频 | 亚洲在线视频播放 | 日韩欧美专区 | 91免费大全 | 96自拍视频 | jjzzjjzz在线观看 | 亚洲人成网站999久久久综合 | a级片免费视频 | 国产成人综合自拍 | 色屋视频 | 欧美视频网站在线观看 | 日本久久网 | 国产精品爱久久久久久久 | 综合网女女网 | 少妇特黄a一区二区三区88av | 亚洲国产一区二区三区四区 | 综合久久久久综合 | 国产精品久久久爽爽爽麻豆色哟哟 |