views:

2918

answers:

6

Hi all,

Hopefully someone can shed a little light on an issue that I'm currently having with an Oracle DB - I'm sure it's something simple!!

I've managed to recreate this in a sample, so here is the DB structure:

CREATE TABLE MyTable(
    ixMyTable NUMBER,
    clobData CLOB
)
/

CREATE OR REPLACE PACKAGE PKGTEST
AS
    PROCEDURE DoSomething(
     cur_OUT OUT SYS_REFCURSOR
     );
END PKGTEST;
/

CREATE OR REPLACE PACKAGE BODY PKGTEST
AS

PROCEDURE DoSomething(
    cur_OUT OUT SYS_REFCURSOR
)
AS
BEGIN
    OPEN cur_OUT FOR 
     SELECT ixMyTable, clobData
     FROM MyTable;
END;

END PKGTEST;
/

GRANT EXECUTE ON PKGTEST TO TEST_ROLE
/

BEGIN
    FOR i IN 1 .. 7000 LOOP
     insert into mytable values (i, TO_CLOB('123456'));
    END LOOP;
END;
/

Extra info:

Schema owner is TEST_SCHEMA

User is CARL

CARL has the TEST_ROLE role

Given the database setup as above, I have a C# test app that uses the standard System.Data.OracleClient.OracleCommand etc. in order to execute PKGTEST.DoSomething and throw the results into a datagrid (DevExpress).

Pretty sure that the grid is irrelevant here, as we experience the same issue through c++ using the open source OTL (not my department, fortunately).

OK, to the problem....

Time from beginning until the grid is populated is ~35-40s, ouch.

However, if I do the following:

GRANT SELECT ON MyTable TO TEST_ROLE
/

and then perform the query again, it takes ~5-6s.

It seems to me that this has something to do with privileges etc., but I'm not quite sure why it still actually works both ways??

Just to throw something else into the pot, if I change the procedure to

SELECT ixMyTable, TO_CLOB(TO_NCLOB(clobData))
FROM MyTable;

Then the time is ~5-6s, with or without the extra SELECT privilege.

Any pointers or straight out solutions would be much appreciated!

Edit:

OS is Vista x86 Business

Oracle Server is 10.2.0.1

Oracle Client is 10.2.0.3

Edit:

As suggested, I've tried changing from the MS OracleClient to the ODP.NET and this does speed up as required.

Unfortunately, the C# app that was being affected was just an internal app that is used for viewing tables / running SPROCS etc.

Our main deliverable is the C++ app that uses OTL (http://otl.sourceforge.net/otl3_intro.htm) for database access. This is not really something that can be changed at this time, and so I would really like to understand the reasons for the difference, without having to throw gratuitous GRANT SELECTs around willy-nilly.

If lack of SELECT privilege caused a complete failure then I could probably live with this, but lack of SELECT appears to be causing some slower route for accessing the CLOB data.

I've marked up the 3 answers - thanks for those - but I could really do with a reason, so I'll add a bounty to this.

P.S. We really wanted to go with OCCI at the outset for our C++, but as Oracle are always supporting a version of the IDE before current we couldn't get it to play nicely with our Visual Studio 2008.

+5  A: 

Are you sure that you are reading the blob from disk each time, and not reading if from disk cache the second and following?

I've seen this problem with performance testing, particularly on Oracle, where the first run of a test is terrible. Then with one minor (and seeming insignificant change) the performance suddenly improves significantly. But really what has happened is that the data you are querying has been loaded into the cache and can be access at 10x or 20x the rate (memory vs. disk).

The proper way to do this test is to bounce the database between query runs. Load a copy of Oracle XE on your machine if the DBA won't let you bounce the test-production server for this test.

Edit: Or better: drop and re-create the table every time. You may be doing this, but didn't mention it.

Thomas Jones-Low
+1; good advice on the cache.
DCookie
Yeah, we've had cache issues before when we've been performance testing, but this is a consistent speed issue. Even the very first call using the TO_CLOB(TO_NCLOB()) technique is fast, and I would not expect this to hit the cache on the first run through. Thanks.
Carl
+1  A: 

I would try odp.net http://www.oracle.com/technology/tech/windows/odpnet/index.html instead of System.Data.OracleClient.

tuinstoel
+1  A: 

Following up on the suggestion above, can you try a different ODBC driver, or client software. The fact that the TO_CLOB(TO_NCLOB()) works so faster, where the straight one doesn't seems to indicate that that is where the problem exists.

First I would take the two queries and run them through SQLDeveloper and get an explain plan. This should give you a baseline for execution on the Oracle side. Adding the conversions should make no difference on the execution path. Run the queries alternately and time both to see how fast they are. If there is no difference, I'd suggest that the client software is your problem.

If my assumption is correct, this would also explain the GRANT SELECT change. The client software is doing some sort of expensive conversion on the CLOB. The grant and/or explicit conversion allows the client to avoid this. I don't know why.

Thomas Jones-Low
A: 

I honestly think the problem here is that the OTL driver and the OracleClient are having problems dealing with the CLOB coming from the SYS_REFCURSOR. SYS_REFCURSORs are weak, meaning the cursor itself can return any type of data, meaning the drivers need to continually go back to database and query the metadata for the cursor (ie. get the types of data in the cursor), then load the CLOB stream, then load the data. Obviously if the user doing the query has access to the tables being queried, the driver can do a better job of retrieving the metadata and returning the correct data.

Does changing the Stored Procedure to return a strong ref_cursor work?

CREATE OR REPLACE PACKAGE PKGTEST
    AS
    TYPE C_DoSomething IS REF CURSOR RETURN MyTable%ROWTYPE;
    PROCEDURE DoSomething(
        cur_OUT OUT c_DoSomething
     );
END PKGTEST;

Can you change the Stored Procedure to do the TO_CLOB(), since that also seems to work?

Thomas Jones-Low
Returning a strong cursor didn't make a difference, unfortunately. It would be possible to change the stored procedure, but TO_CLOB() on it's own does not work - it has to be TO_CLOB(TO_NCLOB()). I'm not sure what those calls actually do, that is, how expensive they are?
Carl
THREE Answers! That's wrong. -1 on them all.
Expense of the TO_CLOB(TO_NCLOB()) depends upon the character set of your database. What are the NLS_CHARACTERSET and NLS_NCHAR_CHARACTERSET values from the following query?select * from nls_database_parameters;
Thomas Jones-Low
A: 

What is the NLS_CHARACTERSET and NLS_NCHAR_CHARACTERSET for your database. Run

select * from nls_database_parameters;

to get the results. And what is the NLS_LANG setting for your client software?

This may give some more insight to the problem, and will help answer the question of how expensive the TO_CLOB(TO_NCLOB()) call is.

Thomas Jones-Low
The NLS_CHARACTERSET is AL32UTF8 and the NLS_NCHAR_CHARACTERSET is AL16UTF16 - I believe that these are the defaults for an install on a Win-based server. I'm not even sure if/how you can change the NLS_LANG for the ADO.NET stuff
Carl
The to_NCLOB() converts to the UTF-16 character set, then the TO_CLOB() converts it back to the UTF-8 character set. So the calls are not going to have a huge performance impact. I still don't have a great reason why, but if it works...
Thomas Jones-Low
A: 

These aren't all necessarily directly linked, but you probably ought to check each one out in case it is related. I suspect that this is caching related: once you've done your first query you then apply select and then it goes fast. In the middle you have to bounce the server to get rid of the caches if you want to performance test it correctly. If you do this test and it suddenly performs better, then try pinning the table into the cache. See below around clob storage inline, since that will probably be related.

I had issues around clob performance in Oracle 10g a year or so back. We got around most of them once we got our awesome dba to help. It took about 2 months to get the performance up to an adequate speed.

Which version of Oracle are you using? In Oracle 10g (early versions) there were massive issues with clob performance. In fact it was actually faster in some cases to just use two tables, and a varchar column (concatenate the varchars together and you have your clob). We upgraded to a later version, and it was much better

Also where is your data being stored? There's also an option to store the clob in the table itself. Depending how big your data is you might find this helps performance. If you've got your data stored on a SAN then it's worth also looking at the cache size on the SAN and also the block sizes. Oracle + SAN can be a bit funny when the caching sizes are incorrect.

Another workaround: If you're finding persistence is slow or even access is slow and you're not CPU bound, zip the data and store it in a blob. We saw a big performance benefit here too.

If you're seeing performance issues (memory related?) in whatever is processing the clobs, we found that we would recreate the objects as new strings. The drivers were pre-creating 32K sized Strings even if the data was smaller.

I did wonder whether the system tables might be fragmented? Are there lots of tables/schemas? What about synonyms?

Also, when you store clobs, don't they get stored into one massive file in Oracle? If I remember correctly you have to be careful about fragmentation; the storage doesn't get released for reuse.

Perhaps you could put a .NET web service in front of your db? That might be one option if you can't solve the performance issues.

Egwor