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

來源:互聯網
上載者:User

標籤:style   blog   http   color   io   使用   ar   strong   sp   

解剖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    =    0xFFFF1    =    0x01002    =    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    =    0x7F1    =    0x812    =    0x82

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

-2    =    0x7E    =    -128 + 126-1    =    0x7F    =    -128 + 1271    =    0x81    =    -128 + 1292    =    0x82    =    -128 + 130

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

 

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

-2    =    0x7E    =    -128 + 126-1    =    0x7F    =    -128 + 1271    =    0x81    =    -128 + 1292    =    0x82    =    -128 + 130

 

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

 

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

 

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在行壓縮和頁壓縮裡的儲存格式揭秘(譯)

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.