常识之外:全表扫描为何产生大量 db file sequential read 单块读?
原创 2016-07-05 熊军 Oracle
编辑手记:在理解Oracle技术细节时,我们不仅应该读懂概念,还要能够通过测试验证细节,理解那些『功夫在诗外』的部分,例如全表扫描和单块读。
开发人员在进行新系统上线前的数据校验测试时,发现一条手工执行的 SQL 执行了超过1小时还没有返回结果。SQL 很简单:
下面是这条 SQL 的真实的执行计划:
很显然,在这个表上建 billing_nbr 和 start_date 的复合索引,这条 SQL 就能很快执行完(实际上最后也建了索引)。但是这里我们要探讨的是,为什么这么一条简单的 SQL 语句,执行了超过1小时还没有结果。 MOBILE_CALL_1204_OLD 这张表的大小约为 12GB ,以系统的 IO 能力,正常情况下不会执行这么长的时间。简单地看了一下,系统的 CPU 以及 IO 压力都不高。假设单进程全表扫描表,每秒扫描 50MB 大小(这实际上是一个很保守的扫描速度了),那么只需要245秒就可以完成扫描。
下面来诊断一下 SQL 为什么会这么不正常地慢。看看会话的等待(以下会用到 Oracle 大牛Tanel Poder的脚本):
明明是全表扫描的 SQL ,为什么99%以上的等待时间是 db file sequential read ,即单块读?!多执行几次 waitprof 脚本,得到的结果是一致的(注意这里的数据,特别是平均等待时间并不一定是准确的值,这里重点关注的是等待时间的分布)。
那么 SQL 执行计划为全表扫描(或索引快速全扫描)的时候,在运行时会有哪些情况实际上是单块读?我目前能想到的有:
1. db_file_multiblock_read_count 参数设置为1
2. 表或索引的大部分块在 buffer cache 中,少量不连续的块在磁盘上。
3. 一些特殊的块,比如段头
4. 行链接的块
5. LOB 列的索引块和 cache 的 LOB 块(虽然10046事件看不到 lob 索引和 cache 的 lob 的读等待,但客观上是存在的。)
那么在这条 SQL 语句产生的大量单块读,又是属于什么情况呢?我们来看看单块读更细节的情况:
多次执行同样的 SQL ,发现绝大部分的单块读发生在3、353-355这四个文件上,我们来看看这4个文件是什么:
原来是 UNDO 表空间。那么另一个疑问就会来了,为什么在 UNDO 上产生了如此之多的单块读?首先要肯定的是,这条简单的查询语句,是进行的一致性读。那么在进行一致性读的过程中,会有两个动作会涉及到读 UNDO 块,延迟块清除和构建 CR 块。下面我们用另一个脚本来查看会话当时的状况:
上面的结果是5秒左右的会话采样数据。再一次提醒,涉及到时间,特别要精确到毫秒的,不一定很精确,我们主要是看数据之间的对比。从上面的数据来看,会话请求了382次 IO 请求,单块读和多块读一共耗时4219.17ms(4.17s+49.17ms),平均每次 IO 耗时 11ms。这个单次 IO 速度对这套系统的要求来说相对较慢,但也不是慢得很离谱。data blocks consistent reads - undo records applied 这个统计值表示进行一致性读时,回滚的 UNDO 记录条数。
比这个统计值可以很明显地看出,这条 SQL 在执行时,为了得到一致性读,产生了大量的 UNDO 记录回滚。那么很显然,在这条 SQL 语句开始执行的时候,表上有很大的事务还没有提交。当然还有另一种可能是 SQL 在执行之后有新的很大的事务(不过这种可能性较小一些,因为那样的话这条 SQL 可能比较快就执行完了)。
询问发测试的人员,称没有什么大事务运行过,耳听为虚,眼见为实:
这张表目前没有事务,但是曾经 update 了超过1.6亿条记录。最后一次 DML 的时间正是这条执行很慢的 SQL 开始运行之后的时间(这里不能说明最后一次事务量很大,也不能说明最后一次修改对 SQL 造成了很大影响,但是这里证明了这张表最近的确是修改过,并不是像测试人员说的那样没有修改过)。
实际上对于这张表要做的操作,我之前是类似的表上是有看过的。这张表的总行数有上亿条,而这张表由于进行数据的人工处理,需要 update 掉绝大部分的行, update 时使用并行处理。那么这个问题到,从时间顺序上来讲,应该如下:
在表上有很大的事务,但是还没有提交。
问题 SQL 开始执行查询。
事务提交。
在检查 SQL 性能问题时,表上已经没有事务。
由于 update 量很大,那么 UNDO 占用的空间也很大,但是可能由于其他活动的影响,很多 UNDO 块已经刷出内存,这样在问题 SQL 执行时,大量的块需要将块回滚到之前的状态(虽然事务开始于查询 SQL ,但是是在查询 SQL 开始之后才提交的,一致性读的 SCN 比较是根据 SQL 开始的 SCN 与事务提交 SCN 比较的,而不是跟事务的开始 SCN 比较),这样需要访问到大量的 UNDO 块,但是 UNDO 块很多已经不在内存中,就不得不从磁盘读入。
对于大事务,特别是更新或 DELETE 数千万记录的大事务,在生产系统上尽量避免单条 SQL 一次性做。这造成的影响特别大,比如:
v 事务可能意外中断,回滚时间很长,事务恢复时过高的并行度可能引起负载增加。
v 表中大量的行长时间被锁住。
v 如果事务意外中断,长时间的回滚(恢复)过程中,可能严重影响 SQL 性能(因为查询时需要回滚块)。
v 事务还未提交时,影响 SQL 性能,比如本文中提到的情况。
v 消耗过多 UNDO 空间。
v 对于 DELETE 大事务,有些版本的 oracle 在空闲空间查找上会有问题,导致在 INSERT 数据时,查找空间导致过长的时间。
v 对于 RAC 数据库,由于一致性读的代价更大,所以大事务的危害更大。
那么,现在我们可以知道,全表扫描过程还会产生单块读的情况有,读 UNDO 块。
对于这条 SQL ,要解决其速度慢的问题,有两种方案:
① 在表上建个索引,如果类似的 SQL 还要多次执行,这是最佳方案。
② 取消 SQL ,重新执行。因为已经没有事务在运行,重新执行只是会产生事务清除,但不会回滚 UNDO 记录来构建一致性读块。
继续回到问题,从统计数据来看:
l 每秒只构建了少量的一致性读块(CR block created,table scan blocks gotten这两个值均为2);
l 每秒的 table scan rows gotten 值为98.4,通过 dump 数据块可以发现块上的行数基本上在49行左右,所以一致性读块数和行数是匹配的;
l session logical reads 每秒为97.6,由于每回滚一条 undo 记录都要记录一次逻辑读,这个值跟每秒获取的行数也是匹配的(误差值很小),与 data blocks consistent reads - undo records applied 的值也是很接近的。
问题到这儿,产生了一个疑问,就是单块读较多(超过70),因此可以推测,平均每个 undo 块只回滚了不到2条的 undo 记录,同时同一数据块上各行对应的 undo 记录很分散,分散到了多个 undo 块中,通常应该是聚集在同一个块或相邻块中,这一点非常奇怪,不过现在已经没有这个环境(undo 块已经被其他事务重用),不能继续深入地分析这个问题,就留着一个疑问,欢迎探讨(一个可能的解释是块是由多个并发事务修改的,对于这个案例,不会是这种情况,因为在数据块的 dump 中没有过多 ITL,另外更不太可能是一个块更新了多次,因为表实在很大,在短时间内不可能在表上发生很多次这样的大事务)。
在最后,我特别要提到,在生产系统上,特别是 OLTP 类型的系统上,尽量避免大事务。
About Me
.........................................................................................................................................................................................................
● 本文来自于微信公众号转载文章,若有侵权,请联系小麦苗及时删除,非常感谢原创作者的无私奉献
● 本文在ITpub(http://blog.itpub.net/26736162)、博客园(http://www.cnblogs.com/lhrbest)和个人微信公众号(xiaomaimiaolhr)上有同步更新
● 小麦苗分享的其它资料:http://blog.itpub.net/26736162/viewspace-1624453/
● QQ群: 230161599 微信群:私聊
● 联系我请加QQ好友(642808185),注明添加缘由
● 【版权所有,文章允许转载,但须以链接方式注明源地址,否则追究法律责任】
.........................................................................................................................................................................................................
长按下图识别二维码或微信客户端扫描下边的二维码来关注小麦苗的微信公众号:xiaomaimiaolhr,学习最实用的数据库技术。
常识之外:全表扫描为何产生大量 db file sequential read 单块读?的更多相关文章
- Facebook对MySQL全表扫描性能的改进
原文博客如下: http://yoshinorimatsunobu.blogspot.com/2013/10/making-full-table-scan-10x-faster-in.html 如下是 ...
- Oracle 高水位线和全表扫描
--Oracle 高水位线和全表扫描--------------------------2013/11/22 高水位线好比水库中储水的水位线,用于描述数据库中段的扩展方式.高水位线对全表扫描方式有着至 ...
- SQL 数据优化索引建suo避免全表扫描
首先什么是全表扫描和索引扫描?全表扫描所有数据过一遍才能显示数据结果,索引扫描就是索引,只需要扫描一部分数据就可以得到结果.如果数据没建立索引. 无索引的情况下搜索数据的速度和占用内存就会比用索引的检 ...
- 优化一个奇葩表设计上的全表扫描SQL
之前在一个比较繁忙的系统抓到的耗时长.消耗CPU多的一条SQL,如下:SELECT * FROM Z_VISU_DATA_ALARM_LOG TWHERE TO_DATE(T.T_TIMESTR, ' ...
- SQL SERVER中关于OR会导致索引扫描或全表扫描的浅析
在SQL SERVER的查询语句中使用OR是否会导致不走索引查找(Index Seek)或索引失效(堆表走全表扫描 (Table Scan).聚集索引表走聚集索引扫描(Clustered Index ...
- oracle优化:避免全表扫描(高水位线)
如果我们查询了一条SQL语句,这条SQL语句进行了全表扫描,那到底是扫描了多少个数据块呢?是表有多少数据,就扫描多少块吗?不是的.而是扫描高水位线一下的所有块.有的时候有人经常说,我的表也不大呀,怎么 ...
- 想通过加HINT让其走全表扫描
一个SQL,通过SPM固定它的执行计划,可以通过DBMS_SPM.LOAD_PLANS_FROM_CURSOR_CACHE实现.也可以通地此功能在不修改原SQL的情况下对其加HINT来固定执行计划.D ...
- Oracle列操作引起的全表扫描
首先是一种比较明显的情况: select * from table where column + 1 = 2 这里对column进行了列操作,加1以后,与column索引里的内容对不上,导致colum ...
- MySql避免全表扫描【转】
原文地址:http://blog.163.com/ksm19870304@126/blog/static/37455233201251901943705/ 对查询进行优化,应尽量避免全表扫描,首先应考 ...
随机推荐
- Scrapy之Spider
Spider Spider类定义了如何爬取某个(或某些)网站.包括了爬取的动作(例如:是否跟进链接)以及如何从网页的内容中提取结构化数据(爬取item). 换句话说,Spider就是您定义爬取的动作及 ...
- 配置Pods和containers--为Containers和Pods分配CPU资源
指定CPU请求和CPU限制 要为容器指定CPU请求,在容器资源清单中使用resources:requests字段.要指定CPU限制,使用resources:limits. cpu-request-li ...
- 将.cer证书导入java密钥库?
导入.cer从浏览器下载的证书文件(打开网址并挖掘详细信息)到cacerts keystore中java_home\jre\lib\security为我工作,而不是尝试生成和使用我自己的密钥库. 去你 ...
- 小程序报错:对应的服务器 TLS 为 TLS 1.0 ,小程序要求的 TLS 版本必须大于等于 1.2
我这里出现此错误的原因是,搭载域名网站的服务器是windows2008 r2,配置的域名证书是TLS1.0版本,需要在服务器注册表中加入TLS的其他版本. 处理办法如下 小程序报错 TLS 版本必须大 ...
- OCR(Optical Character Recognition)算法总结
https://zhuanlan.zhihu.com/p/84815144 最全OCR资料汇总,awesome-OCR
- [Py] 简单的 Python 运行环境
python:https://www.python.org/downloads/ pip:https://pip.pypa.io/en/stable/installing/#upgrading-pip ...
- Mobaxterm使用(类似xshell)
先来看看SSH是什么,定义如下:SSH是一种可以保证用户远程登录到系统的协议.实际上,SSH是一个网络协议,允许通过网络连接到Linux和Unix服务器.SSH使用公钥加密来认证远程的计算机.通常有多 ...
- spring boot的actuator
actuator官方的介绍 Spring Boot includes a number of additional features to help you monitor and manage yo ...
- Google BERT摘要
1.BERT模型 BERT的全称是Bidirectional Encoder Representation from Transformers,即双向Transformer的Encoder,因为dec ...
- (转)IntelliJ IDEA 插件 阿里巴巴Java开发手册(Alibaba Java Coding Guidelines)
背景:idea安装插件,学习使用阿里巴巴开发插件. 在线和离线的安装方式. IntelliJ IDEA 插件 阿里巴巴Java开发手册(Alibaba Java Coding Guidelines) ...