MSSQL Server HASH Match Join Türü

Bu makalede MSSQL Server HASH Match Join türünü ele almış olacağız. Sql server da kullanıcı bir sorgu yazarken inner-left-full-right-cross join türleri olan mantıksal join yapılarını kullanmaktadır. Sql server kullanıcı sorgularını arka tarafta fiziksel olarak hangi  join yapısına dönüştüğünü detaylı bir şekilde ele almış olacağız.

Şimdi ilk olarak execution planda sıklıkla karşılaştığımız HASH Match Join türüne değinelim:

Veritabanı motorunun “kaba kuvvet” (Brute Force) yöntemidir. Genellikle büyük tablolar birleştirilirken ve index bulunmadığında kullanılır.

  • Çalışma Mantığı: Küçük olan tablodan bellekte bir Hash Table (özet tablosu) oluşturulur. Ardından diğer tablodaki satırlar taranarak bu hash tablosuyla eşleştirilir.
  • Neden Görürsünüz?
    • Tablolar çok büyükse ve join sütunlarında uygun index yoksa.
    • Veriler sıralı değilse.
    • Karmaşık, eşitsizlik içermeyen join işlemlerinde.

Hash Join çok fazla bellek (RAM) tüketir. Eğer Execution Plan’da Hash Join üzerinde sarı bir ünlem (Spill to TempDB) görüyorsanız, SQL Server’ın bu işlem için ayırdığı bellek yetmemiş ve veriyi diske yazmış demektir. Bu ciddi bir yavaşlık sebebidir. Eksik index’leri tamamlayarak bu join’i Merge veya Nested Loops’a döndürmeye çalışın.

Tercih edilmeyen bir join türüdür. Execution plan yapımızda join işleminin temsil edildiği bir yapı olarak karşımıza çıkmaktadır. İki veri kümesinden gelen değerleri birleştirmek için kullanılır. Eğer tablolarımız büyük, herhangi bir index yapısına sahip değil ve sırasız bir şekilde oluşturulan tablolar herhangi bir join işlemine girerse sql server iki tabloyu birleştirmek için Hash Match Join operatörü kullanılmaktadır.

Hash Match operatöründe Birleştirme yapılırken öncelikle bellekte küçük tablomuza  uygun bir Hash Table oluşturulur, daha sonra ikinci büyük tablomuzdaki veriler okunmaya ve mevcut hash tablodaki verilerle karşılaştırılmaya başlanır. Birbirini ile uyumlu olan kayıtlar hash tabloda saklanıp diğer veriler silinir ve böylece iki veri kümesinin birleştirilmesiyle elde edilmiş bir tablo oluşturulur.

İki tablodan küçük olan belleğe alınarak bir hash table oluşturulur. Daha sonra büyük tablo taranır ve büyük tablodaki hash değeri ile bellekteki hash table’daki hash değeri karşılaştırılarak eşit olanlar sonuç listesine eklenir.

Execution plan yapımızda bulunan  Hash Match operatörün Tooltip penceresinde bu kavramın ne işe yaradığına değinelim.

Hash Join’de mantık “üst ve alt” giriş olarak ikiye ayrılır. Tooltip’in en üstündeki açıklama bunu netleştirir: “Use each row from the top input to build a hash table, and each row from the bottom input to probe…”.

  • Üst Giriş (Build Input): SQL Server önce üstteki tablodan (veya operatörden) gelen verileri okur ve bellekte (Memory) bir Hash Table oluşturur.
  • Alt Giriş (Probe Input): Alt kısımdaki “Hash Keys Probe” başlığı altında gördüğümüz [Person].[Address].AddressID sütunu, alt tablodan gelerek bellekteki o tabloyla eşleştirilen veridir.
  • Tespit: Üstteki tablo Employee (veya onun bağlı olduğu bir yapı), alttaki (probe edilen) tablo ise Address tablosudur.

Nested Loops örneğinde gördüğümüz o ciddi hata burada da devam ediyor:

  • Actual Number of Rows: 290
  • Estimated Number of Rows for All Executions: 275.573
  • Yorum: SQL Server, bu join sonucunda 275 bin satır oluşacağını tahmin etmiş, ancak gerçekte sadece 290 satır oluşmuş.

SQL Server 275 bin satır beklediği için bellekte (Hash Table için) çok geniş bir yer ayırmış olabilir. Bu, “Memory Grant” (bellek tahsisi) konusunda israfa yol açar. Eğer tam tersi olsaydı (az bekleyip çok gelseydi), bellek yetmeyecek ve işlem TempDB‘ye (diske) taşacaktı (Spill), bu da sorguyu inanılmaz yavaşlatırdı.

Bu operatörü burada görmenizin iki temel sebebi olabilir:

  1. İndeks Eksikliği: Eğer AddressID veya join yapılan diğer sütunlar üzerinde uygun bir indeks olmasaydı, SQL Server en hızlı yöntem olan Nested Loops veya Merge Join’i kullanamaz, bu “kaba kuvvet” yöntemine başvururdu.
  2. Veri Büyüklüğü Tahmini: SQL Server 275 bin satır geleceğini sandığı için, bu kadar büyük bir veriyi en iyi Hash Match ile yönetebileceğine karar vermiş. Ancak gerçekte veri az (290 satır) olduğu için, aslında bir Nested Loops burada çok daha hızlı çalışırdı.

Bu görseli bir Execution Plan’da gördüğünde şu adımları izlemelisin:

  • İstatistikleri Güncelle: Tahmin (275k) ile gerçek (290) arasındaki uçurum, istatistiklerin çok eski olduğunu gösterir. İlk iş olarak UPDATE STATISTICS [Person].[Address] ve ilgili diğer tablolar için bu komutu çalıştır.
  • İndeksleri Kontrol Et: Hash Keys Probe kısmında geçen AddressID sütununun indeksli olup olmadığını kontrol et. Eğer indeks eklersen, SQL Server büyük ihtimalle bu Hash Match’i bırakıp daha hafif bir join türüne geçecektir.
  • Bellek Kullanımı: Eğer bu sorgu çalışırken sunucuda yavaşlık oluyorsa, SQL’in bu join için ayırdığı “Estimated Operator Cost” (%20) ve bellek miktarını incele.

Not: Join işlemine girecek tablolarda hiç index olmadığında bu tabloların birleşimi için Hash Match operatörü kullanılmaktadır.

Kısacası Hash Match çoğunlukla sıralı olmayan büyük tablolar üzerinde birleştirme işlemi gerçekleşeceği zaman kullanılır. Birleştireceği tablolar arasında öncelikli olarak küçük olan tabloyu alır ve bellekte hash function kullanarak bir hash tablo oluşturur. Ardından diğer tablo üzerinde teker teker gezer ve her veriyi aynı hash function’a sokar çıkan değer oluşturulan hash tablosunda mevcut ise bu kayıtları birleştirir. Bu sayede join işlemini gerçekleştirir ama kaynak kullanımı(cpu ve bellek) çok fazla olduğundan dolayı maliyeti yüksektir.

Peki ne yapabiliriz, öncelikle eğer bu iki tablo scan işlemleri sonucunda gelip birleştirilecek ise bu tablolar için index atılabilir bu sayede hem veri seti küçültülür hem de veri sıralanmış olur. Karmaşıklığı O(N*M) dir.

Join TürüTercih Edilen DurumGereksinimBellek Kullanımı
Nested LoopsKüçük veri / İyi Indexİç tabloda IndexÇok Düşük
Merge JoinBüyük veri / Sıralı veriSıralı giriş (Index)Düşük
Hash JoinBüyük veri / Indexsiz veriYokYüksek

Başka makalede görüşmek dileğiyle..

“Kim zerre kadar iyilik yapmışsa onu görür.”Zilzal Suresi; 7. Ayet

Author: Yunus YÜCEL

Bir yanıt yazın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir