Ruby ile KyotoCabinet – 4

Merhabalar,

Bir KyotoCabinet yazısına daha hoÅŸgeldiniz. Bugün sizlerle MySql’in 100 thread üzerinden “insert” yetenekleri ile kyoto cabinet’inkileri birbirleriyle karşılaÅŸtıracağız. Peki bunu neden yapıyoruz, çünkü bir önceki yazımızda tek thread üzerinden kyoto cabinet üzerine veri yazma iÅŸlemi mysql’a göre ~10 kat daha hızlı olduÄŸunu gördük.

Bu durum, tüm transactional tablolarımızı kyoto cabinet’e taşımamıza neden olmasa bile, yazılım geliÅŸtirirken kullandığımız bir çok “log” tablolarını taşımamızın kapısını aralar diye düşünüyorum. Yazılım geliÅŸtirirken bu kayıtları için bir log tablosuna veya dosyaya yazarız. Fakat dosyaya attığımız kayıtları tekrar okumak veya istediÄŸimiz kayıda eriÅŸmek istediÄŸimizde problemlerle karşılaÅŸtığımız için genelde “log” tablolarını tercih ederiz. Ä°ÅŸte bu tercihlerimizde, eÄŸer bu testten de baÅŸarı ile geçerse, log iÅŸlemleri için (hatta belki daha fazlası için) mysql yerine kullanılabilir.

Veya tecrübe ile sabit, web sistemlerinin session yönetimi ve loglaması için kullanılabilir. Aklınıza bir soru gelebilir, çünkü session tablosunda sorgu atmak için kullandığımız tek alan SessionId’dir. Yani ID üzerinden yapacağımız tüm iÅŸlemlerde bu veri tabanı yapılarının klasik veri tabanı yapılarına göre çok daha hızlı olduÄŸunu unutmayalım.

Mysql için ayarlamız bir önceki yazıdan alınabilir;

require "mysql"

start_number = 905420000000
start_time = Time.now
all_threads = []

100.times { |index|
  period_start_number = start_number + (index * 10000)
  puts "isleniyor.. #{period_start_number}"
  current_thread = Thread.new(period_start_number) { |local_start_number|
    begin
      dbh = Mysql.real_connect("localhost", "kyoto_cabinet", "kyoto_cabinet", "kyoto_cabinet")
      dbh.autocommit(false)
      st = dbh.prepare("insert into simple_table values (?,?)")

      10000.times {
        st.execute(local_start_number, local_start_number)
        local_start_number += 1
      }
      dbh.commit
    rescue Mysql::Error => e
      puts "Error code: #{e.errno}"
      puts "Error message: #{e.error}"
      puts "Error SQLSTATE: #{e.sqlstate}" if e.respond_to?("sqlstate")
    ensure
      dbh.close if dbh
    end
  }
  all_threads << current_thread
}
all_threads.each { |thread| thread.join }

puts "1.000.000 kayit eklenmistir. Toplam süre: " + (Time.now.to_f - start_time.to_f).to_s

* Önemli Not: MyISAM motoru yerine InnoDB motoru ile bu denemeyi yaptığım zaman ortaya çıkan sonuç bundan daha kötü olduğu için sizlerle paylaşmıyorum.

Sonuç; 83.836566925048 sn.

Bu sonuç üzerine biraz yorum yapacak olursak, elbette aynı makina üzerinden yine aynı makina üzerindeki bir veri tabanına yapılan bu multi-thread işlemlerin bir ek maliyet getirmesi normal, bizim burada dikkat etmemiz gereken nokta multi-thread işlemlerin performansı ne kadar etkileyeceği olmalı. Bu değerden tek thread üzerinden yaptığımız 78.565817117691 sn değerine göre ~ 5 sn. lik bir fark var. Bu rakam bence oldukça mantıklı.

Peki aynı durumu Kyotocabinet için deneyelim;

Fakat hemen belirtmem gerekiyor, kyoto cabinet'in multi-thread bir sistemde çalışabilmesi için birkaç ayar yapmak gerekiyor. Öncelikle, birden fazla connection açmaya çalışmayın, çok daha fazla problem çıkıyor. Ve ayrıca DB::OCREATE | DB::GCONCURRENT gibi opsiyonların ne olduğunu öğrenmeniz gerekiyor. Diğer durumlarda kyoto cabinet'in performansı sürünüyor, hatta bazı durumlarda mysql'den bile kötü oluyor. Öyle garip davranışlar sergiliyor ki, öyle baka kalıyorsunuz.

DB::OCREATE =>Yeni bir veri tabanı oluşturmak için kullanılır (dosya var ise ezer).
DB::GCONCURRENT => Multi-Thread üzerinden işlem yapmaya olanak sağlar. Dikkat, bu mod açıkken blok vererek yaptığımız işlemler kullanılamıyor.
DB::OREADER => Okuma modunda dosyayı açar.
DB::OWRITER => Yazma modunda dosyayı açar.
DB::OTRUNCATE => Yazmadan önce kayıtları bi güzel temizler.
DB::OAUTOTRAN => Her güncelleme sırasında otomatik transaction işlemleri yapılır.
DB::OAUTOSYNC => Her güncelleme sonrasında dosya üzerinde de güncelleme yapılmasını ifade eder.
DB::ONOLOCK => Dosya üzerine lock işlemi olmaksızın işlemler yapılır.
DB::OTRYLOCK => Dosya üzerinde lock işlemi yapar ama bloklamamaya çalışır. Artık ne kadar yapabilirse tabii.
DB::ONOREPAIR => Bozulmuş olan bir kyoto cabinet dosyasını otomatik olarak tamir etMEMEK üzere işlemler yapar.

require 'kyotocabinet'

include KyotoCabinet

start_number = 905420000000
all_threads = []
start_time = Time.now

begin
  db = DB::new(DB::OCREATE | DB::GCONCURRENT)
  db.open("simple_table.kch")

  100.times { |index|
    period_start_number = start_number + (index * 10000)

    current_thread = Thread.new(period_start_number) { |local_start_number|
      10000.times {
        db[local_start_number] = local_start_number
        local_start_number += 1
      }
    }
    all_threads << current_thread
  }

  all_threads.each { |thread| thread.join }
rescue Error => e
  puts "Error #{e.to_s}"
ensure
  db.close if db
end

puts "1.000.000 kayit eklenmistir. Toplam süre: " + (Time.now.to_f - start_time.to_f).to_s

Sonuç: 6.94712615013123 sn.

Oldukça güzel bir sonuç, bu sonuçla beraber artık kyoto cabinet'i log işlemleri için kullanmamızın önünde herhangi bir engel kalmıyor. Unutmayın sadece session işlemleri veya nokta atışı (tek bir sorgulama kriteri üzerinden) yaptığınız tüm işlemlerde Kyoto Cabinet'i gönül rahatlığı ile kullanabilirsiniz.

Kolay gelsin.

Bir Cevap Yazın

E-posta hesabınız yayımlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir