SQL Server'ın 8 KByte veri sayfasından 512 Bayt kullanılmıyor


13

Aşağıdaki tabloyu oluşturduk:

CREATE TABLE dbo.TestStructure
(
    id INT NOT NULL,
    filler1 CHAR(36) NOT NULL,
    filler2 CHAR(216) NOT NULL
);

ve sonra kümelenmiş bir dizin yarattı:

CREATE CLUSTERED INDEX idx_cl_id 
ON dbo.TestStructure(id);

Sonra ben her boyutu 256 bayt (tablo beyanı dayalı) 30 satır ile doldurdu:

DECLARE @i AS int = 0;

WHILE @i < 30
BEGIN
    SET @i = @i + 1;

    INSERT INTO dbo.TestStructure (id, filler1, filler2)
    VALUES (@i, 'a', 'b');
END;

Şimdi "Eğitim Seti (Sınav 70-461): Microsoft SQL Server 2012'yi Sorgulama (Itzik Ben-Gan)" kitabında okuduğum bilgilere dayanarak:

SQL Server bir veri dosyasındaki verileri dahili olarak sayfalar halinde düzenler. Bir sayfa 8 KB'lık bir birimdir ve tek bir nesneye aittir; örneğin, bir tabloya veya bir dizine. Sayfa en küçük okuma ve yazma birimidir. Sayfalar ayrıca uzantılar halinde düzenlenir. Bir kapsam birbirini takip eden sekiz sayfadan oluşur. Bir derecedeki sayfalar tek bir nesneye veya birden çok nesneye ait olabilir. Sayfalar birden çok nesneye aitse, kapsam karışık bir kapsam olarak adlandırılır; Eğer sayfalar tek bir nesneye aitse, o zaman bu muntazam bir boyut olarak adlandırılır. SQL Server, bir nesnenin ilk sekiz sayfasını karışık uzantılarda saklar. Bir nesne sekiz sayfayı aştığında, SQL Server bu nesne için ek tek biçimli uzantılar ayırır. Bu organizasyonla, küçük nesneler daha az yer kaplar ve büyük nesneler daha az parçalanır.

Bu yüzden burada, 7680 bayt ile doldurulmuş ilk karışık genişlikteki 8KB sayfaya sahibim (30 kez 256 bayt boyut satırı, yani 30 * 256 = 7680) ekledim, boyutu kontrol işlemi yaptığım boyutu kontrol etmek için - aşağıdaki sonucu döndürür

index_type_desc: CLUSTERED INDEX
index_depth: 1
index_level: 0 
page_count: 1 
record_count: 30 
avg_page_space_used_in_percent: 98.1961947121324
name : TestStructure        
rows : 30   
reserved :  16 KB
data : 8 KB 
index_size : 8 KB       
unused :    0 KB

Yani 16 KB tablo için ayrılmıştır, ilk 8 KB sayfa Kök IAM sayfası için, ikincisi ~ 7.5 KB işgali ile 8KB olan yaprak veri depolama sayfası içindir, şimdi 256 Byte ile yeni bir satır eklediğimde:

INSERT INTO dbo.TestStructure (id, filler1, filler2)
VALUES (1, 'a', 'b');

256 baytlık bir alana sahip olmasına rağmen (7680 b + 256 = 7936 hala 8KB'den küçük) aynı sayfada depolanmaz, yeni bir veri sayfası oluşturulur, ancak bu yeni satır aynı eski sayfaya sığabilir , SQL Server alandan tasarruf edebildiğinde neden yeni bir sayfa oluşturuyor ve arama süresi mevcut sayfaya ekleyerek satın alıyor?

Not: yığın dizininde de aynı şey oluyor.

Yanıtlar:


9

Veri satırlarınız 256 bayt değil. Her biri 263 bayt gibidir. Tamamen sabit uzunluklu veri türlerinden oluşan bir veri satırı, SQL Server'daki bir veri satırının yapısı nedeniyle ek yüke sahiptir. Bu siteye bir göz atın ve bir veri satırının nasıl oluşturulduğunu okuyun. http://aboutsqlserver.com/2013/10/15/sql-server-storage-engine-data-pages-and-data-rows/

Örneğin, örnekte 256 baytlık bir veri satırınız var, durum bitleri için 2 bayt, sütun sayısı için 2 bayt, veri uzunluğu için 2 bayt ve boş bitmap için 1 veya daha fazla ekleyin. Bu 263 * 30 = 7.890 bayt. Başka bir 263 ekleyin ve 8kb sayfa sınırını aştınız.


Sağladığınız bağlantı, sayfa yapısını daha iyi görselleştirmemde bana yardımcı oldu, buna benzer bir şey arıyordum, ancak bulamadım, Thax
Alphas Supremum

11

SQL Server'ın 1 veya daha fazla satırı saklamak için 8k (8192 bayt) veri sayfası kullandığı doğru olsa da, her veri sayfasının bir miktar ek yükü (96 bayt) vardır ve her satırın bir ek yükü vardır (en az 9 bayt). 8192 bayt sadece veri değildir.

Bunun nasıl çalıştığına dair daha ayrıntılı bir inceleme için lütfen aşağıdaki DBA.SE sorusuna verdiğim cevaba bakın:

Sys.allocation_units öğesinden tablo boyutuyla eşleşmeyen DATALENGTH toplamı

Bağlantılı yanıttaki bilgileri kullanarak, gerçek satır boyutunun daha net bir resmini elde edebiliriz:

  1. Satır Üstbilgisi = 4 bayt
  2. Sütun Sayısı = 2 bayt
  3. NULL Bitmap = 1 bayt
  4. Sürüm Bilgisi ** = 14 bayt (isteğe bağlı, bkz. Dipnot)
  5. Satır Başına Toplam Ek Maliyet (Yuva Dizisi hariç) = minimum 7 bayt veya sürüm bilgisi varsa 21 bayt
  6. Toplam Gerçek Satır Boyutu = sürüm bilgisi varsa minimum 263 (256 veri + 7 ek yük) veya 277 bayt (256 veri + 21 ek yük)
  7. Yuva Dizisi'ne ek olarak, satır başına alınan toplam alan aslında 265 bayt (sürüm bilgisi olmadan) veya 279 bayttır (sürüm bilgisi ile).

Kullanmak DBCC PAGEhesaplamamı şu şekilde göstererek onaylar: Record Size 263(for tempdb) ve Record Size 277(olarak ayarlanmış bir veritabanı için ALLOW_SNAPSHOT_ISOLATION ON).

Şimdi, 30 sıra ile, yani:

  • Sürüm bilgisi OLMADAN

    30 * 263 bize 7890 bayt verir. Ardından, kullanılan 7986 bayt için 96 bayt sayfa üstbilgisine ekleyin. Son olarak, sayfada kullanılan toplam 8046 bayt ve kalan 146 için alan dizisinin 60 baytını (satır başına 2) ekleyin. Kullanarak DBCC PAGEhesaplamamı göstererek onaylar:

    • m_slotCnt 30 (yani satır sayısı)
    • m_freeCnt 146 (yani sayfada kalan bayt sayısı)
    • m_freeData 7986 (yani, veri + sayfa başlığı - 7890 + 96 - alan dizisi "kullanılmış" bayt hesaplamasına dahil edilmez)
  • Sürüm bilgisi İLE

    Toplam 8310 bayt için 30 * 277 bayt. Ancak 8310, 8192'nin üzerindedir ve bu, satırlar için yalnızca 8036 kullanılabilir bayt vermesi gereken 96 bayt sayfa üstbilgisini veya satır başına 2 bayt dizisini (30 * 2 = 60 bayt) bile hesaba katmadı.

    AMA, 29 sıra ne olacak? Bu bize 8033 bayt veri (29 * 277) + sayfa başlığı için 96 bayt + 8187 bayta eşit olan slot dizisi (29 * 2) için 58 bayt verir. Ve bu sayfa 5 bayt kaldı (8192 - 8187; elbette kullanılamaz). Kullanarak DBCC PAGEhesaplamamı göstererek onaylar:

    • m_slotCnt 29 (yani satır sayısı)
    • m_freeCnt 5 (yani sayfada kalan bayt sayısı)
    • m_freeData 8129 (yani, veri + sayfa başlığı - 8033 + 96 - alan dizisi "kullanılmış" bayt hesaplamasına dahil edilmez)

Yığınlar Hakkında

Veri sayfalarını biraz farklı şekilde yığınlar. Sayfada kalan alan miktarı hakkında çok kaba bir tahmin yaparlar. DBCC çıkışında bakarken, satırındaki bakmak: PAGE HEADER: Allocation Status PFS (1:1). (Kümelenmiş tabloya baktığımda) VALUEsatırlarında 0x60 MIXED_EXT ALLOCATED 0_PCT_FULLveya 0x64 MIXED_EXT ALLOCATED 100_PCT_FULLYığın tabloya bakarken bir şeyler göstermeyi göreceksiniz . Bu, İşlem başına değerlendirildiğinden, burada yapılan test gibi ayrı ekler yapmak Kümelenmiş ve Yığın tabloları arasında farklı sonuçlar gösterebilir. Bununla birlikte, 30 satırın tümü için tek bir DML işlemi yapılması, yığını beklendiği gibi dolduracaktır.

Bununla birlikte, Yığınlarla ilgili bu ayrıntıların hiçbiri, bu özel testi doğrudan etkilemez, çünkü tablonun her iki sürümü de sadece 146 bayt kalan 30 satıra sığar. Kümelenmiş veya Yığın ne olursa olsun, bu başka bir satır için yeterli alan değil.

Lütfen bu testin oldukça basit olduğunu unutmayın . Bir satırın gerçek boyutunu hesaplamak, aşağıdakiler gibi çeşitli faktörlere bağlı olarak çok karmaşık olabilir:, SPARSEVeri Sıkıştırma, LOB verileri, vb.


Veri sayfasının ayrıntılarını görmek için aşağıdaki sorguyu kullanın:

DECLARE @PageID INT,
        @FileID INT,
        @DatabaseID SMALLINT = DB_ID();

SELECT  @FileID = alloc.[allocated_page_file_id],
        @PageID = alloc.[allocated_page_page_id]
FROM    sys.dm_db_database_page_allocations(@DatabaseID,
                            OBJECT_ID(N'dbo.TestStructure'), 1, NULL, 'DETAILED') alloc
WHERE   alloc.[previous_page_page_id] IS NULL -- first data page
AND     alloc.[page_type] = 1; -- DATA_PAGE

DBCC PAGE(@DatabaseID, @FileID, @PageID, 3) WITH TABLERESULTS;

** 14 baytlık "sürüm bilgisi" değeri, veritabanınız ya ALLOW_SNAPSHOT_ISOLATION ONveya olarak ayarlanırsa mevcut olacaktır READ_COMMITTED_SNAPSHOT ON.


Kesin olarak, kullanıcı verileri için sayfa başına 8060 bayt kullanılabilir. OP'nin verileri hala bunun altında.
Roger Wolf

Sürüm bilgisi yoktur, aksi takdirde 30 satır 8310 bayt alır. Gerisi doğru görünüyor.
Roger Wolf

@RogerWolf Evet, "sürüm bilgisi" var. Ve böylece evet, 30 satır 8310 bayt gerektirir. Bu nedenle, bu 30 satır aslında OP'nin kullandığı test sürecine göre inanmaya yönlendirildiği için 1 sayfaya sığmadı. Ancak bu test işlemi yanlış. Sayfaya yalnızca 29 satır sığar. Bu (hatta SQL Server 2012 kullanarak) doğruladı.
Solomon Rutzky

testinizi RCSI etkin olmayan bir veritabanında çalıştırmayı denediniz tempdbmi? OP tarafından sağlanan tam sayıları üretebildim.
Roger Wolf

@RogerWolf Kullandığım DB, RCSI etkin değil, ancak olarak ayarlanmış ALLOW_SNAPSHOT_ISOLATION. Ben de sadece denedim tempdbve "sürüm bilgisi" orada olmadığını gördüm, bu nedenle 30 satır uygun. Yeni bilgileri eklemek için güncelleme yapacağım.
Solomon Rutzky

3

Veri sayfasının gerçek yapısı oldukça karmaşıktır. Genelde kullanıcı verileri için sayfa başına 8060 bayt olduğu belirtilse de, bu davranışla sonuçlanan hiçbir yerde sayılmayan bazı ek yükler vardır.

Ancak, SQL Server'ın 31. satırın sayfaya sığmayacağına dair bir ipucu verdiğini fark etmiş olabilirsiniz. Bir sonraki satırın aynı sayfaya sığması için avg_page_space_used_in_percentdeğer% 100'ün altında olmalıdır - (100/31) = 96.774194 ve sizin durumunuzda bunun çok üzerindedir.

PS Ben Kalen Delaney tarafından "SQL Server Internals" kitaplarından birinde veri sayfası yapısının bayt açıklamasına göre ayrıntılı, gördüm, ama neredeyse 10 yıl önce oldu, bu yüzden daha fazla ayrıntı hatırlamıyorum için lütfen bana izin verin. Ayrıca, sayfa yapısı sürümden sürüme değişme eğilimindedir.


1
Hayır. Benzersizleştirici yalnızca yinelenen anahtar satırlarına eklenir. Her benzersiz anahtar değerinin ilk satırı, fazladan 4 baytlık benzersiz ayırıcı içermez.
Solomon Rutzky

@srutzky, görünüşe göre haklısın. SQL Server'ın değişken genişlik anahtarlarına izin vereceğini hiç düşünmemiştim. Bu çirkin. Verimli, evet, ama çirkin.
Roger Wolf
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.