首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >补偏救弊 | 关于一致性读与语句性能关系的一大误区

补偏救弊 | 关于一致性读与语句性能关系的一大误区

作者头像
数据和云01
发布2019-05-26 20:50:54
3770
发布2019-05-26 20:50:54
举报
文章被收录于专栏:数据库新发现数据库新发现

研究背景

实际上,我们所说的保证同一时间点一致性读的概念,其背后是物理层面的 block 读,Oracle 会依据你发出 select 命令,记录下那一刻的 SCN 值,然后以这个 SCN 值去同所读的每个 block 上的 SCN 比较,如果读到的块上的 SCN 大于 select 发出时记录的 SCN,则需要利用 Undo 得到该 block 的前镜像,在内存中构造 CR 块(Consistent Read)。

一致性读(Consistent Gets,CG)是反映 SQL 语句性能的一项重要数据。它通常作为我们语句调优的指标。一般情况下,通过该数据可以比较两条语句或者同一语句的不同执行计划之间的性能。然而,某些情况下,它并不会完全反映出语句的性能。

分析探讨

我们先看两份性能统计数据:

SQL代码SQL 1: Statistics ----------------------- 0 recursive calls 0 db block gets 460 consistent gets 0 physical reads 0 redo size 1203583 bytes sent via SQL*Net to client 3868 bytes received via SQL*Net from client 306 SQL*Net roundtrips to/from client 0 sorts (memory) 0 sorts (disk) 4563 rows processed SQL 2: Statistics ---------------------------------------------------------- 0 recursive calls 0 db block gets 167 consistent gets 0 physical reads 0 redo size 267325 bytes sent via SQL*Net to client 3868 bytes received via SQL*Net from client 306 SQL*Net roundtrips to/from client 1 sorts (memory) 0 sorts (disk) 4563 rows processed

可以看到,第一条语句的 CG 是第二条语句的近3倍,看起来应该是第二条语句的性能更好。是否真是如此?

那再看看这两条语句是如何构造执行的:

SQL代码HelloDBA.COM> create table t1 as select * from dba_tables; Table created. HelloDBA.COM> create table t2 as select * from dba_users; Table created. HelloDBA.COM> exec dbms_stats.gather_table_stats('DEMO', 'T1'); PL/SQL procedure successfully completed. HelloDBA.COM> exec dbms_stats.gather_table_stats('DEMO', 'T2'); PL/SQL procedure successfully completed. HelloDBA.COM> set timing on HelloDBA.COM> set autot trace HelloDBA.COM> select * from t1; 4563 rows selected. Elapsed: 00:00:00.10 Execution Plan ---------------------------------------------------------- Plan hash value: 3617692013 -------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | -------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 4563 | 1078K| 49 (0)| 00:00:01 | | 1 | TABLE ACCESS FULL| T1 | 4563 | 1078K| 49 (0)| 00:00:01 | -------------------------------------------------------------------------- Statistics ---------------------------------------------------------- 0 recursive calls 0 db block gets 460 consistent gets 0 physical reads 0 redo size 1203583 bytes sent via SQL*Net to client 3868 bytes received via SQL*Net from client 306 SQL*Net roundtrips to/from client 0 sorts (memory) 0 sorts (disk) 4563 rows processed HelloDBA.COM> select * from t1, t2 where t2.username='SYS'; 4563 rows selected. Elapsed: 00:00:00.23 Execution Plan ---------------------------------------------------------- Plan hash value: 1323614827 ----------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | ----------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 4563 | 1581K| 52 (0)| 00:00:01 | | 1 | MERGE JOIN CARTESIAN| | 4563 | 1581K| 52 (0)| 00:00:01 | |* 2 | TABLE ACCESS FULL | T2 | 1 | 113 | 3 (0)| 00:00:01 | | 3 | BUFFER SORT | | 4563 | 1078K| 49 (0)| 00:00:01 | | 4 | TABLE ACCESS FULL | T1 | 4563 | 1078K| 49 (0)| 00:00:01 | ----------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 2 - filter("T2"."USERNAME"='SYS') Statistics ---------------------------------------------------------- 0 recursive calls 0 db block gets 167 consistent gets 0 physical reads 0 redo size 267325 bytes sent via SQL*Net to client 3868 bytes received via SQL*Net from client 306 SQL*Net roundtrips to/from client 1 sorts (memory) 0 sorts (disk) 4563 rows processed

这两条语句并不复杂。如果我们忽略性能统计数据,我们很容易就从其语句逻辑结构或者执行计划判断出它们的性能谁优谁劣。

但是为什么第二条语句的 CG 更少呢?

我们对它们作 SQL 运行跟踪,再看格式化的跟踪结果:

SQL代码Rows (1st) Rows (avg) Rows (max) Row Source Operation ---------- ---------- ---------- --------------------------------------------------- 4563 4563 4563 MERGE JOIN CARTESIAN (cr=167 pr=0 pw=0 time=38433 us cost=52 size=1619865 card=4563) 1 1 1 TABLE ACCESS FULL T2 (cr=3 pr=0 pw=0 time=78 us cost=3 size=113 card=1) 4563 4563 4563 BUFFER SORT (cr=164 pr=0 pw=0 time=22958 us cost=49 size=1104246 card=4563) 4563 4563 4563 TABLE ACCESS FULL T1 (cr=164 pr=0 pw=0 time=11815 us cost=49 size=1104246 card=4563)

这是第二条语句的计划统计数据。显然,它包含两个部分:对 T1 和 T2 的全表扫描访问。

在该执行计划当中,T1 的全表扫描的 CG 为 164,当时为什么在第一条语句中对其的全部扫描产生的 CG 为 466 呢?这是因为数据获取数组大小(fetch array size)设置的影响。

如下,在 SQLPlus 当中,该设置默认值为15,如果我们将其设得足够大,CG 将变为165,没错。因为无论该数组大小设为多大,Oracle 总是在第一次读取时读取第一条记录。

SQL代码HelloDBA.COM> set arraysize 5000 HelloDBA.COM> set autot trace stat HelloDBA.COM> select * from t1; Statistics ---------------------------------------------------------- 0 recursive calls 0 db block gets 165 consistent gets 0 physical reads 0 redo size 1147039 bytes sent via SQL*Net to client 524 bytes received via SQL*Net from client 2 SQL*Net roundtrips to/from client 0 sorts (memory) 0 sorts (disk) 4563 rows processed

关于全部扫描的 CG 可以参考该文章了解更多细节:http://www.hellodba.com/reader.php?ID=39&lang=EN

F2 是一张小表,它的全表扫描访问产生的CG为3。

写到这是否可以结束了呢?现在将第二条语句的过滤条件移除看看。

SQL代码HelloDBA.COM> select * from t1, t2; 246402 rows selected. Statistics ---------------------------------------------------------- 1 recursive calls 0 db block gets 219 consistent gets 0 physical reads 0 redo size 14113903 bytes sent via SQL*Net to client 181209 bytes received via SQL*Net from client 16428 SQL*Net roundtrips to/from client 1 sorts (memory) 0 sorts (disk) 246402 rows processed

仅仅 219 CG?这是一个笛卡尔乘积的关联(无关联条件),怎么会是如此少的 CG 呢?

再次产生 SQL 跟踪文件:

SQL代码Rows (1st) Rows (avg) Rows (max) Row Source Operation ---------- ---------- ---------- --------------------------------------------------- 246402 246402 246402 MERGE JOIN CARTESIAN (cr=219 pr=0 pw=0 time=957833 us cost=2553 size=87472710 card=246402) 54 54 54 TABLE ACCESS FULL T2 (cr=55 pr=0 pw=0 time=728 us cost=3 size=6102 card=54) 246402 246402 246402 BUFFER SORT (cr=164 pr=0 pw=0 time=433549 us cost=2550 size=1104246 card=4563) 4563 4563 4563 TABLE ACCESS FULL T1 (cr=164 pr=0 pw=0 time=10674 us cost=47 size=1104246 card=4563)

T1 的全表扫描的 CG 并未变化,T2 的 CG 增加为 55。55 意味着什么?它是 T2 的数据记录数加一。

SQL代码HelloDBA.COM> select count(*) from t2; COUNT(*) ---------- 54

但是,笛卡尔乘积不是意味着 m×n 吗?为什么结果是 m+n?

实际上,Oracle 确实对 T1 做了多次重复访问。不过,第一次访问后,读取到的数据被缓存到了私有工作区,接下来的访问就是从私有内存而非共享内存中读取数据。因此,这些访问就没有被记入 CG 当中。

为了获取实际的访问次数,我们使用嵌套关联提示使其从共享内存中读取数据:

SQL代码HelloDBA.COM> select /*+use_nl(t1) leading(t1)*/* from t1, t2; 246402 rows selected. Elapsed: 00:00:07.43 Execution Plan ---------------------------------------------------------- Plan hash value: 787647388 ---------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | ----------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 246K| 83M| 5006 (1)| 00:01:01 | | 1 | MERGE JOIN CARTESIAN| | 246K| 83M| 5006 (1)| 00:01:01 | | 2 | TABLE ACCESS FULL | T1 | 4563 | 1078K| 49 (0)| 00:00:01 | | 3 | BUFFER SORT | | 54 | 6102 | 4956 (1)| 00:01:00 | | 4 | TABLE ACCESS FULL | T2 | 54 | 6102 | 1 (0)| 00:00:01 | ----------------------------------------------------------------------------- Statistics ---------------------------------------------------------- 0 recursive calls 0 db block gets 4568 consistent gets 0 physical reads 0 redo size 16632868 bytes sent via SQL*Net to client 181209 bytes received via SQL*Net from client 16428 SQL*Net roundtrips to/from client 1 sorts (memory) 0 sorts (disk) 246402 rows processed

尽管执行计划没有变化,但是 CG 的变化却相当明显。

研究收获

从这个例子中可以注意到两点:

1. 数据获取数组大小会影响 CG;

2. CG 仅包含从共享内存读取的次数;

注:

测试环境为: Oracle 11.2.0.3 on Oracle Linux 5 64bit

资源下载

关注公众号:数据和云(OraNews)回复关键字获取

‘2017DTC’,2017 DTC 大会 PPT

‘DBALIFE’,“DBA 的一天”海报

‘DBA04’,DBA 手记4 经典篇章电子书

‘RACV1’, RAC 系列课程视频及 PPT

‘122ARCH’,Oracle 12.2 体系结构图

‘2017OOW’,Oracle OpenWorld 资料

‘PRELECTION’,大讲堂讲师课程资料

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2018年03月06日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
对象存储
对象存储(Cloud Object Storage,COS)是由腾讯云推出的无目录层次结构、无数据格式限制,可容纳海量数据且支持 HTTP/HTTPS 协议访问的分布式存储服务。腾讯云 COS 的存储桶空间无容量上限,无需分区管理,适用于 CDN 数据分发、数据万象处理或大数据计算与分析的数据湖等多种场景。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档