En tehlikeli optimizasyon sonucu değiştirendir
Yavaş rapor can sıkar, yanlış rapor kararı bozar. Aşağıdakilerin hepsi gerçek işlerde sık görülür. Önce sonucu bozanlara, sonra işe yaramayanlara bakalım.
Sonucu bozan "hızlandırmalar"
| Değişiklik | Neden yapılır | Ne bozulur |
|---|---|---|
UNION yerine UNION ALL | Tekilleştirme adımı kalksın | İki kolda da çıkan satır iki kez sayılır |
OR yerine iki kollu UNION ALL | Her kol kendi indeksini kullansın | Aynı sebeple çoğalma |
LEFT JOIN yerine JOIN | Daha hızlı plan | Eşleşmeyen satırlar kaybolur |
COUNT(DISTINCT x) yerine COUNT(x) | Tekilleştirme pahalı | Çoğalan satırlar sayılır |
BETWEEN ile tarih aralığı | Kısa yazım | Son günün saatli kayıtları düşer |
NOT IN ile NOT EXISTS arasında geçiş | "Biri daha hızlı" | Alt sorguda NULL varsa sonuç değişir |
WITH (NOLOCK) | Kilit beklemesin | Kirli okuma: geri alınacak değişiklikler okunur, satır atlanabilir ya da iki kez okunabilir |
Son satır için not: SQL Server'da NOLOCK, READUNCOMMITTED demektir. Kilit beklemesi sorununu hız sorunu sanıp bir finansal raporun sonucunu riske atma. Çoğu zaman satır sürümlemeye dayalı okuma (READ COMMITTED SNAPSHOT) daha doğru yoldur.
İşe yaramayanlar: efsaneler
"COUNT(1), COUNT(*)'tan hızlıdır"
Aynı tablodaki iki plan aşağıda. Aynı işlem, aynı sayfa sayısı.
VACUUM ANALYZE çalıştırıldı, planlama satırları kısalık için çıkarıldı. Sayılar kendi sunucunda farklı olur; okuma biçimi aynıdır.EXPLAIN (ANALYZE, BUFFERS, TIMING OFF, SUMMARY OFF) SELECT COUNT(*) FROM lk_orders;
Gerçek çıktı
Aggregate (cost=157.90..157.91 rows=1 width=8) (actual rows=1 loops=1)
Buffers: shared hit=8
-> Index Only Scan using lk_orders_restaurant_id_idx on lk_orders (cost=0.28..139.95 rows=7178 width=0) (actual rows=7178 loops=1)
Heap Fetches: 0
Buffers: shared hit=8
EXPLAIN (ANALYZE, BUFFERS, TIMING OFF, SUMMARY OFF) SELECT COUNT(1) FROM lk_orders;
Gerçek çıktı
Aggregate (cost=157.90..157.91 rows=1 width=8) (actual rows=1 loops=1)
Buffers: shared hit=8
-> Index Only Scan using lk_orders_restaurant_id_idx on lk_orders (cost=0.28..139.95 rows=7178 width=0) (actual rows=7178 loops=1)
Heap Fetches: 0
Buffers: shared hit=8
"İndeks her zaman hızlandırır"
Seçiciliği düşük koşulda indeks yararsız kalabilir. Bu veride yaygın bir değerle yapılan okuma, Bitmap Heap Scan kullansa da tablonun tamamından çok sayfa okudu:
Bitmap Heap Scan on bank_txns (cost=234.88..881.67 rows=20463 width=10) (actual rows=20520 loops=1)
Recheck Cond: ((txn_type)::text = 'kart_harcama'::text)
Heap Blocks: exact=390
Buffers: shared hit=410
-> Bitmap Index Scan on ix_tip (cost=0.00..229.76 rows=20463 width=0) (actual rows=20520 loops=1)
Index Cond: ((txn_type)::text = 'kart_harcama'::text)
Buffers: shared hit=20
"Her sütuna indeks ekleyelim"
Her indeks her yazmaya biner, çakışan indeksler boşa yer kaplar. SQL Server belgesi "iyileştiriciye seçenek olsun" diye çok sayıda indeks oluşturmaya karşı uyarır: veri değişikliğini yavaşlatır, eşzamanlılık sorunu çıkarabilir.
"Sorguya ipucu yazarım, motor doğru planı bulur"
İpucu bugünkü veriye göre kilitler. Veri büyüyünce doğru plan değişir, ipucu kalır. SQL Server belgesi ipuçlarını son çare sayar. PostgreSQL'de ise standart bir sorgu ipucu sözdizimi yoktur.
"LIMIT ekledim, fatura düştü"
BigQuery belgesine göre kümelenmemiş tabloda düşmez. Sıralı sorguda da LIMIT her zaman işi azaltmaz: tüm satırlar sıralanmak zorunda kalabilir.
"CTE'ye alınca hızlandı"
Yazım değiştirmek işi azaltmaz. Motor CTE'yi gömüyorsa plan aynı kalır, saklıyorsa kötüleşebilir. Ölç.
Ölçme hataları
- İkinci çalıştırmayı "iyileşme" sanmak: önbellek.
- Küçük test verisinde ölçüp üretime genellemek: plan satır sayısına göre değişir.
- Yalnız ortalamaya bakmak: yavaş olan uçtaki değerlerdir (çarpık veri).
- Yazma tarafını ölçmemek: okuma hızlanır, gece yüklemesi uzar.
- Sonucu karşılaştırmamak.