2

Edit:

Database - Oracle 11gR2 - Over Exadata (X2)


Am framing a problem investigation report for past issues, and I'm slightly confused with a situation below.

Say , I have a table MYACCT. There are 138 Columns. It holds 10 Million records. With regular updates of at least 1000 records(Inserts/Updates/Deletes) per hour.

Primary Key is COL1 (VARCHAR2(18)) (Application rarely uses this, except for a join with other tables)

There's Another Unique Index over COL2 VARCHAR2(9)). Which is the one App regularly uses. What ever updates I meant previously happens based on both these columns. Whereas any SELECT only operation over this table, always refer COL2. So COL2 would be our interest.

We do a Query below,

SELECT COUNT(COL2) FROM MYACCT; /* Use the Unique Column (Not PK) */

There's no issue with the result, whereas I was the one recommending to change it as

SELECT COUNT(COL1) FROM MYACCT; /* Use the primary Index

I just calculated the time taken for actual execution

Query using the PRIMARY KEY was faster by `0.8-1.0 seconds always!

Now, I am trying to explain this behavior. Just drafting the Explain plan behind these Queries.

Query 1: (Using Primary Key)

SELECT COUNT(COL1) FROM MYACCT;

Plan :

SQL> select * from TABLE(dbms_xplan.display);
Plan hash value: 2417095184

---------------------------------------------------------------------------------
| Id  | Operation                     | Name    | Rows  | Cost (%CPU)| Time     |
---------------------------------------------------------------------------------
|   0 | SELECT STATEMENT              |         |     1 | 11337   (1)| 00:02:17 |
|   1 |  SORT AGGREGATE               |         |     1 |            |          |
|   2 |   INDEX STORAGE FAST FULL SCAN| PK_ACCT |    10M| 11337   (1)| 00:02:17 |
---------------------------------------------------------------------------------

9 rows selected.

Statistics
----------------------------------------------------------
          0  recursive calls
          0  db block gets
      41332  consistent gets
          0  physical reads
          0  redo size
        210  bytes sent via SQL*Net to client
        346  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          1  rows processed

Query 2:(NOT using Primary Key)

SELECT COUNT(COL2) FROM MYACCT;

Plan :

Plan hash value: 1130703739

------------------------------------------------------------------------------------------
| Id  | Operation                     | Name     | Rows  | Bytes | Cost (%CPU)| Time     |
------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT              |          |     1 |    10 |  7868   (1)| 00:01:35 |
|   1 |  SORT AGGREGATE               |          |     1 |    10 |            |          |
|   2 |   INDEX STORAGE FAST FULL SCAN| MYINDX01 |    10M|    95M|  7868   (1)| 00:01:35 |
------------------------------------------------------------------------------------------

9 rows selected.

Statistics
----------------------------------------------------------
          1  recursive calls
          0  db block gets
      28151  consistent gets
         23  physical reads
        784  redo size
        233  bytes sent via SQL*Net to client
        346  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          1  rows processed

We can find in terms of Cost and Time the Query without primary key wins. Then how come the execution time of primary key is faster???

EDIT:

SQL> select segment_name, bytes from dba_segments where segment_name in ('MYINDX01','PK_ACCT');
PK_ACCT               343932928
MYINDX01              234881024
Nicholas Carey
  • 71,308
  • 16
  • 93
  • 135
Maheswaran Ravisankar
  • 17,652
  • 6
  • 47
  • 69
  • Why do you say that select count(*) uses the primary key? – Dan Bracuk Jan 29 '14 at 17:13
  • Both `PK` and `*` gave me the same plan. The plan hash value was the same!! Anyway, My interest was still PK. I updated the question. Optimizer is choosing the `PK` for count, if I give it as `PK` or `*` – Maheswaran Ravisankar Jan 29 '14 at 17:17
  • What are the data types of the two columns? I'd guess maybe the faster query is against a smaller type and it's physically having to read less data from disk? What do the execution statistics show? You could also look at the size of the two indexes in `dba_segments`. Mmm, wait, you said 'query using the PK was faster' but the plans seem to show the opposite - are you talking about the plan time or the actual real time taken? – Alex Poole Jan 29 '14 at 17:21
  • Please login to SQLPlus, run the command: `SET AUTOTRACE TRACEONLY`, then execute both queries and append execution results to the question. – krokodilko Jan 29 '14 at 17:23
  • @AlexPoole PK is `VARCHAr2(18)` and the other one is `VARCHAr2(9)`. but PK always holding numeric values. The time I mean faster is the actual execution time. Also, Sorry, how to query the `dba_segments` for the index I look for? – Maheswaran Ravisankar Jan 29 '14 at 17:29
  • @kordirko I added the result of trace too! – Maheswaran Ravisankar Jan 29 '14 at 17:29
  • 1
    @MaheswaranRavisankar it may be holding numeric values, but as it's a varchar2 you might as well be storing strings. It's a really bad choice because even if you're assigning incrementing numerics then they are not "rightwards growing", and the index will be subject to 50/50 block splits instead of the more efficient 90/10 block splits. Your index would be smaller if it were numeric, for this reason. – David Aldridge Jan 29 '14 at 17:36
  • @MaheswaranRavisankar - `select segment_name, bytes from dba_segments where segment_name in ('PK_ACCT', 'MYINDX01');`. How many characters are you storing, roughly, in each column - is `COL1` using all 18 chars, or at least a lot more than `COL2` tends to be? – Alex Poole Jan 29 '14 at 17:39
  • Although I'm still confused - your latest update is stressing that the PK count is actually faster despite what the plan says; is that consistent, or only if that is getting its data from the block buffer and the other one has to read from disk (physical gets)? i.e. does changing the order you run the queries affect the time they take? – Alex Poole Jan 29 '14 at 17:42
  • @DavidAldridge I agree your point. This table is one of the key table of the model, and based on the PK, so many other entities are joined. It was supposed to have alpha characters as well. And this is a legacy model and legends were behind the model's design. For sure, they would have made it for a reason. Atleast I am unaware of it – Maheswaran Ravisankar Jan 29 '14 at 17:47
  • @AlexPoole to say in short, PK's actual execution is faster, Where-as comparing plan, PK is the one taking time.So, I am unable to predict the real reason behind. And I have added the `dba_segments` results you asked for. – Maheswaran Ravisankar Jan 29 '14 at 17:47

3 Answers3

4

You're reading more data from the PK index than you are from the other one. COL1 is VARCHAR2(18) while COL2 is VARCHAR(9), which doesn't necessarily mean anything but implies you probably have values in COL1 that are consistently longer than those in COL2. They will therefore use more storage, both in the table and in the index, and the index scan has to pull more data from the block buffer and/or disk for the PK-based query.

The execution statistics show that; 41332 consistent gets for the PK-based query, and only 28151 for the faster one, so it's doing more work with the PK. And the segment sizes show it too - for the PK you need to read about 328M, for the UK only 224M.

The block buffer is likely to be crucial if you're seeing the PK version run faster sometimes. In the example you've shown both queries are hitting the block buffer - the 23 physical reads are a trivial number, If the index data wasn't cached consistently then you might see 41k consistent gets versus 28k physical reads, which would likely reverse the apparent winner as physical reads from disk will be slower. This often manifests if running two queries back to back shows one faster, but reversing the order they run shows the other as faster.

You can't generalise this to 'PK query is slower then UK query'; this is because of your specific data. You'd probably also get better performance if your PK was actually a number column, rather than a VARCHAR2 column holding numbers, which is never a good idea.

Alex Poole
  • 183,384
  • 11
  • 179
  • 318
  • Alex, I see your comments. I think you misunderstood me. The reason you say justifies the plan showing higher costs for PK. But when comes to real execution, PK performs faster.(making the plan details false) You can see, the query without PK, has some redo log size, and doing 23 physical reads. Also , when you see the plan of the Non PK query, there's a column with Bytes, showing up 90M. I beleive, there's something in it. – Maheswaran Ravisankar Jan 29 '14 at 18:00
  • 2
    @MaheswaranRavisankar - 23 physical reads is nothing really, but the fact they are there at all is interesting. What happens if you run the queries back-to-back in different orders - the plans won't change but do the statistics and actual time? I would expect the 'winner' to vary depending on the order they run, and the physical read number to vary, maybe quite a lot, which would suggest you're reaching the limit of your block buffer size. I would run the PK one three times, then the UK one three times, then PK three times, and UK three times... and compare the timings and stats across runs. – Alex Poole Jan 29 '14 at 18:06
  • :) yes.. the number over physical reads are intersting 0 suddenly.. 200 again.. But the redo logs size too keep increasing according to it.! but after 3 attempts.. it is 0 forever.. might be caching it!! Driving me nuts.. Seems I touched a wrong chapter now. – Maheswaran Ravisankar Jan 29 '14 at 18:12
  • 2
    The operation `INDEX STORAGE FAST FULL SCAN` instead of the typical `INDEX FAST FULL SCAN` implies the query is running on Exadata. That probably makes the question much more complicated because Exadata has a lot of storage tricks. – Jon Heller Jan 29 '14 at 18:17
  • @jonearles I extremely missed it.. yes. it is Exadata (i think X2, quarter rac) DB is oracle 11gr2 – Maheswaran Ravisankar Jan 29 '14 at 18:19
  • @jonearles - I missed the significance of that, but I have no experience with Exadata so I've gone beyond my (limited) usefulness... – Alex Poole Jan 29 '14 at 18:24
  • @AlexPoole none of our DBA had clues on this.! I have been doing so many research (ofcourse googling) for this. – Maheswaran Ravisankar Jan 29 '14 at 18:27
  • @jonearles I always thought, exadata cells are used for SELECT operations. Even COUNT() is influenced by it? – Maheswaran Ravisankar Jan 29 '14 at 18:28
  • @MaheswaranRavisankar Sorry, I don't have any experience with Exadata either. So what was the issue? Was it a caching issue that went away after you ran the queries multiple times? – Jon Heller Jan 30 '14 at 04:42
  • @jonearles these queriies took 4.8 seconds for count(col2) and 2.8 for count(col1) first time.. 0.46 and 0.4 respectively afterwards.. The reason for being in consecutive attempts might be the exadata cells or some other cache.. I still dont understand it completely! – Maheswaran Ravisankar Jan 30 '14 at 04:47
1

Given a statement like

select count(x) from some_table

If there is a covering index for column x, the query optimizer is likely to use it, so it doesn't have to fetch the [ginormous] data page.

It sounds like the two columns (col1 and col2) involved in your similar queries are both indexed[1]. What you don't say is whether or not either of these indices are clustered.

That can make a big difference. If the index is clustered, the leaf node in the B-tree that is the index is the table's data page. Given how big your rows are (or seem to be), that means a scan of the clustered index will likely move a lot more data around -- meaning more paging -- than it would if it was scanning a non-clustered index.

Most aggregate functions eliminate nulls in computing the value of the aggregate function. count() is a little different. count(*) includes nulls in the results while count(expression) excludes nulls from the results. Since you're not using distinct, and assuming your col1 and col2 columns are not null, you might get better performance by trying

select count(*) from myacct

or

select count(1) from myacct

so the optimizer doesn't have to consider whether or not the column is null.

Just a thought.

[1]And I assume that they are the only column in their respective index.

Nicholas Carey
  • 71,308
  • 16
  • 93
  • 135
  • `count(*)` will [use an index on a not-null column if available](http://stackoverflow.com/a/1840613/266304); the PK fits that bill. So `count(*)` and `count()` will act the same, and the comments mention that those get the same plan here. If `col2` is nullable then you might get a different answer; if it isn't then I'd just use `count(*)` and let Oracle choose which index to use... – Alex Poole Jan 30 '14 at 10:00
0

Your PK query is doing 0 physical reads, suggesting you have the results in memory. So even though the execution plan looks slower, it's performing faster. The COL2 query is doing 23 physical reads.

ubanerjea
  • 474
  • 1
  • 7
  • 15