2012-07-12 35 views
7

Bạn có thể giúp tôi hiểu cụm từ này không?Oracle: Thu thập số lượng lớn hiệu suất

Nếu không có phần lớn ràng buộc, PL/SQL sẽ gửi một câu lệnh SQL để động cơ SQL cho mỗi bản ghi được chèn, cập nhật, hoặc xóa dẫn đến tắc bối cảnh đó đau hiệu suất.

Trả lời

17

Trong Oracle, có một máy ảo SQL (VM) và một máy ảo PL/SQL. Khi bạn cần di chuyển từ một máy ảo sang máy ảo khác, bạn phải trả chi phí cho việc thay đổi ngữ cảnh. Cá nhân, những thay đổi ngữ cảnh đó tương đối nhanh, nhưng khi bạn đang thực hiện xử lý từng hàng, chúng có thể thêm đến một phần đáng kể thời gian mã của bạn đang chi tiêu. Khi bạn sử dụng các liên kết hàng loạt, bạn di chuyển nhiều hàng dữ liệu từ một máy ảo sang một máy khác bằng một sự thay đổi ngữ cảnh đơn lẻ, giảm đáng kể số lần thay đổi ngữ cảnh, làm cho mã của bạn nhanh hơn.

Lấy, ví dụ: con trỏ rõ ràng. Nếu tôi viết một cái gì đó như thế này

DECLARE 
    CURSOR c 
     IS SELECT * 
      FROM source_table; 
    l_rec source_table%rowtype; 
BEGIN 
    OPEN c; 
    LOOP 
    FETCH c INTO l_rec; 
    EXIT WHEN c%notfound; 

    INSERT INTO dest_table(col1, col2, ... , colN) 
     VALUES(l_rec.col1, l_rec.col2, ... , l_rec.colN); 
    END LOOP; 
END; 

sau đó mỗi khi tôi thực hiện lấy, tôi

  • Performing một sự thay đổi bối cảnh từ/SQL VM PL đến SQL VM
  • Yêu cầu VM SQL để thực hiện con trỏ để tạo ra các hàng tiếp theo của dữ liệu
  • Performing khác thay đổi bối cảnh từ VM SQL trở lại/SQL VM PL trở lại hàng duy nhất của tôi về dữ liệu

Và mỗi lần tôi chèn một hàng, tôi cũng làm như vậy. Tôi phát sinh chi phí chuyển ngữ cảnh để chuyển một hàng dữ liệu từ máy ảo PL/SQL tới máy ảo SQL, yêu cầu SQL thực thi câu lệnh INSERT và sau đó làm tăng chi phí của một ngữ cảnh khác trở lại PL/SQL.

Nếu source_table có 1 triệu hàng, đó là 4 triệu thay đổi ngữ cảnh có khả năng chiếm một phần hợp lý của thời gian đã trôi qua của mã của tôi. Nếu, mặt khác, tôi thực hiện BULK COLLECT với LIMIT của 100, tôi có thể loại bỏ 99% ngữ cảnh của mình thay đổi bằng cách lấy 100 hàng dữ liệu từ máy ảo SQL vào bộ sưu tập trong PL/SQL mỗi khi tôi chịu chi phí một sự thay đổi ngữ cảnh và chèn 100 hàng vào bảng đích mỗi lần tôi phải thay đổi ngữ cảnh ở đó.

Nếu có thể viết lại mã của tôi để tận dụng thao tác hàng loạt

DECLARE 
    CURSOR c 
     IS SELECT * 
      FROM source_table; 
    TYPE nt_type IS TABLE OF source_table%rowtype; 
    l_arr nt_type; 
BEGIN 
    OPEN c; 
    LOOP 
    FETCH c BULK COLLECT INTO l_arr LIMIT 100; 
    EXIT WHEN l_arr.count = 0; 

    FORALL i IN 1 .. l_arr.count 
     INSERT INTO dest_table(col1, col2, ... , colN) 
     VALUES(l_arr(i).col1, l_arr(i).col2, ... , l_arr(i).colN); 
    END LOOP; 
END; 

Bây giờ, mỗi khi tôi thực hiện lấy, tôi lấy 100 dòng dữ liệu vào bộ sưu tập của tôi với một bộ thay đổi ngữ cảnh. Và mỗi khi tôi thực hiện chèn FORALL của mình, tôi chèn 100 hàng với một bộ thay đổi ngữ cảnh duy nhất. Nếu source_table có 1 triệu hàng, điều này có nghĩa là tôi đã chuyển từ 4 triệu ngữ cảnh sang 40.000 thay đổi ngữ cảnh. Nếu thay đổi ngữ cảnh chiếm 20% thời gian trôi qua của mã của tôi, tôi đã loại bỏ 19.8% thời gian đã trôi qua.

Bạn có thể tăng kích thước của LIMIT để giảm thêm số lần thay đổi ngữ cảnh nhưng bạn nhanh chóng đạt được định luật thu nhập giảm dần. Nếu bạn đã sử dụng LIMIT trong số 1000 thay vì 100, bạn sẽ loại bỏ 99,9% ngữ cảnh thay đổi thay vì 99%. Điều đó có nghĩa là bộ sưu tập của bạn đang sử dụng bộ nhớ PGA gấp 10 lần. Và nó sẽ chỉ loại bỏ thêm 0,18% thời gian trôi qua trong ví dụ giả định của chúng ta. Bạn rất nhanh chóng đạt đến một điểm mà bộ nhớ bổ sung bạn đang sử dụng cho biết thêm thời gian nhiều hơn bạn tiết kiệm bằng cách loại bỏ thay đổi ngữ cảnh bổ sung. Nói chung, một LIMIT ở đâu đó giữa 100 và 1000 có thể là điểm ngọt ngào.

Tất nhiên, trong ví dụ này, nó sẽ hiệu quả hơn vẫn để loại bỏ tất cả các ca bối cảnh và làm tất cả mọi thứ trong một câu lệnh SQL đơn

INSERT INTO dest_table(col1, col2, ... , colN) 
    SELECT col1, col2, ... , colN 
    FROM source_table; 

Nó sẽ chỉ làm cho tinh thần để nghỉ mát để PL/SQL trong nơi đầu tiên nếu bạn đang làm một số loại thao tác của dữ liệu từ bảng nguồn mà bạn không thể thực hiện hợp lý trong SQL.

Ngoài ra, tôi đã sử dụng một con trỏ rõ ràng trong ví dụ của mình một cách có chủ ý. Nếu bạn đang sử dụng các con trỏ ngầm, trong các phiên bản gần đây của Oracle, bạn sẽ nhận được các lợi ích của một BULK COLLECT với một số LIMIT của 100 ngầm định. Có một câu hỏi StackOverflow khác thảo luận về số performance benefits of implicit and explicit cursors with bulk operations tương đối sẽ đi vào chi tiết hơn về các nếp nhăn cụ thể đó.

1

Vì tôi hiểu điều này, có hai công cụ liên quan, PL/SQL engine and SQL Engine. Thực hiện một truy vấn mà làm cho sử dụng một động cơ tại một thời điểm là hiệu quả hơn so với chuyển đổi giữa hai

Ví dụ:

INSERT INTO t VALUES(1) 

được xử lý bởi cơ SQL khi

FOR Lcntr IN 1..20 

    END LOOP 

được thực hiện bởi PL/SQL engine

Nếu bạn kết hợp hai câu lệnh trên, hãy đặt INSERT vào vòng lặp,

FOR Lcntr IN 1..20 
    INSERT INTO t VALUES(1) 
END LOOP 

Oracle sẽ chuyển đổi giữa hai công cụ, cho mỗi (20) lần lặp lại. Trong trường hợp này BULK INSERT được khuyến nghị sử dụng công cụ PL/SQL thông qua việc thực hiện

+0

Câu cuối cùng của bạn là đánh lừa. BULK làm cho chuyển đổi ngữ cảnh chỉ xảy ra một lần, mặc dù nó vẫn xảy ra. – viper

Các vấn đề liên quan