简介: 在sql调优的时候,一个关键指标就是consistent gets,如果这个指标很低,一般认为sql语句执行还是很高效的,反之效率会很低。但是这个指标我们知之甚少,对于这个指标的计算方式我们也是懵懵懂懂。
在sql调优的时候,一个关键指标就是consistent gets,如果这个指标很低,一般认为sql语句执行还是很高效的,反之效率会很低。但是这个指标我们知之甚少,对于这个指标的计算方式我们也是懵懵懂懂。对于逻辑读来说,一般都是基于Logical Reads= Consistent Gets + DB Block Gets
如果我们知道logical reads是1000,我们可能错误地认为查询读取了1000*8k(约为8M)
看了博客https://viveklsharma.wordpress.com/2010/03/04/consistent-gets-myth/后,发现自己的认识是错误的,也按捺不住在本地测试了一把,受益匪浅。
首先我们来创建一个表,数据量为2000条。
n1@TEST11G> create table test_consistent_get as select * from all_objects where rownum between 1 and 2000;
Table created.
然后收集统计信息
n1@TEST11G> exec dbms_stats.gather_table_stats(user,‘TEST_CONSISTENT_GET‘);
PL/SQL procedure successfully completed.
查看相应的数据块为30个
n1@TEST11G> select num_rows,blocks,table_name,last_analyzed,global_stats from user_tables where table_name=‘TEST_CONSISTENT_GET‘;
NUM_ROWS BLOCKS TABLE_NAME LAST_ANAL GLO
---------- ---------- ------------------------------ --------- ---
2000 30 TEST_CONSISTENT_GET 20-APR-15 YES
n1@TEST11G> set autot trace
我们来看看执行计划,很明显走了一个全表扫描。但是我们需要关注的是统计信息中的consistent gets
n1@TEST11G> select * from test_consistent_get;
Execution Plan
----------------------------------------------------------
Plan hash value: 1444268095
-----------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-----------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 2000 | 164K| 10 (0)| 00:00:01 |
| 1 | TABLE ACCESS FULL| TEST_CONSISTENT_GET | 2000 | 164K| 10 (0)| 00:00:01 |
-----------------------------------------------------------------------------------------
Statistics
----------------------------------------------------------
1 recursive calls
0 db block gets
163 consistent gets
0 physical reads
0 redo size
199754 bytes sent via SQL*Net to client
1883 bytes received via SQL*Net from client
135 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
2000 rows processed
可以看到这个表占用的数据块为30,但是consistent gets却为163,很显然不是说这个全表扫描向cache里读入了163*8k的数据
我们可以通过rowid来得到对应的数据块和其中的数据情况
n1@TEST11G> select dbms_rowid.ROWID_BLOCK_NUMBER(rowid) blkno, count(*) cnt
from test_consistent_get
group by dbms_rowid.ROWID_BLOCK_NUMBER(rowid) order by 1;
BLKNO CNT
---------- ----------
263827 88
263828 84
263829 81
263830 76
263831 81
263832 80
263833 82
263834 77
263835 73
263836 78
263837 79
263838 79
263839 81
263841 82
263842 77
263843 81
263844 80
263845 81
263846 78
263847 78
263848 76
263849 78
263850 78
263851 76
263852 81
263853 15
26 rows selected.
可以通过rowid得到相关的数据块为26个。查看段头,发现对应的数据块是263826是不在上面的rowid对应的数据块范围内的。
n1@TEST11G> select header_block,blocks ,extents from dba_segments where segment_name=‘TEST_CONSISTENT_GET‘;
HEADER_BLOCK BLOCKS EXTENTS
------------ ---------- ----------
263826 32 4
对应的区和数据块信息如下:
n1@TEST11G> select EXTENT_ID, FILE_ID, BLOCK_ID, BLOCKS from dba_extents where SEGMENT_NAME=‘TEST_CONSISTENT_GET‘;
EXTENT_ID FILE_ID BLOCK_ID BLOCKS
---------- ---------- ---------- ----------
0 4 263824 8
1 4 263832 8
2 4 263840 8
3 4 263848 8
下面的语句可以算出对于每个数据块对应的consistent gets的值。
n1@TEST11G>
variable b1 number;
exec :b1:=15;
compute sum of total_cnt on report
compute sum of touch_cnt on report
break on report
select blkno, total_cnt, final_cnt, rows_remaining,
case when rows_remaining=0 then touch_cnt+1 else touch_cnt end touch_cnt
from (
select blkno, total_cnt, final_cnt, rows_remaining,
case when total_cnt = final_cnt then ceil(final_cnt/:b1) else ceil(final_cnt/:b1)+1 end touch_cnt
from (
select blkno, cnt total_cnt,
case when rownum=1 or lag(rows_remaining) over (order by blkno)=0
then cnt else (cnt-(:b1-lag(rows_remaining) over (order by blkno))) end final_cnt,
rows_remaining
from (
select blkno, cnt, rr,
lead(rr) over(order by blkno) next_rr,
lead(blkno) over(order by blkno) next_blk,
ceil(rr/:b1) touch_cnt,
mod(rr,:b1) rows_remaining
from (
select dbms_rowid.ROWID_BLOCK_NUMBER(rowid) blkno, count(*) cnt,
sum(count(*)) over(order by dbms_rowid.ROWID_BLOCK_NUMBER(rowid)) rr
from test_consistent_get
group by dbms_rowid.ROWID_BLOCK_NUMBER(rowid) order by 1))));
BLKNO TOTAL_CNT FINAL_CNT ROWS_REMAINING TOUCH_CNT
---------- ---------- ---------- -------------- ----------
263827 88 88 13 6
263828 84 82 7 7
263829 81 73 13 6
263830 76 74 14 6
263831 81 80 5 7
263832 80 70 10 6
263833 82 77 2 7
263834 77 64 4 6
263835 73 62 2 6
263836 78 65 5 6
263837 79 69 9 6
263838 79 73 13 6
263839 81 79 4 7
263841 82 71 11 6
263842 77 73 13 6
263843 81 79 4 7
263844 80 69 9 6
263845 81 75 0 7
263846 78 78 3 6
263847 78 66 6 6
263848 76 67 7 6
263849 78 70 10 6
263850 78 73 13 6
263851 76 74 14 6
263852 81 80 5 7
263853 15 5 5 2
---------- ----------
sum 2000 159
可以看到对于这个全表扫描的场景,consistent gets不是衡量对于cache的数据块数而是次数。
比如对于上面的数据块263827 ,数据条数为88条,arraysize为15,则可以简单说明一下是如何计算这个consistent gets值的。
对于数据块263827,放入PGA中,得到了15行,这个时候可以理解为consistent gets=1
对于数据块263827,再次从PGA中得到,得到了15行,这个时候consistent gets=2
依次类推
对于数据块263827,再次从PGA中得到,得到了13行,这个时候consistent gets=6
或者也可以基本按照这个公式来计算,数据行数/arraysize+数据块数=consistent gets
比如这个例子,2000/15+30 大概是163.3左右,所以163还是靠谱的。
对于arraysize未20,30,的情况下,相应的consistent gets也会减少。简单模拟一下。
n1@TEST11G> set arraysize 20
n1@TEST11G> set autot trace exp stat
n1@TEST11G> select *from test_consistent_get;
Statistics
----------------------------------------------------------
1 recursive calls
0 db block gets
128 consistent gets
0 physical reads
0 redo size
195334 bytes sent via SQL*Net to client
1509 bytes received via SQL*Net from client
101 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
2000 rows processed
n1@TEST11G> set autot off
n1@TEST11G> select 2000/20+30 from dual;
2000/20+30
----------
130
n1@TEST11G> set arraysize 30
n1@TEST11G> set autot trace stat
n1@TEST11G> select *from test_consistent_get;
Statistics
----------------------------------------------------------
0 recursive calls
0 db block gets
96 consistent gets
0 physical reads
0 redo size
191044 bytes sent via SQL*Net to client
1146 bytes received via SQL*Net from client
68 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
2000 rows processed
n1@TEST11G> set autot off
n1@TEST11G> select 2000/30+30 from dual;
2000/30+30
----------
96.6666667