Sei sulla pagina 1di 6

Database

HINTS

ON

HINTING
Jonathan Lewis, JL Computer Consultancy

THE

PROBLEM WITH HINTS

Hints give us the opportunity to direct the optimizer away from the path it would take by default, and there are several kinds of hints to help us. Some hints are strategic, some are for feature control, some are for micro management, some are used to adjust the optimizers arithmetic and some control run-time behavior. The trouble with hints, though, is that they are badly documented and very few people really know how to use them well. Consequently they are badly understood and it is very easy to make mistakes while hinting. Because of this, I generally advise people to avoid hinting in production systems and, if they do decide that hints are necessary, to stick to just the feature and strategic hints. In this document, though, I want to give you some ideas about how hints really work, what some of the more popular ones mean, and how to minimize the risk of using them.

ARE

HINTS HINTS

The name hint is a bad one. An Oracle hint is an order to the optimizer (or, just occasionally, to the run-time engine) that must be obeyed if it is legal. There are reasons, though, why Oracle may appear to ignore hints. When using a hint you may have made a simple spelling or syntax error (and the correct syntax can change with Oracle version), you could be using the hint in a context where it is meaningless, you may have built several contradictory hints into a statement with the result that Oracle ignores all the hints which are not self-consistent and, finally, there are some Oracle bugs which result in hints disappearing. But if you can get your hints right, Oracle must obey them. One of the more interesting context reasons for Oracle ignoring your hints is the use of the parameter (new to 10g):
alter session set _optimizer_ignore_hints = true;

(You could also do this at the system level or in the parameter file). If you have a lot of time for testing when you upgrade Oracle versions, heres an interesting exercise to consider: how about doing a test run with all hints disabled to find out which statements no longer need hinting and then remove the redundant hints from the code. (I believe that hints in sys-recursive SQL are not ignored when this parameter is set to true, and stored outlines and SQL profiles will still work. but that belief is based on just a couple of quick tests.) But if hints are really orders that cannot be ignored, why do some many people think that Oracle can ignore them? The answer is that we dont have the information we need to understand exactly what even the simplest hints mean. Consider, for example, the simple parallel hint. I have a table which is list-partitioned on a column called pt_group, with a local index on columns (n1, n2). I put a parallel hint into a particular query, and the optimizer appears to ignore it:
select /*+ parallel(t1,2) */ {list of columns} from t1
1 Session #239

Database

where and and ; t1.n1 = 5 t1.n2 = 10 t1.pt_group in (0,10)

. |Id|Operation |Name |Cost |Pstart| Pstop| | 0|SELECT STATEMENT | | 48| | | | 1| PARTITION LIST INLIST | | 48|KEY(I)|KEY(I)| |*2| TABLE ACCESS BY LOCAL INDEX ROWID|T1 | 48|KEY(I)|KEY(I)| |*3| INDEX RANGE SCAN |T1_I1| 3|KEY(I)|KEY(I)|

Has the optimizer ignored my hint? The answer is no, and we could prove that by examining the 10053 trace file. However, its rarely necessary to turn to the 10053 trace file, when a couple of simple tests will show us whats going on. Notice that the query is expected to run serially using an inlist iterator to access some partitions through the local index take a quick look at the cost (48) of this access path, and then add a hint to the query to block the use of any indexes on the table:
select /*+ full(t1) parallel(t1,2) */ {list of columns} from t1 where and and ; t1.n1 = 5 t1.n2 = 10 t1.pt_group in (0,10) . |Pstart|Pstop | TQ |IN-OUT|PQ Distrib| | | | | | | | | | | | | | | |Q1,00| P->S |QC (RAND) | |KEY(I)|KEY(I)|Q1,00| PCWC | | |KEY(I)|KEY(I)|Q1,00| PCWP | |

| Id| Operation |Name |Cost | 0| SELECT STATEMENT | | 94 | 1| PX COORDINATOR | | | 2| PX SEND QC (RANDOM)|:TQ10| 94 | 3| PX BLOCK ITERATOR | | 94 |* 4| TABLE ACCESS FULL|T1 | 94

With the addition of the full(t1) hint we get a full tablescan (after all, Oracle has to obey the hint), and the tablescan should run as a parallel tablescan. But notice that the cost has gone up to 94. Now lets take out the full(t1) hint, and change the hinted degree of parallelism from two to four:
select /*+ parallel(t1,4) */ {list of columns} from t1 where and and ; t1.n1 = 5 t1.n2 = 10 t1.pt_group in (0,10) |Name |Cost |Pstart|Pstop |
2

| Id| Operation

. TQ |IN-OUT|PQ Distrib|
Session #239

Database

| | | | |*

0| SELECT STATEMENT | | 1| PX COORDINATOR | | 2| PX SEND QC (RANDOM)|:TQ10| 3| PX BLOCK ITERATOR | | 4| TABLE ACCESS FULL|T1 |

47 | | | | | | | | | | | | 47 | | |Q1,00| P->S |QC (RAND) | 47 |KEY(I)|KEY(I)|Q1,00| PCWC | | 47 |KEY(I)|KEY(I)|Q1,00| PCWP | |

Again, we see a tablescan appearing unhinted that Oracle expects to run in parallel. Notice, though, that the cost of this path is now 47 just a little bit less than the cost of the serial index access path. The hint parallel(t1, N) does not tell Oracle to run the query in parallel at degree N it tells the optimizer that when it is calculating the cost of a tablescan on table t1 it should cost for parallel degree N, and if that plan then happens to be the cheapest of all the plans to consider that thats the plan to use. In this example, the cost of a tablescan at degree two is 94, which is more than the cost of the indexed access path at 48 which is why Oracle will only take the tablescan if we force the tablescan with the full hint. But when we indicate a degree of four the cost of the tablescan at degree four is slightly less than the cost of the serial index path, so Oracle takes it without needing to be forced into the tablescan. The parallel hint tells Oracle to change the arithmetic it doesnt force Oracle to run parallel and it isnt supposed to force Oracle to run parallel.

FEATURE

HINTS

In 10g, we see more and more hints that I call the feature hints. As the Oracle developers and programmers add a new feature to the optimizers list of tricks they often seem to add a pair of matching hints to go with the feature. You may have seen the new hash aggregation that appeared in 10.2 to handle variants of the group by clause. In general its a good idea, but you could be unlucky and find a few cases where it manages to introduce a performance problem. So, to go with the new feature, there are two new hints, one to force it to happen, and one to stop it happening: /*+ use_hash_aggregation */, and /*+ no_use_hash_aggregation */ respectively. Its worth noting that in 11g theres a new view called v$sql_hint which lists all the hints, the version they appeared in, and the version where they were firsts coded into stored outlines. If you see some strange new behavior in a query, its worth checking this view to see if you can discover any hints that look as if they might be related to the feature.

STRATEGY

HINTS

There are a few hints that allow you to dictate the overall strategy that you want Oracle to take. I call these the strategy hints, and the small number of hints I put into this list usually come in pairs: unnest / no_unnest push_subq / no_push_subq merge / no_merge push_pred / no_push_pred driving_site

These are the hints I am happy to use on production systems, as they allow me to impose a high-level shape on the way Oracle optimises a query without requiring me to micro-manage every single detail of the query plan. Along with these hints, I also include the qb_name hint which appeared in 10g to allow you to give names to query blocks.
3 Session #239

Database

These hints have the following effects Unnest /no_unnest: determine whether or not a subquery should operate as a subquery in the final transformed query (no_unnest), or whether it should be transformed into an inline view (unnest). Push_subq / no_push_subq: if you have subqueries that are not going to be unnested, should they run as the very last steps of the query (no_push_subq the traditional behaviour), or should they run at the first possible moment (push_subq). Merge / no_merge: if you use a complex view (e.g. aggregate view, or join view) in your query, should you rewrite the query to merge the tables in the view into a single from clause with all the other tables (merge), or should you evaluate the view to produce a standalone result set and then join the result set to the remaining tables (no_merge). Push_pred / no_push_pred: If you have a non-mergeable view (possible because of a no_merge hint) in your query, how should you operate the join from other tables; should you create one large view result and join it once (no_push_pred) or should you push the join predicate down into the view definition and recreate the view result set for every driving row from another table (push_pred). Driving_site: When running distributed queries, one of the factors affecting performance is the network traffic. The optimizer does not consider network costs (despite various notes and documents that suggest otherwise) and will always operate a query from the local database by default. There are cases where it is more efficient to run the query at a remote site and then pull the result set back to the local site. The driving_site hint allow use to specify where the query runs.

In most cases, it is possible to use only these hints to produce a stable and efficient execution plan; although I will also use the parallel() and parallel_index() hints to force queries into parallel execution. But, as I mentioned above, there is one other hint that I use with the strategy hints; the qb_name() hint that labels query blocks. Consider the following:
select /*+ qb_name(main) unnest(@subq2) no_unnest(@subq4) no_push_subq(@subq4) */ {columns} from t1, t3 where and t1.n2 = 15 exists ( select /*+ qb_name(subq2) */ null from t2 where t2.n1 = 15 and t2.id = t1.id and and and ) t3.n1 = t1.n1 t3.n2 = 15 exists ( select
4 Session #239

Database

/*+ qb_name(subq4) */ null from t4 where t4.n1 = 15 and t4.id = t3.id ) ;

Note that I have three query blocks the main query, and two subqueries. I have added a hint to each query block (and you can use this hint with insert, update, and so on, its not just queries) to give each query block a name; so I have named the main query block main, the subquery against table t2 has the name subq2, the subquery against table t4 has the name subq4. Once the subqueries are named, I can start using fully-qualified hints which means I specify the query block that a hint applies to as the first parameter to the hint; so, in the opening set of hints, I have an unnest() hint, but this applies to the query block called subq2; the no_unnest() hint applies to query block subq4, and having blocked unnesting for subq4 Ive also used the no_push_subq() hint referencing subq4 in that hint to tell Oracle not to run it early.

MICRO-MANAGEMENT
In general the strategy hints (with the parallel and naming hints) are sufficient to solve most problems of execution plans. However there are a lot of hints that you can use to micro-manage execution plans. I avoid these wherever possible as it can be very difficult to produce stable plans if you have to specify every detail. Consider this fragment of a plan:
... NESTED LOOP TABLE ACCESS (FULL) OF T1 TABLE ACCESS BY INDEX ROWID T2 INDEX RANGE SCAN T2_IND_SECOND ...

-- wrong index

While trying to improve the performance of the underlying query, we notice that the optimizer is choosing the wrong index at this point in the plan, and decide it should be using an index called t2_ind_first. So we add an index hint to the code:
select /*+ index(t2 t2_ind_first) */

(Note that this is the pre-10g form for an index hint. If youre going to use index hints, you should get into the habit of specifying them by using the index description (list of columns) in the hint, not the index name.) Unfortunately, this works correctly for a while and then one day a change in the data and statistics makes Oracle choose the following path (which obeys our hint, but doesnt do what we wanted): ...
HASH JOIN TABLE ACCESS (FULL) OF T1 TABLE ACCESS BY INDEX ROWID T2 INDEX FULL SCAN T2_IND_FIRST ... -- wrong join method

-- right index, wrong access method.

This is the problem of micro-management you have to do it 100% completely and correctly and very few people know enough about hints to do this properly.

Session #239

Database

CONCLUSION
Hints are badly documented, and not well understood. There are some hints that can be used fairly safely to produce reasonably stable execution plans. There are many hints, though, that are commonly used and abused without a full appreciation of the potential instability they introduce to a system. If you are going to use hints, you want to try to stick with the feature hints and the strategy hints and avoid the micro-management hints.

Session #239

Potrebbero piacerti anche