ORA-01000, maksimum açık imleç hatası, Oracle veritabanı geliştirmede oldukça yaygın bir hatadır. Java bağlamında, uygulama bir veritabanı örneğinde yapılandırılmış imleçlerden daha fazla ResultSets açmaya çalıştığında gerçekleşir.
Yaygın nedenler şunlardır:
Yapılandırma hatası
- Uygulamanızda veritabanını sorgulayan, DB'deki imleçlerden daha fazla iş parçacığı var. Veritabanındaki imleç sayısından daha büyük bir bağlantınız ve iş parçacığı havuzunuzun olduğu durumlardan biri.
- Aynı DB örneğine bağlı birçok geliştiriciniz veya uygulamanız var (muhtemelen birçok şema içerecektir) ve birlikte çok fazla bağlantı kullanıyorsunuz.
Çözüm:
- Veritabanındaki imleç sayısını artırmak (kaynaklar izin veriyorsa) veya
- Uygulamadaki iş parçacığı sayısını azaltmak.
İmleç sızıntısı
- Uygulamalar, ResultSets'i (JDBC'de) veya imleçleri (veritabanındaki saklı yordamlarda) kapatmıyor
- Çözüm : İmleç sızıntıları hatalardır; DB'deki imleç sayısının artırılması, kaçınılmaz arızayı basitçe geciktirir. Sızıntılar, statik kod analizi , JDBC veya uygulama düzeyinde günlük kaydı ve veritabanı izleme kullanılarak bulunabilir .
Arka fon
Bu bölüm, imleçlerin arkasındaki bazı teorileri ve JDBC'nin nasıl kullanılması gerektiğini açıklamaktadır. Arka planı bilmeniz gerekmiyorsa, bunu atlayabilir ve doğrudan "Sızıntıları Ortadan Kaldırma" bölümüne gidebilirsiniz.
İmleç nedir?
İmleç, veritabanında bir sorgunun durumunu, özellikle de okuyucunun ResultSet'te bulunduğu konumu tutan bir kaynaktır. Her SELECT deyiminin bir imleci vardır ve PL / SQL saklı yordamları, ihtiyaç duydukları kadar çok imleci açabilir ve kullanabilir. Orafaq'ta imleçler hakkında daha fazla bilgi edinebilirsiniz .
Bir veritabanı örneği tipik olarak , her biri birden çok oturuma sahip birçok farklı kullanıcı olan birkaç farklı şemaya hizmet eder . Bunu yapmak için, tüm şemalar, kullanıcılar ve oturumlar için kullanılabilir sabit sayıda imleci vardır. Tüm imleçler açık olduğunda (kullanımda) ve yeni bir imleç gerektiren istek geldiğinde, istek ORA-010000 hatasıyla başarısız olur.
İmleç sayısını bulma ve ayarlama
Numara normalde kurulum sırasında DBA tarafından yapılandırılır. Şu anda kullanımda olan imleç sayısı, maksimum sayı ve konfigürasyona Oracle SQL Developer'daki Yönetici işlevlerinden erişilebilir . SQL'den şu şekilde ayarlanabilir:
ALTER SYSTEM SET OPEN_CURSORS=1337 SID='*' SCOPE=BOTH;
JVM'deki JDBC'yi DB'deki imleçlerle ilişkilendirme
Aşağıdaki JDBC nesneleri, aşağıdaki veritabanı kavramlarına sıkı sıkıya bağlıdır:
- JDBC Bağlantısı , bir veritabanı oturumunun müşteri temsilidir ve veritabanı işlemlerini sağlar . Bir bağlantı herhangi bir anda yalnızca tek bir işlem açık olabilir (ancak işlemler iç içe yerleştirilebilir)
- JDBC ResultSet , veritabanında tek bir imleç tarafından desteklenir . ResultSet'te close () çağrıldığında imleç serbest bırakılır.
- JDBC CallableStatement , genellikle PL / SQL'de yazılan veritabanında bir saklı yordamı çağırır . Depolanan yordam, sıfır veya daha fazla imleç oluşturabilir ve bir imleci JDBC Sonuç Kümesi olarak döndürebilir.
JDBC iş parçacığı güvenlidir: Çeşitli JDBC nesnelerini iş parçacıkları arasında geçirmek oldukça uygundur.
Örneğin, bağlantıyı tek bir iş parçacığında oluşturabilirsiniz; başka bir iş parçacığı bu bağlantıyı bir PreparedStatement oluşturmak için kullanabilir ve üçüncü bir iş parçacığı sonuç kümesini işleyebilir. Tek önemli kısıtlama, herhangi bir zamanda tek bir PreparedStatement üzerinde birden fazla ResultSet'in açık olmamasıdır. Bkz bağlantı başına mu Oracle DB desteği çoklu (paralel) işlemleri?
Bir Bağlantıda bir veritabanı kesinleştirme gerçekleştiğini ve bu nedenle bu bağlantıdaki tüm DML'lerin (INSERT, UPDATE ve DELETE'ler) birlikte işleme alınacağını unutmayın. Bu nedenle, aynı anda birden fazla işlemi desteklemek istiyorsanız, her bir eşzamanlı İşlem için en az bir Bağlantınız olmalıdır.
JDBC nesnelerini kapatma
Bir ResultSet çalıştırmanın tipik bir örneği:
Statement stmt = conn.createStatement();
try {
ResultSet rs = stmt.executeQuery( "SELECT FULL_NAME FROM EMP" );
try {
while ( rs.next() ) {
System.out.println( "Name: " + rs.getString("FULL_NAME") );
}
} finally {
try { rs.close(); } catch (Exception ignore) { }
}
} finally {
try { stmt.close(); } catch (Exception ignore) { }
}
Nihayet cümlesinin close () tarafından oluşturulan istisnaları nasıl yok saydığına dikkat edin:
- ResultSet'i try {} catch {} olmadan kapatırsanız, başarısız olabilir ve İfadenin kapanmasını engelleyebilir.
- Denemenin gövdesinde ortaya çıkan herhangi bir istisnanın, arayan kişiye yayılmasına izin vermek istiyoruz. Örneğin, İfadeler oluşturmak ve yürütmek gibi bir döngünüz varsa, döngü içindeki her bir Deyimi kapatmayı unutmayın.
Java 7'de Oracle, Java 6 standart metninin çoğunu güzel sözdizimsel şekerle değiştiren AutoCloseable arayüzünü tanıttı .
JDBC nesnelerini tutma
JDBC nesneleri, yerel değişkenlerde, nesne örneğinde ve sınıf üyelerinde güvenli bir şekilde tutulabilir. Aşağıdakileri yapmak genellikle daha iyi bir uygulamadır:
- Connections ve PreparedStatements gibi daha uzun bir süre boyunca birden çok kez yeniden kullanılan JDBC nesnelerini tutmak için nesne örneğini veya sınıf üyelerini kullanın
- ResultSets için yerel değişkenler kullanın, çünkü bunlar elde edilir, döngü yapılır ve sonra tipik olarak tek bir işlev kapsamında kapatılır.
Bununla birlikte, bir istisna vardır: EJB'ler veya bir Servlet / JSP konteyneri kullanıyorsanız, katı bir iş parçacığı modeli izlemeniz gerekir:
- Yalnızca Uygulama Sunucusu iş parçacıkları oluşturur (bununla gelen istekleri ele alır)
- Yalnızca Uygulama Sunucusu bağlantılar oluşturur (bağlantı havuzundan elde ettiğiniz)
- Aramalar arasında değerleri (durum) kaydederken çok dikkatli olmalısınız. Değerleri asla kendi önbelleklerinizde veya statik üyelerinizde saklamayın - bu, kümeler ve diğer garip koşullar için güvenli değildir ve Uygulama Sunucusu verilerinize korkunç şeyler yapabilir. Bunun yerine durum bilgisi olan fasulye veya bir veritabanı kullanın.
- Özellikle, farklı uzaktan çağrılar üzerinden JDBC nesnelerini (Connections, ResultSets, PreparedStatements, vb.) Asla tutmayın - bırakın Uygulama Sunucusu bunu yönetsin. Uygulama Sunucusu yalnızca bir bağlantı havuzu sağlamakla kalmaz, aynı zamanda PreparedStatements'ınızı da önbelleğe alır.
Sızıntıları ortadan kaldırmak
JDBC sızıntılarını tespit etmeye ve ortadan kaldırmaya yardımcı olacak bir dizi süreç ve araç vardır:
Geliştirme sırasında - böcekleri erken yakalamak, açık ara en iyi yaklaşımdır:
Geliştirme uygulamaları: İyi geliştirme uygulamaları, geliştiricinin masasından ayrılmadan önce yazılımınızdaki hataların sayısını azaltmalıdır. Belirli uygulamalar şunları içerir:
- Yeterli deneyime sahip olmayanları eğitmek için eşli programlama
- Birçok göz birden daha iyi olduğu için kod incelemeleri
- Birim testi ; bu, kod tabanınızın herhangi birini veya tamamını bir test aracından uygulayabileceğiniz anlamına gelir; bu da sızıntıları yeniden üretmeyi önemsiz hale getirir.
- Kendi kütüphanenizi oluşturmak yerine bağlantı havuzu oluşturmak için mevcut kitaplıkları kullanın
Statik Kod Analizi: Statik kod analizi yapmak için mükemmel Findbug'lar gibi bir araç kullanın . Bu, close () öğesinin doğru şekilde işlenmediği birçok yeri alır. Findbugs, Eclipse için bir eklentiye sahiptir, ancak aynı zamanda tek seferlik kullanım için bağımsız çalışır, Jenkins CI ve diğer oluşturma araçlarına entegrasyonları vardır
İşlem esnasında:
Tutulabilirlik ve taahhüt
- ResultSet tutulabilirliği ResultSet.CLOSE_CURSORS_OVER_COMMIT ise, Connection.commit () yöntemi çağrıldığında ResultSet kapatılır. Bu, Connection.setHoldability () kullanılarak veya aşırı yüklenmiş Connection.createStatement () yöntemi kullanılarak ayarlanabilir.
Çalışma zamanında günlük kaydı.
- Kodunuza iyi günlük ifadeleri ekleyin. Müşteri, destek personeli ve ekip arkadaşlarının eğitim almadan anlayabilmesi için bunlar açık ve anlaşılır olmalıdır. Kısa olmalılar ve işlem mantığını izleyebilmeniz için anahtar değişkenlerin ve özniteliklerin durum / dahili değerlerini yazdırmayı içermelidirler. İyi günlük kaydı, uygulamalarda, özellikle konuşlandırılmış uygulamalarda hata ayıklama için çok önemlidir.
Projenize bir hata ayıklama JDBC sürücüsü ekleyebilirsiniz (hata ayıklama için - gerçekten dağıtmayın). Bir örnek (kullanmadım) log4jdbc'dir . Daha sonra, hangi yürütmelerin karşılık gelen kapanışına sahip olmadığını görmek için bu dosya üzerinde bazı basit analizler yapmanız gerekir. Açık ve kapalı sayıları saymak, potansiyel bir sorun olup olmadığını vurgulamalıdır
- Veritabanını izleme. SQL Developer 'Monitor SQL' işlevi veya Quest's TOAD gibi araçları kullanarak çalışan uygulamanızı izleyin . Bu makalede izleme anlatılmaktadır . İzleme sırasında, açık imleçleri (örneğin tablo v $ sesstat'tan) sorgulayıp SQL'lerini gözden geçirirsiniz. İmleçlerin sayısı artıyorsa ve (en önemlisi) tek bir özdeş SQL ifadesi tarafından yönetiliyorsa, o SQL'de bir sızıntınız olduğunu bilirsiniz. Kodunuzu arayın ve inceleyin.
Diğer düşünceler
Bağlantıları kapatmak için WeakReferences'ı kullanabilir misiniz?
Zayıf ve yumuşak referanslar, bir nesneye, JVM'nin uygun gördüğü herhangi bir zamanda referansı çöp toplamasına izin verecek şekilde referans vermenize izin verme yollarıdır (bu nesneye yönelik güçlü referans zincirleri olmadığı varsayılarak).
Yapıcıda bir ReferenceQueue'yu yumuşak veya zayıf Reference'a iletirseniz, nesne oluştuğunda GC'lendiğinde (eğer ortaya çıkarsa) nesne ReferenceQueue'ya yerleştirilir. Bu yaklaşımla, nesnenin sonlandırılmasıyla etkileşime girebilir ve nesneyi o anda kapatabilir veya sonlandırabilirsiniz.
Hayali referanslar biraz daha tuhaftır; amaçları yalnızca sonlandırmayı kontrol etmektir, ancak orijinal nesneye asla bir referans alamazsınız, bu nedenle üzerinde close () yöntemini çağırmak zor olacaktır.
Ancak, GC çalıştırıldığında kontrol etmeye çalışmak nadiren iyi bir fikirdir (Weak, Soft ve PhantomReferences , nesnenin GC için kuyruğa alındıktan sonra size haber verir ). Aslında, JVM'deki bellek miktarı büyükse (örneğin, -Xmx2000m) , nesneyi asla GC yapamazsınız ve yine de ORA-01000 deneyimini yaşarsınız. JVM belleği, programınızın gereksinimlerine göre küçükse, ResultSet ve PreparedStatement nesnelerinin oluşturulduktan hemen sonra (siz bunlardan okuyabilmeniz için) GC'ye tabi tutulduğunu görebilirsiniz, bu da muhtemelen programınızda başarısız olur.
TL; DR: Zayıf referans mekanizması, Statement ve ResultSet nesnelerini yönetmek ve kapatmak için iyi bir yol değildir.
for (String language : additionalLangs) {