Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Скачиваний:
50
Добавлен:
16.04.2013
Размер:
5.97 Mб
Скачать

Guidelines for Avoiding PL/SQL Performance Problems

Programs that do a lot of mathematical calculations. You will want to investigate the datatypes PLS_INTEGER, BINARY_FLOAT, and BINARY_DOUBLE.

Functions that are called from PL/SQL queries, where the functions might be executed millions of times. You will want to look at all performance features to make the function as efficient as possible, and perhaps a function-based index to precompute the results for each row and save on query time.

Programs that spend a lot of time processing INSERT, UPDATE, or DELETE statements, or looping through query results. You will want to investigate the FORALL statement for issuing DML, and the BULK COLLECT INTO and

RETURNING BULK COLLECT INTO clauses for queries.

Older code that does not take advantage of recent PL/SQL language features. (With the many performance improvements in Oracle Database 10g, any code from earlier releases is a candidate for tuning.)

Any program that spends a lot of time doing PL/SQL processing, as opposed to issuing DDL statements like CREATE TABLE that are just passed directly to SQL. You will want to investigate native compilation. Because many built-in database features use PL/SQL, you can apply this tuning feature to an entire database to improve performance in many areas, not just your own code.

Before starting any tuning effort, benchmark the current system and measure how long particular subprograms take. PL/SQL in Oracle Database 10g includes many automatic optimizations, so you might see performance improvements without doing any tuning.

Guidelines for Avoiding PL/SQL Performance Problems

When a PL/SQL-based application performs poorly, it is often due to badly written SQL statements, poor programming practices, inattention to PL/SQL basics, or misuse of shared memory.

Avoiding CPU Overhead in PL/SQL Code

Make SQL Statements as Efficient as Possible

PL/SQL programs look relatively simple because most of the work is done by SQL statements. Slow SQL statements are the main reason for slow execution.

If SQL statements are slowing down your program:

Make sure you have appropriate indexes. There are different kinds of indexes for different situations. Your index strategy might be different depending on the sizes of various tables in a query, the distribution of data in each query, and the columns used in the WHERE clauses.

Make sure you have up-to-date statistics on all the tables, using the subprograms in the DBMS_STATS package.

Analyze the execution plans and performance of the SQL statements, using:

EXPLAIN PLAN statement

SQL Trace facility with TKPROF utility

Oracle Trace facility

Rewrite the SQL statements if necessary. For example, query hints can avoid problems such as unnecessary full-table scans.

11-2 PL/SQL User's Guide and Reference

Guidelines for Avoiding PL/SQL Performance Problems

For more information about these methods, see Oracle Database Performance Tuning Guide.

Some PL/SQL features also help improve the performance of SQL statements:

If you are running SQL statements inside a PL/SQL loop, look at the FORALL statement as a way to replace loops of INSERT, UPDATE, and DELETE statements.

If you are looping through the result set of a query, look at the BULK COLLECT clause of the SELECT INTO statement as a way to bring the entire result set into memory in a single operation.

Make Function Calls as Efficient as Possible

Badly written subprograms (for example, a slow sort or search function) can harm performance. Avoid unnecessary calls to subprograms, and optimize their code:

If a function is called within a SQL query, you can cache the function value for each row by creating a function-based index on the table in the query. The CREATE INDEX statement might take a while, but queries can be much faster.

If a column is passed to a function within an SQL query, the query cannot use regular indexes on that column, and the function might be called for every row in a (potentially very large) table. Consider nesting the query so that the inner query filters the results to a small number of rows, and the outer query calls the function only a few times:

BEGIN

--Inefficient, calls my_function for every row.

FOR item IN (SELECT DISTINCT(SQRT(department_id)) col_alias FROM employees) LOOP

dbms_output.put_line(item.col_alias); END LOOP;

--Efficient, only calls function once for each distinct value. FOR item IN

( SELECT SQRT(department_id) col_alias FROM

( SELECT DISTINCT department_id FROM employees)

)

LOOP

dbms_output.put_line(item.col_alias); END LOOP;

END;

/

If you use OUT or IN OUT parameters, PL/SQL adds some performance overhead to ensure correct behavior in case of exceptions (assigning a value to the OUT parameter, then exiting the subprogram because of an unhandled exception, so that the OUT parameter keeps its original value).

If your program does not depend on OUT parameters keeping their values in such situations, you can add the NOCOPY keyword to the parameter declarations, so the parameters are declared OUT NOCOPY or IN OUT NOCOPY.

This technique can give significant speedup if you are passing back large amounts of data in OUT parameters, such as collections, big VARCHAR2 values, or LOBs.

This technique also applies to member subprograms of object types. If these subprograms modify attributes of the object type, all the attributes are copied when the subprogram ends. To avoid this overhead, you can explicitly declare the first parameter of the member subprogram as SELF IN OUT NOCOPY, instead of relying on PL/SQL's implicit declaration SELF IN OUT.

Tuning PL/SQL Applications for Performance 11-3

Guidelines for Avoiding PL/SQL Performance Problems

Make Loops as Efficient as Possible

Because PL/SQL applications are often built around loops, it is important to optimize the loop itself and the code inside the loop:

Move initializations or computations outside the loop if possible.

To issue a series of DML statements, replace loop constructs with FORALL statements.

To loop through a result set and store the values, use the BULK COLLECT clause on the query to bring the query results into memory in one operation.

If you have to loop through a result set more than once, or issue other queries as you loop through a result set, you can probably enhance the original query to give you exactly the results you want. Some query operators to explore include UNION,

INTERSECT, MINUS, and CONNECT BY.

You can also nest one query inside another (known as a subselect) to do the filtering and sorting in multiple stages. For example, instead of calling a PL/SQL function in the inner WHERE clause (which might call the function once for each row of the table), you can filter the result set to a small set of rows in the inner query, and call the function in the outer query.

Don't Duplicate Built-in String Functions

PL/SQL provides many highly optimized string functions such as REPLACE, TRANSLATE, SUBSTR, INSTR, RPAD, and LTRIM. The built-in functions use low-level code that is more efficient than regular PL/SQL.

If you use PL/SQL string functions to search for regular expressions, consider using the built-in regular expression functions, such as REGEXP_SUBSTR.

Reorder Conditional Tests to Put the Least Expensive First

PL/SQL stops evaluating a logical expression as soon as the result can be determined (known as short-circuit evaluation).

When evaluating multiple conditions separated by AND or OR, put the least expensive ones first. For example, check the values of PL/SQL variables before testing function return values, because PL/SQL might be able to skip calling the functions.

Minimize Datatype Conversions

At run time, PL/SQL converts between different datatypes automatically. For example, assigning a PLS_INTEGER variable to a NUMBER variable results in a conversion because their internal representations are different.

Avoiding implicit conversions can improve performance. Use literals of the appropriate types: character literals in character expressions, decimal numbers in number expressions, and so on.

In the example below, the integer literal 15 must be converted to an Oracle NUMBER before the addition. The floating-point literal 15.0 is represented as a NUMBER, avoiding the need for a conversion.

DECLARE

n NUMBER; c CHAR(5);

BEGIN

n := n + 15; -- converted implicitly; slow n := n + 15.0; -- not converted; fast

c := 25; -- converted implicitly; slow

11-4 PL/SQL User's Guide and Reference

Guidelines for Avoiding PL/SQL Performance Problems

c := TO_CHAR(25); -- converted explicitly; still slow

c := '25';

-- not converted; fast

END;

 

/

 

Minimizing conversions might mean changing the types of your variables, or even working backward and designing your tables with different datatypes. Or, you might convert data once (such as from an INTEGER column to a PLS_INTEGER variable) and use the PL/SQL type consistently after that.

Use PLS_INTEGER or BINARY_INTEGER for Integer Arithmetic

When you need to declare a local integer variable, use the datatype PLS_INTEGER, which is the most efficient integer type. PLS_INTEGER values require less storage than INTEGER or NUMBER values, and PLS_INTEGER operations use machine arithmetic.

The BINARY_INTEGER datatype is just as efficient as PLS_INTEGER for any new code, but if you are running the same code on Oracle9i or Oracle8i databases, PLS_INTEGER is faster.

The datatype NUMBER and its subtypes are represented in a special internal format, designed for portability and arbitrary scale and precision, not performance. Even the subtype INTEGER is treated as a floating-point number with nothing after the decimal point. Operations on NUMBER or INTEGER variables require calls to library routines.

Avoid constrained subtypes such as INTEGER, NATURAL, NATURALN, POSITIVE, POSITIVEN, and SIGNTYPE in performance-critical code. Variables of these types require extra checking at run time, each time they are used in a calculation.

Use BINARY_FLOAT and BINARY_DOUBLE for Floating-Point Arithmetic

The datatype NUMBER and its subtypes are represented in a special internal format, designed for portability and arbitrary scale and precision, not performance. Operations on NUMBER or INTEGER variables require calls to library routines.

The BINARY_FLOAT and BINARY_DOUBLE types can use native machine arithmetic instructions, and are more efficient for number-crunching applications such as scientific processing. They also require less space in the database.

These types do not always represent fractional values precisely, and handle rounding differently than the NUMBER types. These types are less suitable for financial code where accuracy is critical.

Avoiding Memory Overhead in PL/SQL Code

Be Generous When Declaring Sizes for VARCHAR2 Variables

You might need to allocate large VARCHAR2 variables when you are not sure how big an expression result will be. You can actually conserve memory by declaring VARCHAR2 variables with large sizes, such as 32000, rather than estimating just a little on the high side, such as by specifying a size such as 256 or 1000. PL/SQL has an optimization that makes it easy to avoid overflow problems and still conserve memory. Specify a size of 2000 or more characters for the VARCHAR2 variable; PL/SQL waits until you assign the variable, then only allocates as much storage as needed.

Group Related Subprograms into Packages

When you call a packaged subprogram for the first time, the whole package is loaded into the shared memory pool. Subsequent calls to related subprograms in the package

Tuning PL/SQL Applications for Performance 11-5

Соседние файлы в папке Oracle 10g