
1. Başlangıç
İndeks tasarımı, DBA olarak yaptığımız işin omurgasında. Yıllarca tek tip B-tree üzerinde dönüp duruyorduk; SQL Server 2016’dan itibaren columnstore, 2025 ile JSON ve vector index dünyaları eklendi. Artık ‘index ekle’ demek dört ayrı kararı tetikliyor: hangi tip, hangi kolonlar, hangi sıralama, hangi maintenance stratejisi?
Bu yazıda dört index dünyasını birden ele alacağız. Önce klasik B-tree fundamental’larını hızlıca tazeleyip workshop ile covering index’in performansa etkisini ölçeceğiz. Sonra columnstore’a geçip OLAP/analytic workload’larda neden 10x speed-up gördüğümüzü açıklayacağız. SQL Server 2025’te gelen ordered nonclustered columnstore ve online build yeniliklerini ayrı bir workshop’la izleyeceğiz. JSON Index’i geçen hafta zaten konuşmuştuk; vektör index’i ise vector search serisinin ileri bölümlerinden hatırlayacaksınız, bu yazıda dört tipi tek bir karar matrisinde birleştireceğiz.
Beklentim: yazıyı bitirdikten sonra önünüze düşen herhangi bir tablo için ‘şu kolona index gerek var, B-tree mi, columnstore mu, INCLUDE eklemeli miyim, ordered yapayım mı, online build için ne gerekiyor’ sorularını rahat cevaplaman.
2. Index Ailesi Dört Dünyaya Genel Bakış
SQL Server 2025’te aktif kullandığımız beş ana index tipi var: B-tree (rowstore), columnstore (clustered/nonclustered), JSON, vector ve full-text. İlk dörtü günlük tuning’in büyük kısmı.

SQL Server 2025 index ailesi: kullanım alanı, depolama yapısı ve günümüzdeki durumu.
B-tree ve heap rowstore dünyası: OLTP iş yüklerinin temeli. Tek tek satır okuma, transactional update, burada hâlâ B-tree (rowstore) kazanır. Clustered index tablonun fiziksel sıralamasını belirler; nonclustered index’ler ek erişim yolları sunar. Optimum tasarım için kolonların seçimi, sıralamasi ve INCLUDE listesi kritik.
Columnstore dünyası: data warehouse, reporting, OLAP. Veriler kolon-bazlı saklanır, segment’lere bölünür, segment metadata’sıyla skip edilebilir. SQL Server 2025’te gelen ordered nonclustered columnstore ve online build, bu dünyayı OLTP yakınına çekti, operational analytics senaryosu artık daha pürüzsüz.
JSON Index dünyası: yarı yapılandırılmış veri üzerinde JSON_VALUE, JSON_PATH_EXISTS, JSON_CONTAINS predicate’lerini optimize eder. Geçen haftaki Native JSON yazısının pratik tarafıydı; bugün diğer index tipleriyle birlikte konumlandıracağız.
Vector Index dünyası: AI/ML, similarity search. Cosine, euclidean, dot product metric’leri; approximate nearest neighbor algoritmaları. Bu seri serimizin ilk üç haftasında detaylı işlemiştik; burada karar matrisi içinde yerini koruyalım.
3. B-tree Fundamental’ları
B-tree (gerçekte B+ tree implementasyonu) bir ağaç yapısı. Root sayfa üst, intermediate sayfalar orta katmanlar, leaf sayfalar en alt — gerçek veriyi tutan seviye. Clustered index’te leaf tablonun kendisidir; nonclustered’da leaf key + clustered key (bookmark) tutar.

B-tree depth: 100M satırlık tabloda tipik olarak 4 seviye. Root + 2 intermediate + leaf, herhangi bir satıra 4 sayfa erişimle ulaşılır.
Index seek’in tipik 4-5 page erişimle çalışmasının sebebi tam burada. B+ tree balanced bir yapı; depth tablonun büyüklüğüyle logaritmik artar. Bu yüzden aramalarımız satır sayısından bağımsız neredeyse sabit hızda. Tipik bir DBA, 8 milyar satırlık bir tabloda bile bir lookup’ın 5 sayfa erişimle çalıştığını bilir.
Key kolonları seçerken üç kuralı akılda tutmak yeter:
1 – WHERE’in en sık filtrelediği kolon en sola gelmeli (leftmost prefix rule).
2 – Equality predicate’lerin solu, range’lerin sağı tercih edilir.
3 – Ordering işine yarayacak ek kolonlar eklenebilir, ama key boyutu büyürse INCLUDE’a kaydır.
INCLUDE clause ile non-key kolonları leaf’e ekleyebiliyorsunuz. Bu kolonlar üzerinde arama yapmaz ama covering index için önemli, Key Lookup’tan kurtulmak için. Geçen haftaki Workshop 1’i hatırla: City üzerine index + Name/Email INCLUDE = Key Lookup’sız plan.
4. Index Tasarım Checklist’i
Bir tabloya index ekleyeceğin zaman önce bu kontrol listesinden geç. Yıllar içinde yaptığım pratik:
- Sorgunun execution plan’ına bak, Key Lookup, Sort, Hash Match veriyorsa muhtemelen index gerekiyor.
- Workload’u tara, sys.dm_db_missing_index_details DMV’sini incele, ama körü körüne uyma; advisor agresif INCLUDE öneriyor, schema’yı şişiriyor.
- Aynı amaca hizmet eden iki index varsa birini düşür, duplicate index kötü, write maliyetini iki katına çıkarır.
- INCLUDE’ı INCLUDE yap, leaf’e koymak yerine key’e eklemek B-tree’yi şişirir, query’ler için fayda olmadan.
- Filter index’i unutma, SoftDelete bayraklı tabloda WHERE deleted = 0 üzerine filtered index, %90 satırı düşürmenin keyfini verir.
- Statistics auto-update’i kontrol et, büyük tablolarda %20 değişim eşiği geç tetiklenir, manuel UPDATE STATISTICS planla.
- FILLFACTOR’u akıllı kur, write-heavy index’lerde 80-85, read-heavy reporting’de 95-100. Default 0 (=100), insert hot-spot yaratabilir.
- Aralıklı maintenance, fragmente olmuş büyük index’lerde REORGANIZE (online), %30+ fragmentation veya leaf gap için REBUILD.
Production’da ay sonunda raporlama yoğunlaşan tablolarda fragmentation ve statistic bayatlığı en sinsi performans gerileme sebebi. Maintenance window’da Ola Hallengren’in maintenance script’i veya SQL Server Agent ile rolünü kuruyor olmalısın.
5. Columnstore Index’leri Analytic Workload’ın Silahı
Columnstore mantığı temelinde basit: rowstore’da satır satır saklarız, columnstore’da kolon kolon. 1 milyarlık fact tablo üzerinde SUM(amount) hesaplayacaksak rowstore tüm satırı sayfaya getirir; columnstore sadece amount kolonunu okur, geri kalan kolonlar tek byte bile transfer olmaz.

Columnstore segment yapısı: 1M satırlık rowgroup’lar, her kolon ayrı compressed segment, segment metadata ile elimination.
İki yapı taşı: rowgroup ve segment. Rowgroup yaklaşık 1 milyon satırı kapsar; her kolon için ayrı bir segment’e parçalanır. Her segment compression’a tabi (genelde 5-10x), her segment’in min/max metadata’sı saklanır. Sorgu çalıştığında engine WHERE clause’a göre segment elimination yapar, örneğin WHERE date >= ‘2025-01-01’ ise 2024 segment’lerini hiç okumaz, metadata’dan eler.
Clustered columnstore tablonun kendisinin storage formatını belirler, fact tablolarınız için ideal. Nonclustered columnstore mevcut rowstore tablonun üzerine konulan ek bir index, operational analytics senaryosunda OLTP transaction’larını bozmadan reporting’i hızlandırır. SQL Server 2025’te bu ikinci yapı önemli yenilikler aldı.
6. SQL Server 2025 Ordered Nonclustered Columnstore
Ordered columnstore index’lerin avantajı segment elimination’ı agresifleştirmek. Eğer veriler segment’ler arası çakışmıyorsa (örneğin tarihe göre temiz ayrılmışsa), WHERE date BETWEEN … gibi range sorgularda binlerce segment atlanır.
SQL Server 2022’de ordered clustered columnstore vardı ama nonclustered tarafa yoktu. SQL Server 2025 nonclustered tarafa da getirdi — yani OLTP rowstore tablonuz üzerine ordered nonclustered columnstore koyup operational reporting’inizi hızlandırabilirsiniz, original heap/clustered organizasyonu bozmadan.
-- Ordered nonclustered columnstore (SQL Server 2025+)CREATE NONCLUSTERED COLUMNSTORE INDEX nccsi_orders_ordered ON dbo.Orders (CustomerID, OrderDate, Amount) ORDER (OrderDate);-- Sorgu artık OrderDate üzerinden segment elimination yaparSELECT SUM(Amount)FROM dbo.OrdersWHERE OrderDate >= '2025-01-01' AND OrderDate < '2025-04-01';
AdventureWorks2025 database inde çalışıyorsanız, orders tablosu yoktur, orders tablosu açalım ve random data kaydedelim
-- 1. Veritabanını oluştur (Eğer yoksa)IF NOT EXISTS (SELECT * FROM sys.databases WHERE name = 'AdventureWorks2025')BEGIN CREATE DATABASE AdventureWorks2025;ENDGOUSE AdventureWorks2025;GO-- 2. Tabloyu oluştur (Eğer daha önce varsa sil ve yeniden yarat)IF OBJECT_ID('dbo.Orders', 'U') IS NOT NULL DROP TABLE dbo.Orders;GOCREATE TABLE dbo.Orders ( OrderID INT IDENTITY(1,1) PRIMARY KEY CLUSTERED, CustomerID INT NOT NULL, OrderDate DATE NOT NULL, Amount DECIMAL(18, 2) NOT NULL);GO-- 3. 10.000 Rastgele Kayıt Ekleme (Hızlı Set-Based Yöntemi)SET NOCOUNT ON;PRINT '10.000 rastgele kayıt ekleniyor...';-- CTE (Common Table Expression) ile hızlıca 10.000 satırlık sanal bir tablo oluşturuyoruzWITH L0 AS (SELECT 1 AS c UNION ALL SELECT 1), L1 AS (SELECT 1 AS c FROM L0 AS A CROSS JOIN L0 AS B), -- 4 satır L2 AS (SELECT 1 AS c FROM L1 AS A CROSS JOIN L1 AS B), -- 16 satır L3 AS (SELECT 1 AS c FROM L2 AS A CROSS JOIN L2 AS B), -- 256 satır L4 AS (SELECT 1 AS c FROM L3 AS A CROSS JOIN L3 AS B), -- 65.536 satır Nums AS (SELECT ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) AS n FROM L4)INSERT INTO dbo.Orders (CustomerID, OrderDate, Amount)SELECT TOP (10000) -- CustomerID: 1 ile 1000 arasında rastgele bir ID ABS(CHECKSUM(NEWID())) % 1000 + 1, -- OrderDate: '2023-01-01' ile '2025-12-31' arasında rastgele bir tarih (~1095 günlük aralık) DATEADD(DAY, ABS(CHECKSUM(NEWID())) % 1095, '2023-01-01'), -- Amount: 10.00 ile 5000.00 arasında rastgele ondalıklı bir tutar CAST((ABS(CHECKSUM(NEWID())) % 499000 + 1000) / 100.0 AS DECIMAL(18,2))FROM Nums;GOPRINT 'Kayıt işlemi tamamlandı.';GO
Toplam tutar aşağıdaki geldi

Önemli detay: ORDER clause’a koyduğunuz kolon büyük olasılıkla tarihtir; veriniz zaten zaman serisinde geliyorsa segment’ler arası çakışma az olur. Çakışmayı sys.column_store_segments üzerinden ölçebilirsiniz; Microsoft’un ordered-columnstore sample script’i kolay bir başlangıç.
7. SQL Server 2025 Online Build & Improved Sort
Eskiden ordered columnstore yapmak için tabloyu offline almanız gerekirdi, create veya rebuild süresince DML bloke olurdu. SQL Server 2025 bunu değiştiriyor: ORDER clause’lı CREATE INDEX artık ONLINE = ON ile çalışıyor. Yani fact tablonuz milyarlarca satırken bile uygulamanızı durdurmadan ordered columnstore inşa edebiliyorsunuz.

Online ordered columnstore build akışı: shadow yapı, paralel sort (tempdb), atomic switch.
-- Online + ordered + improved sort (MAXDOP=1 perfect order için)CREATE CLUSTERED COLUMNSTORE INDEX ccsi_sales_orderdate ON dbo.SalesFact ORDER (OrderDate) WITH (ONLINE = ON, MAXDOP = 1);-- MAXDOP > 1 için paralel sort, daha hızlı build ama kısmi overlap olabilirCREATE CLUSTERED COLUMNSTORE INDEX ccsi_sales_orderdate ON dbo.SalesFact ORDER (OrderDate) WITH (ONLINE = ON, MAXDOP = 4, DROP_EXISTING = ON);
Improved sort quality için ek kazanım: MAXDOP=1 ile inşa ederseniz engine tempdb-bazlı sort kullanıyor (eski memory-only sort yerine), sonuç olarak fully ordered yani segment overlap’ı sıfır bir index üretiyor. Build daha uzun sürer (tempdb spill nedeniyle), ama sonraki query’lerde segment elimination %100 verim. Trade-off’u bilerek seç.

Bir başka 2025 yeniliği: clustered columnstore’lu tablolarda DBCC SHRINKDATABASE ve DBCC SHRINKFILE artık LOB kolonları içeren sayfaları hareket ettirebiliyor. Eskiden varchar(max)/nvarchar(max) içeren CCSI’da shrink yarım kalırdı, çoğu zaman boş yer geri kazanılamazdı. Yeni davranış, eski tablolarınızda shrink operasyonlarını tekrar değerlendirmenizi gerektirebilir.
8. JSON ve Vector Index Karar Matrisinde Konumlandır
Geçen hafta CREATE JSON INDEX detaylarına girmiştik. Karar matrisi açısından bilmemiz gereken: JSON Index, json kolonu üzerindeki JSON_VALUE, JSON_PATH_EXISTS ve JSON_CONTAINS predicate’lerini optimize eder. Klasik computed column + standart index yaklaşımının yerini büyük ölçüde alır.
Vector Index için temel bilgi: vector(N) tipindeki kolonlar üzerinde approximate nearest neighbor search’ü hızlandırır. SQL Server 2025’te preview, COSINE / EUCLIDEAN / DOT metric’leriyle. Bu seri serimizin ilk üç yazısında detaylarına girmiştik. İndex tasarım açısından dikkat: vector index için tabloda en az 100 satır olmalı, online build desteklenmiyor (şu an), ve PREVIEW_FEATURES açık olmalı.

Hangi index’i ne zaman? Workload tipine göre karar matrisi.
Karar matrisi: OLTP transactional workload → B-tree (clustered + nonclustered covering). OLAP analytic workload → clustered columnstore. HTAP (operational analytics) → rowstore + nonclustered columnstore (mümkünse ordered). JSON predicate’li sorgular → CREATE JSON INDEX. AI/ML similarity search → CREATE VECTOR INDEX. Full-text arama → FTS index.
9. Workshop 1 — Covering Index ile Key Lookup’tan Kurtulmak
Geçen hafta da değinmiştik ama bir kez daha pratik etmekte fayda var. Covering index, sorgunun ihtiyaç duyduğu tüm kolonları index’inde içerir; engine clustered’a gitmek zorunda kalmaz.
-- TestDROP TABLE IF EXISTS dbo.Customers_W7;CREATE TABLE dbo.Customers_W7( CustomerID INT IDENTITY PRIMARY KEY, Name NVARCHAR(100), Email NVARCHAR(200), Country NVARCHAR(50));INSERT INTO dbo.Customers_W7 (Name, Email, Country)SELECT TOP (100000) CONCAT('User_', ROW_NUMBER() OVER (ORDER BY (SELECT NULL))), CONCAT('user', ROW_NUMBER() OVER (ORDER BY (SELECT NULL)), '@x.com'), CHOOSE(ABS(CHECKSUM(NEWID())) % 4 + 1, N'TR', N'DE', N'US', N'UK')FROM sys.all_columns a CROSS JOIN sys.all_columns b;-- 1. Sadece key index — Country üzerineCREATE INDEX ix_country_keyonly ON dbo.Customers_W7 (Country);-- Test sorgusuSET STATISTICS IO ON;SELECT CustomerID, Name, EmailFROM dbo.Customers_W7WHERE Country = N'TR';-- Plan: Index Seek + Key Lookup, ~25K kez lookup-- Logical reads: ~2300-- 2. Covering — INCLUDE eklenmiş hâliDROP INDEX ix_country_keyonly ON dbo.Customers_W7;CREATE INDEX ix_country_covering ON dbo.Customers_W7 (Country) INCLUDE (Name, Email);-- Aynı sorguSELECT CustomerID, Name, EmailFROM dbo.Customers_W7WHERE Country = N'TR';-- Plan: sadece Index Seek, Key Lookup yok-- Logical reads: ~280 — yaklaşık 8x düşüş
IO ların nasıl düştüğünü görebilirsiniz

INCLUDE’ın bir maliyeti var: index leaf level büyür, write maliyeti artar. Read-heavy workload’da kazanç açık, write-heavy’de dikkatli olmak lazım. Aşağıdaki sorgu ile mevcut index leaf size’ları karşılaştırabilirsin:
SELECT OBJECT_NAME(i.object_id) AS table_name, i.name AS index_name, SUM(au.used_pages) * 8 AS used_kbFROM sys.indexes iJOIN sys.partitions p ON i.object_id = p.object_id AND i.index_id = p.index_idJOIN sys.allocation_units au ON p.partition_id = au.container_idWHERE OBJECT_NAME(i.object_id) = 'Customers_W7'GROUP BY OBJECT_NAME(i.object_id), i.name;

10. Workshop 2 Clustered Columnstore vs Rowstore (Analytic Query)
1 milyon satırlık fact tablo üzerinde aynı analitik sorguyu hem rowstore hem clustered columnstore üzerinde koşacağız. Spoiler: fark dramatik.
-- Rowstore versiyonuDROP TABLE IF EXISTS dbo.Sales_RS;CREATE TABLE dbo.Sales_RS( SalesID INT IDENTITY PRIMARY KEY, ProductID INT, StoreID INT, SaleDate DATE, Amount DECIMAL(10,2));INSERT INTO dbo.Sales_RS (ProductID, StoreID, SaleDate, Amount)SELECT TOP (1000000) ABS(CHECKSUM(NEWID())) % 1000 + 1, ABS(CHECKSUM(NEWID())) % 50 + 1, DATEADD(DAY, -ABS(CHECKSUM(NEWID())) % 730, GETDATE()), ABS(CHECKSUM(NEWID())) % 5000FROM sys.all_columns a CROSS JOIN sys.all_columns b;
-- Columnstore versiyonu — aynı veri, CCSI ileDROP TABLE IF EXISTS dbo.Sales_CS;CREATE TABLE dbo.Sales_CS( SalesID INT IDENTITY, ProductID INT, StoreID INT, SaleDate DATE, Amount DECIMAL(10,2));-- Önce veriyi yükle, sonra CCSI ekleINSERT INTO dbo.Sales_CS (ProductID, StoreID, SaleDate, Amount)SELECT ProductID, StoreID, SaleDate, Amount FROM dbo.Sales_RS;CREATE CLUSTERED COLUMNSTORE INDEX ccsi_sales ON dbo.Sales_CS ORDER (SaleDate);
-- Test sorgusu — son 90 gün ürün bazlı toplamSET STATISTICS IO, TIME ON;SELECT TOP (10) ProductID, SUM(Amount) AS totalFROM dbo.Sales_RSWHERE SaleDate >= DATEADD(DAY, -90, GETDATE())GROUP BY ProductIDORDER BY total DESC;-- Aynı sorgu, CS tablodaSELECT TOP (10) ProductID, SUM(Amount) AS totalFROM dbo.Sales_CSWHERE SaleDate >= DATEADD(DAY, -90, GETDATE())GROUP BY ProductIDORDER BY total DESC;

Aynı analytic sorgu: rowstore (clustered B-tree scan) vs columnstore (segment elimination + batch mode aggregation). 8x hız farkı, 12x logical reads farkı.
Test sonucum: rowstore tarafta 1.4 saniye, ~25K logical reads. Columnstore tarafta 170 ms, ~2.1K logical reads. Üstelik columnstore tarafta SaleDate ORDER ile rowgroup elimination kazanımı var; segment metadata’yı kontrol ettiğimde 90 günlük filter’da yalnızca 4 segment okuyor, geri kalan 6’sını atlıyor.


Önemli not: 1M satır columnstore için marjinal, gerçek kazanç 100M+ satırda geliyor. Test ortamında ölçek küçük olduğu için fark biraz daha az dramatik görünebilir. Production’da fact tablo boyutu büyüdükçe çarpan büyüyor.
11. Workshop 3 Operational Analytics: Rowstore + Nonclustered Columnstore
OLTP tablonuza dokunmadan reporting’i hızlandırmak istiyorsanız nonclustered columnstore aklınızda olsun. SQL Server 2025’te ordered nonclustered columnstore desteği bu yapıyı hatırı sayılır şekilde yukarı çekti.
-- Mevcut OLTP tabloya ek olarak NCCSICREATE NONCLUSTERED COLUMNSTORE INDEX nccsi_sales_ord ON dbo.Sales_RS (ProductID, StoreID, SaleDate, Amount) ORDER (SaleDate) WITH (ONLINE = ON);-- OLTP transaction'lar B-tree clustered üzerinde devam eder-- Reporting sorguları otomatik olarak NCCSI'yi kullanır-- Aynı top-10 product sorgusunu çalıştır, plan'a bakSELECT TOP (10) ProductID, SUM(Amount) AS totalFROM dbo.Sales_RSWHERE SaleDate >= DATEADD(DAY, -90, GETDATE())GROUP BY ProductIDORDER BY total DESC;
Engine plan’a baktığında WHERE clause + GROUP BY’ı segment elimination + batch mode aggregation üzerinden çözmenin en ucuz olduğunu görür ve nonclustered columnstore’u tercih eder. OLTP write’lar etkilenmiyor çünkü clustered B-tree hâlâ aktif; sadece NCCSI’nin maintain edilmesi için ek I/O var.

Operational analytics için iki kritik trade-off var:
1 – Yazma maliyeti, her INSERT/UPDATE/DELETE NCCSI’yi de günceller (delta store + tuple mover üzerinden). Yüksek-write tablolarda bu yük hissedilebilir, monitor et.
2 – Disk space, NCCSI kolonlu tablonun boyutuna ek olarak gelir. Compression genelde yardım ediyor ama yine de %20-40 ek alan.
12. Workshop 4 Index Maintenance
Index’i kurduktan sonra hayata bırakmıyoruz. Fragmentation, statistic bayatlığı, rowgroup quality — bunlar zamanla performansı düşüren sinsi sebepler.
-- B-tree fragmentationSELECT OBJECT_NAME(ips.object_id) AS table_name, i.name AS index_name, ips.avg_fragmentation_in_percent, ips.page_count, ips.avg_page_space_used_in_percentFROM sys.dm_db_index_physical_stats(DB_ID(), NULL, NULL, NULL, 'LIMITED') ipsJOIN sys.indexes i ON ips.object_id = i.object_id AND ips.index_id = i.index_idWHERE i.name IS NOT NULL AND ips.page_count > 100 AND ips.avg_fragmentation_in_percent > 10ORDER BY ips.avg_fragmentation_in_percent DESC;
Fragmentation %30 ve üstündeyse REBUILD, %10-30 arasında REORGANIZE düşün. Online ya offline kararı edition’a ve table boyutuna bağlı. Enterprise edition’da WITH (ONLINE = ON) ile büyük tabloları durdurmadan rebuild edebilirsin.
-- Columnstore rowgroup qualitySELECT OBJECT_NAME(rg.object_id) AS table_name, i.name AS index_name, rg.row_group_id, rg.state_desc, rg.total_rows, rg.deleted_rows, rg.transition_to_compressed_state_descFROM sys.dm_db_column_store_row_group_physical_stats rgJOIN sys.indexes i ON rg.object_id = i.object_id AND rg.index_id = i.index_idWHERE rg.state_desc IN ('OPEN', 'CLOSED') OR (rg.deleted_rows * 1.0 / NULLIF(rg.total_rows, 0)) > 0.1ORDER BY rg.deleted_rows DESC;
Columnstore tarafında izlemek istediğin iki şey: çok sayıda OPEN/CLOSED rowgroup (delta store’a takılmış, tuple mover’a yetişmemiş satırlar) ve yüksek deleted_rows oranı (logical delete marker’lar disk’te taşınıyor). REORGANIZE ile delta’yı çevir, ALTER INDEX … REBUILD ile deleted satırları fiziksel olarak temizle.
Tabi index bakımları için Ola Hallegren scriptlerini öneririm, çünkü bu scriptler çok daha efektif olabiliyor. Özellikle büyük veri tabanlarında performansı ciddi anlamda artıran otomasyon özellikleri sayesinde, yönetim süreçlerini kolaylaştırıyor. Ayrıca, özelleştirilebilir yapısı sayesinde ihtiyaçlarınıza göre daha verimli hale getirilebiliyor; bu da, uygulamanızın genel işleyişini iyileştiriyor. Böylece, hem zaman kazanıyor hem de kaynaklarınızı daha verimli kullanma imkanı buluyorsunuz.
-- Statistik güncelleme — büyük tablolarda manuelUPDATE STATISTICS dbo.Sales_RS WITH FULLSCAN;-- Veya sadece bayat olanlar içinEXEC sp_updatestats;
13. Sınırlar ve Pratik Uyarılar
- Ordered nonclustered columnstore SQL Server 2025+ ve Azure SQL DB/MI AUTD’da. SQL 2022’de yok.
- Online ordered build için Enterprise edition gerekiyor; Standard’da offline kalır.
- JSON Index online build desteklemiyor, büyük tablolarda bakım penceresinde kur.
- Vector Index minimum 100 satır gerektiriyor, PREVIEW_FEATURES açık olmalı.
- Filter index’lerde WHERE expression sabit kalmalı; runtime parametre kullanmaz.
- Computed column üzerine index için PERSISTED gerekebilir; deterministic olmayan expression’larda zorunlu.
- Kolonu çoklu nonclustered’da tekrar tekrar key olarak verme, duplicate index, write amplifikasyonu yapar.
- INCLUDE kolonu nonclustered’a koymak hash collision veya range query’leri optimize etmez; sadece covering içindir.
- FILLFACTOR < 100 disk space yer alır; default 0 (=100) write hot-spot riski. Workload’a göre ayarla.
- Memory grant gerektiren büyük sort/hash operasyonlarda RESOURCE_GOVERNOR ile sınırlama gerekebilir.
14. Bölüm Sonu
Index stratejisi tek bir kural değil, workload’a göre değişen bir karar tablosu. B-tree OLTP’nin temeli, columnstore analytics’in silahı, JSON Index yarı yapılandırılmış veriler için, Vector Index AI/ML için. Hepsini bir arada kullanabilmek SQL Server 2025’te mümkün, operational analytics senaryosunda rowstore + ordered nonclustered columnstore kombinasyonu özellikle güçlü.
Workshoplar kendi ortamında Adventurewokrs2025 database inde çalıştırmanı tavsiye ederim. Özellikle Workshop 3’teki rowstore + NCCSI kombinasyonu, mevcut OLTP tabloların reporting sorgularını ne kadar hızlandırdığını somutlaştırır. Maintenance script’lerini (Workshop 4) hâlâ kurmadıysan ay sonu rapor öncesi panik kaçınılmaz; sistemi rutinleştirmek lazım.
Index üzerine sorularınızı yorumlara yazın. Özellikle ordered nonclustered columnstore’u büyük production tabloda online build edenler, build süresi, tempdb spill gözlemleri, sonraki query performansı, paylaşırsanız ikinci bölümde vaka analizi yapabilirim.
Yazar hakkında
Yavuz Filizlibay — Database Solution Architect
SQL Server ekosisteminde uzun yıllardır performans, güvenlik, yüksek erişilebilirlik üzerine çalışıyorum. SQL Server Administration, Querying, Performans ve Security eğitimleri ile danışmanlık hizmeti veriyorum. Yeni makalelerden haberdar olmak için LinkedIn’de bağlanabilir, eğitim veya danışmanlık için iletişime geçebilirsiniz.
