PAGE NEDİR?
Bilgisayarımın karşısına oturdum ve index mantığını anlatmak üzere yazmaya başladım. Ancak farkettim ki herşey page yapısını anlamak ile başlıyor. İşte bu yüzden şuan yazacaklarım SQL ENGINE’ı anlamak ve onun gibi düşünmeye başlamak için bir rehber niteliğinde. Ayrıca bu şekilde bir yazı dizisi yapmayı düşünüyorum. Eğer SQL ENGINE’ı gerçekten anlamak istiyorsanız takipte kalsanız iyi edersiniz.
Nasıl maddenin en küçük yapı taşı atom ise SQL SERVER’ın da en küçük yapı taşı pagelerdir. Bir kitapta tüm içerik sayfalara yazıldığı gibi SQL SERVER’da da tüm içerik pagelere yazılır. Her bir page en fazla 8KB data içerebilir.
Normal bir kitabın içerisinde bilgi içeren safalar olduğu gibi meta data içeren sayfalar da vardır. Örneğin içindekiler sayfası gibi. SQL SERVER’da durumlar çokta farklı değil, içerisinde data bulunan sayfalar, metadata bulunan sayfalar image/text bulunan sayfalar gibi sayfalar vardır.
Her page 96byte boyutunda bir header ile başlar. Bu headerin içerisinde page numarası, page tipi, page içerisindeki boşluk miktarı ve allocation unit id si bulunur.
Veri satırları, başlıktan hemen sonra başlayarak sayfada birbiri ardına yazılarak saklanır. Satır ofseti tablosu sayfanın sonunda başlar (footer diyebiliriz) ve satır ofseti tablosu, sayfadaki her satır için bir kayıt içerir. Her satır ofseti kaydı, satırın ilk baytının sayfanın başından ne kadar uzakta olduğunu bilgisini tutar. Satır ofset tablosunun işlevi, SQL Server’ın bir sayfadaki satırları hızlı bir şekilde bulmasına yardımcı olmaktır. Satır ofset tablosundaki girişler, sayfadaki satırların dizilişinden ters sıradadır.
Satırlar sayfalara yayılamaz, bir satır maksimum bir page içerisinde olabilir! Muhtemelen bu noktada aklınıza şu soru gelecek: sqlde makisumum satır büyüklüğüm sadece 8kb mı olabilir? (Hem EVET hemde HAYIR)
Bir tablodaki tüm sütunların toplam satır boyutu 8.060 bayt sınırlamasını aştığında, SQL Server, en büyük genişliğe sahip sütundan başlayarak, bir veya daha fazla sütunu ROW_OVERFLOW_DATA ayırma birimindeki sayfalara taşır. Bu işlem sadece insert edilen data için değil update edilen data içinde dinamik olarak yapılır. Bir sütun ROW_OVERFLOW_DATA birimine taşındığında orijinal sayfasına sütunun yerini belirten 24baytlık bir pointer koyulur bu sayede satır çok daha büyük olabilir.
Örneğin iki kolondan oluşan bir satırımız olsun, kolonlarımızı varchar(6000) ve varchar(3000) boyutunda ve dolu olarak farz edelim. Kolonlar tek başlarına 8.060 byte limitini geçmiyor ancak birlikte oldukları zaman bu limiti aşıyorlar. Böyle bir durumda varchar(3000) boyutundaki kolon page içerisinde kalır ancak varchar(6000) boyutlu kolon ROW_OVERFLOW_DATA birimlerine taşınarak pointer’ı page içerisine eklenir.
Bu taşıma işlemleri direkt olarak eş zamanlı olarak yapılır ve eş zamanlı yapıldığı için ilgili tablonun sorgulanma performansını düşürür. Bu noktada büyük kolonlara sahip tabloları normalize edebilir, birkaç tabloya ayırabiliriz.
Large Object Data (BLOB) dediğimiz veriler varbinary(MAX), varchar(MAX), IMAGE, TEXT gibi kolonlar bu 8,060 byte sınırından muaftır. Bu kolonlarda bulunan veriler hâlihazırda satırın bulunduğu page içerisinde tutulmaz LOB_DATA page yapılarında saklanır ve aynı şekilde pointer’ı page içerisine eklenir.
Bu kadar kavramsal bilgi yeter şimdi SQL SERVER içerisinde ufak bir uygulama ile pekiştirelim.
Basit bir tablo oluşturuyorum.
1 2 3 4 5 6 7 8 9 10 11 |
CREATE TABLE [dbo].[PageTest]( [Id] [int] NULL, [varchar3000] [varchar](3000) NULL, [varchar6000] [varchar](6000) NULL, [varcharMAX] [varchar](max) NULL ) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY] |
Sonra bu tabloya sadece tek bir satır veri ekleyeceğim.
1 2 3 |
insert into PageTest Select 1, replicate('x',3000),replicate('x',6000),replicate('x',9000) |
Varchar3000 isimli alana 3000 adet x ekledim 6000 olana 6000 adet ve son olarak varcharmax alana 9000 adet ekledim. Peki bu verinin page üzerinde dağılımı nasıl oldu dersiniz?
Bunu da sys.dm_db_index_physical_stats fonksiyonu ile görebiliriz.
1 2 3 4 5 6 7 8 9 |
SELECT OBJECT_NAME([object_id]) AS TableName, alloc_unit_type_desc, record_count, page_count, round(avg_page_space_used_in_percent,0) as avg_page_space_used_in_percent , min_record_size_in_bytes, max_record_size_in_bytes FROM sys.dm_db_index_physical_stats(DB_ID(), NULL, NULL, NULL, 'DETAILED') WHERE OBJECT_NAME([object_id]) LIKE 'PageTest' |
Yukarda gördüğünüz gibi bir kolonumuz ROW_OVERFLOW_DATA kısmına aktarılmış. Bu kolonumuz varchar6000 kolonumuz oluyor. LOB_DATA kısmına aktarılan ise varcharmax kolonumuzdur. Hatta bu kaydı update ettiğimizde oluşacak değişikliği de görebiliriz.
1 |
Update PageTest SET [varcharMAX]= replicate('x',1) |
Varcharmax alana sadece tek bir x karekteri olarak update ettik. Ve sonuç :
1 |
Update PageTest SET [varchar6000]= replicate('x',4000) |
Varchar6000 alanının içinde bulunan kaydı 4000 karakter yaptığımızda ise satırın toplam boyutu 8KB’ı geçmeyeceği için ROW_OVERFLOW_DATA kısmına taşınmayacaktır.
Böylelikle SQL SERVER’ın en temelde veriyi nasıl depoladığı konusunu işlemiş olduk. Muhtemelen sıradaki yazım extendler ile alakalı olacaktır. Bu kısımlar ilk etapta sıkıcı gelebilir. Ama temel mekanikleri öğrenmek size çok şey katar. SQL ile alakalı bir problem yaşadığınızda hiç görmediğiniz bir sorun olsa dahi, muhtemelen şu sebeptendir diyebilmek için bu arkadaki mekanizmayı iyi bilmelisiniz.
- SQL SERVER – Page Yapısı - 5 Temmuz 2023
- Sql Server Numa Ayarları - 30 Mayıs 2023
- SQL Server Log Shipping Kurulumu - 2 Mayıs 2023