This session was presented 2016-01-28 at Dallas Oracle User's Group January meeting.
How long does your code take to run? Is it changing? When it is slow, WHY is it slow? Is it your fault, or somebody else's? Can you prove it? How much faster could your code be? Do you know how to measure the performance of your code as user workloads and data volumes increase? These are fundamental questions about performance, but the vast majority of Oracle application developers can't answer them. The most popular performance tools available to them—and to the database administrators that run their code in production—are incapable of answering any of these questions. But the Oracle Database can give you exactly what you need to answer these questions and many more. You can know exactly where YOUR CODE is spending YOUR TIME. This session explains how.
14. @CaryMillsap
has to finish quickly.”
click
button
link
row
query
report
job
}{“My
14
This is what performance is.
15. @CaryMillsap
has to finish quickly.”
click
button
link
row
query
report
job
}{“My
15
A performance problem is when it doesn’t.
16. @CaryMillsap 16
“How long does it take?”
Response time (R)
Duration from service request to
service fulfillment.
Sanjay Nancy Ken Jorge
R
t0
t1
R = t1 – t0
Two big questions...
1. How long did it take?
2. Why?
26. @CaryMillsap
EXADATAD A T A B A S E
ENTERPRISE EDITION
D A T A B A S E
STANDARD EDITION
D A T A B A S E
EXPRESS EDITION
Oracle extended SQL tracing
is a feature of every Oracle Database.
26
Oracle7 1992 Oracle8 1997 Oracle8i 2000 Oracle9i 2001 Oracle 10g 2004 Oracle 11g 2007 Oracle 12c 2013
40. @CaryMillsap
// JDBC 11g example
String metrics[] = new String[OraCxn.END_TO_END_STATE_INDEX_MAX];
metrics[END_TO_END_MODULE_INDEX] = "OE BOOK";
metrics[END_TO_END_ACTION_INDEX] = UUID.randomUUID().toString();
conn.setEndToEndMetrics(metrics, (short) 0);
// Your OE BOOK code
metrics[END_TO_END_MODULE_INDEX] = "";
metrics[END_TO_END_ACTION_INDEX] = "";
conn.setEndToEndMetrics(metrics, (short) 0);
40
Java ADF
To set your code’s module and action names...
41. @CaryMillsap
// JDBC 12c example
Properties p = new Properties();
p.put("OCSID.MODULE", "OE BOOK");
p.put("OCSID.ACTION", UUID.randomUUID().toString());
connection.setClientInfo(p);
// Your OE BOOK code
p.put("OCSID.MODULE", "");
p.put("OCSID.ACTION", "");
connection.setClientInfo(p);
41
Java ADF
To set your code’s module and action names...
64. @CaryMillsap 64
begin prepare
CPU
latch-related syscall
CPU
end prepare
begin exec
CPU
write(SQLNET_OUT, result_to_client);
end exec
read(SQLNET_IN, next_request_from_client);
begin fetch
CPU
latch-related syscall
CPU
write(SQLNET_OUT, result_to_client);
end fetch
read(SQLNET_IN, next_request_from_client);
begin fetch
CPU
write(SQLNET_OUT, result_to_client);
write(SQLNET_OUT, more_results);
write(SQLNET_OUT, more_results);
end fetch
read(SQLNET_IN, next_request_from_client);
begin fetch
CPU
write(SQLNET_OUT, result_to_client);
write(SQLNET_OUT, more_results);
write(SQLNET_OUT, more_results);
end fetch
read(SQLNET_IN, next_request_from_client);
begin fetch
CPU
write(SQLNET_OUT, result_to_client);
write(SQLNET_OUT, more_results);
write(SQLNET_OUT, more_results);
end fetch
read(SQLNET_IN, next_request_from_client);
Oracle kernel code path
This is the
kind of stuff
your code
causes the
Oracle kernel
to do.
65. @CaryMillsap 65
WAIT #42: nam='latch: library cache'…
PARSE #42:c=10000,…
WAIT #42: nam='SQL*Net message to client'…
EXEC #42:c=10000,…
WAIT #42: nam='SQL*Net message from client'…
WAIT #42: nam='latch: cache buffers chains'…
WAIT #42: nam='SQL*Net message to client'…
FETCH #42:c=20000,…
WAIT #42: nam='SQL*Net message from client'…
WAIT #42: nam='SQL*Net message to client'…
WAIT #42: nam='SQL*Net more data to client'…
WAIT #42: nam='SQL*Net more data to client'…
FETCH #42:c=20000,…
WAIT #42: nam='SQL*Net message from client'…
WAIT #42: nam='SQL*Net message to client'…
WAIT #42: nam='SQL*Net more data to client'…
WAIT #42: nam='SQL*Net more data to client'…
FETCH #42:c=20000,…
WAIT #42: nam='SQL*Net message from client'…
WAIT #42: nam='SQL*Net message to client'…
WAIT #42: nam='SQL*Net more data to client'…
WAIT #42: nam='SQL*Net more data to client'…
FETCH #42:c=20000,…
WAIT #42: nam='SQL*Net message from client'…
Oracle extended SQL trace data
begin prepare
CPU
latch-related syscall
CPU
end prepare
begin exec
CPU
write(SQLNET_OUT, result_to_client);
end exec
read(SQLNET_IN, next_request_from_client);
begin fetch
CPU
latch-related syscall
CPU
write(SQLNET_OUT, result_to_client);
end fetch
read(SQLNET_IN, next_request_from_client);
begin fetch
CPU
write(SQLNET_OUT, result_to_client);
write(SQLNET_OUT, more_results);
write(SQLNET_OUT, more_results);
end fetch
read(SQLNET_IN, next_request_from_client);
begin fetch
CPU
write(SQLNET_OUT, result_to_client);
write(SQLNET_OUT, more_results);
write(SQLNET_OUT, more_results);
end fetch
read(SQLNET_IN, next_request_from_client);
begin fetch
CPU
write(SQLNET_OUT, result_to_client);
write(SQLNET_OUT, more_results);
write(SQLNET_OUT, more_results);
end fetch
read(SQLNET_IN, next_request_from_client);
Oracle kernel code path
This is the
kind of
trace data
the kernel
produces.
66. @CaryMillsap 66
WAIT #42: nam='latch: library cache'…
PARSE #42:c=10000,…
WAIT #42: nam='SQL*Net message to client'…
EXEC #42:c=10000,…
WAIT #42: nam='SQL*Net message from client'…
WAIT #42: nam='latch: cache buffers chains'…
WAIT #42: nam='SQL*Net message to client'…
FETCH #42:c=20000,…
WAIT #42: nam='SQL*Net message from client'…
WAIT #42: nam='SQL*Net message to client'…
WAIT #42: nam='SQL*Net more data to client'…
WAIT #42: nam='SQL*Net more data to client'…
FETCH #42:c=20000,…
WAIT #42: nam='SQL*Net message from client'…
WAIT #42: nam='SQL*Net message to client'…
WAIT #42: nam='SQL*Net more data to client'…
WAIT #42: nam='SQL*Net more data to client'…
FETCH #42:c=20000,…
WAIT #42: nam='SQL*Net message from client'…
WAIT #42: nam='SQL*Net message to client'…
WAIT #42: nam='SQL*Net more data to client'…
WAIT #42: nam='SQL*Net more data to client'…
FETCH #42:c=20000,…
WAIT #42: nam='SQL*Net message from client'…
Oracle extended SQL trace data
Of course,
you don’t
directly get
to see the
kernel code
path.
67. @CaryMillsap 67
WAIT #42: nam='latch: library cache'…
PARSE #42:c=10000,…
WAIT #42: nam='SQL*Net message to client'…
EXEC #42:c=10000,…
WAIT #42: nam='SQL*Net message from client'…
WAIT #42: nam='latch: cache buffers chains'…
WAIT #42: nam='SQL*Net message to client'…
FETCH #42:c=20000,…
WAIT #42: nam='SQL*Net message from client'…
WAIT #42: nam='SQL*Net message to client'…
WAIT #42: nam='SQL*Net more data to client'…
WAIT #42: nam='SQL*Net more data to client'…
FETCH #42:c=20000,…
WAIT #42: nam='SQL*Net message from client'…
WAIT #42: nam='SQL*Net message to client'…
WAIT #42: nam='SQL*Net more data to client'…
WAIT #42: nam='SQL*Net more data to client'…
FETCH #42:c=20000,…
WAIT #42: nam='SQL*Net message from client'…
WAIT #42: nam='SQL*Net message to client'…
WAIT #42: nam='SQL*Net more data to client'…
WAIT #42: nam='SQL*Net more data to client'…
FETCH #42:c=20000,…
WAIT #42: nam='SQL*Net message from client'…
Oracle extended SQL trace data
...Or that
helpful grid
that I drew
for you.
68. @CaryMillsap 68
WAIT #42: nam='latch: library cache'…
PARSE #42:c=10000,…
WAIT #42: nam='SQL*Net message to client'…
EXEC #42:c=10000,…
WAIT #42: nam='SQL*Net message from client'…
WAIT #42: nam='latch: cache buffers chains'…
WAIT #42: nam='SQL*Net message to client'…
FETCH #42:c=20000,…
WAIT #42: nam='SQL*Net message from client'…
WAIT #42: nam='SQL*Net message to client'…
WAIT #42: nam='SQL*Net more data to client'…
WAIT #42: nam='SQL*Net more data to client'…
FETCH #42:c=20000,…
WAIT #42: nam='SQL*Net message from client'…
WAIT #42: nam='SQL*Net message to client'…
WAIT #42: nam='SQL*Net more data to client'…
WAIT #42: nam='SQL*Net more data to client'…
FETCH #42:c=20000,…
WAIT #42: nam='SQL*Net message from client'…
WAIT #42: nam='SQL*Net message to client'…
WAIT #42: nam='SQL*Net more data to client'…
WAIT #42: nam='SQL*Net more data to client'…
FETCH #42:c=20000,…
WAIT #42: nam='SQL*Net message from client'…
Oracle extended SQL trace data
All you
get to see
is this.
69. @CaryMillsap
WAIT #42: nam='latch: library cache'…
PARSE #42:c=10000,…
WAIT #42: nam='SQL*Net message to client'…
EXEC #42:c=10000,…
WAIT #42: nam='SQL*Net message from client'…
WAIT #42: nam='latch: cache buffers chains'…
WAIT #42: nam='SQL*Net message to client'…
FETCH #42:c=20000,…
WAIT #42: nam='SQL*Net message from client'…
WAIT #42: nam='SQL*Net message to client'…
WAIT #42: nam='SQL*Net more data to client'…
WAIT #42: nam='SQL*Net more data to client'…
FETCH #42:c=20000,…
WAIT #42: nam='SQL*Net message from client'…
WAIT #42: nam='SQL*Net message to client'…
WAIT #42: nam='SQL*Net more data to client'…
WAIT #42: nam='SQL*Net more data to client'…
FETCH #42:c=20000,…
WAIT #42: nam='SQL*Net message from client'…
WAIT #42: nam='SQL*Net message to client'…
WAIT #42: nam='SQL*Net more data to client'…
WAIT #42: nam='SQL*Net more data to client'…
FETCH #42:c=20000,…
WAIT #42: nam='SQL*Net message from client'…
69
Oracle extended SQL trace dataOracle kernel code path
begin prepare
CPU
latch-related syscall
CPU
end prepare
begin exec
CPU
write(SQLNET_OUT, result_to_client);
end exec
read(SQLNET_IN, next_request_from_client);
begin fetch
CPU
latch-related syscall
CPU
write(SQLNET_OUT, result_to_client);
end fetch
read(SQLNET_IN, next_request_from_client);
begin fetch
CPU
write(SQLNET_OUT, result_to_client);
write(SQLNET_OUT, more_results);
write(SQLNET_OUT, more_results);
end fetch
read(SQLNET_IN, next_request_from_client);
begin fetch
CPU
write(SQLNET_OUT, result_to_client);
write(SQLNET_OUT, more_results);
write(SQLNET_OUT, more_results);
end fetch
read(SQLNET_IN, next_request_from_client);
begin fetch
CPU
write(SQLNET_OUT, result_to_client);
write(SQLNET_OUT, more_results);
write(SQLNET_OUT, more_results);
end fetch
read(SQLNET_IN, next_request_from_client);
You can learn
to envision the
kernel’s code
path that
motivated
your trace file.
86. @CaryMillsap
OPTIM
IZE ANYT
HING
M
eTH O D
R
You might have known that you should
“use bind variables,” but you couldn’t
have quantified the R impact on the
experience without this trace file.
86
88. @CaryMillsap
BASELINE: BAD
for each invoice number {
cursor = parse(“select ...where invoice_number = ” + number);
exec(cursor);
loop over the result set to fetch all the rows;
}
FIX 1 “Hey, let’s use bind variables”:
for each invoice number {
cursor = parse(“select ...where invoice_number = :a1”);
exec(cursor, number);
loop over the result set to fetch all the rows;
}
88
STILL BAD
A little better, but still really awful:
• Uses too much CPU for PARSE calls
• Serialization on library cache latches
• Maybe, too many network round-trips