Dion Cho – Oracle Performance Storyteller

We are natural born scientists

Posts Tagged ‘hard parse

Making SQL always hard parsed – Using row level security

leave a comment »

I hit a very interesting OTN Forum discussion. To make the long story short, the request was as following

How can I make the SQL always hard parsed even when bind variables are being used? (Changing the code is not an option)

Not common in OLTP, right? But on DSS environment, this could be very common and even useful!

As far as I remember, MSSQL Server has the functionality not to load the SQL onto shared memory. Very cool feature.

But Oracle? No, it’s impossbile.

The discussion got many suggestions. Let’s have a look at them.

1. Adaptive cursor sharing will save us.

Cool. It’s 11g new feature. Love it. But this is 10g database.

2. SQL Tuning advisor(SQL profile) or stored outline

Not relevant.

3. Get rid of statistics and sample dynamically

But dynamic sampling works only on optimization time. Dynamic sampling itself does not make SQL always optimized.

4. Change code to use literal value

Not an option.

5. Invalidate the cursor by updating statistics.
Not bad. By updating statistics like this,

exec dbms_stats.set_table_stats('schema_name', 'table_name', -
num_rows=>null, no_invalidate=>false);

We can invalidate all the cursors dependent on the table without the danger of changing statistics.  But this is also not an option because we can’t change the software code.

(This was my suggestion, actually)


6. Apply row-level security model to generate random predicate.

Very clever idea by this guy. Clever, Randolf! Maybe the only way to meet the request.

Do not change code, but make my SQL always hard parsed!

It works like this:

create table t1
as
select * from all_objects
where rownum <= 1000;

exec dbms_stats.gather_table_stats(user, 't1');

drop sequence s1;

create sequence s1 cache 1000;

create or replace package pkg_rls_hard_parse is
  function force_hard_parse (in_schema varchar2, in_object varchar2)
      return varchar2;
end pkg_rls_hard_parse;
/ 

create or replace package body pkg_rls_hard_parse is
  function force_hard_parse (in_schema varchar2, in_object varchar2)
      return varchar2
  is
    s_predicate varchar2(100);
    n_random pls_integer;
  begin
    select s1.nextval || ' = ' || s1.currval into s_predicate from dual;
    return s_predicate;
  end force_hard_parse;
end pkg_rls_hard_parse;
/ 

exec dbms_rls.add_policy (user, 't1', 'hard_parse_policy', user, -
        'pkg_rls_hard_parse.force_hard_parse', 'select');  <-- Clever!

alter system flush shared_pool;

exec dbms_monitor.session_trace_enable;

-- Execute the same query 10 times
select /* placeholder */ count(*) from t1;
select /* placeholder */ count(*) from t1;
select /* placeholder */ count(*) from t1;
select /* placeholder */ count(*) from t1;
select /* placeholder */ count(*) from t1;
select /* placeholder */ count(*) from t1;
select /* placeholder */ count(*) from t1;
select /* placeholder */ count(*) from t1;
select /* placeholder */ count(*) from t1;
select /* placeholder */ count(*) from t1;

exec dbms_monitor.session_trace_disable;

The result of tkprof is like following:

select /* placeholder */ count(*)
from
 t1

call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse       10      0.00       0.02          0          0          0           0
Execute     10      0.01       0.01          0          0          0           0
Fetch       20      0.07       0.06          0        150          0          10
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total       40      0.09       0.10          0        150          0          10

Misses in library cache during parse: 10   <-- Look here!
Misses in library cache during execute: 10

Cool!

One of the problems pointed out was that this row level security feature has some overhead. For instance, have a look at this:

SELECT S1.NEXTVAL || ' = ' || S1.CURRVAL
FROM
 DUAL

call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse        1      0.00       0.00          0          0          0           0
Execute     30      0.00       0.00          0          0          0           0
Fetch       30      0.00       0.00          0          0          1          30
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total       61      0.00       0.01          0          0          1          30
begin :con := PKG_RLS_HARD_PARSE.FORCE_HARD_PARSE(:sn, :on); end;

call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse       30      0.00       0.01          0          0          0           0
Execute     30      0.01       0.01          0          0          0          30
Fetch        0      0.00       0.00          0          0          0           0
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total       60      0.01       0.03          0          0          0          30

It looks like Oracle calls the policy function multiple times to apply row the level security policy. Not sure why, but there would be some overhead especially when the original queries are very frequently called.

Another problem to note is that it would flood the shared pool. We are generating tons of unshareable SQLs! But this is very heavy DDS system that parse overhead wouldn’t be a big deal.

This kind of clever tricks really makes me happy. :)

Written by Dion Cho

February 6, 2009 at 5:47 am

Posted in Misc.

Tagged with ,

Follow

Get every new post delivered to your Inbox.

Join 61 other followers