Apress.Expert.Oracle.Database.Architecture.9i.and.10g.Programming.Techniques.and.Solutions.Sep.2005
CHAPTER 10 ■ DATABASE TABLES 359 ops$tkyte@ORA10GR1> begin 2 for x in ( select empno from emp ) 3 loop 4 for y in ( select emp.ename, a.street, a.city, a.state, a.zip 5 from emp, heap_addresses a 6 where emp.empno = a.empno 7 and emp.empno = x.empno ) 8 loop 9 null; 10 end loop; 11 end loop; 12 end; 13 / PL/SQL procedure successfully completed. Here, we are just emulating a busy period and running the query some 45,000 times, once for each EMPNO. If we run that for the HEAP_ADRESSES and IOT_ADDRESSES tables, TKPROF shows us the following: SELECT EMP.ENAME, A.STREET, A.CITY, A.STATE, A.ZIP FROM EMP, HEAP_ADDRESSES A WHERE EMP.EMPNO = A.EMPNO AND EMP.EMPNO = :B1 call count cpu elapsed disk query current rows ------- ------ -------- ---------- ---------- ---------- ---------- ---------- Parse 1 0.00 0.00 0 0 0 0 Execute 48244 7.66 7.42 0 0 0 0 Fetch 48244 6.29 6.56 0 483393 0 192976 ------- ------ -------- ---------- ---------- ---------- ---------- ---------- total 96489 13.95 13.98 0 483393 0 192976 Rows Row Source Operation ------- --------------------------------------------------- 192976 NESTED LOOPS (cr=483393 pr=0 pw=0 time=5730335 us) 48244 TABLE ACCESS BY INDEX ROWID EMP (cr=144732 pr=0 pw=0 time=1594981 us) 48244 INDEX UNIQUE SCAN EMP_PK (cr=96488 pr=0 pw=0 time=926147 us)... 192976 TABLE ACCESS BY INDEX ROWID HEAP_ADDRESSES (cr=338661 pr=0 pw=0 time=... 192976 INDEX RANGE SCAN SYS_C008073 (cr=145685 pr=0 pw=0 time=1105135 us)... ******************************************************************************** SELECT EMP.ENAME, A.STREET, A.CITY, A.STATE, A.ZIP FROM EMP, IOT_ADDRESSES A WHERE EMP.EMPNO = A.EMPNO AND EMP.EMPNO = :B1 call count cpu elapsed disk query current rows ------- ------ -------- ---------- ---------- ---------- ---------- ---------- Parse 1 0.00 0.00 0 0 0 0 Execute 48244 8.17 8.81 0 0 0 0 Fetch 48244 4.31 4.12 0 292918 0 192976
360 CHAPTER 10 ■ DATABASE TABLES ------- ------ -------- ---------- ---------- ---------- ---------- ---------- total 96489 12.48 12.93 0 292918 0 192976 Rows Row Source Operation ------- --------------------------------------------------- 192976 NESTED LOOPS (cr=292918 pr=0 pw=0 time=3429753 us) 48244 TABLE ACCESS BY INDEX ROWID EMP (cr=144732 pr=0 pw=0 time=1615024 us) 48244 INDEX UNIQUE SCAN EMP_PK (cr=96488 pr=0 pw=0 time=930931 us)... 192976 INDEX RANGE SCAN SYS_IOT_TOP_59607 (cr=148186 pr=0 pw=0 time=1417238 us)... Both queries fetched exactly the same number of rows, but the HEAP table performed considerably more logical I/O. As the degree of concurrency on the system goes up, we would likewise expect the CPU used by the HEAP table to go up more rapidly as well, while the query possibly waits for latches into the buffer cache. Using runstats (a utility of my own design), we can measure the difference in latching. On my system, I observed the following: STAT...consistent gets 484,065 293,566 -190,499 STAT...no work - consistent re 194,546 4,047 -190,499 STAT...consistent gets from ca 484,065 293,566 -190,499 STAT...session logical reads 484,787 294,275 -190,512 STAT...table fetch by rowid 241,260 48,260 -193,000 STAT...buffer is not pinned co 337,770 96,520 -241,250 LATCH.cache buffers chains 732,960 349,380 -383,580 Run1 latches total versus runs -- difference and pct Run1 Run2 Diff Pct 990,344 598,750 -391,594 165.40% where Run1 was the HEAP_ADDRESSES table and Run2 was the IOT_ADDRESSES table. As you can see, there was a dramatic and repeatable decrease in the latching taking place, mostly due to the cache buffers chains latch (the one that protects the buffer cache). The IOT in this case would provide the following benefits: • Increased buffer cache efficiency, as any given query needs to have fewer blocks in the cache • Decreased buffer cache access, which increases scalability • Less overall work to retrieve our data, as it is faster • Less physical I/O per query possibly, as fewer distinct blocks are needed for any given query and a single physical I/O of the addresses most likely retrieves all of them (not just one of them, as the heap table implementation does) The same would be true if you frequently use BETWEEN queries on a primary or unique key. Having the data stored physically sorted will increase the performance of those queries as well. For example, I maintain a table of stock quotes in my database. Every day, for hundreds of stocks, I gather together the stock ticker, date, closing price, days high, days low, volume, and other related information. The table looks like this:
- Page 353 and 354: 308 CHAPTER 9 ■ REDO AND UNDO So,
- Page 355 and 356: 310 CHAPTER 9 ■ REDO AND UNDO ops
- Page 357 and 358: 312 CHAPTER 9 ■ REDO AND UNDO ops
- Page 359 and 360: 314 CHAPTER 9 ■ REDO AND UNDO •
- Page 361 and 362: 316 CHAPTER 9 ■ REDO AND UNDO ...
- Page 363 and 364: 318 CHAPTER 9 ■ REDO AND UNDO •
- Page 365 and 366: 320 CHAPTER 9 ■ REDO AND UNDO bac
- Page 367 and 368: 322 CHAPTER 9 ■ REDO AND UNDO As
- Page 369 and 370: 324 CHAPTER 9 ■ REDO AND UNDO ops
- Page 371 and 372: 326 CHAPTER 9 ■ REDO AND UNDO wil
- Page 373 and 374: 328 CHAPTER 9 ■ REDO AND UNDO Thi
- Page 375 and 376: 330 CHAPTER 9 ■ REDO AND UNDO ops
- Page 377 and 378: 332 CHAPTER 9 ■ REDO AND UNDO Whe
- Page 379 and 380: 334 CHAPTER 9 ■ REDO AND UNDO Tha
- Page 381 and 382: 336 CHAPTER 9 ■ REDO AND UNDO tou
- Page 383 and 384: 338 CHAPTER 10 ■ DATABASE TABLES
- Page 385 and 386: 340 CHAPTER 10 ■ DATABASE TABLES
- Page 387 and 388: 342 CHAPTER 10 ■ DATABASE TABLES
- Page 389 and 390: 344 CHAPTER 10 ■ DATABASE TABLES
- Page 391 and 392: 346 CHAPTER 10 ■ DATABASE TABLES
- Page 393 and 394: 348 CHAPTER 10 ■ DATABASE TABLES
- Page 395 and 396: 350 CHAPTER 10 ■ DATABASE TABLES
- Page 397 and 398: 352 CHAPTER 10 ■ DATABASE TABLES
- Page 399 and 400: 354 CHAPTER 10 ■ DATABASE TABLES
- Page 401 and 402: 356 CHAPTER 10 ■ DATABASE TABLES
- Page 403: 358 CHAPTER 10 ■ DATABASE TABLES
- Page 407 and 408: 362 CHAPTER 10 ■ DATABASE TABLES
- Page 409 and 410: 364 CHAPTER 10 ■ DATABASE TABLES
- Page 411 and 412: 366 CHAPTER 10 ■ DATABASE TABLES
- Page 413 and 414: 368 CHAPTER 10 ■ DATABASE TABLES
- Page 415 and 416: 370 CHAPTER 10 ■ DATABASE TABLES
- Page 417 and 418: 372 CHAPTER 10 ■ DATABASE TABLES
- Page 419 and 420: 374 CHAPTER 10 ■ DATABASE TABLES
- Page 421 and 422: 376 CHAPTER 10 ■ DATABASE TABLES
- Page 423 and 424: 378 CHAPTER 10 ■ DATABASE TABLES
- Page 425 and 426: 380 CHAPTER 10 ■ DATABASE TABLES
- Page 427 and 428: 382 CHAPTER 10 ■ DATABASE TABLES
- Page 429 and 430: 384 CHAPTER 10 ■ DATABASE TABLES
- Page 431 and 432: 386 CHAPTER 10 ■ DATABASE TABLES
- Page 433 and 434: 388 CHAPTER 10 ■ DATABASE TABLES
- Page 435 and 436: 390 CHAPTER 10 ■ DATABASE TABLES
- Page 437 and 438: 392 CHAPTER 10 ■ DATABASE TABLES
- Page 439 and 440: 394 CHAPTER 10 ■ DATABASE TABLES
- Page 441 and 442: 396 CHAPTER 10 ■ DATABASE TABLES
- Page 443 and 444: 398 CHAPTER 10 ■ DATABASE TABLES
- Page 445 and 446: 400 CHAPTER 10 ■ DATABASE TABLES
- Page 447 and 448: 402 CHAPTER 10 ■ DATABASE TABLES
- Page 449 and 450: 404 CHAPTER 10 ■ DATABASE TABLES
- Page 451 and 452: 406 CHAPTER 10 ■ DATABASE TABLES
- Page 453 and 454: 408 CHAPTER 10 ■ DATABASE TABLES
CHAPTER 10 ■ DATABASE TABLES 359<br />
ops$tkyte@ORA10GR1> begin<br />
2 for x in ( select empno from emp )<br />
3 loop<br />
4 for y in ( select emp.ename, a.street, a.city, a.state, a.zip<br />
5 from emp, heap_addresses a<br />
6 where emp.empno = a.empno<br />
7 <strong>and</strong> emp.empno = x.empno )<br />
8 loop<br />
9 null;<br />
10 end loop;<br />
11 end loop;<br />
12 end;<br />
13 /<br />
PL/SQL procedure successfully completed.<br />
Here, we are just emulating a busy period <strong>and</strong> running the query some 45,000 times, once<br />
for each EMPNO. If we run that for the HEAP_ADRESSES <strong>and</strong> IOT_ADDRESSES tables, TKPROF shows us<br />
the following:<br />
SELECT EMP.ENAME, A.STREET, A.CITY, A.STATE, A.ZIP<br />
FROM EMP, HEAP_ADDRESSES A<br />
WHERE EMP.EMPNO = A.EMPNO AND EMP.EMPNO = :B1<br />
call count cpu elapsed disk query current rows<br />
------- ------ -------- ---------- ---------- ---------- ---------- ----------<br />
Parse 1 0.00 0.00 0 0 0 0<br />
Execute 48244 7.66 7.42 0 0 0 0<br />
Fetch 48244 6.29 6.56 0 483393 0 192976<br />
------- ------ -------- ---------- ---------- ---------- ---------- ----------<br />
total 96489 13.95 13.98 0 483393 0 192976<br />
Rows Row Source Operation<br />
------- ---------------------------------------------------<br />
192976 NESTED LOOPS (cr=483393 pr=0 pw=0 time=5730335 us)<br />
48244 TABLE ACCESS BY INDEX ROWID EMP (cr=144732 pr=0 pw=0 time=1594981 us)<br />
48244 INDEX UNIQUE SCAN EMP_PK (cr=96488 pr=0 pw=0 time=926147 us)...<br />
192976 TABLE ACCESS BY INDEX ROWID HEAP_ADDRESSES (cr=338661 pr=0 pw=0 time=...<br />
192976 INDEX RANGE SCAN SYS_C008073 (cr=145685 pr=0 pw=0 time=1105135 us)...<br />
********************************************************************************<br />
SELECT EMP.ENAME, A.STREET, A.CITY, A.STATE, A.ZIP<br />
FROM EMP, IOT_ADDRESSES A<br />
WHERE EMP.EMPNO = A.EMPNO AND EMP.EMPNO = :B1<br />
call count cpu elapsed disk query current rows<br />
------- ------ -------- ---------- ---------- ---------- ---------- ----------<br />
Parse 1 0.00 0.00 0 0 0 0<br />
Execute 48244 8.17 8.81 0 0 0 0<br />
Fetch 48244 4.31 4.12 0 292918 0 192976