---
title: 'Fiziksel Depolama Düzeni'
source: 'https://academia.sh/tr/kurslar/veritabani-yonetimi/fiziksel-depolama-duzeni'
course: 'İlişkisel Veritabanı Yönetimi'
language: tr
updated: '2026-08-17T18:08:56+00:00'
license: 'CC BY-SA 4.0'
---

# Fiziksel Depolama Düzeni

Sayfanın motorun en küçük okuma ve yazma birimi olması, sayfa boyutu ile sayfa sayısının dosya boyutunu tam olarak belirlemesi, sayfa içi doluluk ve sayfa başına satır sayısının ölçülmesi, taşma sayfaları ve tablo alanı kavramı.

Önceki ders sayfayı bellekteki bir birim olarak kullandı: tampon havuzuna giren, atılan,
isabet ya da ıska üreten bir şey. Sayfanın kendisinin ne olduğu sorulmadı. Neden satır satır
değil de sayfa sayfa okunuyor, bir sayfaya kaç satır giriyor, bir satır sayfaya sığmazsa ne
oluyor?

Bu dersin sorusu verinin diskteki hâlidir. Ölçülecek üç ilişki vardır: sayfa boyutu ile
dosya boyutu arasındaki ilişki, sayfa içindeki doluluk ve tablo ile dizinin toplam yer
içindeki payı. Üçü de sayılabilir; hiçbiri tahmine bırakılmaz.

## Sayfa Neden Vardır

**Sayfa (page)**, motorun disk ile bellek arasında taşıdığı en küçük birimdir. Tek bir
satır okunmak istendiğinde bile o satırın bulunduğu sayfanın tamamı okunur.

Nedeni donanımdadır. Bilgisayarlar Nasıl Çalışır kursundaki bellek hiyerarşisi
tartışmasının aynısıdır bu: disk erişiminin sabit bir maliyeti vardır ve bu maliyet, okunan
bayt sayısıyla değil, erişim sayısıyla ölçülür. Yüz baytlık bir satırı okumak için yapılan
iş ile dört kilobaytlık bir sayfayı okumak için yapılan iş neredeyse aynıdır. Sabit boyutlu
bir birim ayrıca defter tutmayı kolaylaştırır: tampon havuzundaki her yuva aynı büyüklüktedir,
bir sayfanın yerine başka bir sayfa parçalanma üretmeden konabilir.

Sayfa boyutu veritabanı oluşturulurken belirlenir ve sonradan değiştirilmesi verinin
yeniden yazılmasını gerektirir. Küçük sayfa, seçici okumalarda daha az boşa taşınan bayt
demektir; büyük sayfa, sıralı taramalarda daha az erişim ve daha az defter tutma demektir.

## Sayfa Sayısı Dosya Boyutunu Belirler

İlişki tam olarak ölçülebilir. Aşağıdaki blok, önceki kurslardan devam eden kütüphane veri
kümesini kurar ve üç sayıyı yan yana koyar.

```sh
rm -f kutuphane.db
cat > kurulum.sql <<'SQL'
CREATE TABLE sube (
  sube_id INTEGER PRIMARY KEY,
  ad      TEXT NOT NULL,
  sehir   TEXT NOT NULL
);
CREATE TABLE uye (
  uye_id       INTEGER PRIMARY KEY,
  ad           TEXT NOT NULL,
  sehir        TEXT NOT NULL,
  kayit_tarihi TEXT NOT NULL
);
CREATE TABLE kitap (
  kitap_id INTEGER PRIMARY KEY,
  baslik   TEXT NOT NULL,
  yazar    TEXT NOT NULL,
  yil      INTEGER NOT NULL,
  sube_id  INTEGER NOT NULL
);
CREATE TABLE odunc (
  odunc_id    INTEGER PRIMARY KEY,
  kitap_id    INTEGER NOT NULL,
  uye_id      INTEGER NOT NULL,
  alis_tarihi TEXT NOT NULL,
  iade_tarihi TEXT
);

INSERT INTO sube (sube_id, ad, sehir) VALUES
  (1,'Merkez','Ankara'),(2,'Bahcelievler','Ankara'),(3,'Kadikoy','Istanbul'),
  (4,'Beyoglu','Istanbul'),(5,'Konak','Izmir'),(6,'Nilufer','Bursa'),
  (7,'Selcuklu','Konya'),(8,'Cankaya','Ankara');

INSERT INTO uye (uye_id, ad, sehir, kayit_tarihi)
WITH RECURSIVE sayac(n) AS (SELECT 1 UNION ALL SELECT n+1 FROM sayac WHERE n < 120000)
SELECT n, 'Uye ' || n,
       CASE n % 5 WHEN 0 THEN 'Ankara' WHEN 1 THEN 'Istanbul' WHEN 2 THEN 'Izmir'
                  WHEN 3 THEN 'Bursa' ELSE 'Konya' END,
       date('2015-01-01', '+' || (n % 3200) || ' days')
FROM sayac;

INSERT INTO kitap (kitap_id, baslik, yazar, yil, sube_id)
WITH RECURSIVE sayac(n) AS (SELECT 1 UNION ALL SELECT n+1 FROM sayac WHERE n < 200000)
SELECT n, 'Kitap ' || n, 'Yazar ' || (n % 4000), 1950 + (n % 75), 1 + (n % 8)
FROM sayac;

INSERT INTO odunc (odunc_id, kitap_id, uye_id, alis_tarihi, iade_tarihi)
WITH RECURSIVE sayac(n) AS (SELECT 1 UNION ALL SELECT n+1 FROM sayac WHERE n < 2000000)
SELECT n, 1 + ((n * 7) % 200000), 1 + ((n * 13) % 120000),
       date('2018-01-01', '+' || ((n * 37) % 2437) || ' days'),
       CASE WHEN n % 9 = 0 THEN NULL
            ELSE date('2018-01-01', '+' || (((n * 37) % 2437) + 14) || ' days') END
FROM sayac;
SQL
sqlite3 kutuphane.db < kurulum.sql

sqlite3 -header -column kutuphane.db <<'SQL'
SELECT (SELECT page_size FROM pragma_page_size) AS sayfa_boyutu,
       (SELECT page_count FROM pragma_page_count) AS sayfa_sayisi,
       (SELECT page_size FROM pragma_page_size) * (SELECT page_count FROM pragma_page_count) AS hesaplanan_bayt;
.shell echo "dosyanin gercek boyutu: $(wc -c < kutuphane.db)"
SQL
```

```
sayfa_boyutu  sayfa_sayisi  hesaplanan_bayt
------------  ------------  ---------------
4096          20859         85438464       
dosyanin gercek boyutu:  85438464
```

Hesaplanan değer ile dosyanın gerçek boyutu birbirinin aynısı. Bu bir yaklaşıklık değil,
tanımdır: veri dosyası, art arda dizilmiş eşit boyutlu sayfalardan başka bir şey içermez.
Dosya büyüdüğünde sayfa sayısı artar; sayfa sayısı azalmadan dosya küçülmez. Bu iki cümle,
ileride şişme ve boşluk geri kazanımı tartışılırken doğrudan kullanılacaktır.

Sayfa boyutu bu ortamda 4096 bayttır. Sayfa sayısı ve dolayısıyla dosya boyutu veriye
bağlıdır; farklı bir sayfa boyutuyla kurulmuş bir veritabanında iki sayı da değişir, ilişki
değişmez.

## Sayfanın İçinde Ne Var

Sayfalar hepsi aynı işi görmez. B-ağacında yer alan bir tablo için üç sayfa türü ayırt
edilir: **yaprak sayfa (leaf page)** satırların kendisini tutar, **iç sayfa (internal page)**
alt ağaçlara yönlendiren anahtarları tutar, **taşma sayfası (overflow page)** ise bir sayfaya
sığmayan değerin artan bölümünü tutar.

```sh
[ -f kutuphane.db ] || sqlite3 kutuphane.db < kurulum.sql   # veri kümesi ilk blokta kuruldu

sqlite3 -header -column kutuphane.db <<'SQL'
SELECT pagetype AS sayfa_turu, count(*) AS sayfa, sum(ncell) AS hucre,
       round(1.0 * sum(ncell) / count(*), 1) AS sayfa_basina_hucre
FROM dbstat WHERE name = 'odunc' GROUP BY pagetype;

SELECT name AS nesne, count(*) AS sayfa, sum(ncell) AS hucre,
       round(100.0 * sum(payload) / sum(pgsize), 1) AS doluluk_yuzde
FROM dbstat GROUP BY name ORDER BY sayfa DESC;
SQL
```

```
sayfa_turu  sayfa  hucre    sayfa_basina_hucre
----------  -----  -------  ------------------
internal    46     17966    390.6             
leaf        17967  2000000  111.3             
nesne          sayfa  hucre    doluluk_yuzde
-------------  -----  -------  -------------
odunc          18013  2017966  82.5         
kitap          1780   201773   82.4         
uye            1064   121059   82.3         
sube           1      8        3.4          
sqlite_schema  1      4        16.2         
```

İki milyon satır 17.967 yaprak sayfaya yerleşmiş: sayfa başına ortalama 111 satır. Bunların
üstünde 46 iç sayfa var ve her biri ortalama 390 anahtar taşıyor. Ağacın yüksekliği buradan
okunur: 46 iç sayfanın biri köktür, kalan 45'i onun altındadır ve yapraklara işaret eder.
Yani iki milyon satırlık bir tabloda herhangi bir satıra ulaşmak üç sayfa okumakla
tamamlanır. Veri Yapıları kursundaki B-Ağaçları dersinin "yükseklik kayıt sayısının
logaritmasıyla artar" sonucu, burada ölçülmüş bir sayıdır.

Doluluk sütunu ikinci bir bilgi verir: sayfaların yaklaşık %82'si gerçek veriyle doludur.
Kalan pay, hücre işaretçileri ve sayfa başlığı gibi defter bilgisiyle, bir de kasıtlı
bırakılmış boşlukla gider. Boşluğun bir kısmı gereklidir: sayfa tıka basa dolu olursa
araya eklenen tek bir satır sayfayı bölmeye zorlar. `sube` tablosunun %3,4 dolulukta
görünmesi bir kusur değildir; sekiz satır tek bir sayfaya girmiş, sayfanın geri kalanı
boş kalmıştır. Küçük tablolarda en küçük birim yine bir sayfadır.

## Satır Sayfaya Sığmadığında

Sayfa boyutu sabit olduğuna göre, bir satırın uzunluğu sayfa boyutunu aşarsa ne olur? Motor
satırın başlangıcını normal sayfada tutar ve artanı taşma sayfalarına zincirler.

```sh
rm -f tasma.db
sqlite3 -header -column tasma.db <<'SQL'
CREATE TABLE kitap_ozet(kitap_id INTEGER PRIMARY KEY, ozet TEXT NOT NULL);
INSERT INTO kitap_ozet VALUES (1, replace(hex(zeroblob(40)),'0','a'));
INSERT INTO kitap_ozet VALUES (2, replace(hex(zeroblob(600)),'0','b'));
INSERT INTO kitap_ozet VALUES (3, replace(hex(zeroblob(6000)),'0','c'));
SELECT kitap_id, length(ozet) AS ozet_uzunlugu FROM kitap_ozet;
SELECT pageno AS sayfa, pagetype AS tur, ncell AS hucre, payload AS yuk, unused AS bos
FROM dbstat WHERE name = 'kitap_ozet' ORDER BY pageno;
SQL
```

```
kitap_id  ozet_uzunlugu
--------  -------------
1         80           
2         1200         
3         12000        
sayfa  tur       hucre  yuk   bos 
-----  --------  -----  ----  ----
2      internal  1      0     4065
3      overflow  0      4080  0   
4      overflow  0      4080  0   
5      leaf      2      1288  2779
6      leaf      1      3845  222 
```

İlk iki özet — 80 ve 1200 karakter — tek bir yaprak sayfaya birlikte sığmış (sayfa 5).
Üçüncüsü 12.000 karakter ve tek başına bir yaprak sayfayı doldurmuş: o sayfada 3.845 bayt
duruyor, kalanı iki taşma sayfasına yayılmış (3.845 + 4.080 + 4.080 = 12.005; fark satır
başlığındandır). Sayfa numaralarının sıralı olmaması, sayfaların ayrılma sırasını yansıtır;
mantıksal sıra değil, ayırma sırasıdır.

Bunun bedeli okumada ortaya çıkar. Uzun bir metin sütunu, o sütuna hiç bakmayan sorgular
için bile satırı ağırlaştırır — sayfa başına daha az satır sığar, tarama daha çok sayfa
okur. Şema tasarımındaki bir alışkanlığın gerekçesi budur: nadiren okunan uzun metin
alanlarını ayrı bir tabloya taşımak, ana tablonun sayfa başına satır yoğunluğunu korur.

## Tablo ve Dizinin Dosyadaki Payı

Dizin de bir B-ağacıdır ve o da sayfalarda durur. Aynı sayaç, dizinin yer maliyetini
tabloyla aynı ölçekte gösterir.

```sh
[ -f kutuphane.db ] || sqlite3 kutuphane.db < kurulum.sql   # veri kümesi ilk blokta kuruldu

sqlite3 kutuphane.db 'CREATE INDEX odunc_uye ON odunc(uye_id);'
sqlite3 -header -column kutuphane.db "
SELECT name AS nesne, count(*) AS sayfa, count(*) * 4096 / 1048576 AS mib
FROM dbstat GROUP BY name ORDER BY sayfa DESC;"
```

```
nesne          sayfa  mib
-------------  -----  ---
odunc          18013  70 
odunc_uye      5754   22 
kitap          1780   6  
uye            1064   4  
sube           1      0  
sqlite_schema  1      0  
```

Tek bir tamsayı sütunu üzerindeki dizin, tablonun yaklaşık üçte biri kadar yer kaplıyor:
70 MiB'lik tabloya 22 MiB. Oran şaşırtıcı değildir — dizin girdisi anahtar değerini ve
satıra ulaşmayı sağlayan başvuruyu tutar, yani satırın tamamını değil ama satır başına
sabit bir maliyeti. İleri SQL kursundaki Dizin Kavramı dersinde "dizin yer kaplar" cümlesiyle
geçilen maliyet budur; burada nesne başına sayılabilir hâldedir.

Bu sayaç yönetimde doğrudan işe yarar. Bir veritabanının hangi tablo ve dizin tarafından
şişirildiği, tahminle değil bu sorguyla belirlenir; kapasite planlaması da gereksiz dizinlerin
tespiti de aynı listeden başlar.

## Dosya Düzeni ve Tablo Alanı

Sayfaların hangi dosyalara yerleştiği motora göre değişir ve bu, yönetimin görünür yüzüdür.
Üç düzen yaygındır: bütün veritabanının tek bir dosyada tutulması, her tablo ve dizinin ayrı
bir dosyaya yazılması, ya da birden çok dosyanın önceden ayrılmış bir alan kümesi olarak
yönetilmesi.

**Tablo alanı (tablespace)**, bu dosyaların bulunduğu yeri adlandıran bir soyutlamadır.
Tablo oluşturulurken bir tablo alanı adı verilir; o adın hangi dizine ya da hangi diske
karşılık geldiği yönetim tarafından ayrıca tanımlanır. Kazancı, verinin fiziksel yerini
şemadan ayırmasıdır: sık okunan bir tablo hızlı bir depolama birimine, arşiv nitelikli bir
tablo ucuz bir birime yerleştirilebilir ve bunun için tablo tanımına dokunulmaz.

Bu ayrımın gösterilemediği bir ortamda çalışıldığı için açıkça söylemek gerekir: buradaki
koşumlarda veritabanının tamamı tek bir dosyadır ve tablo alanı kavramının karşılığı yoktur.
Sayfa, doluluk ve taşma üzerine ölçülen her şey düzenden bağımsızdır; dosya sayısı ve tablo
alanı adlandırması ise motora özgüdür.

## Özet

- Sayfa, motorun disk ile bellek arasında taşıdığı en küçük birimdir; erişimin sabit
  maliyeti tek satır okumayı anlamsız kılar.
- Dosya boyutu, sayfa boyutu ile sayfa sayısının çarpımıdır; ölçümde 4096 × 20.859 =
  85.438.464 bayt, dosyanın gerçek boyutuna birebir eşit çıktı.
- İki milyon satır 17.967 yaprak sayfaya yerleşti (sayfa başına 111 satır) ve üstlerinde 46
  iç sayfa var; ağacın yüksekliği üçtür ve sayfaların doluluğu %82 civarındadır.
- Sayfaya sığmayan değer taşma sayfalarına zincirlenir; 12.000 karakterlik bir metin yaprak
  sayfada 3.845 bayt bırakıp iki taşma sayfası kullandı.
- Dizin de sayfalarda durur: tek tamsayı sütunlu bir dizin, 70 MiB'lik tablonun yanında
  22 MiB yer kapladı.

## Sonraki Adım

Bu ders verinin diskte nasıl durduğunu gösterdi, ancak oraya nasıl vardığını sormadı. Bir
satır güncellendiğinde ilgili sayfa bellekte değişir; o sayfa diske yazılmadan önce
elektrik kesilirse ne olur? Kesinleştirilmiş bir işlemin sonucunun kaybolmaması, sayfaların
diske yazılmasına bağlı olsaydı her kesinleştirme rastgele yerlere yazma anlamına gelirdi.
Sonraki ders bu sorunun çözümünü ele alır: değişiklikleri veri dosyasından önce sıralı bir
günlüğe yazmak. Günlük dosyasının oluşması, büyümesi ve bir çökmeden sonra verinin oradan
geri gelmesi ölçülerek gösterilecek.
