dataiotech Labs
dbaguidedbaguideDBAdataxtoolsdataopsphere
Home/Hata Teşhis Kütüphanesi/postgresql-index-row-size-exceeds-maximum
PostgreSQL
PostgreSQL 14–18İndeksleme & Depolama
7 dk okuma
ERROR: index row size exceeds btree version 4 maximum 2704

PostgreSQL "index row size exceeds maximum" — Neden Uzun Metin İndekslenemez?

Uzun metin kolonunu indekslerken çıkan "index row size exceeds" hatasının nedeni, prefix/ifade indeksi, MD5 ve GIN trigram alternatifleri.

İnceleyen:Principal Database Reliability Architect
Test Edilen Sürümler: PostgreSQL 18.6 & 17.11 (Alpine Linux / Docker 29.7) | Tarihsel: PostgreSQL 14, 15, 16

Operasyonel Doğrulama & Güvenilirlik Standardı

Son Teknik İnceleme: 29 Ekim 2026
Test Edilen SürümlerPostgreSQL 18.6 & 17.11 (Alpine Linux / Docker 29.7) | Tarihsel: PostgreSQL 14.10, 15.6, 16.4
Gerekli YetkiHedef tabloda CREATE INDEX veya ALTER TABLE yetkisi
Risk Seviyesi
MediumYanlış indeks tipi seçimi arama performansını düşürebilir veya gereksiz disk/yazma maliyeti yaratabilir
Beklenen Çıktıoctet_length() analizi sonrası MD5, prefix veya GIN trigram indeksinin 0 hata ile oluşturulması
Geri Dönüş (Rollback) Planı:DROP INDEX CONCURRENTLY ile oluşturulan geçersiz indeksi kaldırıp şema kısıtlarını eski haline getirme
PostgreSQL Docs: B-Tree Implementation Limits
İlk 60 Saniye Teşhis Protokolü:

Hata mesajı semptomdur, kök neden değildir. Sunucuyu yeniden başlatmadan veya rastgele ayar değiştirmeden önce aşağıdaki "Nedeni ayırt eden tek sorgu"yu çalıştırarak gerçek senaryoyu netleştirin.

▸Belirti

plaintext
ERROR: index row size 3112 exceeds btree version 4 maximum 2704 for index "idx_makale_ozet" DETAIL: Index row references tuple (0,14) in relation "makale". HINT: Values larger than 1/3 of a buffer page cannot be indexed.

Hata, indeks oluştururken ya da yeni veri eklerken ortaya çıkar. Sebep mimaridir: standart PostgreSQL btree indeksinde tek bir girdi, 8 KB'lık buffer sayfasının üçte birini geçemez (sayfa başlıkları ve overhead düşüldüğünde yaklaşık 2.704 bayt). Bu sınır keyfi değildir; bir btree indeks sayfasının B-Tree dengesini koruyabilmesi için her yaprak sayfada en az üç girdi barındırabilmesi zorunludur.


▸Nedeni Doğrulayan Teşhis Sorgusu

Tabloda hangi satırların bu sınırı aştığını tespit edin:

sql
SELECT id, length(ozet) AS karakter_sayisi, octet_length(ozet) AS bayt_boyutu FROM makale ORDER BY octet_length(ozet) DESC LIMIT 10;
UYARI
Karakter ve Bayt Ayrımı: Sınır karakter değil, bayt cinsindendir. UTF-8 kodlamasında Türkçe karakterler (ğ, ü, ş, ı, ö, ç) 2 bayt, özel semboller ve emojiler 4 bayt yer tutar. Dolayısıyla varchar(2000) tanımlı bir kolon 4.000 bayta kadar çıkabilir; yani "2.000 karakter sınırım var" varsayımı indeksin patlamasını engellemez.

▸Çözüm: Hangi Erişim Desenine İhtiyacınız Var?

Çözüm, o kolonu nasıl sorguladığınıza bağlıdır. İhtiyaca uymayan bir yöntem indeksi hatasız oluştursa bile sorgu planlayıcısının indeksi yoksaymasına yol açar.

1. Eşitlik Araması Yapıyorsanız → MD5 / SHA-256 Hash İndeksi

sql
CREATE INDEX idx_makale_ozet_md5 ON makale (md5(ozet)); -- Sorgu da aynı ifade ile yazılmalıdır: SELECT * FROM makale WHERE md5(ozet) = md5('aranan uzun metin...');

32 karakterlik sabit çıktı üretir ve 2.704 bayt sınırına asla takılmaz. Yalnızca birebir eşitlik (=) için çalışır; aralık veya LIKE sorgularında kullanılamaz.

2. Ön Ek (Prefix) Araması Yapıyorsanız → İfade İndeksi

sql
CREATE INDEX idx_makale_ozet_prefix ON makale (left(ozet, 200)); -- Sorgu kullanımı: SELECT * FROM makale WHERE left(ozet, 200) = left('aranan metin...', 200);

Bu indeks WHERE left(ozet, 200) = ... veya ORDER BY left(ozet, 200) sorgularında kullanılır. Sorguda left() ifadesini açıkça belirtmezseniz planner indeksi görmez.

3. LIKE '%kelime%' Araması Yapıyorsanız → GIN Trigram

sql
CREATE EXTENSION IF NOT EXISTS pg_trgm; CREATE INDEX idx_makale_ozet_trgm ON makale USING gin (ozet gin_trgm_ops);

GIN trigram indeksi metni 3'er karakterlik parçalara (trigram) böler; sayfa sınırı problemi yaşanmaz ve ILIKE '%arama%' sorguları dramatik şekilde hızlanır. Dezavantajı: İndeks boyutu ve yazma maliyeti btree'ye göre yüksektir.

4. Tam Metin Araması Yapıyorsanız → tsvector

Kelime bazlı doğal dil aramaları için en doğru araçtır:

sql
ALTER TABLE makale ADD COLUMN arama tsvector GENERATED ALWAYS AS (to_tsvector('turkish', coalesce(baslik,'') || ' ' || coalesce(ozet,''))) STORED; CREATE INDEX idx_makale_arama ON makale USING gin (arama);

5. İndekslemeye Gerçekten Gerek Var mı?

Uzun metin kolonları çoğunlukla yalnızca kullanıcı arayüzünde gösterilmek üzere okunur. WHERE koşullarında filtreleme veya sıralama için kullanılmıyorsa doğrudan indeksten çıkarın; disk alanı ve yazma (HOT tuple) kazancı elde edersiniz.


▸Ortak Karşılaştırma: PostgreSQL vs. MySQL İndeks Boyut Sınırları

Mimari KriterPostgreSQL (B-Tree)MySQL InnoDB (DYNAMIC)MySQL InnoDB (COMPACT/REDUNDANT)
Azami Anahtar Boyutu2.704 bayt (8 KB / 3)3.072 bayt767 bayt
Karakter Sınırı (utf8mb4)~676 karakter768 karakter191 karakter
Sınır Aşım Hatasıindex row size exceeds maximumError 1071: Specified key too longError 1071: max key length is 767
Önerilen ÇözümMD5 hash / left(col, n) / GIN TrigramPrefix index col(191) / Generated HashROW_FORMAT=DYNAMIC geçişi
İlgili Kardeş RehberBu Rehber (H-21)MySQL Hata 1071 Rehberi (H-22)MySQL Hata 1071 Rehberi (H-22)

▸Bunu Bir Daha Yaşamamak İçin

Sınırsız uzunluktaki (text, limitsiz varchar) kolonları doğrudan indekslemeyi mimari bir anti-pattern kabul edin. Şema tasarımında veri girişi katmanına koruma ekleyin:

sql
ALTER TABLE makale ADD CONSTRAINT chk_ozet_bayt_siniri CHECK (octet_length(ozet) <= 1000);

Bu kural, sorunu indeks oluşturma aşamasında değil veri yazma anında yakalayarak operasyonel sürprizleri engeller.

Diğer Veritabanı Hata Teşhisleri

Kriz anında başvurabileceğiniz diğer doğrulanmış hata rehberleri.

Tüm Hatalar (50)