Abstract
This paper will present how queries involving database links are processed. We will look at four cases: (1) query against a single remote table, (2) a join involving one local table and one remote table, (3) a join of two tables located on the same remote database, and (4) a join of two tables existing on two separate remote databases.
Introduction
The method Oracle uses to process distributed queries is one of the less understood features offered by Oracle. This presentation will explain some of the things that happen behind the scenes of a distributed query and will give some techniques an application developer can use to write better distributed queries.
SQL*Net Demystified
In order to perform a distributed query, an RDBMS needs to have a means of communicating with other databases. SQL*Net is Oracle's remote data access protocol and application programmatic interface (API) that runs on top of existing network protocols. Its main function is to allow a front-end application such as SQL*Forms to run on one computer while accessing Oracle data which resides on a database either on the same machine or on a remote machine.
Architecturally, the front-end tool is responsible for generating SQL statements depending upon user input (eg user fills in fields in a form and then hits the INSERT key) or application design (eg embedded SQL in a C program). We also refer to this as a client process. The Oracle back-end or server process is responsible for parsing and executing SQL statements as optimally as possible. Once processed, the results of the statement are returned to the front-end tool. In the case of a SELECT statement, the server process retrieves a record or set of records from the datafiles and sends them the client upon request. The front-end tool then decides how the data is presented to the user. This leaves SQL*Net with the deceptively simple tasks of managing a communication session between two computers, transporting data between two processes, and providing any necessary character set conversions. Since SQL*Net itself has no role in the decision making process of executing a query, one can say that this paper is actually an RDBMS topic in SQL*Net clothing!
The Sample Environment
For the purposes of this paper, we will use a sample environment consisting of:
- three computers running an OS such as Unix (identified as unix1,
unix2, and unix3) connected via some networking protocol such as
TCP/IP.
- an Oracle database running on each machine (identified as instance A, B, and C respectively)
- an Oracle database user account of scott/tiger on each database
We will assume that our user, scott, is already logged onto unix1 and has initiated a SQL*Plus session with database A. Theoretically, it should be possible for the three nodes to be running any of the operating systems supported by Oracle and be able to communicate with any supported networking protocol. Some environments, however, will present some minor limitations not covered in this paper. For example, DOS cannot act as a server to other machines.
Database Links
In order for two Oracle databases to share data, one Oracle instance temporarily acts as a client to the other Oracle instance. This action is accomplished through database links stored in the initiating database. The syntax for creating a database link is as follows (optional clause enclosed by []):
CREATE DATABASE LINK dblinkname
[CONNECT TO user IDENTIFIED BY password]
USING 'connect_string'
For our example, we initially create two database links from unix1 to unix2 and unix3 as follows:
CREATE DATABASE LINK lax
USING 'T:unix2:B';
CREATE DATABASE LINK syd
USING 'T:unix3:C';
SQL*Net V2 based DBLINKs using a V2 service name example:
CREATE DATABASE LINK lax
USING 'v2servicename';
-or-
CREATE DATABASE LINK lax
USING 'TNS:v2servicename';
Simple Remote Queries
Before we look at what happens with a complex distributed query involving joins of tables on sereral remote databases, it helps to consider what the mechanics are behind a simple query using a database link. We will use the following example SQL statement:
SELECT MAX(ename), 'dept # =' || deptno
FROM emp@syd
WHERE deptno BETWEEN 10 AND 100
GROUP BY deptno
ORDER BY 1;
The local database on unix1 will process the query and send the following to the database on unix3:
SELECT ename, deptno
FROM emp
WHERE deptno >= 10 AND deptno <= 100;
As the records are fetched into unix1, it performs the group by applies the MAX() function to each group then orders what it has and sends those results back to its client. This implies several important points about how a remote query is handled by Oracle V6. Some are not easily implied but noted
below:
1. Aggregate functions like MAX(), COUNT() are NOT passed across the net but rather are performed on the local node.
2. Expressions in the SELECT list are decomposed into columns and evaluated when the data is retrieved.
3. Only a relevant subset of columns are fetched across the net.
4. An expression in a WHERE clause is passed across to the remote
database if it compares the remote column to a literal (eg ename =
'KING').
5. Expressions in a WHERE clause are passed to the remote database
if all columns are in the expression are located in the same remote
table the remote database (eg emp.sal * 0.20 > emp.bonus)
6. Datatype conversion functions like TO_CHAR() in a WHERE clause are subject to the conditions in #4 and #5.
7. The optimizer only has knowledge of local indexes when making decisions about how to execute a query.
8. Remote indexes can be still be used to resolve the decomposed query sent to the remote database.
9. GROUP BY is not sent to the remote database.
10. ORDER BY is not sent to the remote database.
Simple Distributed Joins
With joins that involve tables on a remote database and a local
or second remote database, the points mentioned in the case of a
simple remote query are still true. What happens to allow this
is the decomposition of a join into separate independent queries
whose results are then joined on the local node. Here is a
simple example of how a join is decomposed.
SELECT ename, dname
FROM emp@syd e, dept@lax d
WHERE e.deptno = d.deptno
AND e.job != 'CLERK'
AND d.loc = 'NEW YORK';
becomes:
unix3: SELECT ename, job, deptno
FROM emp
WHERE job <> 'CLERK';
unix2: SELECT dname, loc, deptno
FROM dept
WHERE loc = 'NEW YORK';
Note that, at least in Oracle V6, the same decomposition occurs
even if both tables exist on the same remote node.
Putting It All Together
With these facts in mind, it is easy to see how the use of views
can help in cases where it is clearly better for a function to be
performed on a remote node, rather than the local one. For
example, instead of issuing a count of rows against a remote
database and having the data returned to be counted locally, a
remote view could be queried to return only the count value.
This increases performance by reducing network traffic.
Example:
SELECT COUNT(*)
FROM emp@syd
WHERE deptno = 10;
becomes...
unix3:
CREATE VIEW empcount (numemps, deptno) AS
SELECT COUNT(empno), deptno
FROM emp
GROUP BY deptno;
unix1:
SELECT numemps, deptno
FROM empcount@lax
WHERE deptno = 10;
Views can also be used to help control the node where the join
will actually take place. In the case where there is a small
table on the local database and a large table on a remote
database, it is usually better to have the smaller table sent
across the network to be processed remotely than retrieve the
large table and perform a local join. This method requires the
existence of database links from each of the three machines to
the other two.
Example:
SELECT ..
FROM small, big@syd
WHERE small.key=big.key;
becomes...
unix2:
CREATE VIEW bigsmall AS
SELECT ..
FROM small@mia, big
WHERE small.key=big.key;
unix1:
SELECT ..
FROM bigsmall@syd;
When both tables being joined are on the same remote node, one
can use a view to make sure the join is performed on that node.
At times, the above scenario exists in a subtle form. Consider
the following query:
SELECT *
FROM A@mia a, B@lax b, C@mia c
WHERE a.x = b.x
AND b.x = c.x;
Upon examining the WHERE clause, one sees that the transitive
expression of "a.x = c.x" can also be generated. This shows that
tables A and C can be joined at the same remote node rather than
being joined at the local node. The predicates taken directly do
not always show that a remote join is possible.
In the case where both tables are on separate databases one can
create a view on the machine where the join may be performed the
quickest. While deciding this, one can consider which node has
the largest table, the fastest CPU, or the lightest load.
If views are not possible, it may be possible to fool the
optimizer into choosing a different path that may be faster than
what the rules would predict. For example if one were joining a
subset of a local small dept table with a large remote emp table,
a nested loops join may return faster than a sort merge. Here is
an example.
unix1:
SELECT e.ename, d.dname
FROM emp@hq e, dept d
WHERE d.loc IN ('BUTTE', 'FLAGSTAFF')
AND e.deptno = d.deptno;
In the above query, the ename and deptno columns of the whole emp
table will be pulled over the net to have it's deptno value
compared with the department numbers of departments located in
Butte, Montana and Flagstaff, Arizona. If the total number of
employees working in those cities are a small percentage of the
total employees working worldwide, then much network traffic and
IO was wasted reading and sending data that would just be
rejected.
If emp had an index on the deptno column, it would be ideal to
just have it make an index scan for rows that have departments
located in the two cities we want and just return those rows
back. The following query will persuade the optimizer to choose
that execution path.
unix1:
SELECT e.ename, d.dname
FROM emp@hq e, dept d
WHERE d.loc IN ('BUTTE', 'FLAGSTAFF')
AND e.deptno <= d.deptno
AND e.deptno >= d.deptno
Note that the last two clauses taken together have the same logic
as a straight equality. However, the optimizer analyzes each
clause and concludes that it will have to do a full table scan
for each dept.deptno.
In this case, Oracle performs a nested loops join and sends the
following query to the remote database:
SELECT ename, dept
FROM emp
WHERE deptno >= :1
AND deptno <=: 2;
It then loops through the appropriate rows in dept, binds the
deptno values and executes the remote query.
One can also use the TKPROF and EXPLAIN utilities as another way
to see how queries are processed. To enable these facilities,
one may turn on global tracing by setting sql_trace = true in the
init.ora file. Then one may simulate a distributed database by
creating and using database links that loop back to the database
where the query originated. This will create multiple trace
files -- one for the "local" session and another for each
"remote" connection query. If one had to, one could copy trace
files from a remote machine and use the EXPLAIN parameter over
SQL*Net with the command "tkprof .. EXPLAIN=user/pswd@remote."
Note 1004553.6 DISTRIBUTED QUERY ANALYSIS.
* 여기저기서 좋다는 책 몇권 추천합니다. 고수가 되는 그날까지.. 파이팅!!