从网站建设到定制行业解决方案,为提供做网站、网站建设服务体系,各种行业企业客户提供网站建设解决方案,助力业务快速发展。创新互联建站将不断加快创新步伐,提供优质的建站服务。
如果你的数据库性能经常抖动,时快时慢,你会怎么入手,又会怎么一步步排查解决的?是先看等待事件?还是看AWR或ASH?接下来优化SQL?调整参数?甚至更换硬件?本次分享一个我在GCS处理的案例,希望大家读后会有新的收获。
问题来了
在一个宁静的早晨,本来打算能够看会儿书,做几个实验来打发时间,刚刚拿起书,电话就如期而至,让本来宁静的早晨变成了工作的开始。仔细想想,这就是运维人的常态吧,也就释然了。简单聊了几句,对问题的基本情况了解一些,并且让客户搜集了相关的信息,开始了问题的分析。
几个rebuild索引操作等待超过10个小时!!!
问题分析
背景介绍
数据库版本:12.2.0.1 RAC
OS:Oracle Linux
问题:rebuild索引慢
“套路来了”
有几件事要确认:
1.索引大不大?---确认时间过长是正常还是异常,如果索引很大那么我认为等待时间长是正常的。已经确认索引不大,只有几个G
2.时间花在哪?---决定下一步的排查方向。
大概思路有了开始发招:
第一招: 确认问题:
看起来重建索引的操作是通过 sqlplus 连接发起的。
根据上面的信息可知,一共记录了三条索引的rebuild。
第二招:时间花在哪了?
既然确认了会话的确是在进行索引重建的工作,接下来到 ASH 里面去看一下这个会话在等待什么东西。
根据上面的输出,看起来重建索引的语句有很多次都是在等待“ gc cr multi block request ”。这说明这个重建索引的操作需要通过 undo 中保存的 cr 块来构造数据的一致性的镜像 , 而且要访问多个cr块才可以完成操作。具体原理可以去看中亦学院第一期公开课的视频。没看的抓紧下载哈。
下面是关于这个等待事件的详细的解释:
原理解释
: 这个等待事件说明申请者实例需要向远程实例(可能是多个)申请多个(具体的数据块数量取决于参数db_file_mulitblock_read_count的设置) cr 块。这个等待事件只有在所有申请的数据块都被成功返回之后才会结束,也就是说,如果有其中的一个数据块因为某种原因没有被成功接受的话,就需要重新申请所有的数据块。这也是为什么gc current/cr multi block request 经常和等待事件gc cr failure/ gc current retry 同时出现的原因。 关于更多cache fusion 相关的等待事件的解释,请参考《Oracle RAC核心技术详解》 第十二章中的内容。
现在我已经有答案了,很多人看到 gc current/cr multi block request会觉得已经无从下手,似乎已经山穷水尽了, 到这里基本信息都有了,结合上边的原理大家自己思考几秒钟,如果是你下边改怎么继续排查问题?问题原因就在后面,什么时候往下翻,由你决定…
.........
.........
.........
.........
.........
.........
.........
原因:重建索引需要向另外的实例申请多个数据块。申请效率低,网络参数设置不合理。
头脑风暴:三个初步要排查的方向。
1 :在远程实例上有一个 blocker 进程一直持有了一些块,导致了申请者无法拿到数据;
2 :申请的数据块太多了,导致了私网的负载太大,产生了性能问题;
3 : RAC 的私网配置存在一些问题,导致了数据无法被快速的传递到远程节点。
继续发招
第三招:排查blocker。
下面是 AWR 中的信息
Top 10 Foreground Events by TotalWait Time
以上的信息说明 global cache 相关的 cr 多块读取产生的等待是最多的,一共产生了 3030 次等待,总共的等待时间是 1733.7 秒,平均每次的等待时间是 572.19ms 。所以看起来每次等待的时间不是很长,并不像是由于某一个 blocker 进程阻塞了其它进程导致的。如果真是这种情况,应该出现的情况是这个等待事件的 waits 值 比较低,但是 Total Wait Time (sec) 和 Avg Wait 很高 。本着一切以事实说话的原则,继续排查。
查看等待事件的柱状图信息:
绝大多数的等待时间都小于 512us
还有一部分的等待 1 小于 32ms
最长的等待时间也不超过 2s
说明: 11g , 12c 的 AWR 报告中,会包含每一个等待事件产生的等待时间对应的柱状图信息,它把等待事件对应的等待时间根据时间的长短分成了若干组,这样就可以区分出来某一个等待事件所导致的等待时间是如何分布的,对应了解等待时间的构成很有帮助。
上面的信息说明主要的等待时间分布都小于512us,而少部分的等待要低于2s,没有超过2s的等待。所以,更加证实了这个问题不是由于某一个Blocker进程阻塞了其它进程造成的 。
看到这里,基本上就可以排除方向 1 了,继续发招:
第四招:排查私网吞吐量
根据 AWR 中私网的流量信息( EstdInterconnect traffic (KB) )看起来当时私网的流量只有大概 1M/s 左右,并不是很高,在和正常情况下对比后,也没有发现私网流量有大的变化。看到这里,方向 2 基本上也可以排除了。
那么,就只剩下方向 3 了。看看 OSW 的 netstat 输出就能得到答案了。下面是部分截取出来的 OSW 的信息。
最后大招:一锤定音
netstat 输出中的ip 层统计信息:
上面的信息说明节点 1 一直存在 IP 层的包重组失败的问题。再回头看了一下数据库参数 db_file_multiblock_read_count 的配置,发现这个参数被设置为了 126 ,又看了一下 ifconfig 的输出 :
看起来问题的原因就比较明显了,上面的信息说明私网网卡的 mtu 值为不到 2k。
对于一个数据库 block size 为 8k 而且 db_file_multiblock_read_count = 126 ( 这意味着每个数据库的多块读,例如: gc cr multi block read 需要在 IP 层被切割成 8192*128/2044=513 ,也就是大概 500 多个数据块 )的数据库,多块读会被切割得很散,而 UDP 协议本身是 Unreliable 的协议,这不仅是 IP 包的发送并不是有顺序的,同样的应用程序层( 例如: oracle 的 lms 进程 )一旦发现这 500 多个包里面有一个是无效的,整个包就需要被重组。
另外,我们在 awr 中也发现了这个数据库实例一直存在着“ gc cr block lost ”的等待,这本身也说明了私网的传输效率的确是存在一些问题的 。
10 Foreground Events by Total WaitTime
给出解决方案之前先解答一个疑问:很多人会问三个方向是怎么来的?且容我细细讲来 :
+
+
第一:等待事件的含义是想要从远程实例申请多个数据块儿过来,而且还必须是所有申请的块都拿到之后,才算完成。
第二:RAC的cachefusion 是通过UDP 作为数据传输协议的,而UDP本身又是一种不可靠的数据传输协议,也就是说数据的传输是没有编号的(不像TCP协议),而且数据完整性是要通过应用程序自己来验证的。
第三:UDP协议底层也是要把数据切割成一个个IP包来进行传输的
。
+ +
引用《 Oracle RAC 核心技术详解》中的内容,一个数据包从一个实例到另外的一个实例是要经历下面的步骤的:
步骤1:节点1的lms进程需要发送一个数据块(假设db_block_size=8k,MTU=1500)给节点2的一个进程,并把这个请求通知OS(操作系统); 步骤2:节点1的操作系统需要将这个数据块切成大概6个数据分片,放入节点1相应端口的UDP 发送缓存(SendBuffer); 步骤3:数据分片被陆续的通过网络发送到了节点2; 步骤4:节点2 的操作系统收到了发送过来的数据包,保存到对应的UDP接收缓存(Recevie Buffer); 步骤5:节点2开始组装数据分片,当所有的6个数据分片都被成功组装之后,OS将组装过的数据包发送给相应的接收进程; 步骤6:节点2的接收进程处理收到的数据包
。
用一个类比的例子:
我到银行去存
100
万(虽然我没有这么多钱!),在银行宣布存钱成功之前,它要完成以下的过程: 1
:把我的
100
万,交给
10
个人,
10
台点钞机,确认我存的钱数是
100
万; 2
:把这
100
万放到银行的保险柜里; 3
:通知我存钱成功。 在整个过程中,可能会出现的状况是: ++
有一张
100
元纸币卡在了点钞机里面,怎么也拿不出来,那么这台点钞机就是一个
Blocker
,它阻塞了别人,让点钱的过程不能完成。 ++
每台点钞机都很慢,每分钟只能点
10
张纸币,整体拖慢了点钞的时间。 ++
把钱放到保险柜里面的工作人员每次只能运送
1
万块到保险柜里,也就是传输的时候太慢了。
既然知道了这中间发生的整个过程,又有了类比的过程,那么就很容易想到,中间可能出现的问题的可能性了。那么上面提到的三个方向也就自然而然的浮现到我的头脑中了。
解决方案
既然知道了问题原因是由于 ip 层需要把每个多块读的 udp 包切成 500 多个小的 ip 包进行传输,导致了 IP 层进程出现碎片重组失败,那么解决的方法也就有以下的几种 。
+ + 方法
1
:把私网网卡的
MTU
值调整成至少
7000
,这样就能够让
IP
层不需要把数据切得太碎,从而提高传输效率;
方法
2
:把数据库参数
db_file_multiblock_read_count
调小,让每次发送的数据包大小下降,让
IP
层不需要把数据块切得太碎,提高传输效率; 方法
3
:把
UDP
的碎片重组
buffer
调高,使
IP
层有更多的时间把碎片重组成完整的数据包,降低整个数据包重传的概率。 + +
最后,用户选择了把下面的参数调大
net.ipv4.ipfrag_high_thresh=16777216
net.ipv4.ipfrag_low_thresh= 15728640
并且把数据库参数 db_file_multiblock_read_count 调整到 16, 在应用了改变之后,重建索引的操作大概 5 分钟就完成。
总结
1.对于
RAC
数据库,
cache fusion
的私网通信是通过
UDP
实现的,而对于多块读,需要所有请求的数据块都到达远程节点才算请求操作完成。哪怕,只有一个数据包没有被传输过来,都需要所有数据块儿进行重传; 2.对于多块读的操作,每次请求的数据块数量是通过参数 db_file_multiblock_read_count来控制的; 3.对于RAC系统,要特别注意UDP相关的操作系统内核参数的设置,不光要注意udp buffer大小的配置,还要注意碎片重组的buffer大小配置。
本文转载于中亦安图