DBMS_PARALLEL_EXECUTE Paketi ile Parallel Sorgu Çalıştırma

Herkese Selam,

Bu yazıyı bir kaç haftadır aldığım yoğun sorular üzerine bir çözüm önerisi olarak yazma gereği hissettim. Umarım farkındalık anlamında faydalı bir yazı olur.

Problem tanım olarak şöyle;

Problem: Çok büyük bir tablom var ve bu tablodaki bir kolonu update etmek istiyorum ancak sorguyu çalıştırdığımda bir kaç saat çalışmasına karşılık sorgu sonuçlanmıyor ve update işlemini gerçekleştiremiyorum?

Evet problemimizin tanımı bu şekilde. Şimdi bu problemimizi çözmek için bir örnek gerçekleyeğim ancak bu örnek her sistemde %100 çalışır ve beklediğiniz gibi bir sonuç üretir diyerek bir genelleme yapmak istemem çünkü çözümümüzün temeli parallel execution ve parallel execution sistemin o anki available kaynak durumu ve  DB’de verilen parallelik kısıtları ile doğrudan alakalı. Bu bağlamda çözümü uygulamadan önce çalıştığınız sistemin yoğunluğunu ve varsa paralel execution kısıtlarını kontrol etmekte fayda olacaktır.

Şimdi içerisinde 11 M kayıt olan bir örnek tablo yaratıyorum.

CREATE TABLE hr.parallel_test
AS
       SELECT LEVEL id, LEVEL * 2 || 'abc' tmp_id
         FROM DUAL
   CONNECT BY LEVEL<= 11000000;

Evet şimdi klasik olarak tablomuzu update edelim ve süremizi kontrol edelim.

SET TIMING ON

UPDATE hr.parallel_test
   SET tmp_id = 'parallel_test';

COMMIT;
   
11000000 rows updated.
Elapsed: 00:03.20.72

Update işlemimiz 3 dakika 20 sn seviyesinde sürdü. Şimdi aynı işi dbms_parallel_execute paketi ile yapmaya çalışalım. Ancak örneği yapmadan önce aslında işi nasıl paralelize edeceğimizden bahsedelim.

– Elimizdeki data’yı overlap (üst üste gelmeyecek) etmeyecek şekilde parçalara bölmemiz gerekiyor. Yani data yı eğer parçalayabilirsek, her biri birbirinden bağımsız çalışabilen iş parçaları elde etmiş olacağız. Buda aynı anda tek iş yerine birden fazla işi eş zamanlı çalıştırabilmemiz anlamına gelecek. Normal şartlar altında bu anlattığım işi yapmak için kocaman bir kod yazıp bu işi bizde sisteme gerçekletebiliriz ancak Oracle dbms_parallel_execute paketi ile bu işi bizim daha rahat gerçekleyebilmemiz için alt yapıyı hazırlamış durumda.

Elimizdeki datayı 3 farklı şekilde parçalayabiliriz.

1– ROWID => Uniform veri dağılımı için en doğru seçim olabilir.
2– Tablomuzdaki Number tipindeki başka bir kolonu olabilir => Genellikle Primary Key kolonumuz seçilebilir. Ama aynı değerin tekrar ettiği satırlar mevcutsa her paralel parçaya eşit sayıda kayıt düşmeyebilir. Tekrar eden satır sayısına göre işin süresini uzatabilir.
3- Üreteceğimiz bir SQL statement’a göre datayı parçalayabiliriz.

Benim bu örnekte tercih edeceğim yöntem datayı rowid’ye göre parçalamak.
Şimdi bu yöntemin ilk adımı olan dbms_parallel_execute kullarak işi yaratma adımını gerçekleyelim.

BEGIN
   dbms_parallel_execute.create_task ('deneme_parallel');
   dbms_parallel_execute.create_chunks_by_rowid (
      task_name     => 'deneme_parallel',
      table_owner   => 'HR',
      table_name    => 'PARALLEL_TEST',
      by_row        => FALSE,
      chunk_size    => 10000);
END;

Evet buradaki tanımda dikkat etmemiz gerek parametre chunk_size parametresi. Bu parametre her parallel çalıştırılabilecek parça için kaç data block luk datanın o parça için kapsanacağını belirler. Bu sayının küçük olması, paralel çalıştırmak için oluşturulacak parça sayısını arttırıken, çok yüksek olması da paralel çalıştırmak için yaratılacak parça sayısını azaltır. Dolayısıyla burada kullanacağımız rakam işin kaç küçük parçaya bölüneceğini belirliyor.

Şimdi yarattığımız iş parçası için arka tarafta nasıl bir organizasyon yapıldı onu görüntüleyelim.

SELECT CHUNK_ID,
       TASK_NAME,
       STATUS,
       START_ROWID,
       END_ROWID,
       START_ID,
       END_ID,
       JOB_NAME,
       START_TS,
       END_TS,
       ERROR_CODE,
       ERROR_MESSAGE
  FROM user_parallel_execute_chunks
 WHERE task_name = 'deneme_parallel';

parallel goruntuleme

Ekran görüntüsünde tüm kayıtları alamasamda toplamda bu iş için 110 tane chunk oluşturduğunu görüntüleyebildim. Bu ekran görüntüsünde dikkat etmemiz gereken şey bu örnek için start_rowid ve end_rowid kolonları. Biz işi create ettiğimiz adımda dbms_parallel_execute paketi çalışarak parametre olarak verdiğimiz tabloyu verdiğimiz chunk_size parametresine göre bölümlere ayırdı ve her bir bölümün hangi rowid ile başladığını ve hangi rowid ile biteceğini hesaplayarak dictionary ye yazdı. Şuan elimizde mantıksal parallel çalışacak iş parçaları oluşmuş durumda.

Şimdi sıra bu oluşan mantıksal parçaları run edecek kodu yazmaya geldi.

BEGIN
   dbms_parallel_execute.run_task (
      task_name           => 'deneme_parallel',
      sql_stmt            => 'update hr.PARALLEL_TEST set tmp_id = ''parallel_test'' where rowid between :start_id and :end_id',
      language_flag       => DBMS_SQL.native,
      parallel_level      => 8);
END;

Evet yukarıdaki kodu çalıştırdığımda işin paralel parçalarının parallel_level parametresi ile belirttiğim seviyede çalışıp sonucun daha hızlı bittiğini gözlemledim. parallel_level parametresi  ile alt parçaların aynı anda kaç parallelikte çalışmasını istediğimizi belirttik ancak şu uyarıyı yapmakta fayda var sizin parallel_level parametresi ile belirlediğiniz seviyede paralleik için sisteminizde yeteri kadar kaynak olmayabilir. Bu bağlamda buraya yazacağımız parametre oldukça dikkatli verilmesi gereken bir değerdir. Yazının başında da söylediğim gibi bu yöntem ile her sistemde benzer sonuçları elde edemeyebilirsiniz. Parallel Execution bir çok parametreye bağımlı olarak arka planda yapılmaktadır. Bu ayrıntıları düşünerek ve ölçerek beklentimizi belirlemekte fayda görüyorum.

Çalışan işlerin statülerini, eğer hata almış ise hata kodu mesajlarını paralel işi sisteme verdikten sonra aşağıdaki sql ile monitör edebilirsiniz.

SELECT CHUNK_ID,
       TASK_NAME,
       STATUS,
       START_ROWID,
       END_ROWID,
       START_ID,
       END_ID,
       JOB_NAME,
       START_TS,
       END_TS,
       ERROR_CODE,
       ERROR_MESSAGE
  FROM user_parallel_execute_chunks
 WHERE task_name = 'deneme_parallel';

progress
Not: dbms_parallel_execute.run_task adımında verdiğmizi sorgunun içinde mutlaka :start_id ve :end_id bind variableları olmak zorundadır. Sistem otomatik olarak runtime da bu değerlerin yerine kendi hesapladığı rowid veya neye göre chunklara böldüyseniz data yı o bilgiler ile değişerek sorgunuzu çalıştırır.

REFERENCES
http://docs.oracle.com/cd/E11882_01/appdev.112/e40758/d_parallel_ex.htm#ARPLS67367

Advertisements

About ... from Emrah METE

Bilgisayar Mühendisi
This entry was posted in Oracle, Root, Uncategorized and tagged , , , . Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s