2011-09-09 32 views
9

Theo tài liệu tôi đã đọc, bộ lưu trữ mặc định cho một CLOB hoặc BLOB là nội tuyến, có nghĩa là nếu nó nhỏ hơn kích thước khoảng 4k thì nó sẽ được giữ trong bảng.Oracle 10g nhỏ Blob hoặc Clob không được lưu trữ nội tuyến?

Nhưng khi tôi thử nghiệm điều này trên một bảng giả trong Oracle (10.2.0.1.0) hiệu suất và phản hồi từ Oracle Monitor (bởi Allround Automations) cho thấy rằng nó đang được tổ chức bên ngoài bảng.

Dưới đây là kịch bản thử nghiệm của tôi ...

create table clobtest (x int primary key, y clob, z varchar(100)) 
; 
insert into clobtest 
    select object_id, object_name, object_name 
    from all_objects where rownum < 10001 
; 
select COLUMN_NAME, IN_ROW 
from user_lobs 
where table_name = 'CLOBTEST' 
; 

này hiển thị: Y YES (gợi ý rằng Oracle sẽ lưu trữ clob ở hàng)

select x, y from CLOBTEST where ROWNUM < 1001 -- 8.49 seconds 
select x, z from CLOBTEST where ROWNUM < 1001 -- 0.298 seconds 

Vì vậy, trong trường hợp này, các giá trị CLOB sẽ có độ dài tối đa 30 ký tự, vì vậy phải luôn là nội dòng. Nếu tôi chạy Oracle Monitor, nó cho thấy một LOB.Length theo sau là một LOB.Read() cho mỗi hàng trả về, một lần nữa cho thấy rằng các giá trị clob được tổ chức bên ngoài bảng.

Tôi cũng đã cố gắng tạo bảng như thế này

create table clobtest 
    (x int primary key, y clob, z varchar(100)) 
    LOB (y) STORE AS  (ENABLE STORAGE IN ROW) 

nhưng có chính xác kết quả tương tự.

Có ai có bất kỳ đề xuất nào về cách tôi có thể bắt buộc (thuyết phục, khuyến khích) Oracle lưu trữ giá trị trong dòng trong bảng không? (Tôi hy vọng để đạt được thời gian đáp ứng tương tự như đọc z cột varchar2)

UPDATE: Nếu tôi chạy SQL này

select COLUMN_NAME, IN_ROW, l.SEGMENT_NAME, SEGMENT_TYPE, BYTES, BLOCKS, EXTENTS 
from user_lobs l 
     JOIN USER_SEGMENTS s 
     on (l.Segment_Name = s. segment_name) 
where table_name = 'CLOBTEST' 

sau đó tôi nhận được kết quả như sau ...

Y YES SYS_LOB0000398621C00002$$ LOBSEGMENT 65536 8 1 

Trả lời

0

Có hai sự không đồng ý khi nói đến CLOB và BLOB:

  1. Giá trị LOB có thể là s tored trong một phân đoạn cơ sở dữ liệu khác với phần còn lại của hàng.

  2. Khi bạn truy vấn hàng, chỉ các trường không phải LOB được chứa trong tập kết quả và truy nhập LOB trường requries một hoặc nhiều chuyến đi vòng bổ sung giữa khách hàng và máy chủ (mỗi hàng!).

Tôi không biết bạn đo thời gian thực hiện như thế nào và tôi chưa bao giờ sử dụng Oracle Monitor, nhưng bạn chủ yếu có thể bị ảnh hưởng bởi lần thứ hai. Tùy thuộc vào phần mềm máy khách mà bạn sử dụng, bạn có thể giảm các chuyến đi khứ hồi. Ví dụ. khi bạn sử dụng ODP.NET, thông số được gọi là InitialLobFetchSize.

Cập nhật:

Một ai để kể nào trong hai indirections là có liên quan, bạn có thể chạy truy vấn LOB của bạn với 1000 hàng hai lần. Nếu thời gian giảm đáng kể từ lần đầu tiên sang lần chạy thứ hai, nó là gián tiếp 1. Vào lần chạy thứ hai, bộ đệm ẩn sẽ trả về và truy cập vào phân đoạn cơ sở dữ liệu riêng biệt không còn phù hợp nữa. Nếu thời gian vẫn giữ nguyên, đó là sự gián đoạn thứ hai, cụ thể là các chuyến đi khứ hồi giữa máy khách và máy chủ, mà không thể cải thiện giữa hai lần chạy.

Thời gian hơn 8 giây cho 1000 hàng trong một truy vấn rất đơn giản cho biết đó là số 2 vì 8 giây cho 1000 hàng không thực sự được giải thích với quyền truy cập đĩa trừ khi dữ liệu của bạn rất phân tán và hệ thống đĩa cứng tải.

0

Thật vậy, nó được lưu trữ trong hàng. Bạn có thể đối phó với chi phí đơn giản của việc sử dụng một LOB thay vì một varchar. Không có gì miễn phí. DB có lẽ không biết trước thời gian để tìm hàng, vì vậy nó có thể vẫn "theo một con trỏ" và làm thêm công việc chỉ trong trường hợp LOB là lớn. Nếu bạn có thể nhận được bằng với một varchar, bạn nên. Ngay cả hack cũ như 2 varchars để đối phó với 8000 ký tự có thể giải quyết trường hợp kinh doanh của bạn với hiệu suất cao hơn.

LOBS chậm, khó truy vấn, v.v. Về mặt tích cực, chúng có thể là 4G.

Điều gì sẽ là thú vị khi thử là để đẩy thứ gì đó chỉ hơn 4000 byte vào nhánh đó và xem hiệu suất trông như thế nào. Có lẽ nó là về cùng một tốc độ? Điều này sẽ cho bạn biết rằng đó là trên không làm chậm bạn xuống.

Cảnh báo, tại một số điểm lưu lượng truy cập mạng vào máy tính của bạn sẽ làm chậm các loại kiểm tra này.

Giảm thiểu này bằng cách gói trong một số, điều này tách biệt công việc đến máy chủ:

select count (*) từ (chọn x, y từ clobtest nơi rownum < 1001)

Bạn có thể đạt được tương tự hiệu ứng với "thiết lập dấu vết autot", nhưng sẽ có truy tìm trên không quá.

8

Hành vi của Oracle LOBs như sau.

Một LOB được lưu trữ inline khi:

(
    The size is lower or equal than 3964 
    AND 
    ENABLE STORAGE IN ROW has been defined in the LOB storage clause 
) OR (
    The value is NULL 
) 

Một LOB được lưu trữ out-of-hàng khi:

(
    The value is not NULL 
) AND (
    Its size is higher than 3964 
    OR 
    DISABLE STORAGE IN ROW has been defined in the LOB storage clause 
) 

Bây giờ đây không phải là vấn đề duy nhất mà có thể ảnh hưởng hiệu suất.

Nếu LOB cuối cùng không được lưu trữ nội tuyến, hành vi mặc định của Oracle là tránh lưu vào bộ nhớ đệm (chỉ LOB nội tuyến được lưu trong bộ nhớ đệm với các trường khác của hàng). Để yêu cầu Oracle cũng lưu trữ các LOB không nội tuyến, tùy chọn CACHE nên được sử dụng khi LOB được định nghĩa.

Hành vi mặc định là ENABLE STORAGE IN ROW và NOCACHE, có nghĩa là LOB nhỏ sẽ được gạch chân, các LOB lớn sẽ không (và sẽ không được lưu trong bộ nhớ cache).

Cuối cùng, cũng có sự cố về hiệu suất ở cấp giao thức truyền thông. khách hàng Oracle điển hình sẽ biểu diễn 2 roundtrips thêm mỗi LOB để lấy chúng: - một để lấy kích thước của LOB và cấp phát bộ nhớ phù hợp - một để lấy dữ liệu riêng của mình (với điều kiện LOB là nhỏ)

Những roundtrips thêm được thực hiện ngay cả khi giao diện mảng được sử dụng để truy xuất kết quả. Nếu bạn lấy 1000 hàng và kích thước mảng của bạn đủ lớn, bạn sẽ trả tiền cho 1 vòng để truy xuất các hàng và 2000 vòng tròn để truy xuất nội dung của các LOB.

Xin lưu ý rằng không phải phụ thuộc vào thực tế LOB được lưu trữ nội tuyến hay không.Chúng hoàn toàn khác nhau.

Để tối ưu hóa ở cấp giao thức, Oracle đã cung cấp một động từ OCI mới để tìm nạp một số LOB trong một vòng tròn (OCILobArrayRead). Tôi không biết có cái gì đó tương tự tồn tại với JDBC không.

Một tùy chọn khác là liên kết LOB ở phía khách hàng như thể đó là một tệp RAW/VARCHAR2 lớn. Điều này chỉ hoạt động nếu kích thước tối đa của LOB có thể được xác định (vì kích thước tối đa phải được cung cấp tại thời điểm ràng buộc). Thủ thuật này tránh được các rountrips thêm: các LOB chỉ được xử lý như RAW hoặc VARCHAR2. Chúng tôi sử dụng nó rất nhiều trong các ứng dụng chuyên sâu LOB của chúng tôi.

Sau khi số lượng vòng kết nối đã được tối ưu hóa, kích thước gói (SDU) có thể được thay đổi kích thước trong cấu hình mạng để phù hợp hơn với tình huống (nghĩa là số lượng vòng tròn lớn). Nó có xu hướng giảm các dữ liệu "SQL * Net nhiều hơn cho khách hàng" và "SQL * Net nhiều hơn từ khách hàng" sự kiện chờ đợi.

+0

Nếu tôi có thể giới thiệu cho bạn nhiều hơn tôi muốn, đây là một phản hồi tuyệt vời –

1

Nếu bạn đang "hy vọng đạt được thời gian phản hồi tương tự để đọc cột varchar2 z", thì bạn sẽ thất vọng trong hầu hết các trường hợp. Nếu bạn đang sử dụng CLOB, giả sử bạn cần lưu trữ hơn 4.000 byte, phải không? Sau đó, nếu bạn cần đọc thêm các byte sẽ mất nhiều thời gian hơn. NHƯNG nếu bạn có trường hợp có, bạn sử dụng CLOB, nhưng bạn quan tâm (trong một số trường hợp) chỉ trong 4000 byte đầu tiên của cột (hoặc ít hơn), thì bạn có cơ hội nhận được tương tự hiệu suất. Có vẻ như Oracle có thể tối ưu hóa quá trình truy xuất nếu bạn sử dụng thứ gì đó như DBMS_LOB.SUBSTR và ENABLE BẢO QUẢN TRONG mệnh đề ROW CACHE với bảng của bạn. Ví dụ:

CREATE TABLE clobtest (x INT PRIMARY KEY, y CLOB) 
LOB (y) STORE AS (ENABLE STORAGE IN ROW CACHE); 

INSERT INTO clobtest VALUES (0, RPAD('a', 4000, 'a')); 
UPDATE clobtest SET y = y || y || y; 
INSERT INTO clobtest SELECT rownum, y FROM all_objects, clobtest WHERE rownum < 1000; 

CREATE TABLE clobtest2 (x INT PRIMARY KEY, z VARCHAR2(4000)); 

INSERT INTO clobtest2 VALUES (0, RPAD('a', 4000, 'a')); 
INSERT INTO clobtest2 SELECT rownum, z FROM all_objects, clobtest2 WHERE rownum < 1000; 

COMMIT; 

Trong các thử nghiệm của tôi trên 10.2.0.4 và 8K khối, hai truy vấn này cho hiệu suất rất giống nhau:

SELECT x, DBMS_LOB.SUBSTR(y, 4000) FROM clobtest; 
SELECT x, z FROM clobtest2; 

mẫu từ SQL * Plus (Tôi chạy các truy vấn nhiều lần để loại bỏ vật lý IO):

SQL> SET AUTOTRACE TRACEONLY STATISTICS 
SQL> SET TIMING ON 
SQL> 
SQL> SELECT x, y FROM clobtest; 

1000 rows selected. 

Elapsed: 00:00:02.96 

Statistics 
------------------------------------------------------ 
      0 recursive calls 
      0 db block gets 
     3008 consistent gets 
      0 physical reads 
      0 redo size 
    559241 bytes sent via SQL*Net to client 
    180350 bytes received via SQL*Net from client 
     2002 SQL*Net roundtrips to/from client 
      0 sorts (memory) 
      0 sorts (disk) 
     1000 rows processed 

SQL> SELECT x, DBMS_LOB.SUBSTR(y, 4000) FROM clobtest; 

1000 rows selected. 

Elapsed: 00:00:00.32 

Statistics 
------------------------------------------------------ 
      0 recursive calls 
      0 db block gets 
     2082 consistent gets 
      0 physical reads 
      0 redo size 
     18993 bytes sent via SQL*Net to client 
     1076 bytes received via SQL*Net from client 
     68 SQL*Net roundtrips to/from client 
      0 sorts (memory) 
      0 sorts (disk) 
     1000 rows processed 

SQL> SELECT x, z FROM clobtest2; 

1000 rows selected. 

Elapsed: 00:00:00.18 

Statistics 
------------------------------------------------------ 
      0 recursive calls 
      0 db block gets 
     1005 consistent gets 
      0 physical reads 
      0 redo size 
     18971 bytes sent via SQL*Net to client 
     1076 bytes received via SQL*Net from client 
     68 SQL*Net roundtrips to/from client 
      0 sorts (memory) 
      0 sorts (disk) 
     1000 rows processed 

Như bạn thấy, phù hợp được khá cao, nhưng SQL * Net roundtrips và byte là gần như giống hệt trong hai truy vấn cuối cùng, và điều đó dường như làm cho một sự khác biệt lớn trong thực hiện tim e!

Một cảnh báo mặc dù: sự khác biệt về tính nhất quán có thể trở thành vấn đề hiệu suất nhiều hơn nếu bạn có tập hợp kết quả lớn, vì bạn sẽ không thể giữ mọi thứ trong bộ đệm đệm và bạn sẽ có lần đọc ...

Chúc bạn may mắn!

Cheers

0

Đây là thông tin quan trọng (làm thế nào để đọc LOB không roundtrips thêm), mà không phải là có sẵn trong tài liệu của Oracle Tôi nghĩ:

lựa chọn khác là để ràng buộc các LOB về phía khách hàng như thể nó là RAW/VARCHAR2 lớn. Điều này chỉ hoạt động nếu kích thước tối đa của LOB có thể là được xác định (vì kích thước tối đa phải được cung cấp tại thời điểm ràng buộc). Điều này lừa tránh các rountrips thêm: các LOB chỉ được xử lý như RAW hoặc VARCHAR2. Chúng tôi sử dụng nó rất nhiều trong các ứng dụng chuyên sâu LOB của chúng tôi.

Tôi gặp vấn đề với tải bảng đơn giản (vài GB) với một cột blob (14KB => hàng nghìn hàng) và tôi đã nghiên cứu nó trong một thời gian dài, đã thử rất nhiều điều chỉnh lưu trữ lob (DB_BLOCK_SIZE) không gian bảng, đặc tả lưu trữ lob - CHUNK), cài đặt sqlnet.ora, các thuộc tính tìm nạp trước khách hàng, nhưng điều này (xử lý BLOB như LONG RAW với OCCI ResultSet-> setBufferData ở phía máy khách) là điều quan trọng nhất (thuyết phục oracle gửi cột blob ngay lập tức) gửi định vị lob lúc đầu và tải từng lob một cách riêng biệt dựa trên thiết bị định vị lob

Bây giờ tôi có thể nhận được thông lượng ~ 500Mb/s (với các cột < 3964B) Blob 14KB của chúng tôi sẽ là được chia thành nhiều cột - vì vậy nó sẽ được lưu trữ trong hàng để có được hầu hết các lần đọc tuần tự từ HDD. Với một đốm trắng 14KB (một cột) tôi nhận được ~ 150Mbit/s vì không đọc tuần tự (iostat: số lượng thấp các yêu cầu đọc đã hợp nhất).

LƯU Ý: đừng quên để thiết lập cũng lob prefetch kích thước/chiều dài:

err = OCIAttrSet (phiên, (ub4) OCI_HTYPE_SESSION, (void *) & default_lobprefetch_size, 0, (ub4) OCI_ATTR_DEFAULT_LOBPREFETCH_SIZE, errhp);

Nhưng tôi không biết làm thế nào để có thể đạt được cùng một thông lượng tìm nạp với đầu nối ODBC. Tôi đã thử nó mà không thành công.

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