Friday, April 15, 2016

CURSOR_SHARING



Back a thousand years ago (in Oracle time) there weren’t bind variables in SQL.  Everything was a literal.  We had substitution parameters in SQLPlus, but to the optimizer it was still a literal because the substitution happened in SQLPlus before the SQL was sent to the Optimizer.  Along comes V7 and binds come into the mix, and this is very good.  Now we can have one SQL with one cursor in the shared pool (which also was new in V7) and run it with multiple values.  (By the way, substitution parameters still exist, and are cool for scripts, these are the “&” things you might see some times.)

But there was a major problem.  There were likely millions of SQL statements out in the world that should be using binds that weren’t.  Without rewriting everything how could we take advantage of this great new feature?

To combat this problem, Oracle introduced CURSOR_SHARING.  It was (and still is) the lazy programmer’s way to get binds into the SQL.  In the beginning it was the only practical way to take advantage of binds.  But it was always just a “Band-Aid”, not really the best way to run your system.

It had three values: EXACT, FORCE and SIMILAR.  The default is EXACT and that is what you should be using.  This means only shared cursors that match the SQL text “exactly”.  Different bind values are fine, but even the names of the binds have to be exactly the same.   FORCE does what it sounds like it “forces” binds into a SQL statement for every literal it finds.  This will make statements that only differ in the literals used be sharable.  The setting of SIMILAR was supposed to make intelligent choices about binds.  It worked like FORCE, but would come up with different named binds if it thought that there might be different plans.  This option never worked well for the most part. Too often it created lots of different version of the SQL which were not helpful and tending to make things worse not better for sharing of statements.

Starting in 11.2.0.3 SIMILAR is now desupported.  You can still set CURSOR_SHARING to SIMILAR, but it now works the same as FORCE.  This came about because of Adaptive Cursor Sharing which is much better at working with different bind values for the same statement.  Not perfect either but certainly much better and more robust.

Here is a little demo.  The demo just creates a simple table, with no data:
CREATE TABLE rvd99 (x NUMBER, y NUMBER);

Then I have a set of ten queries that look like this:
select count(y) from rvd99 where x = 1;

But each one has a different value for the predicate.  The values are 1, 10, 20, 30, 40, 50, 60, 70, 80 and 99.

I’ll use a select from v$sql_bind_capture and v$sql to see the SQL statements generated for setting CURSOR_SHARING to the values of FORCE and SIMILAR.
Prior to 11.2.0.3 when running this set of queries with FORCE you would see this:

SQL> SELECT s.sql_text, b.value_string, s.plan_hash_value, s.child_number
  2  FROM v$sql_bind_capture b, v$sql s
  3  WHERE s.hash_value = b.hash_value
  4  AND s.address = b.address
  5  AND s.child_number = b.child_number
  6  AND s.sql_text LIKE 'select count(y) from rvd99 %';

SQL_TEXT                                           VALUE PLAN_HASH_VALUE CHILD_NUMBER
-------------------------------------------------- ----- --------------- ------------
select count(y) from rvd99 where x = :"SYS_B_0"    1          4162104232            0

This seems reasonable, just put in the value at run time.  Only one cursor was created for all 10 runs.  But for SIMILAR the output looks like this:

SQL_TEXT                                           VALUE PLAN_HASH_VALUE CHILD_NUMBER
-------------------------------------------------- ----- --------------- ------------
select count(y) from rvd99 where x = :"SYS_B_0"    1          4162104232            0
select count(y) from rvd99 where x = :"SYS_B_0"    1          4162104232            1
select count(y) from rvd99 where x = :"SYS_B_0"    10         4162104232            2
select count(y) from rvd99 where x = :"SYS_B_0"    20         4162104232            3
select count(y) from rvd99 where x = :"SYS_B_0"    30         4162104232            4
select count(y) from rvd99 where x = :"SYS_B_0"    40         4162104232            5
select count(y) from rvd99 where x = :"SYS_B_0"    50         4162104232            6
select count(y) from rvd99 where x = :"SYS_B_0"    60         4162104232            7
select count(y) from rvd99 where x = :"SYS_B_0"    70         4162104232            8
select count(y) from rvd99 where x = :"SYS_B_0"    80         4162104232            9
select count(y) from rvd99 where x = :"SYS_B_0"    99         4162104232           10

Notice how similar ended up creating a new child for each value, which is pretty much what we were hoping to avoid with CURSOR_SHARING.  After 11.2.0.3 you get the same thing for FORCE and SIMILAR which is just the one plan:

SQL_TEXT                                           VALUE PLAN_HASH_VALUE CHILD_NUMBER
-------------------------------------------------- ----- --------------- ------------
select count(y) from rvd99 where x = :"SYS_B_0"    1          4162104232            0

So what does all this mean?  Two things that I can think of, use EXACT unless you have code you can’t change, then use FORCE as a temporary fix until you can get binds in the code where they should be.  And two, let Adaptive Cursor Sharing do what SIMILAR was supposed to do and never did very well, that is have different plans for different sets of binds.

No comments:

Post a Comment