运维不背锅!持续两年数据库零故障的运维优化之道数据库

来源:互联网 / 作者:SKY / 2018-05-02 18:03 / 点击:
我是来自平安科技数据库技术部运维团队的刘书安,从14年开始,配合平安集团的互联网金融转型,我们运维的数据库也从单纯的Oracle数据库转向到了多种数据库的运维

大家好,我是来自平安科技数据库技术部运维团队的刘书安,从14年开始,配合平安集团的互联网金融转型,我们运维的数据库也从单纯的Oracle数据库转向到了多种数据库的运维,当前管理的各类数据库的实例已经超过了一万个。在这种情况下,我们连续两年保持了数据库零故障的状态。

事实上在之前,运维团队也是天天忙着应付各种异常,长久处于高压状态下。之后经过我们团队一系列的优化和改造,目前系统已稳定很多。这期间也确实发生了很多事情。接下来就跟大家简单介绍一下我们这两年对一些运维问题的分析和团队管理的方法,抛砖引玉一下。

一、问题解决

首先我们从以下这张图说起:

运维不背锅!持续两年数据库零故障的运维优化之道

这个是扁鹊向魏王介绍他们三兄弟的医术:扁鹊自己是在病人病入膏肓时用虎狼之药将对方救活;扁鹊的二哥是在别人生小病时将人治愈;而扁鹊的大哥则是在病情未发时铲除病因、避免生病。

在扁鹊看来,三个人的医术排序应该是:大哥>二哥>扁鹊,但在世人眼里却是:扁鹊>二哥>大哥。

我比较认同扁鹊的观点,因为我一直都觉得DB的运维人员不应该只是背锅侠,而是应该把自己当成医生来对待问题,不只是关注问题的解决,更需要多关注避免问题的发生。

我们在数据库异常时去解决问题,别人可能会认为我们是高手,能把问题解决好、事情处理掉。但实际上这时的运维已经是一个被动式的处理,即便我们用了最快的手段去解决,故障已经发生了,可能还造成了比较严重的影响。

如果我们能提前发现这些问题并解决掉,就能避免很多故障和影响的发生。因此在我看来,运维相对高明的手段应该是:在做架构或设计时就把能想到的问题预先解决掉,确保系统的可拓展性和高可用性。运维也应该多从架构的角度去考虑问题,并将这些问题提前解决好,而非被动地等待问题发生后才去解决。

接下来和大家介绍我们团队解决的三个案例,以及之后我们通过什么方式避免问题的发生:

案例一

这是我们在2016年解决的案例,版本是11.2.0.4 的Oracle数据库,这种数据库使用SPM固化TOP SQL的执行计划,确保系统的稳定。

运维不背锅!持续两年数据库零故障的运维优化之道

当时异常的问题是,几乎每次发完大的版本后,已有的功能都会多少受到影响,有一些语句的执行计划会发生异常。

我们发现,绝大多数语句都和这个语句是类似的,中间有一个SKIPSCAN(跳跃式扫描),很明显可以看出它是一个输入时间(对着用户的),有一个范围查询。所以我们当时就基本判断问题出在这里了,使用的解决方案就是通过重新固化执行计划来选择好的执行计划。

接着就开始分析问题产生的原因。

对于索引跳跃式扫描而言,在一般情况下,如果运维索引的第一个列没有用到,当它开始使用到第二个列时,就只能用跳跃式的方法去进行一个索引扫描。而在分析问题原因时,因为类似的语句比较多,我们当时在固化了几十条后,发现还有源源不断的类似语句出现,就考虑到问题可能并没有那么简单。所以我们又进行了进一步分析,最后发现可能是索引的统计信息有问题。于是我们就重新收集了索引的统计信息,至此,类似的语句问题才算是解决了。

运维不背锅!持续两年数据库零故障的运维优化之道

但其实这个问题并没有彻底结束:

我们在处理完后又重新分析了这个索引的问题,发现索引第一列是一个空值,但不知道是谁在空列和输入时间上建了一个符合索引,导致这个索引有可能会被使用。

发现问题后,我们就查询了这个索引的访问方式,看看是否全都是INDEX SKIP  SCAN。后来发现,基本上访问这个索引的语句都用的是这种索引跳跃式扫描,所以我们当时就把这个索引设置为不可用,后面把它删除了。类似的索引,我们当时处理了有三个,之后这个系统就没有再出现这样类似的问题。

SPM也是一种固化执行计划的方式,但为什么在这个库里,SPM会失效呢?

之后我们分析了它的原因:是因为每次发版本时,有可能会多查一些字段,导致语句发生变化。SPM这类的固化执行计划的方式都和语句有强关联,只要语句上有一个小小的改动,都会导致固化的方式失效。这也是每次更新版本语句都会发生异常的原因之一。

之后我们继续分析,为什么这个时间索引有这种类似问题?

这是我们之前整理的一个案例分析的原因:我们在做索引的范围查询时,它的选择率公式如下

运维不背锅!持续两年数据库零故障的运维优化之道

但是在不同情况下,比如这种右侧索引,比如创建时间、更新时间、输入时间,我们写入数据都是用sysdate写入的,那么它永远都在索引列的右侧,类似这种方式是往里面插入数据的。

然而我们统计信息的收集又不是一个实时收集的,主要是对一些大表,比如一个一千万的表可能要到10%,也就是100万的DMR量;更大的表的DMR量会更大。这就会导致我们的统计信息和当前的值永远是过时的,就会产生这种问题。

对于这三个查询来说:第一个查询发生在有效范围内,所以它可以反映出一个比较真实的数据,第二个查询也可以反映一部分,但第三个查询就相当于完成一个超范围的查询,计算出一个很低的值,这样就会导致我们的语句偏异常。

更坑的是在OLPP系统里,新数据查询的几率永远比老数据的大,越新的数据被访问的几率越高,这也导致我们的语句每次都会出现异常的情况。

发现这些问题后,我们立即展开了一个行动,就是把数据库里所有与时间索引相关的字段都提取一下,然后定期修改索引字段上面的HIGH VALUE,统计信息里面的HIGHVALUE,就能避免出现这种问题。

运维不背锅!持续两年数据库零故障的运维优化之道

如上图所示,是一个范围查询的情况,即在一个索引前导列的区别,类似于我们在创建时间和OWNER之间建索引。如果把创建时间放在前面,把OWNER放在后面就是第一种情况;如果把OWNER放在前面,把CREATED放在后面就是第二种情况。

现在来分析这两个不同索引的区别:

阅读延展

1
3