In my first post about the amazing in-memory feature (others are coming) I will talk about some “unwanted behavior” with the in-memory feature when choosing the best execution plan and the table was not yet populated in the in-memory column store.
Here is a quick and simple example :
My test database is of course 12.1.0.2
SQL> show parameter inm
NAME TYPE VALUE |
POOL ALLOC_BYTES USED_BYTES POPULATE_STATUS CON_ID
————————– ———– ———- ————————– ———- |
I will begin by creating a sample table and collect stats :
create table TEST_INMEMORY as select * from dba_objects; |
Check the execution plan for the flowing query.
select count(OBJECT_NAME) from TEST_INMEMORY;
Plan d’exÚcution ———————————————————————————— |
Create an index on “object_name” and then check the execution plan for the flowing query.
select count(OBJECT_NAME) from TEST_INMEMORY;Plan d’exÚcution ———————————————————- Plan hash value: 71185874 ——————————————————————————————- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | ——————————————————————————————- | 0 | SELECT STATEMENT | | 1 | 25 | 251 (1)| 00:00:01 | | 1 | SORT AGGREGATE | | 1 | 25 | | | | 2 | INDEX FAST FULL SCAN| TEST_INMEMORY_IDX | 183K| 4472K| 251 (1)| 00:00:01 | ——————————————————————————————- |
>So using index fast full scan is cheaper than a full table scan.
Suppose now that we mark the table for in-memory.
alter table TEST_INMEMORY inmemory; |
And now execute our query :
Plan d’exÚcution ———————————————————- Plan hash value: 1029766195 ——————————————————————————————— | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | ——————————————————————————————— | 0 | SELECT STATEMENT | | 1 | 25 | 44 (3) | 00:00:01 | | 1 | SORT AGGREGATE | | 1 | 25 | | | | 2 | TABLE ACCESS INMEMORY FULL| TEST_INMEMORY | 183K| 4472K| 44 (3)| 00:00:01 | ——————————————————————————————— |
let’s check session stats : (I used Tanel Poder snapper for this)
—————————————————————————————————— SID, USERNAME , TYPE, STATISTIC , DELTA, —————————————————————————————————— 60, HATEM , STAT, Requests to/from client , 3, 60, HATEM , STAT, opened cursors cumulative , 8, 60, HATEM , STAT, user calls , 4, 60, HATEM , STAT, recursive calls , 9, 60, HATEM , STAT, session logical reads , 3114, . . . 60, HATEM , STAT, consistent gets , 3114, 60, HATEM , STAT, consistent gets from cache , 3114, 60, HATEM , STAT, consistent gets pin , 3110, 60, HATEM , STAT, consistent gets pin (fastpath) , 3110, 60, HATEM , STAT, consistent gets examination , 4, 60, HATEM , STAT, consistent gets examination (fastpath) , 4, 60, HATEM , STAT, logical read bytes from cache , 25509888, 60, HATEM , STAT, calls to kcmgcs , 14, 60, HATEM , STAT, calls to get snapshot scn: kcmgss , 8, 60, HATEM , STAT, no work – consistent read gets , 3098, 60, HATEM , STAT, table scans (long tables) , 1, 60, HATEM , STAT, table scan rows gotten , 183255, 60, HATEM , STAT, table scan disk non-IMC rows gotten , 183255, 60, HATEM , STAT, table scan blocks gotten , 3094, 60, HATEM , STAT, index scans kdiixs1 , 6, 60, HATEM , STAT, IM populate segments requested , 1, 60, HATEM , STAT, IM scan segments disk , 1, 60, HATEM , STAT, session cursor cache hits , 7, . . . 60, HATEM , TIME, DB time , 38804, 60, HATEM , WAIT, SQL*Net message to client , 7, 60, HATEM , WAIT, SQL*Net message from client , 5059941, |
As we expect on the first execution the table columns are not yet populated in the column store so we will trigger it’s population “IM populate segments requested” . But wait we are doing a full table scan of the table(look at the statistics “table scans (long tables)” ” table scan rows gotten” etc ).So even when we know that using the index is faster we are doing a full table scan. Imagine also that you’re in-memory store is full (like in my case) so subsequent execution also will do full table scan if the table is still not populated.
The optimizer will choose the best execution plan but cannot know in advance if the table is populated or not. (There is no such info in optimizer trace)
UPDATE 18/11/2015 : After investigation the problem appeared to be related to “IM Quotient” statistics for more info check this blog post
===================================== Access path analysis for TEST_INMEMORY *************************************** SINGLE TABLE ACCESS PATH Single Table Cardinality Estimation for TEST_INMEMORY[TEST_INMEMORY] SPD: Return code in qosdDSDirSetup: NOCTX, estType = TABLE Table: TEST_INMEMORY Alias: TEST_INMEMORY Card: Original: 183187.000000 Rounded: 183187 Computed: 183187.000000 Non Adjusted: 183187.000000 Scan IO Cost (Disk) = 0.000000 Scan CPU Cost (Disk) = 0.000000 Scan IO Cost (IMC) = 31.760000 (read invalid rows) + 0.500000 (read extent map) = 32.260000 Scan CPU Cost (IMC) = 45000.000000 (overhead) + 10000.000000 (traverse IMCU) + 0.000000 (min-max eval) (= 200.000000 (min-max eval per cu) * 0 (#min-max CUs)) + 0.000000 (decompression) (= 0.000000 (per IMCU) * 1 (#IMCUs) * 1.000000 (prune ratio)) + 0.000000 (pred eval) (= 0.000000 (per row) * 183187 (#IMCrows) * 1.000000 (prune ratio)) + 18318700.000000 (row stitch) (= 100.000000 (per col) * 1 (#cols) * 183187 (#IMCUs) * 1.000000 (prune ratio)) + 16486830.000000 (scan journal) (= 3600.000000 (per row) * 4580 (#journal rows)) = 34860530.000000 Total Scan IO Cost = 0.000000 (scan (Disk)) + 32.260000 (scan (IMC)) = 32.260000 Total Scan CPU Cost = 0.000000 (scan (Disk)) + 34860530.000000 (scan (IMC)) = 34860530.000000 Access Path: TableScan Cost: 33.137930 Resp: 33.137930 Degree: 0 Cost_io: 32.260000 Cost_cpu: 34860530 Resp_io: 32.260000 Resp_cpu: 34860530 Access Path: index (index (FFS)) Index: TEST_INMEMORY_IDX resc_io: 250.000000 resc_cpu: 28498558 ix_sel: 0.000000 ix_sel_with_filters: 1.000000 Access Path: index (FFS) Cost: 250.717710 Resp: 250.717710 Degree: 1 Cost_io: 250.000000 Cost_cpu: 28498558 Resp_io: 250.000000 Resp_cpu: 28498558 ****** Costing Index TEST_INMEMORY_IDX Access Path: index (FullScan) Index: TEST_INMEMORY_IDX resc_io: 917.000000 resc_cpu: 43167760 ix_sel: 1.000000 ix_sel_with_filters: 1.000000 Cost: 918.087140 Resp: 918.087140 Degree: 1 Best:: AccessPath: TableScan Cost: 33.137930 Degree: 1 Resp: 33.137930 Card: 183187.000000 Bytes: 0.000000 |
I think that a feature like adaptive plan can be of use in these case. If the table is not populated then go for the best alternative plan. May be in the next patchset 🙂 .
[…] This is the second blog post about the new in-memory feature of oracle database 12c.For the preceding post Part 1 […]
[…] other parts : Part1 , […]
[…] ), it can be in the first execution when no priority is set (Like the example i demonstrated in this blog post ) or like this case when in-memory store is full.This seems very buggy […]