Performance Tuning Is An Art Which Has Lots of Section Like The Below
Performance Tuning Is An Art Which Has Lots of Section Like The Below
Identifying which query takes much time, by the use of trace, TKPROF and execution plans at
various load scenarios.
Tuning the identified time taking query based on the existing scenario and with the knowledge
base.
Tuning the DB parameters and hardware requirements.
Monitoring the tuned query for quite some time at various load scenarios.
Giving different solutions according to the load levels.
Scope:
Now we are going to see just the part “Tuning the identified time taking query, based on the
existing scenario and with the knowledge base”. This will say the don’ts in a query or PL/SQL
code and then it will explain you how to tune them.
This session will help you to understand the oracle basic architecture and do some basic tuning
which can improve your query and PL/SQL code.
SGA
Shared Pool
Data
Dictionary Java Pool Large Pool
Cache
Back-
Ground
PMON SMON DBWR LGWR CKPT Others Processes
Oracle's library cache, which is responsible for collecting, parsing, interpreting, and executing
all of the SQL statements that go against the Oracle database. Hence, the shared pool is a key
component, so it's necessary for the Oracle database administrator to check for shared pool
contention.
Oracle's parsing algorithm ensures that identical SQL statements do not have to be parsed each
time they're executed.
Control Structures - Common control structure information, for example, lock information
The dictionary cache stores “metadata” (data about your tables and indexes) and it’s also
known as the row cache. It is used to cache data dictionary related information in RAM for quick
access.
The buffer cache is part of the SGA. It holds copies of data blocks so as they can be accessed
quicker by oracle than by reading them off disk. The purpose of the buffer cache is to minimize
physical io. When a block is read by Oracle, it places this block into the buffer cache, because
there is a chance that this block is needed again. Reading a block from the buffer cache is less
costly (in terms of time) than reading it from the disk.
Each Oracle database has a redo log. This redo log records all changes made in datafiles. This
is to restore data back when failure happens. The redo log makes it possible to replay SQL
statements. Before Oracle changes data in a datafile it writes these changes to the redo log. If
something happens to one of the datafiles, a backed up datafile can be restored and the redo,
that was written since, replied, which brings the datafile to the state it had before it became
unavailable
Large Pool:
Oracle Large Pool is an optional memory component of the oracle database SGA. This area is
used for providing large memory allocations in many situations that arise during the operations
of an oracle database instance.
Indexes are optional structures associated with tables. Indexes can be created to increase the
performance of data retrieval. Just as the index in a manual helps you quickly locate specific
information, an Oracle index provides an access path to table data.
Indexes are useful when applications frequently query a table for a range of rows (for example,
all employees with a salary greater than 1000 dollars) or a specific row. Indexes are created on
one or more columns of a table.Changes to table data (such as adding new rows, updating
rows, or deleting rows) are automatically incorporated into all relevant indexes. Indexes are the
primary means of reducing disk I/O when properly used. However, the presence of many
indexes on a table decreases the performance of updates, deletes, and inserts, because Oracle
must also update the indexes associated with the table.
An index is merely a fast access path to the data. It affects only the speed of execution. Given a
data value that has been indexed, the index points directly to the location of the rows containing
that value. Indexes are logically and physically independent of the data in the associated table.
You can create or drop an index at any time without affecting the base tables or other indexes.
Using Indexes is always preferrable to using a full table scan as it allows more immediate
access to just the desired rows.
Full table scan is preferable when you know that more than about 25% of the records in the
queried tables will be selected;
Using Indexes is always preferrable to using a full table scan as it allows more immediate
access to just the desired rows.
Full table scan is preferable when you know that more than about 25% of the records in the
queried tables will be selected;
Index segment:
Each index has an index segment that stores all of its data.
B-tree indexes
Bitmap indexes
Function-Based Indexes
ROWID is an internal number Oracle uses to uniquely identify each row. NOT a primary key! Is
the actual location of a row on a disk. Very efficient for retrieval. Format specifies block, row,
and file (and object in 8). This is called pseudo-column since can be selected
Oracle 7: BBBBBBB.RRRR.FFFFF
Oracle 8: OOOOOO.FFF.BBBBBB.RRR
As given in the figure - below are the steps for searching Patrick:
Parts table
partno color size
1 GREEN MED
2 RED MED
3 RED SMALL
4 BLUE LARGE
Part number 1 2 3 4
4. Bind variables
This is where your variables (like department_id) get attached. PLEASE use bind variables
6. Run
This is when Oracle takes a big breath and executes your statement. If you are running a
DELETE or UPDATE statement, those rows will be locked for data integrity until a commit or
rollback.
7. Fetch rows (Queries only)
If you ran a SELECT, this is the step that gets your rows and sends them back to you (ordered if
you requested them to be).
8. Close cursor
"The final stage of processing a SQL statement is closing the cursor."
Before you can use the EXPLAIN PLAN command, you need to have a PLAN_TABLE installed.
This can be done by simply running the $ORACLE_HOME/rdbms/admin/utlxplan.sql script in
your schema. It creates the table for you. After you have the PLAN_TABLE created, you issue
an EXPLAIN PLAN for the query you are interested in tuning. The command is of the form:
Example:
It is a perfect valid question to ask why hints should be used. Oracle comes with an optimizer
that promises to optimize a query's execution plan. When this optimizer is really doing a good
job, no hints should be required at all. Sometimes, however, the characteristics of the data in the
database are changing rapidly, so that the optimizer (or more accuratly, its statistics) are out of
date. In this case, a hint could help. It must also be noted, that Oracle allows to lock the
statistics when they look ideal which should make the hints meaningless again.
All hints except /*+ rule */ cause the CBO to be used. Therefore, it is good practise to analyze
the underlying tables if hints are used (or the query is fully hinted).
The ANALYZE statement can be used to gather statistics for a specific table, index or cluster.
The statistics can be computed exactly, or estimated based on a specific number of rows, or a
percentage of rows. If a table has not been analyzed, the cost-based optimizer can use only
rule-based logic to select the best access path.
If the table has proper statistics then the CBO will decide easily that which is the optimal path to
travel and retrieve the data, which will improve the performance.
If the table driving a join is not optimal, there can be a significant increase in the amount of time
required to execute a query. Consider the following example:
SELECT COUNT(*) FROM acct a, trans b
WHERE b.cost_center = 'MASS‘ AND a.acct_name = 'MGA‘ AND a.acct_name = b.acct_name;
In this example, if ACCT_NAME represents a unique key index and COST_CENTER represents
a single column non-unique index, the unique key index would make the ACCT table the driving
table.
If both COST_CENTER and ACCT_NAME were single column, non-unique indexes, the rule-
based optimizer would select the TRANS table as the driving table, because it is listed last in the
FROM clause. Having the TRANS table as the driving table would likely mean a longer
response time for a query, because there is usually only one ACCT row for a selected account
name but there are likely to be many transactions for a given cost center.
With the rule-based optimizer, if the index rankings are identical for both tables, Oracle simply
executes the statement in the order in which the tables are parsed. Because the parser
processes table names from right to left, the table name that is specified last (e.g., DEPT in the
example above) is actually the first table processed (the driving table).
The response time following the re-ordering of the tables in the FROM clause is as follows:
SELECT COUNT(*) FROM trans b, acct a
WHERE b.cost_center= 'MASS‘ AND a.acct_name = 'MGA‘ AND a.acct_name = b.acct_name;
It is important that the table that is listed last in the FROM clause is going to return the fewest
number of rows. There is also potential to adjust your indexing to force the driving table. For
example, you may be able to make the COST_CENTER index a concatenated index, joined with
another column that is frequently used in SQL enquires with the column. This will avoid it
ranking so highly when joins take place.
Technology Focus - performance fine tuning in Oracle 15
Incorrect Index
WHERE clauses often provide the rule-based optimizer with a number of indexes that it could
utilize. The rule-based optimizer is totally unaware of how many rows each index will be required
to scan and the potential impact on the response time. A poor index selection will provide a
response time much greater than it would be if a more effective index was selected.
The rule-based optimizer has simple rules for selecting which index to use. These rules and
scenarios are described earlier in the various "What the RBO rules don't tell you" sections.
Let's consider an example.
An ERP package has been developed in a generic fashion to allow a site to use columns for
reporting purposes in any way its users please. There is a column called BUSINESS_UNIT that
has a single-column index on it. Most sites have hundreds of business units. Other sites have
only one business unit.
The single-column index will be used in preference to the three-column index, despite the fact
that the three-column index returns the result in a fraction of the time of the single-column index.
This is because all columns in the single-column index are used in the query. In such a situation,
the only options open to us are to drop the index or use the cost-based optimizer. If you're not
using packaged software, you may also be able to use hints.
The way you specify conditions in the WHERE clause(s) of your SELECT statements has a
major impact on the performance of your SQL, because the order in which you specify
conditions impacts the indexes the optimizer choose to use.
If two index rankings are equal -- for example, two single-column indexes both have their
columns in the WHERE clause -- Oracle will merge the indexes. The merge (AND-EQUAL)
order has the potential to have a significant impact on runtime. If the index that drives the query
returns more rows
than the other index, query performance will be suboptimal. The effect is very similar to that from
the ordering of tables in the FROM clause. Consider the following example:
SELECT COUNT(*) FROM trans
WHERE cost_center = 'MASS‘ AND bmark_id = 9;
Response Time = 4.255 seconds
The index that has the column that is listed first in the WHERE CLAUSE will drive the query. In
this statement, the indexed entries for COST_CENTER = `MASS' will return significantly more
rows than those for BMARK_ID=9, which will return at most only one or two rows.
The following query reverses the order of the conditions in the WHERE clause, resulting in a
much faster execution time.
SELECT COUNT(*) FROM trans
WHERE bmark_id = 9 AND cost_center = 'MASS';
WHERE EXISTS can be better than join when driving from parent records and want to make
sure that at least on child exists. Optimizer knows to bail out as soon as finds one record. Join
would get all records and then distinct them!
SELECT *
FROM suppliers, orders
WHERE suppliers.supplier_id = orders.supplier_id;
SELECT *
FROM suppliers
WHERE EXISTS
(select *
from orders
where suppliers.supplier_id = orders.supplier_id);
Exists returns TRUE whenever the subquery gives a row count > 1.
In this case once the where clause gets the true condition from EXISTS, it will select all rows
from the table.
If you specify 2 or more tables in the FROM clause of a SELECT statement, then Oracle parser
will process the tables from right to left, so the table name you specify last will be processed
first. In this case you have to choose one table as driving table.
Always choose the table with less number of records as the driving table.
There are three good reasons why it is better to name the columns in a query rather than to use
"select * from ...".
1. Network traffic is reduced. This can have a significant impact on performance if the table has
a large number of columns, or the table has a long or long raw column (both of which can be up
to 2 GB in length). These types of columns will take a long time to transfer over the network and
so they should not be fetched from the database unless they are specifically required.
2. The code is easier to understand.
3. It could save the need for changes in the future. If any columns is added to or removed from
the base table/view, then “select * “statement can produce wrong results set and statement may
fail.
Always use table alias and prefix all column names with the aliases when you are using more
than one table.
All expressions return NULL if one of the operands is NULL. This is applicable for all operators
except Concatenation operator (||).
It is also better to use bind variables in queries. That way the query becomes generic and
therefore re-usable. For example, instead of writing a query like -
Change it to -
The first query can be re-used for deptno number 20 only, whereas the second query can be
reused for any other deptno also.
Use EXISTS in place of DISTINCT if you want the result set to contain distinct values while
joining tables.
For example:
SELECT DISTINCT d.deptno, d.dname FROM dept d, emp e WHERE d.deptno = e.deptno;
If a SQL statement contains the first condition, the optimizer simplifies it into the second
condition. Please note that optimizer does not simplify expressions across comparison
operators. The optimizer does not simplify the third expression into the second. For this reason,
we should write conditions that compare columns with constants whenever possible, rather than
conditions with expressions involving columns.
The Optimizer does not use index for the following statement:
SELECT *
FROM emp
WHERE sal*12 > 24000 ;
SELECT *
FROM emp
WHERE sal > 24000/12 ;
Never use NOT operator on an indexed column. Whenever Oracle encounters a NOT on an
index column, it will perform full-table scan.
For Example:
SELECT * FROM emp WHERE NOT deptno = 0;
Instead use the following:
SELECT * FROM emp WHERE deptno > 0;
Never use a function or calculation on an indexed column. If there is any function is used on an
index column, optimizer will not use index.
For Example:
Often, it is necessary to calculate different aggregates on various sets of tables. Usually, this is
done with multiple scans on the table, but it is easy to calculate all the aggregates with one
single scan. Eliminating n-1 scans can greatly improve performance.
Combining multiple scans into one scan can be done by moving the WHERE condition of each
scan into a CASE statement, which filters the data for the aggregation. For each aggregation,
there could be another column that retrieves the data.
The following example has count of all employees who earn less then 2000, between 2000 and
4000, and more than 4000 each month. This can be done with three separate queries.
However, it is more efficient to run the entire query in a single statement. Each number is
calculated as one column. The count uses a filter with the CASE statement to count only the
rows where the condition is valid. For example:
PROCEDURE calc_m IS
m NUMBER NOT NULL:=0;
a NUMBER;
b NUMBER;
BEGIN
...
m := a + b;
...
END;
Several of the PL/SQL numeric data types are constrained. PL/SQL allows any variable to be
constrained to only NOT NULL values. Constraint checks, including the NOT NULL constraint,
require an extra byte code instruction on every assignment. The NOT NULL constraint,
therefore, does not come for free; it requires the PL/SQL compiler to ensure NOT NULLness
after every operation. Constrained data types used in PL/SQL blocks which have no associated
exception handler are excellent candidates for replacement by corresponding unconstrained
types.
Consider the example on the above, which uses the NOT NULL constraint for m.
Because m is constrained by NOT NULL, the value of the expression a + b is assigned to a
temporary variable, which is then tested for nullity. If the variable is not null, its value is assigned
to m. Otherwise, an exception is raised. However, if m were not constrained, the value would be
assigned to m directly
A more efficient way to write the same example is shown on the below.
– Scenario 1
IF (sal < 1500) OR (comm IS NULL) THEN
...
END IF;
– Scenario 2
IF credit_ok(cust_id) AND (loan < 5000) THEN
...
END IF;
In this example, the Boolean function CREDIT_OK is always called. However, if you
switch the operands of AND as follows, the function is called only when the expression
loan < 5000 is true (because AND returns TRUE only if both its operands are true):
IF (loan < 5000) AND credit_ok(cust_id) THEN
...
END IF;
One way to improve performance is to pin frequently used packages in the shared pool.
When a package is pinned, it is not aged out with the normal least recently used (LRU)
mechanism that Oracle otherwise uses to flush out a least recently used package. The package
remains in memory no matter how full the shared pool gets or how frequently you access the
package.
Example:
SYS.DBMS_SHARED_POOL.KEEP('SYS.STANDARD','P');
SYS.DBMS_SHARED_POOL.UNKEEP('SYS.STANDARD','P');
Subquery factoring, also known as the WITH clause, provides a convenient and flexible way for
us to define subqueries and in-line views in Oracle 9i. The primary purpose of subquery
factoring is to reduce repeated table accesses by generating temporary datasets during query
execution. However, even if we do not take advantage of this internal query optimisation, we can
use subquery factoring to structure our complex SQL statements in a more logical and
understandable format.
The following syntax example is possibly more common in everyday use. We define two named
subqueries, but the second builds on the first.
This form of subquery factoring is the semantic equivalent of the following pseudo-SQL.
The NOCOPY clause tells to PL/SQL engine to pass the variable by reference, thus avoiding the
cost of copying the variable at the end of the procedure. The PL/SQL engine has requirements
that must be met before passing the variable by reference and if those requirements are not
met, the NOCOPY clause will simply be ignored by the PL/SQL engine.
Example:
1. BULK COLLECT avoids context switching. context switching is nothing but movement of data
between PLSQL engine and SQL Engine.
A bulk collect is a method of fetching data where the PL/SQL engine tells the SQL engine to
collect many rows at once and place them in a collection. The SQL engine retrieves all the rows
and loads them into the collection and switches back to the PL/SQL engine. All the rows are
retrieved with only 2 context switches.
The tradeoff with BULK COLLECT, like so many other performance-enhancing features, is "Run
faster but consume more memory." Specifically, memory for collections is stored in the Program
Global Area (PGA), not the System Global Area (SGA). SGA memory is shared by all sessions
connected to Oracle Database, but PGA memory is allocated for each session