Oracle
 sql >> Cơ Sở Dữ Liệu >  >> RDS >> Oracle

java.sql.SQLException:- ORA-01000:vượt quá con trỏ mở tối đa

ORA-01000, lỗi con trỏ mở tối đa, là một lỗi cực kỳ phổ biến trong quá trình phát triển cơ sở dữ liệu Oracle. Trong ngữ cảnh của Java, điều này xảy ra khi ứng dụng cố gắng mở nhiều Bộ kết quả hơn số con trỏ được định cấu hình trên một phiên bản cơ sở dữ liệu.

Nguyên nhân phổ biến là:

  1. Lỗi cấu hình

    • Bạn có nhiều luồng trong ứng dụng của mình truy vấn cơ sở dữ liệu hơn là các con trỏ trên DB. Một trường hợp là bạn có kết nối và nhóm luồng lớn hơn số lượng con trỏ trên cơ sở dữ liệu.
    • Bạn có nhiều nhà phát triển hoặc ứng dụng được kết nối với cùng một phiên bản DB (có thể sẽ bao gồm nhiều lược đồ) và cùng nhau, bạn đang sử dụng quá nhiều kết nối.
    • Giải pháp:

      • Tăng số lượng con trỏ trên cơ sở dữ liệu (nếu tài nguyên cho phép) hoặc
      • Giảm số lượng chuỗi trong ứng dụng.
  2. Rò rỉ con trỏ

    • Các ứng dụng không đóng ResultSets (trong JDBC) hoặc con trỏ (trong các thủ tục được lưu trữ trên cơ sở dữ liệu)
    • Giải pháp :Rò rỉ con trỏ là lỗi; việc tăng số lượng con trỏ trên DB chỉ đơn giản là trì hoãn lỗi không thể tránh khỏi. Rò rỉ có thể được tìm thấy bằng cách sử dụng phân tích mã tĩnh, JDBC hoặc ghi nhật ký cấp ứng dụng và giám sát cơ sở dữ liệu.

Bối cảnh

Phần này mô tả một số lý thuyết đằng sau con trỏ và cách JDBC nên được sử dụng. Nếu bạn không cần biết thông tin cơ bản, bạn có thể bỏ qua phần này và chuyển thẳng đến 'Loại bỏ rò rỉ'.

Con trỏ là gì?

Con trỏ là một tài nguyên trên cơ sở dữ liệu chứa trạng thái của một truy vấn, cụ thể là vị trí nơi người đọc ở trong Tập kết quả. Mỗi câu lệnh SELECT có một con trỏ và các thủ tục được lưu trữ PL / SQL có thể mở và sử dụng bao nhiêu con trỏ tùy theo yêu cầu. Bạn có thể tìm hiểu thêm về con trỏ trên Orafaq.

Một phiên bản cơ sở dữ liệu thường phân phát một số lược đồ khác nhau , nhiều người dùng khác nhau mỗi người có nhiều phiên . Để làm điều này, nó có sẵn một số lượng con trỏ cố định cho tất cả các lược đồ, người dùng và phiên. Khi tất cả các con trỏ đang mở (đang sử dụng) và yêu cầu xuất hiện yêu cầu một con trỏ mới, yêu cầu không thành công với lỗi ORA-010000.

Tìm và đặt số lượng con trỏ

Số thường được cấu hình bởi DBA khi cài đặt. Số lượng con trỏ hiện đang được sử dụng, số lượng tối đa và cấu hình có thể được truy cập trong các chức năng Quản trị viên trong Oracle SQL Developer. Từ SQL, nó có thể được đặt bằng:

ALTER SYSTEM SET OPEN_CURSORS=1337 SID='*' SCOPE=BOTH;

Liên hệ JDBC trong JVM với con trỏ trên DB

Các đối tượng JDBC bên dưới được kết hợp chặt chẽ với các khái niệm cơ sở dữ liệu sau:

  • JDBC Kết nối là đại diện khách hàng của một cơ sở dữ liệu phiên và cung cấp cơ sở dữ liệu giao dịch . Một kết nối có thể chỉ có một giao dịch duy nhất được mở tại bất kỳ thời điểm nào (nhưng các giao dịch có thể được lồng vào nhau)
  • Một JDBC ResultSet được hỗ trợ bởi một con trỏ duy nhất trên cơ sở dữ liệu. Khi hàm close () được gọi trên ResultSet, con trỏ sẽ được giải phóng.
  • Một JDBC CallableStatement gọi một thủ tục được lưu trữ trên cơ sở dữ liệu, thường được viết bằng PL / SQL. Thủ tục được lưu trữ có thể tạo không hoặc nhiều con trỏ và có thể trả về một con trỏ dưới dạng Bộ kết quả JDBC.

JDBC là luồng an toàn:Khá ổn khi chuyển các đối tượng JDBC khác nhau giữa các luồng.

Ví dụ, bạn có thể tạo kết nối trong một luồng; một luồng khác có thể sử dụng kết nối này để tạo PreparedStatement và một luồng thứ ba có thể xử lý tập kết quả. Hạn chế chính duy nhất là bạn không thể mở nhiều hơn một ResultSet trên một PreparedStatement duy nhất vào bất kỳ lúc nào. Xem Oracle DB có hỗ trợ nhiều hoạt động (song song) trên mỗi kết nối không?

Lưu ý rằng một cam kết cơ sở dữ liệu xảy ra trên một Kết nối và vì vậy tất cả DML (CHÈN, CẬP NHẬT và XÓA của) trên kết nối đó sẽ cam kết cùng nhau. Do đó, nếu bạn muốn hỗ trợ nhiều giao dịch cùng một lúc, bạn phải có ít nhất một Kết nối cho mỗi Giao dịch đồng thời.

Đóng các đối tượng JDBC

Ví dụ điển hình về việc thực thi Tập kết quả là:

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) { }
}

Lưu ý cách mệnh đề cuối cùng bỏ qua bất kỳ ngoại lệ nào được nêu ra bởi close ():

  • Nếu bạn chỉ đóng ResultSet mà không thử {} catch {}, nó có thể không thành công và khiến cho Statement bị đóng
  • Chúng tôi muốn cho phép bất kỳ ngoại lệ nào được nêu ra trong phần nội dung của try được truyền tới người gọi. Ví dụ:nếu bạn có một vòng lặp lặp lại khi tạo và thực hiện các Câu lệnh, hãy nhớ đóng từng Câu lệnh trong vòng lặp.

Trong Java 7, Oracle đã giới thiệu giao diện AutoClosable, giao diện này thay thế hầu hết các bản soạn sẵn của Java 6 bằng một số cú pháp đẹp mắt.

Giữ các đối tượng JDBC

Các đối tượng JDBC có thể được giữ an toàn trong các biến cục bộ, cá thể đối tượng và các thành viên lớp. Nói chung, thực hành tốt hơn là:

  • Sử dụng cá thể đối tượng hoặc các thành viên lớp để giữ các đối tượng JDBC được sử dụng lại nhiều lần trong một khoảng thời gian dài hơn, chẳng hạn như Kết nối và Trạng thái chuẩn bị
  • Sử dụng các biến cục bộ cho ResultSets vì chúng được lấy, lặp lại và sau đó đóng thường trong phạm vi của một hàm duy nhất.

Tuy nhiên, có một ngoại lệ:Nếu bạn đang sử dụng EJB hoặc một vùng chứa Servlet / JSP, bạn phải tuân theo một mô hình phân luồng nghiêm ngặt:

  • Chỉ Máy chủ ứng dụng mới tạo các chuỗi (mà nó xử lý các yêu cầu đến)
  • Chỉ Máy chủ ứng dụng mới tạo kết nối (mà bạn lấy từ nhóm kết nối)
  • Khi lưu các giá trị (trạng thái) giữa các lần gọi, bạn phải rất cẩn thận. Không bao giờ lưu trữ các giá trị trong bộ nhớ đệm của riêng bạn hoặc các thành viên tĩnh - điều này không an toàn trên các cụm và các điều kiện kỳ ​​lạ khác và Máy chủ ứng dụng có thể gây ra những điều tồi tệ đối với dữ liệu của bạn. Thay vào đó, hãy sử dụng các bean trạng thái hoặc một cơ sở dữ liệu.
  • Đặc biệt, không bao giờ giữ các đối tượng JDBC (Connections, ResultSets, PreparedStatements, v.v.) trên các lệnh gọi từ xa khác nhau - hãy để Máy chủ ứng dụng quản lý điều này. Máy chủ ứng dụng không chỉ cung cấp nhóm kết nối, nó còn lưu trữ các Bài toán chuẩn bị của bạn vào bộ nhớ đệm.

Loại bỏ rò rỉ

Có một số quy trình và công cụ có sẵn để giúp phát hiện và loại bỏ rò rỉ JDBC:

  1. Trong quá trình phát triển - cho đến nay việc bắt lỗi sớm là cách tiếp cận tốt nhất:

    1. Các phương pháp phát triển:Các phương pháp phát triển tốt sẽ làm giảm số lượng lỗi trong phần mềm của bạn trước khi nó rời khỏi bàn làm việc của nhà phát triển. Các thực hành cụ thể bao gồm:

      1. Ghép nối lập trình để đào tạo những người không có đủ kinh nghiệm
      2. Đánh giá mã vì nhiều mắt tốt hơn một mắt
      3. Kiểm tra đơn vị có nghĩa là bạn có thể thực hiện bất kỳ và tất cả cơ sở mã của mình từ một công cụ kiểm tra khiến việc sao chép rò rỉ trở nên tầm thường
      4. Sử dụng các thư viện hiện có để tổng hợp kết nối thay vì xây dựng thư viện của riêng bạn
    2. Phân tích mã tĩnh:Sử dụng một công cụ như Findbugs tuyệt vời để thực hiện phân tích mã tĩnh. Điều này dẫn đến nhiều chỗ mà dấu đóng () không được xử lý chính xác. Findbugs có một plugin cho Eclipse, nhưng nó cũng chạy độc lập cho một lần duy nhất, có tích hợp vào Jenkins CI và các công cụ xây dựng khác

  2. Trong thời gian chạy:

    1. Khả năng nắm giữ và cam kết

      1. Nếu khả năng lưu giữ ResultSet là ResultSet.CLOSE_CURSORS_OVER_COMMIT, thì ResultSet sẽ bị đóng khi phương thức Connection.commit () được gọi. Điều này có thể được đặt bằng cách sử dụng Connection.setHoldability () hoặc bằng cách sử dụng phương thức Connection.createStatement () đã quá tải.
    2. Ghi nhật ký trong thời gian chạy.

      1. Đặt các báo cáo nhật ký tốt vào mã của bạn. Những điều này phải rõ ràng và dễ hiểu để khách hàng, nhân viên hỗ trợ và đồng đội có thể hiểu mà không cần đào tạo. Chúng phải ngắn gọn và bao gồm in trạng thái / giá trị bên trong của các biến và thuộc tính chính để bạn có thể theo dõi logic xử lý. Ghi nhật ký tốt là điều cơ bản để gỡ lỗi các ứng dụng, đặc biệt là những ứng dụng đã được triển khai.
      2. Bạn có thể thêm trình điều khiển JDBC gỡ lỗi vào dự án của mình (để gỡ lỗi - không thực sự triển khai nó). Một ví dụ (tôi chưa sử dụng nó) là log4jdbc. Sau đó, bạn cần thực hiện một số phân tích đơn giản trên tệp này để xem tệp thực thi nào không có giá trị đóng tương ứng. Việc đếm số lần mở và đóng cửa nên đánh dấu nếu có sự cố tiềm ẩn

        1. Giám sát cơ sở dữ liệu. Giám sát ứng dụng đang chạy của bạn bằng các công cụ như chức năng 'Giám sát SQL' của Nhà phát triển SQL hoặc TOAD của Quest. Giám sát được mô tả trong bài báo này. Trong quá trình theo dõi, bạn truy vấn các con trỏ đang mở (ví dụ từ bảng v $ sesstat) và xem lại SQL của chúng. Nếu số lượng con trỏ ngày càng tăng và (quan trọng nhất) bị chi phối bởi một câu lệnh SQL giống hệt nhau, bạn biết rằng bạn đã bị rò rỉ với câu lệnh SQL đó. Tìm kiếm mã của bạn và xem lại.

Suy nghĩ khác

Bạn có thể sử dụng WeakRefferences để xử lý các kết nối đang đóng không?

Tham chiếu yếu và mềm là những cách cho phép bạn tham chiếu đến một đối tượng theo cách cho phép JVM thu thập tham chiếu bất kỳ lúc nào mà nó cho là phù hợp (giả sử không có chuỗi tham chiếu mạnh đến đối tượng đó).

Nếu bạn truyền một ReferenceQueue trong phương thức khởi tạo đến một tham chiếu mềm hoặc yếu, đối tượng sẽ được đặt trong ReferenceQueue khi đối tượng được GC'ed khi nó xảy ra (nếu nó xảy ra). Với cách tiếp cận này, bạn có thể tương tác với quá trình hoàn thiện của đối tượng và bạn có thể đóng hoặc hoàn thiện đối tượng tại thời điểm đó.

Các tài liệu tham khảo về bóng ma hơi kỳ lạ hơn một chút; mục đích của chúng chỉ là để kiểm soát quá trình hoàn thiện, nhưng bạn không bao giờ có thể nhận được một tham chiếu đến đối tượng ban đầu, vì vậy sẽ rất khó để gọi phương thức close () trên đó.

Tuy nhiên, hiếm khi cố gắng kiểm soát khi GC được chạy (Các tài liệu tham khảo yếu, mềm và Phantom cho bạn biết sau thực tế rằng đối tượng được xếp hàng cho GC). Trên thực tế, nếu dung lượng bộ nhớ trong JVM lớn (ví dụ -Xmx2000m), bạn có thể không bao giờ GC đối tượng, và bạn vẫn sẽ trải nghiệm ORA-01000. Nếu bộ nhớ JVM nhỏ so với yêu cầu của chương trình của bạn, bạn có thể thấy rằng các đối tượng ResultSet và PreparedStatement được GCed ngay sau khi tạo (trước khi bạn có thể đọc từ chúng), điều này có thể sẽ làm hỏng chương trình của bạn.

TL; DR: Cơ chế tham chiếu yếu không phải là cách tốt để quản lý và đóng các đối tượng Statement và ResultSet.



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Sử dụng mệnh đề IN với một chuỗi được phân tách bằng dấu phẩy từ đầu ra của một hàm Replace () trong Oracle SQL

  2. Biểu thức chính quy (RegEx) cho IPv6 Tách biệt với IPv4

  3. Sử dụng các giao dịch lồng nhau trong oracle

  4. Lỗi khi sử dụng oracle.dataaccess.dll

  5. SQL Server:làm thế nào để bắt chước truy vấn oracle keep secure_rank?