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

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

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

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

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

當(dāng)解決OrcaMDF對(duì)行壓縮的支持的時(shí)候,視圖解析整數(shù)的時(shí)候遇到了一些挑戰(zhàn)。

和正常的未壓縮整數(shù)存儲(chǔ)不同的是這些都是可變長度--這意味著1個(gè)整數(shù)的值50只占用1個(gè)字節(jié),而不是通常的4個(gè)字節(jié)。

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

注意雖然我只是實(shí)現(xiàn)行壓縮,他跟頁面壓縮中使用的行壓縮是一樣的,并沒有區(qū)別

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

?

?

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

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

?

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

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

Little-Endian

從1,2 這兩個(gè)值開始,他們很直接很簡單的轉(zhuǎn)換為decimal和你想要的實(shí)際數(shù)值。然而,-1有點(diǎn)不一樣,顯示0xFEFF 將他轉(zhuǎn)換為decimal是65.535 --我們能存儲(chǔ)的最大的無符號(hào)整形值是2個(gè)字節(jié),

SQLSERVER對(duì)于一個(gè)smallint 的范圍是–32768 to 32767

?

計(jì)算實(shí)際值依賴于所使用的整數(shù)溢出。看看下面的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
      
    


如果我們這樣計(jì)算 0+有符號(hào)short的最大值,那么最大值就是有符號(hào)短整型 32767,很明顯負(fù)數(shù)就是-32767,

然而,如果我們這樣計(jì)算 0+32.768=32768,那么就會(huì)超出short的范圍,我們將最高位翻轉(zhuǎn)變成負(fù)數(shù) -32768 卻不會(huì)溢出。

因?yàn)檫@些數(shù)都是常數(shù),編譯器不允許溢出--除非我們將代碼封裝在uncheck {}section里面

?

你可能曾經(jīng)聽過虛構(gòu)的 符號(hào)位 。基本上它的最高位被用于指示一個(gè)數(shù)是正數(shù)還是負(fù)數(shù)。

從上面的例子應(yīng)該很明顯的顯示符號(hào)位不是那么特別--通過查詢這個(gè)符號(hào)位決定一個(gè)給定的數(shù)的符號(hào)。看一下當(dāng)溢出的時(shí)候符號(hào)位會(huì)怎樣

      
        32767
      
          =
      
            0b0111111111111111


      
      -
      
        32768
      
          =
      
            0b1000000000000000


      
      -
      
        32767
      
          =    0b1000000000000001
    

?

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

那么,我們有一些背景知識(shí)知道一個(gè)常規(guī)的非壓縮integers 是如何存儲(chǔ)的。現(xiàn)在看一下那些同樣數(shù)值的smallint 是如何存儲(chǔ)在行壓縮表里的

      -
      
        2
      
          =    
      
        0x7E
      
      

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

讓我們嘗試將這些值轉(zhuǎn)換為decimal,我做如下轉(zhuǎn)換

      -
      
        2
      
          =    
      
        0x7E
      
          =    -
      
        128
      
       + 
      
        126
      
      

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

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

?

如果我們使用相同的方法,我們明顯會(huì)獲得錯(cuò)誤的結(jié)果 。1 <> 0 + 129 訣竅是在本例中將存儲(chǔ)的值作為無符號(hào)整數(shù),然后最小值作為偏移量
而不是使用0來作為偏移,我們將使用有符號(hào) 的一個(gè)字節(jié)最小值-128 作為偏移

      -
      
        2
      
          =    
      
        0x7E
      
          =    -
      
        128
      
       + 
      
        126
      
      

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

?

這意味著一旦我們超出有符號(hào) 的1個(gè)字節(jié)的范圍 我們將需要用2個(gè)字節(jié)來存儲(chǔ),對(duì)嗎?

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

?

一個(gè)非常重要的區(qū)別是,非壓縮值會(huì)永遠(yuǎn)使用小字節(jié)序來存儲(chǔ),然而使用了行壓縮的整數(shù)值卻使用大字節(jié)序來存儲(chǔ)!
所以,他們不只使用不同的偏移值,而使用不同的字節(jié)序。但是最終的結(jié)果都是相同的,不過計(jì)算方式卻有很大的不同

?

Int 和 bigint
一旦我找到字節(jié)序的規(guī)律和行壓縮整型值的數(shù)值架構(gòu),int和bigint的實(shí)現(xiàn)就很簡單了。和其他類型一樣,他們也是可變長度的所以你有可能會(huì)碰到5字節(jié)長的bigint值和1字節(jié)長的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);

}
      
    

可變長度的值是一個(gè)包含字節(jié)數(shù)據(jù)的字節(jié)數(shù)組存儲(chǔ)在磁盤上。如果長度是0,沒有東西存儲(chǔ)因此我們知道他的值為0。

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

對(duì)于非壓縮值我們可以使用BitConverter 類直接將輸入值使用系統(tǒng)字節(jié)序轉(zhuǎn)為期望值,對(duì)于大多數(shù)的英特爾和AMD系統(tǒng),一般都是小字節(jié)序(意味著OrcaMDF 不會(huì)運(yùn)行在一個(gè)大字節(jié)序的系統(tǒng)上)。然而,當(dāng)壓縮值使用大字節(jié)序進(jìn)行壓縮,我必須重新映射輸入的數(shù)組為小端字節(jié)格式,并且在字節(jié)尾補(bǔ)上0 以便匹配short,int和long的大小


對(duì)于shorts和ints 我將無符號(hào)數(shù)值讀取進(jìn)來,因?yàn)檫@是我所感興趣的。工作原理是將int 和uint強(qiáng)制轉(zhuǎn)換為long值。我不能對(duì)long類型做同樣的事情因?yàn)闆]有其他數(shù)據(jù)類型比long 更大了。對(duì)于long的最大值為9.223.372.036.854.775.807,在磁盤里實(shí)際存儲(chǔ)為0xFFFFFFFFFFFFFFFF。解析有符號(hào)long型使用BitConverter得出的結(jié)果 -1 由于會(huì)導(dǎo)致溢出。由于額外的負(fù)數(shù)溢出這有可能會(huì)導(dǎo)致出錯(cuò)

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

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


      
        9.223
      
      .
      
        372.036
      
      .
      
        854.775
      
      .
      
        807
      
    

?

結(jié)論
通常我有很多的有趣的嘗試通過執(zhí)行一個(gè)select語句去找出數(shù)值在磁盤上以哪一個(gè)字節(jié)結(jié)束。
這不會(huì)花很長的時(shí)間去實(shí)現(xiàn),技術(shù)內(nèi)幕的書只是作為引導(dǎo),還有很多東西需要我們深入挖掘

?

第十三篇完

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


更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主

微信掃碼或搜索:z360901061

微信掃一掃加我為好友

QQ號(hào)聯(lián)系: 360901061

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

【本文對(duì)您有幫助就好】

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

發(fā)表我的評(píng)論
最新評(píng)論 總共0條評(píng)論
主站蜘蛛池模板: 国产一区亚洲二区三区 | 久久婷婷激情综合中文字幕 | 男女拍拍视频黄的全免费 | 午夜免费福利影院 | 亚洲爱爱久久精品 | 色在线综合 | 国产精品免费看 | 国产成人在线视频 | 亚洲美色综合天天久久综合精品 | 特黄特级毛片免费视 | 午夜一级毛片不卡 | 欧美一级特黄乱妇高清视频 | 国产一区二区三区成人久久片 | 日韩中文字幕视频在线观看 | 另类重口性色老妇 | 国产a级网站 | 一级欧美毛片成人免费视频 | 在线视频www | www色婷婷 | 最新中文字幕在线观看 | 国产一区二区三区免费看 | 亚洲视频在线网 | 亚洲免费视频网 | 国产精品第一页爽爽影院 | 欧美日韩中文在线视频 | 在线播放波多野结衣 | 欧美经典人人爽人人爽人人片 | 精品精品国产高清a毛片 | 国产成人精品亚洲2020 | 91精品国产高清久久久久久io | 亚洲精品色一区二区三区 | 亚洲精品综合久久 | 欧美aⅴ | 九九视频国产免 | 亚洲小视频网站 | 天天摸夜夜摸夜夜狠狠摸 | 欧美高清无砖专区欧美精品 | 久久一区二区三区免费播放 | 曰韩三级 | 日本黄色网址免费 | 国产福利在线观看永久免费 |