根据我读过的文件,CLOB或BLOB的默认存储是内联的,这意味着如果它的大小小于约4k,那么它将保存在表中。
但是当我在Oracle(10.2.0.1.0)中的虚拟表上测试它时,Oracle Monitor(由Allround Automations)的性能和响应表明它已经被表格保留了。
这是我的测试场景......
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'
;
这显示:是(建议Oracle将clob存储在行中)
select x, y from CLOBTEST where ROWNUM < 1001 -- 8.49 seconds
select x, z from CLOBTEST where ROWNUM < 1001 -- 0.298 seconds
因此,在这种情况下,CLOB值的最大长度为30个字符,因此应始终为内联。如果我运行Oracle Monitor,它会显示一个LOB.Length,后面跟着返回的每一行的LOB.Read(),再次表明clob值是用表保存的。
我也试过像这样创建表格
create table clobtest
( x int primary key, y clob, z varchar(100) )
LOB (y) STORE AS (ENABLE STORAGE IN ROW)
但得到了完全相同的结果。
有没有人有任何建议我如何强制(说服,鼓励)Oracle将clob值存储在表中? (我希望获得与读取varchar2列z相似的响应时间)
更新:如果我运行此SQL
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'
然后我得到以下结果......
Y YES SYS_LOB0000398621C00002$$ LOBSEGMENT 65536 8 1
答案 0 :(得分:9)
Oracle LOB的行为如下。
LOB在以下情况下内联存储:
(
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
)
在以下情况下,LOB存储在行外:
(
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
)
现在这不是影响性能的唯一问题。
如果LOB最终没有内联存储,则Oracle的默认行为是避免缓存它们(只有内联LOB缓存在缓冲区缓存中,并与该行的其他字段一起缓存)。要告诉Oracle还要缓存非内联LOB,在定义LOB时应使用CACHE选项。
默认行为是ENABLE STORAGE IN ROW和NOCACHE,这意味着小型LOB将被内联,大型LOB将不会(并且不会被缓存)。
最后,通信协议级别也存在性能问题。典型的Oracle客户端将为每个LOB执行2次额外的往返以获取它们: - 一个用于检索LOB的大小并相应地分配内存 - 一个用于获取数据本身(假设LOB很小)
即使使用数组接口检索结果,也会执行这些额外的往返。如果检索1000行并且数组大小足够大,则需要支付1次往返来检索行,并支付2000次往返以检索LOB的内容。
请注意它不取决于LOB是否内联存储的事实。他们是完全不同的问题。
为了在协议级别进行优化,Oracle提供了一个新的OCI动词,用于在一次往返中获取多个LOB(OCILobArrayRead)。我不知道JDBC是否存在类似的东西。
另一个选择是在客户端绑定LOB,就好像它是一个大的RAW / VARCHAR2。这仅在可以定义LOB的最大大小时才起作用(因为必须在绑定时提供最大大小)。这个技巧避免了额外的rountrips:LOB只是像RAW或VARCHAR2一样处理。我们在LOB密集型应用程序中大量使用它。
一旦优化了往返次数,就可以在网络配置中调整数据包大小(SDU)的大小以更好地适应这种情况(即有限数量的大型往返)。它倾向于减少“SQL * Net更多数据到客户端”和“SQL * Net更多来自客户端的数据”等待事件。
答案 1 :(得分:1)
如果您“希望获得与读取varchar2列z相似的响应时间”,那么在大多数情况下您会感到失望。 如果您正在使用CLOB,我认为您需要存储超过4,000个字节,对吧?然后,如果你需要读取更多需要更长时间的字节。
但是如果你有一个肯定的情况,你使用CLOB,但你只对列的前4,000个字节(或更少)感兴趣(在某些情况下),那么你有可能获得类似的性能。 如果您在表中使用类似DBMS_LOB.SUBSTR和ENABLE STORAGE IN ROW CACHE子句的内容,Oracle看起来可以优化检索。例如:
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;
在我对10.2.0.4和8K块的测试中,这两个查询的性能非常相似:
SELECT x, DBMS_LOB.SUBSTR(y, 4000) FROM clobtest;
SELECT x, z FROM clobtest2;
来自SQL * Plus的示例(我多次运行查询以删除物理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
正如您所看到的,一致的获取相当高,但SQL * Net往返和字节在后两个查询中几乎相同,这显然会对执行时间产生很大影响!
虽然有一个警告:如果你有大量的结果集,一致性获取的差异可能会成为更可能的性能问题,因为你将无法将所有内容保存在缓冲区缓存中,并且最终会导致非常昂贵的物理读取...
祝你好运!干杯
答案 2 :(得分:0)
CLOB和BLOB有两个方向:
LOB值可能存储在与行的其余部分不同的数据库段中。
当您查询该行时,结果集中只包含非LOB字段,并且访问LOB字段需要在客户端和服务器之间进行一次或多次额外的往返(每行!)。 / p>
我不太清楚你是如何测量执行时间的,我从来没有使用过Oracle Monitor,但你可能主要受第二个间接影响。根据您使用的客户端软件,可以减少往返次数。例如。当您使用ODP.NET时,该参数称为 InitialLobFetchSize 。
<强>更新强>
要知道两个间接中的哪一个是相关的,您可以使用1000行两次运行LOB查询。如果时间从第一次运行到第二次运行显着下降,则它是间接的1.在第二次运行时,缓存得到回报,并且对单独的数据库段的访问不再是非常相关的。如果时间保持不变,那就是第二个间接,即客户端和服务器之间的往返,这在两次运行之间无法改善。
在一个非常简单的查询中,1000行超过8秒的时间表示它是间接2,因为1000行的8秒无法用磁盘访问来解释,除非您的数据非常分散且磁盘系统负载很重
答案 3 :(得分:0)
实际上,它存储在行中。您可能正在处理使用LOB而不是varchar的简单开销。没有什么是免费的。 DB可能不知道提前找到行的位置,所以它可能仍然“跟随指针”并且在LOB很大的情况下做额外的工作。如果你可以使用varchar,你应该。即使像2个varchars这样处理8000个字符的老式黑客也可能以更高的性能解决您的业务案例。
LOBS很慢,难以查询等。积极的,它们可以是4G。
尝试的有趣之处是将超过4000个字节的内容推入该clob中,并查看性能的样子。也许它的速度大致相同?这会告诉你,这会降低你的速度。
警告,在某些时候,到您的PC的网络流量会减慢您的速度。
通过包装计数来最小化,这会将工作隔离到服务器:
选择count(*)from(选择x,y来自clobtest,其中rownum&lt; 1001)
您可以通过“set autot trace”实现类似的效果,但也会产生跟踪开销。
答案 4 :(得分:0)
这是关键信息(如何在没有额外往返的情况下读取LOB),这在我认为的Oracle文档中是不可用的:
另一个选择是在客户端绑定LOB,就好像它是一个大的 RAW / VARCHAR2。这仅适用于LOB的最大大小 已定义(因为必须在绑定时提供最大大小)。这个 技巧避免额外的rountrips:LOB只是像RAW一样处理 或VARCHAR2。我们在LOB密集型应用程序中大量使用它。
我在加载一个blob列(14KB =&gt;数千行)的简单表(几GB)时出现问题,我正在调查它很长一段时间,尝试了很多lob存储调整(DB_BLOCK_SIZE用于新的表空间, lob存储规范 - CHUNK),sqlnet.ora设置,客户端预取属性,但是这个(在客户端使用OCCI ResultSet-&gt; setBufferData将BLOB视为LONG RAW)是最重要的事情(说服oracle立即发送blob列而不发送首先是吊耳定位器,然后根据吊环定位器分别加载每个吊钩。
现在我可以获得~500Mb / s的吞吐量(列数<3964B)。 我们的14KB blob将被分成多个列 - 因此它将被存储在行中以从HDD获得几乎顺序的读取。使用一个14KB的blob(一列),由于非顺序读取(iostat:少量的合并读取请求),我得到~150Mbit / s。
注意:不要忘记设置lob预取大小/长度:
err = OCIAttrSet(session,(ub4)OCI_HTYPE_SESSION,(void *)&amp; default_lobprefetch_size,0,(ub4) OCI_ATTR_DEFAULT_LOBPREFETCH_SIZE ,errhp);
但我不知道如何使用ODBC连接器实现相同的获取吞吐量。我没有成功地尝试它。