Showing posts with label performace tuning. Show all posts
Showing posts with label performace tuning. Show all posts

Monday, August 29, 2011

Just Learned - What happened, when segment bigger than KEEP pool?

I read idea "KEEP Pool – What is Wrong with this Quote?" That helped to think much about KEEP pool. What is good idea for it? If you read Expert Oracle Database Architecture book or this link
- Default pool: The location where all segment blocks are normally cached. This is the original¿and previously only¿buffer pool.
- Keep pool: An alternate buffer pool where by convention you would assign segments that were accessed fairly frequently, but still got aged out of the default buffer pool due to other segments needing space.
- Recycle pool: An alternate buffer pool where by convention you would assign large segments that you access very randomly, and which would therefore cause excessive buffer flushing but would offer no benefit because by the time you wanted the block again it would have been aged out of the cache. You would separate these segments out from the segments in the default and keep pools so that they would not cause those blocks to age out of the cache.

I had a idea - What happened, when segment bigger than KEEP pool?
SQL> show parameter db_keep_cache_size

NAME TYPE VALUE
------------------------------------ ----------- ------------------------------
db_keep_cache_size big integer 100M

SQL> alter system flush buffer_cache;

System altered.

SQL> alter table demo.tb_data storage(buffer_pool keep);

Table altered.

SQL> set autot on
SQL> select count(*) from demo.tb_data;

COUNT(*)
----------
100500

Execution Plan
----------------------------------------------------------
Plan hash value: 143091466
----------------------------------------------------------------------
| Id | Operation | Name | Rows | Cost (%CPU)| Time |
----------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 376 (1)| 00:00:01 |
| 1 | SORT AGGREGATE | | 1 | | |
| 2 | TABLE ACCESS FULL| TB_DATA | 103K| 376 (1)| 00:00:01 |
----------------------------------------------------------------------
Statistics
----------------------------------------------------------
0 recursive calls
0 db block gets
1339 consistent gets
1334 physical reads
0 redo size
423 bytes sent via SQL*Net to client
419 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
1 rows processed

SQL> set autot off
SQL> select decode(pd.bp_id,1,'KEEP',2,'RECYCLE',3,'DEFAULT',
4,'2K SUBCACHE',5,'4K SUBCACHE',6,'8K SUBCACHE',
7,'16K SUBCACHE',8,'32KSUBCACHE','UNKNOWN') subcache,
bh.object_name,bh.blocks
from x$kcbwds ds,x$kcbwbpd pd,(select /*+ use_hash(x) */ set_ds,
o.name object_name,count(*) BLOCKS
from obj$ o, x$bh x where o.dataobj# = x.obj
and x.state !=0 and o.owner# !=0
group by set_ds,o.name) bh
where ds.set_id >= pd.bp_lo_sid
and ds.set_id <= pd.bp_hi_sid
and pd.bp_size != 0
and ds.addr=bh.set_ds
/

SUBCACHE OBJECT_NAME BLOCKS
------------ ------------------------------ ----------
KEEP TB_DATA 1334

SQL> set autot on
SQL> select count(*) from demo.tb_data;

COUNT(*)
----------
100500

Execution Plan
----------------------------------------------------------
Plan hash value: 143091466
----------------------------------------------------------------------
| Id | Operation | Name | Rows | Cost (%CPU)| Time |
----------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 376 (1)| 00:00:01 |
| 1 | SORT AGGREGATE | | 1 | | |
| 2 | TABLE ACCESS FULL| TB_DATA | 103K| 376 (1)| 00:00:01 |
----------------------------------------------------------------------
Statistics
----------------------------------------------------------
0 recursive calls
0 db block gets
1339 consistent gets
0 physical reads
0 redo size
423 bytes sent via SQL*Net to client
419 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
1 rows processed
After first query, data block was kept in KEEP Pool. When you query again, database will load from KEEP Pool. Then I tested with small KEEP Pool.
SQL> alter system set db_keep_cache_size=8M;

System altered.

SQL> show parameter db_keep_cache_size

NAME TYPE VALUE
------------------------------------ ----------- ------------------------------
db_keep_cache_size big integer 8M

SQL> select buffer_pool from dba_tables where table_name='TB_DATA' and owner='DEMO';

BUFFER_
-------
KEEP

SQL> alter system flush buffer_cache;

System altered.

SQL> select decode(pd.bp_id,1,'KEEP',2,'RECYCLE',3,'DEFAULT',
4,'2K SUBCACHE',5,'4K SUBCACHE',6,'8K SUBCACHE',
7,'16K SUBCACHE',8,'32KSUBCACHE','UNKNOWN') subcache,
bh.object_name,bh.blocks
from x$kcbwds ds,x$kcbwbpd pd,(select /*+ use_hash(x) */ set_ds,
o.name object_name,count(*) BLOCKS
from obj$ o, x$bh x where o.dataobj# = x.obj
and x.state !=0 and o.owner# !=0
group by set_ds,o.name) bh
where ds.set_id >= pd.bp_lo_sid
and ds.set_id <= pd.bp_hi_sid
and pd.bp_size != 0
and ds.addr=bh.set_ds
/

no rows selected

SQL> set autot on
SQL> select count(*) from demo.tb_data;

COUNT(*)
----------
100500

Execution Plan
----------------------------------------------------------
Plan hash value: 143091466
----------------------------------------------------------------------
| Id | Operation | Name | Rows | Cost (%CPU)| Time |
----------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 376 (1)| 00:00:01 |
| 1 | SORT AGGREGATE | | 1 | | |
| 2 | TABLE ACCESS FULL| TB_DATA | 103K| 376 (1)| 00:00:01 |
----------------------------------------------------------------------
Statistics
----------------------------------------------------------
0 recursive calls
0 db block gets
1337 consistent gets
1334 physical reads
0 redo size
423 bytes sent via SQL*Net to client
419 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
1 rows processed

SQL> set autot off
SQL> select decode(pd.bp_id,1,'KEEP',2,'RECYCLE',3,'DEFAULT',
4,'2K SUBCACHE',5,'4K SUBCACHE',6,'8K SUBCACHE',
7,'16K SUBCACHE',8,'32KSUBCACHE','UNKNOWN') subcache,
bh.object_name,bh.blocks
from x$kcbwds ds,x$kcbwbpd pd,(select /*+ use_hash(x) */ set_ds,
o.name object_name,count(*) BLOCKS
from obj$ o, x$bh x where o.dataobj# = x.obj
and x.state !=0 and o.owner# !=0
group by set_ds,o.name) bh
where ds.set_id >= pd.bp_lo_sid
and ds.set_id <= pd.bp_hi_sid
and pd.bp_size != 0
and ds.addr=bh.set_ds
/

SUBCACHE OBJECT_NAME BLOCKS
------------ ------------------------------ ----------
KEEP TB_DATA 306

SQL> set autot on
SQL> select count(*) from demo.tb_data;

COUNT(*)
----------
100500

Execution Plan
----------------------------------------------------------
Plan hash value: 143091466
----------------------------------------------------------------------
| Id | Operation | Name | Rows | Cost (%CPU)| Time |
----------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 376 (1)| 00:00:01 |
| 1 | SORT AGGREGATE | | 1 | | |
| 2 | TABLE ACCESS FULL| TB_DATA | 103K| 376 (1)| 00:00:01 |
----------------------------------------------------------------------
Statistics
----------------------------------------------------------
0 recursive calls
0 db block gets
1337 consistent gets
1330 physical reads
0 redo size
423 bytes sent via SQL*Net to client
419 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
1 rows processed

SQL> select count(*) from demo.tb_data;

COUNT(*)
----------
100500

Execution Plan
----------------------------------------------------------
Plan hash value: 143091466
----------------------------------------------------------------------
| Id | Operation | Name | Rows | Cost (%CPU)| Time |
----------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 376 (1)| 00:00:01 |
| 1 | SORT AGGREGATE | | 1 | | |
| 2 | TABLE ACCESS FULL| TB_DATA | 103K| 376 (1)| 00:00:01 |
----------------------------------------------------------------------
Statistics
----------------------------------------------------------
0 recursive calls
0 db block gets
1337 consistent gets
1330 physical reads
0 redo size
423 bytes sent via SQL*Net to client
419 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
1 rows processed
What happened? From first query, database was kept 306 blocks in KEEP Pool ONLY!!!... When you query again, you will see "physical reads" value (not sure value... it displayed 1330). then changed buffer_pool default
SQL> alter table demo.tb_data storage (buffer_pool default);

Table altered.

SQL> select buffer_pool from dba_tables where table_name='TB_DATA' and owner='DEMO';

BUFFER_
-------
DEFAULT

SQL> select count(*) from demo.tb_data;

COUNT(*)
----------
100500

Execution Plan
----------------------------------------------------------
Plan hash value: 143091466
----------------------------------------------------------------------
| Id | Operation | Name | Rows | Cost (%CPU)| Time |
----------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 376 (1)| 00:00:01 |
| 1 | SORT AGGREGATE | | 1 | | |
| 2 | TABLE ACCESS FULL| TB_DATA | 103K| 376 (1)| 00:00:01 |
----------------------------------------------------------------------
Statistics
----------------------------------------------------------
48 recursive calls
0 db block gets
1430 consistent gets
1034 physical reads
0 redo size
423 bytes sent via SQL*Net to client
419 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
4 sorts (memory)
0 sorts (disk)
1 rows processed

SQL> select count(*) from demo.tb_data;

COUNT(*)
----------
100500

Execution Plan
----------------------------------------------------------
Plan hash value: 143091466
----------------------------------------------------------------------
| Id | Operation | Name | Rows | Cost (%CPU)| Time |
----------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 376 (1)| 00:00:01 |
| 1 | SORT AGGREGATE | | 1 | | |
| 2 | TABLE ACCESS FULL| TB_DATA | 103K| 376 (1)| 00:00:01 |
----------------------------------------------------------------------
Statistics
----------------------------------------------------------
0 recursive calls
0 db block gets
1339 consistent gets
0 physical reads
0 redo size
423 bytes sent via SQL*Net to client
419 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
1 rows processed

SQL> select decode(pd.bp_id,1,'KEEP',2,'RECYCLE',3,'DEFAULT',
4,'2K SUBCACHE',5,'4K SUBCACHE',6,'8K SUBCACHE',
7,'16K SUBCACHE',8,'32KSUBCACHE','UNKNOWN') subcache,
bh.object_name,bh.blocks
from x$kcbwds ds,x$kcbwbpd pd,(select /*+ use_hash(x) */ set_ds,
o.name object_name,count(*) BLOCKS
from obj$ o, x$bh x where o.dataobj# = x.obj
and x.state !=0 and o.owner# !=0
group by set_ds,o.name) bh
where ds.set_id >= pd.bp_lo_sid
and ds.set_id <= pd.bp_hi_sid
and pd.bp_size != 0
and ds.addr=bh.set_ds
/

SUBCACHE OBJECT_NAME BLOCKS
------------ ------------------------------ ----------
KEEP TB_DATA 306
DEFAULT TB_DATA 1028
Wow!!! ... TB_DATA object was kept in KEEP Pool (306) and DEFAULT Pool (1028). Interesting -) What happened? if changed back buffer_pool keep again.
SQL> select buffer_pool from dba_tables where table_name='TB_DATA' and owner='DEMO';

BUFFER_
-------
DEFAULT

SQL> set autot on
SQL> select count(*) from demo.tb_data;

COUNT(*)
----------
100500

Execution Plan
----------------------------------------------------------
Plan hash value: 143091466
----------------------------------------------------------------------
| Id | Operation | Name | Rows | Cost (%CPU)| Time |
----------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 376 (1)| 00:00:01 |
| 1 | SORT AGGREGATE | | 1 | | |
| 2 | TABLE ACCESS FULL| TB_DATA | 103K| 376 (1)| 00:00:01 |
----------------------------------------------------------------------
Statistics
----------------------------------------------------------
0 recursive calls
0 db block gets
1339 consistent gets
0 physical reads
0 redo size
423 bytes sent via SQL*Net to client
419 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
1 rows processed

SQL> alter table demo.tb_data storage (buffer_pool keep);

Table altered.

SQL> select count(*) from demo.tb_data;

COUNT(*)
----------
100500

Execution Plan
----------------------------------------------------------
Plan hash value: 143091466
----------------------------------------------------------------------
| Id | Operation | Name | Rows | Cost (%CPU)| Time |
----------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 376 (1)| 00:00:01 |
| 1 | SORT AGGREGATE | | 1 | | |
| 2 | TABLE ACCESS FULL| TB_DATA | 103K| 376 (1)| 00:00:01 |
----------------------------------------------------------------------
Statistics
----------------------------------------------------------
48 recursive calls
0 db block gets
1428 consistent gets
1330 physical reads
0 redo size
423 bytes sent via SQL*Net to client
419 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
4 sorts (memory)
0 sorts (disk)
1 rows processed

SQL> select decode(pd.bp_id,1,'KEEP',2,'RECYCLE',3,'DEFAULT',
4,'2K SUBCACHE',5,'4K SUBCACHE',6,'8K SUBCACHE',
7,'16K SUBCACHE',8,'32KSUBCACHE','UNKNOWN') subcache,
bh.object_name,bh.blocks
from x$kcbwds ds,x$kcbwbpd pd,(select /*+ use_hash(x) */ set_ds,
o.name object_name,count(*) BLOCKS
from obj$ o, x$bh x where o.dataobj# = x.obj
and x.state !=0 and o.owner# !=0
group by set_ds,o.name) bh
where ds.set_id >= pd.bp_lo_sid
and ds.set_id <= pd.bp_hi_sid
and pd.bp_size != 0
and ds.addr=bh.set_ds
/

SUBCACHE OBJECT_NAME BLOCKS
------------ ------------------------------ ----------
KEEP TB_DATA 306
DEFAULT TB_DATA 1028
After changed buffer_pool=keep, A query didn't read data from DEFAULT Pool (I think!!!)... because it read "1330 physical reads" again.

However, KEEP Pool is just a buffer cache and had some situation what you should determine more... It's a good idea, if you will separate POOL to keep warm data by using KEEP Pool. One question!!! How many KEEP pool size should you set?
*** From sample, 1334 x db_block_size(8k) ~= 10M ***
*** This is sample, if you use it... you should determine more... ***
KEEP BUFFER POOL Does Not Work for Large Objects on 11g [ID 1081553.1]

Saturday, August 27, 2011

Just learned - Real-Time SQL Monitoring

I interest some topic in "Oracle Database 11g Performance Tuning Recipes A Problem-Solution Approach" book. Real-Time SQL Monitoring. How? This is 11g feature.
SQL monitoring is automatically started when a SQL statement runs parallel or when it has consumed at least 5 seconds of CPU or I/O time.
Before you will use this feature, your database have to have "statistics_level" = TYPICAL or ALL, "control_management_pack_access"=DIAGNOSTIC+TUNING
SQL> show parameter statistics_level

NAME TYPE VALUE
------------------------------------ ----------- ------------------------------
statistics_level string TYPICAL

SQL> show parameter control_management_pack_access

NAME TYPE VALUE
------------------------------------ ----------- ------------------------------
control_management_pack_access string DIAGNOSTIC+TUNING
When you need to test Real-Time SQL Monitoring, you can use HINTS:
MONITOR : To force real-time SQL monitoring
Or if
NO_MONITOR : To prevent the query from being monitored

How to monitor?
You can use V$SQL_MONITOR and V$SQL_PLAN_MONITOR views to monitor the statistics.
The V$SQL_MONITOR view contains a subset of the statistics available in V$SQL.
The V$SQL_PLAN_MONITOR view contains plan level monitoring statistics for each SQL statement in V$SQL_MONITOR.

What should you know from both views?
KEY NUMBER : Artificial join key to efficiently join V$SQL_MONITOR with its corresponding plan level monitoring statistics stored in V$SQL_PLAN_MONITOR.

STATUS : SQL execution status. Values are below :
■ EXECUTING - SQL statement is still executing
■ DONE (ERROR) - Execution terminated with an error
■ DONE (FIRST N ROWS) - Execution terminated by the application before all rows were fetched
■ DONE (ALL ROWS) - Execution terminated and all rows were fetched
■ DONE - Execution terminated (parallel execution)

Tested!!!
- On some session:
SQL> select /*+ MONITOR */ * from TB_TEST;
During SQL statement is still executing
- Find information from SQL statement
select key, status, username, module ,service_name, sql_text, cpu_time, buffer_gets from v$sql_monitor where status = 'EXECUTING'
/
- Find Execution Plan
select plan_line_id id, lpad (' ', plan_depth) || plan_operation operation , plan_options , plan_object_name , plan_cardinality card, plan_cost from v$sql_monitor q, v$sql_plan_monitor p where q.key = p.key and q.key = &KEY order by id
/
KEY= 158913789964
However, If You checked by "dbms_sqltune.report_sql_monitor"
SQL> select dbms_sqltune.report_sql_monitor from dual;
REPORT_SQL_MONITOR
---------------------------------------------------------------------------------------------------------------------------------
SQL Monitoring Report

SQL Text
------------------------------
select /*+ MONITOR */ * from TB_TEST

Global Information
------------------------------
Status : EXECUTING
Instance ID : 1
Session : DEMO (36:327)
SQL ID : 45fs1021jz1dg
SQL Execution ID : 16777219
Execution Started : 08/27/2011 17:38:05
First Refresh Time : 08/27/2011 17:38:05
Last Refresh Time : 08/27/2011 17:38:06
Duration : 2s
Module/Action : SQL*Plus/-
Service : SYS$USERS
Program : sqlplus@linuxtest01 (TNS V1-V3)
Fetch Calls : 244

Global Stats
=================================================
| Elapsed | Cpu | Other | Fetch | Buffer |
| Time(s) | Time(s) | Waits(s) | Calls | Gets |
=================================================
| 0.08 | 0.06 | 0.02 | 244 | 290 |
=================================================

SQL Plan Monitoring Details (Plan Hash Value=1092599453)
==============================================================================================================================
| Id | Operation | Name | Rows | Cost | Time | Start | Execs | Rows | Activity | Activity Detail |
| | | | (Estim) | | Active(s) | Active | | (Actual) | (%) | (# samples) |
==============================================================================================================================
| 0 | SELECT STATEMENT | | | | | | 1 | | | |
| -> 1 | TABLE ACCESS FULL | TB_TEST | 162K | 597 | 2 | +0 | 1 | 0 | | |
==============================================================================================================================
This is just sample for manually tuning SQL idea. If you need to monitor your SQL. Use it MONITOR hint -)

Sunday, August 07, 2011

Oracle Database 11g Performance Tuning for Beginner to Advanced

I am talking about "Oracle Database 11g Performance Tuning Recipes A Problem-Solution Approach" book By Darl Kuhn , Sam Alapati , Bill Padfield.
Thank You Jonathan Gennick the chance for me to become technical review this book.

This book can not help you to become Oracle Expert (Because, this book was written from the real-world for database administrators in need of immediate help with performance issues relating to Oracle Database). If you work with Oracle Database as Oracle Database Administrator (Beginner to Advanced). You are looking for solutions to common database performance problems or reference book to help with performance issues. I guarantee this book is useful.
I used "Beginner to Advanced" word, I don't mean: "If you have worked with Oracle Database a long time. You don't need read this book". Actually You should read it!!! Because This book was written about Oracle Database 11g also, This book will helpful to figure you out for solutions on 11gR1 and 11gR2.

If you are Oracle DBA (Newbie). You are asking How to solve database performance issue? You have to read many documents, many concepts. But The database performance issue can not wait... and wait. read some documents or some books what help you immediately.

If you are Oracle DBA (Intermediate/Advanced). You had worked on Oracle 8, 9i, 10g ...whatever. In Today, You have to work on Oracle 11g. You have to learn some new features and you may find the new performance issue. This book can figure you out. You can save your time to find out the information to solve the issue.

I work on Oracle Database 10gR*, and 11gR*. I have the change to review this Book(good for me). I often see the performance issue on Oracle Database and have the change to use the idea, the command-line and the samples from this book to solve the issue.

The "Oracle Database 11g Performance Tuning Recipes A Problem-Solution Approach" book has 15 chapters (related 11gR1 to 11gR2). If you need to know about Table of contents(I will publish later), but I can tell you what you will learn in each of chapter.
Chapter 1: Learn to optimize table performance. You have to work with TABLE object and How to optimize it? This chapter was writen about database features that impact the performance of storing and retrieving data within a table.
Chapter 2: Learn about Indexes. An index is a database object used primarily to improve the performance of SQL queries. How to use index more efficient?
Chapter 3: Learn to optimize Instance Memory. You work as DBA. You have to know about SGA,PGA and ... You work on 10g, You have to know "Oracle Automatic Shared Memory Management". You work on 11g, You have to know "Oracle Automatic Memory Management". This chapter deals with managing and optimizing Oracle's memory.
Chapter 4: You are Oracle DBA. You need to monitor system and database performance. You need to know about AWR. You will learn about AWR in this chapter.
Chapter 5: Learn to reduce Contention in the Database - You often see Oracle Wait Events, example: "enq: TX - row lock contention". How to reduce them?
Chapter 6: Solving database performance issues sometimes requires the use of operating system (OS) utilities. You will learn to use OS utilities (Linux/Unix) for Solving database performance issues.
Chapter 7: How to use the database's built-in diagnostic infrastructure to resolve database performance issues. Learn to use adrci, oradebug and etc...
Chapter 8: Learn to create Efficient SQL. You will learn idea and sample for creating Efficient SQL, example: Optimizing Joins,Controlling Transaction Sizes
Chapter 9: Learn to manual Tuning SQL. Learn to use Execution Plan and read it. Learn to use SQL Monitoring and what you should know about Tuning SQL by Manual.
Chapter 10: Tracing session activity is at the heart of most SQL performance tuning exercises. You will learn from this chapter.
Chapter 11: When you work on Oracle Database 11g, you need to know about Automated SQL tuning features. You will learn about it in this chapter.
Chapter 12: Learn about SQL Profie and How to use SQL Plan Baselines.
Chapter 13: Learn to use the Optimizer for 11g. Example: set preferences for statistics collection, enable/disable Optimizer Statistics Gathering and etc.
Chapter 14: Learn to use Query Hints.
Chapter 15: Parallelism can help improve performance, this chapter you will learn about it.

You can read book description.

About Authors:
Sam R. Alapati is an Oracle ACE and an experienced Oracle database administrator (OCP Oracle Database 11g). Sam is currently the senior technical director for Miro Consulting, Inc., in Woodbridge, New Jersey, and regularly consults with Fortune 500 companies in the areas of Oracle Database, Oracle E-Business, and Oracle Fusion Middleware technology. Sam has written several books on Oracle database management, including Expert Oracle Database 11g Administration, Oracle Database 11g: New Features for DBAs and Developers (with Charles Kim), and RMAN Recipes for Oracle Database 11g (with Darl Kuhn and Arup Nanda), all published by Apress. Sam lives in Dallas, Texas with his wife, Valerie, and children, Nina and Nicholas.

Darl Kuhn is a senior database administrator working for Oracle. He handles all facets of database administration from design and development to production support. He also teaches advanced database courses at Regis University in Colorado. Darl does volunteer DBA work for the Rocky Mountain Oracle Users Group. He has a graduate degree from Colorado State University and lives near Spanish Peaks, Colorado with his wife, Heidi, and daughters, Brandi and Lisa.

Bill Padfield is an Oracle Certified Professional, working for a large telecommunications company in Denver, Colorado as a lead database administrator. Bill helps administer and manage a large data warehouse environment consisting of more than 75 databases. Bill has been an Oracle Database administrator for more than 14 years, and has been in the IT industry since 1985. Bill also teaches graduate database courses at Regis University and currently resides in Aurora, Colorado with his wife, Oyuna, and son, Evan.