Query Out of Memory Hatası

Delphi'de kod yazma ile ilgili sorularınızı bu foruma yazabilirsiniz.
Cevapla
delphi-x
Üye
Mesajlar: 10
Kayıt: 09 Eki 2011 05:57

Query Out of Memory Hatası

Mesaj gönderen delphi-x »

2-3 milyon kayıtlık bir Firebird DBim var. DBNavigator nesnesi üzerinden sayfa sayfa ilerliyorum. Bunda sorun yok. Ancak son kayda git dediğimde Out of Memory hatası alıyorum. Sebebi ve çözümü nedir acaba?
Sorun grid nesnesinden kaynaklanmıyor çünkü gridsiz olarak denediğimde aynı hatayı veriyor. Problemin query nesnesinden kaynaklandığını düşünüyorum. Rowsetsize ı 1000 e ayarlı. Query nin fetchAll özelliği ni disable yapmak da işe yaramıyor. Unidirectional yapınca sorun düzeliyor gerçi ama bu sefer de program 30 sn. kitleniyor.
Halbuki kullandığım harici DB Tool da böyle bir sıkıntı yok. Hiç takılmadan son sayfaya gidiyor.
Yardımlarınız için teşekkürler..
ertank
Kıdemli Üye
Mesajlar: 1650
Kayıt: 12 Eyl 2015 12:45

Re: Query Out of Memory Hatası

Mesaj gönderen ertank »

Merhaba,

Kullanıcınız bu derece büyük bir tablonun içinde filtreleme yapmadan çalışmamalı. Kesinlikle 2-3 milyon kaydı inceleyecek bir kullanıcı olacağını düşünmüyorum. Önce filtre kıstaslarını belirleyip ardından filtrelenmiş veri ekranda gösterilmeli. Tablo olduğu gibi komple okunup DBGrid filtreleme özellikleri kullanılmamalı.

Sorununuz yüksek ihtimal ile Windows işletim sisteminin EXE başına uyguladığı hafıza kullanım limitleri ile ilgili.
1- FetchAll kapalı olduğu zaman query bileşeni DBGrid tarafından istenilen sayıda kayıt okur ve devamını okumaz. Siz en son kayda git dediğiniz zaman FetchAll kapalı olsa dahi bileşen zorunlu olarak tüm kayıtları okumak durumunda kalır (yoksa son kayıt olduğunu bilemez).
2- RowSetSize anladığım kadarıyla FetchAll ile birlikte çalışıyor ve 1000 adetlik satırlar okuyor. Yukarıdaki durum aynen geçerlidir. Tablo sonuna gidene kadar 1000'lik kayıt bloklarını okumaya devam edecektir.

Windows işletim sistemi içinde 32bit uygulamalar 2GB, 64bit uygulamalar 4GB maksimum RAM kullanımına tabidir. Bu limitler 32bit için Integer limitlerinden (fiziki adres bloğu bitişi), 64bit için ise işletim sisteminin kendisinden (Microsoft firmasının bizim için yeterli gördüğü hafıza kullanım miktarı) gelmektedir. Siz 3 milyon satırlık tabloda her bir satırın hafızada 1Kb yer kapladığını düşündüğünüzde Query bileşeni 2.9GB boyutunda *sadece* bu veriler için hafıza gereksinimine ihtiyaç duyar. Kullanılan DBGrid eğer bu kayıtların bir kopyasını kendisi hafızada tutmak ister ise bir bu kadar daha hafıza ihtiyacınız oluşur. Bunlar dışında EXE uygulama tasarım sırasında kullandığınız her bileşen için hafıza kullanır. Dolayısıyla form üzerinde gözüken bileşenler ve gözükmeyenler, kod ile çalışma zamanında oluşturduğunuz bileşen/sınıflar bu rakama eklenecektir. Örnek hesaplamalar geçerli olması durumunda 32bit uygulama limitlerini fazlasıyla aşmış oluyorsunuz.

Kullandığınız database yönetim aracı nasıl çalışıyor bilemiyoruz. Belki 64bit bir uygulama ve 4GB hafıza kullanım limiti içinde kalıyor ya da büyük tabloları özel bir şekilde erişim sağlayarak getiriyor olabilir sizin karşınıza (Örn: 3 milyon kayıt var, sayfa başına 60 kayıt gösterilebiliyor. Tablo sonuna gidilmesi istenildiği zaman matematik hesabı yaparak sadece son 60 kayıt database sisteminden sogulanıp karşınızda gösteriliyor olabilir)

Her halukarda ilk yazdığım paragrafın üzerinde düşünmenizi tavsiye ederim. Uygulamanızı bu anlamda tasarım değişikliğine tabi tutmalısınız. Verilen örnek veya farklı bir yöntem ancak kesinlikle 3 milyon kaydın tamamını ekranda listelemek dışında bir yöntem olmalı. Bırakın bir kişinin 10000 kayıt içinde bile kendisini ilgilendiren kayıt sayısı çok çok daha az olacaktır. Ya da sadece bir/birkaç kolonun toplam, ortalama vb değerini görmek istiyordur.

Son kayıt neden görülmek isteniliyor? Belli bir bilgiye bakılacak ise bu bilgiyi özel bir Query ile sorgulayıp ekranın görünür bir yerinde kullanıcıya gösterebilirsiniz.
delphi-x
Üye
Mesajlar: 10
Kayıt: 09 Eki 2011 05:57

Re: Query Out of Memory Hatası

Mesaj gönderen delphi-x »

Ertan hocam tebrik ederim. Konuyu iyi açıklamışınız.. Zannedersem db tool dediğiniz gibi değişik bir mantıkla olayı çözüyor. Tüm kayıt sayısını kısa sürede hesaplayıp, son bin kayda odaklanıyor.
Select First 1000 Skip 2900000 * from LISTE
cümlesi ile.

Peki cachedupdates özelliği tam olarak ne işe yarar, hıza bir katkısı var mıdır bu sorguda? bu konuyu da izah ederseniz sevinirim...
Teşekkürler..
ertank
Kıdemli Üye
Mesajlar: 1650
Kayıt: 12 Eyl 2015 12:45

Re: Query Out of Memory Hatası

Mesaj gönderen ertank »

CachedUpdates genellikle aşağıdaki durumlarda kullanılır:
- Sağlıklı olmayan network bağlantılarında
- Çok yoğun database işlemlerinin yapılmasının istenmediği durumlarda (yoğun ve büyük veriler ile çalışan database sunucular ile iletişim kurulur iken)
- Kullanıcı çok yoğun değişiklik yapacağının düşünüldüğü ortamlarda sadece en son değişikliklerin tek seferde kaydedilmesi için

CachedUpdates özetle şunu yapar:
- Veriler database üzerinden okunur. Database sunucu ile iletişim bu noktada biter.
- Veriler üzerinde client tarafında her türlü değişikliğin yapılmasına izin verilir. Ancak bu değişiklikler tamamen hafızada yapılır.
- "kaydet" (bileşenden bileşene değişir. ApplyChanges() benzeri komutlar kullanılır) denildiği zaman tüm hafızadaki değişiklikler sunucuya bir defada iletilir.

Sizin durumunuzda (milyonlarda satır içeren tablolar ile çalışıyorken) CachedUpdates uygulamanın RAM ihtiyacını arttırıcı bir yönde etkisi olacaktır. Bütün veriler, değişiklikler hafızada tutulacaktır. Kayıtları 1000'er 1000'er oku gibi bir şansınız CachedUpdate kullanır iken yoktur. SQL cümlesi içinde seçilecek tüm satırlar Query bileşeni açıldığı anda okunacaktır.

Kendi dilimizde bir anlatımını buradan inceleyebilirsiniz. Her ne kadar eski database sistemleri üzerinden örnek veriliyor olsa dahi konuyu anlamak adına yardımcı olacağını düşnüyorum.
http://slideplayer.biz.tr/slide/1908230/

Aynı zamanda forum içindeki şu sayfayı da inceleyebilirsiniz: http://www.delphiturkiye.com/index.php?page=dbcomps.htm
delphi-x
Üye
Mesajlar: 10
Kayıt: 09 Eki 2011 05:57

Re: Query Out of Memory Hatası

Mesaj gönderen delphi-x »

Hocam bu kendi tasarladığım DB nav ile ileri geri hareketlerini yaptırmak istiyorum. mesela tablo açıldı. Bir sonraki 1000e skip yöntemi ile ilerlemek için nasıl bir yapı kurmam lazım. bookmark falanmı kullanmak gerek yoksa daha pratik bir yolu var mıdır??
ertank
Kıdemli Üye
Mesajlar: 1650
Kayıt: 12 Eyl 2015 12:45

Re: Query Out of Memory Hatası

Mesaj gönderen ertank »

Yapmak istediğiniz genel olarak "paging" olarak geçer. Özetle her seferinde belirlenen sayıda kayıt sorgulanır.

Bunu kod yazarak yapmanız oldukça uğraştırıcı olabilir. Database sisteminin özellikleri arasında LIMIT ve OFFSET (veya benzeri komutları) destekliyor olması işinizi kolaylaştıracaktır.

MySQL ve PostgreSQL için örnek SQL komutu aynıdır:

Kod: Tümünü seç

SELECT * FROM tablo_adi LIMIT 1000 OFFSET 4999000
FirebirdSQL için aşağıdaki şeklide bir komut kullanılabilir:

Kod: Tümünü seç

SELECT FIRST 1000 SKIP 4999000 FROM tablo_adi
Bunu yapabilmek için öncelikle tablo içindeki toplam kayıt sayısını sorgulamanız gerekecektir. Ardından sadece posizyonunuzu belirleyen bir değişken tanımlayarak her defasında yeni pozisyonu belirleyip sorgu içinde sadece LIMIT veya SKIP rakamlarını değiştirmeniz yeterli olacaktır.

Ancak yine ilk cevabımdaki ilk paragrafı neden uygulamak istemediğinizi anlamıyorum. Yapmaya çalıştığınız bir database yönetim uygulaması geliştirmek mi?
delphi-x
Üye
Mesajlar: 10
Kayıt: 09 Eki 2011 05:57

Re: Query Out of Memory Hatası

Mesaj gönderen delphi-x »

Aynen hocam. Bir nevi yönetim uygulaması gibi olacak. Basit bir tool hazırlamak istiyorum kendim için. Portable olacak. Biraz daha geliştireyim burdan paylaşırım kodlarıyla birlikte..
Bu arada çok aramama rağmen query execution time ı nasıl elde edeceğimi bulamadım.. Bir fikriminiz var mı acaba??
ertank
Kıdemli Üye
Mesajlar: 1650
Kayıt: 12 Eyl 2015 12:45

Re: Query Out of Memory Hatası

Mesaj gönderen ertank »

Firebird bize bir query bazında CPU kullanım zamanını bildirmiyor. Bunu dolaylı yollardan temin etmek gerekiyor ve yaklaşık bir değer olmuş oluyor.

Aşağıdaki örnek SQL komutunu kullanabilirsiniz.

Kod: Tümünü seç

SELECT CURRENT_TIMESTAMP - MON$TIMESTAMP DURATION
  FROM MON$STATEMENTS
  WHERE MON$STATE = 2
Ancak kullanımı için bazı şartlar var.
- Her bir SQL komutunun hemen arkasından çalıştırmanız gerekli
- Başka bir SQL komutu çalıştırmadan önce aktif Transaction'ı commit etmelisiniz. (FirebirdSQL için genelde CommitRetaining() işlemi kullanılır. Ancak bu aslında gerçek bir Commit() değildir. Yapılması gereken gerçek bir Commit() işleminin gerçekleştirilmesidir)

Bunlar çok yapılabilir değil ise basitçe her bir SQL komutu çalıştırmadan önce GetTickCount() ile mevcut zaman saklanıp komut bittikten sonra yine (GetTickCount() - OncekiDeger) şeklinde geçen süreyi yakın bir değer olarak hesaplamak mümkün olabilir.
Cevapla