运维团队每天忙于处理各种异常,长期处于高压状态。本文作者将分享他近两年对一些运维问题和团队管理方法的分析。大家好,我是平安科技数据库技术部运维组刘栓。2014年开始配合平安集团互联网金融转型。我们运维的数据库已经从单纯的Oracle数据库,变成了多种多样的数据库运维。目前管理着各类数据库实例超过10000个。在这种情况下,我们已经连续两年保持了数据库零故障的状态。其实在过去,运维团队也是每天忙于处理各种异常,长期处于高压状态。经过我们团队的一系列优化和改造,系统现在更加稳定。这期间确实发生了很多事情。下面简单介绍一下我们这两年对一些运维问题和团队管理方法的分析,以期对大家有所启发。解决问题首先,我们先从这张图说起:这是扁鹊向魏王介绍他们三兄弟的医术:扁鹊自己临终时用虎狼之药救了病人。扁鹊的二哥为人治病。扁鹊的大哥把病根除掉,防患于未然,让人们免于生病。在扁鹊看来,三人医术的顺序应该是:大哥>二哥>扁鹊;但在世人眼里却是:扁鹊>二哥>大哥。我很赞同扁鹊的观点,因为我一直认为DB的运维人员不应该一味地受到指责,而应该把自己当做医生。他们不仅要关注问题的解决,更需要关注如何避免问题的发生。发生了。当我们解决数据库异常的问题时,别人可能会认为我们是能解决问题、能处理事情的专家。但实际上,此时的运维已经是一个被动的过程。即使用最快的方法解决,故障已经发生,可能造成比较严重的影响。如果我们能够提前发现这些问题并加以解决,就可以避免很多失败和影响。所以,在我看来,比较巧妙的运维方法应该是:解决在做架构或者设计的时候能够提前想到的问题,保证系统的可扩展性和高可用。运维也要从架构的角度考虑问题,提前解决这些问题,而不是被动地等问题出现了再去解决。下面介绍一下我们团队解决的三个案例,以及后来我们是如何避免问题的:案例1这是我们在2016年解决的一个案例,当时Oracle数据库的版本是11.2.0.4,这个数据库使用SPM固化了TOPSQL的执行计划,保证系统的稳定性。exception当时的问题是,几乎每发布一个大版本,现有的功能都会受到一定的影响,某些语句的执行计划会出现异常。我们发现大部分语句都和这个语句类似,中间有一个SKIPSCAN(跳过扫描),很明显是输入时间(面向用户),还有一个范围查询。所以我们基本判断问题就出在这里,我们采用的解决方案是通过重新固化执行计划来选择一个好的执行计划。然后开始分析问题的原因。对于索引跳过扫描,一般情况下,如果运维索引的第一列没有被使用,当开始使用第二列时,只能使用skip方式进行一次索引扫描。在分析问题原因的时候,因为相似的句子很多,我们发现固化了几十个之后,相似的句子还是源源不断,所以我们认为问题可能没有那么简单。于是我们进一步分析,最终发现可能是该指标的统计信息有问题。所以我们重新收集了索引的统计信息,至此,类似的句子问题已经解决了。但其实这个问题并没有完全结束:我们处理完后重新分析了索引问题,发现索引***列是空值,但是不知道是谁在空列和空列之间建立了匹配输入时间索引,导致这个索引可能被使用。发现问题后,我们检查了这个索引的访问方式,看是否都是INDEXSKIPSCAN。后来发现基本上访问这个索引的语句都是用这个索引skipscan,所以我们当时设置这个索引不可用,然后删除。我们当时处理了三个类似的指标,之后系统就没有出现过类似的问题了。SPM也是一种固化执行计划的方式,但是为什么SPM在这个库中会失败呢?然后我们分析了原因:是因为每发布一个版本,可能会多检查一些字段,导致语句发生变化。SPM等固化执行计划的方法与语句有很强的相关性。只要语句稍有改动,固定的方法就会失效。这也是每次更新版本语句都会出现异常的原因之一。那么我们继续分析,为什么这个时间指数会出现这样类似的问题呢?这是我们之前整理过的一个案例分析的原因:当我们做索引范围查询的时候,它的选择性公式是这样的:但是在不同的情况下,比如这个对的索引,比如创建时间,更新时间,输入时间,我们用sysdate写数据,所以它总是在索引列的右边,用类似的方法往里面插入数据。但是,我们收集的统计信息并不是实时收集的。主要是针对一些大表。比如1000万的表可能需要10%的DMR量,也就是100万;较大表的DMR数量会更高。大的。这会导致我们的统计数据和当前值永远过时,从而导致以下问题。对于这三个查询:第一个查询发生在有效范围内,所以可以反映一个比较真实的数据;第二个查询也可以反映一部分。但是第三次??查询相当于完成了一次越界查询,计算出一个很低的值,就会导致我们的语句异常。更糟糕的是,在OLPP系统中,查询新数据的概率总是大于查询旧数据的概率,越新的数据越容易被访问到,这也导致我们的语句每次都会出现异常情况。发现这些问题后,我们立即展开行动,提取数据库中所有与时间索引相关的字段。然后定期修改索引字段上的HIGHVALUE和统计信息中的HIGHVALUE就可以避免这个问题。如上图所示,是范围查询的情况,即索引前导列的差异,类似于我们建索引的创建时间和OWNER。如果把创建时间放在前面,把OWNER放在后面是第一种情况;如果把OWNER放在前面,把CREATED放在后面就是第二种情况。下面我们来分析一下这两个不同的指标的区别:当我们把创建时间放在第一位的时候,问题就大了。我们通过时间字段查询时,很难做到等值查询,即无法找到每秒插入的值。对于这种查询,我们一般使用范围查询,比如查询一个月或者一天或者一周的数据。所以你可以看到,如果我在这个语句中查看这一分钟和一天DBMGR的创建,它的整个范围都会涉及到第一个索引,然后取三个关联值。但是在第二个索引中,三个值是串联的,因为DBMGR是有序的,时间也是有序的,所以它们可以完成只涉及到自己相关值的值。还有一些小区别:如果我先进行范围查询,再进行等价查询,对于这样的索引,在过滤的时候会多执行一个Filter步骤。但如果调整了这个顺序,就不是这样了。所以,我们在创建这种匹配索引的时候,一定要尽量把等价查询放在前面。之前有个说法:在选择符合指标的前导列时,应该将选择率比较低的值放在前导列中。但是我们认为这个说法是不完整的:比如对于一个时间字段,一天有86400秒,100天可能有超过800万个不同的值,一年有更多不同的值。但是如果用这个作为前导列,有时候就不合适了,因为对于它来说,有可能我们需要查询一天或者一个月的数据,而一年有365天,也就是十二个月。因此,更准确的说法是将查询条件中选择率低的列作为复合索引的前导列。所以通过这个案例,我们把运维问题的解决分为三个步骤:快速解决问题,保证应用恢复。对于运维人员来说,恢复应用是第一要务。查看问题是否重复出现。比如上面提到的案例1,如果我们当时的解决方案只是固化执行计划或者收集统计信息,你没有办法保证以后不会再出现类似的情况。如果我们采用收集统计信息的方式,这种情况可能再过一两个月就会再次发生,所以根本的解决办法就是找到这个问题的原因,并保证这个问题解决后不会再发生。复发。避免出现问题,看看这个问题是不是通病,其他库有没有类似的问题。如果有类似的问题,就要形成一个规范,避免此类问题的发生。尤其是一些新的应用,只有你制定规范,让开发合规,才会减少后续类似问题的发生,否则就会演变成我们解决问题,新问题不断出现的情况,**我们可以只有继续解决这个反复出现的问题。记得之前有一个案例,是一个普遍的问题:在一个实际的库中,我们分析发现它有内存泄漏,但我们并没有立即着手处理。结果第二天另一个库也出现了内存泄露,只好紧急重启。当时我们分析是某个bug导致的问题后,就去查找那个bug的相关资料,发现有同事在两三年前(2014年)就已经解决了这个问题。只是在另一个库中使用了相应的PATCH来解决问题,但是没有将这个问题扩展到所有系统导致检查其他库是否也有这个问题。从那时起,我们就特别关注这种常见问题。如果每个系统、每个问题都要发生一次,代价太大了。因此,我们尽量尽快解决常见问题,并尽量排除其他库中出现的类似问题。案例2第二个案例是Oracle数据库版本12.1.0.2。每天晚上,宿主机的CPU会时不时地持续到100%,应用程序会同时创建大量的数据到数据库中。当时我们的应急方案是把相关的等待时间全部杀掉,因为这些系统都在比较核心的库里,基本上每个系统杀掉几千个进程,成本还是比较高的。后来这个问题发生了两次之后,我们就开始着重分析问题。通过ASH分析,我们发现这个异常等待的发生是因为一条很简单的语句——SELECTUSERFROMSYS.DUAL。之后我们用这条语句一步一步关联起来,看看是在哪里调用的。原来是一个应用用户的登录TRIGGER中的用户判断步骤。这个USER是Oracle的内部函数,但是这么简单的一条语句,却让整个库都挂了。然后我们开始分析原因,我们通过ASH发现在我们恢复应用之前重新加载了语句。当时我们怀疑是硬件引起的,就这样分析,发现是晚上10点Oracle的自动任务自动收集统计信息。采集之后因为是登录触发,用户一直登录,做登录分析的时候无法解析这条语句,所以用户一直卡在那里。但是应用需要创建新的连接,而新创建的连接不能进库,会导致连接越来越多,全部卡在那里。***我们通过锁定双表统计信息的收集从根本上解决了这个问题。案例三第三个案例有两个问题,后来发现这两个问题是同一个原因引起的。我们有一个从10.2.0.5.X升级到10.2.0.5.18的数据库。升级后,会时不时出现一些与cursor:pin相关的等待。其实出现cursor:pin是正常的,因为这个数据库的负载比较高,变化也大,但是问题是升级后才出现。运维认为这是升级后的异常,于是开始分析问题原因。第二个问题是我们是在紧急情况下发现的。有时当异常发生时,某个库中某些语句的执行次数会非常高,甚至在15分钟内达到上亿次。对于一个正常的业务系统来说,出现这样高的执行频率是不正常的。后来我们分析了这些问题,发现这两个问题有一些相似之处:比如语句中间出现了一个函数调用;例如,在这种情况下,如果A表访问的数据量很大,这些函数可能会被调用多次。我们发现有一条执行函数调用的语句可能会出现几十万次。如果调用过程中关联表的执行计划发生变化,比如A表全量扫描,可能会发生千万级的函数调用。当时我们也总结了一些关于用什么方法快速定位,是否是函数调用导致的意见。Oracle10g之前确实没有什么好办法,因为里面没有展示关联,所以只能通过代码扫一扫找到对应的语句。Oracle11g之后,会更简单。通过与AS值相关的TOPLEVELSQLID,可以直接关联到导致问题的语句调用的函数。这里的另一个问题是函数调用。因为它调用的函数可能很快,但是次数会比较多,性能波动可能影响比较大。之前,我们有一个案例发生在月底的高峰期。我们发现在某个数据库中会有大量的CBC在等待。后来发现有一个小表被频繁访问了。那个小表有100多行数据,但是可能相关语句每15分钟被调用几千万次。其实在这么高的并发下,出现这种CBC等待是很正常的。但是因为它只有100多行数据,而且都集中在一个数据块中,所以这个数据块特别热,这种CBC等待总会出现。于是我们找到了解决这个热块问题的方法,但是因为我们不能在月底停止运行来进行更改。于是我们想出了一个方案:建立一个PCTFREE99索引,包括表中所有列的数据,保证每个索引块只保留一行数据,将100多行数据分成更多变相超过100。堵塞。做了这个操作之后,解决了CBC相关的问题,顺利度过了业务高峰期,但是第二天一早的报告又发现在坑里。因为我们需要在每个月初向监管机构提交一份报告,这份报告是一项长期交易。但它也调整了该报告中先前优化的指数。优化后的语句虽然减少了高峰期的CBC等待时间,但由于需要访问100多个数据块,单次访问时间从0.25毫秒变成了1毫秒,相当于效率降低了4倍。由于报告是一个长期的事务处理,这意味着该过程比以前花费了两倍多的时间并且没有运行完。所以我们发现这个问题后,只好把那个索引杀掉,恢复到原来的状态。尤其是现在对IT的要求越来越高,时限要求也越来越高。很多系统基本都是采用这种敏捷开发的方式,尽快上线。新系统上线一个很大的问题就是刚上线的时候压力和负载不是太高,但其实一开始就隐藏了很多问题。真正出现问题,负载高或者压力大的时候,解决起来会比较困难。尤其是数据库,当数据库很小的时候,比如300、500M的数据,怎么整这个表很简单,但是当表增加到300、500G甚至1、2T的时候,我就想做这个表的数据类整改难度会大很多。比如我们之前在整顿staging表的时候就使用了这种在线重定义的方式,但是对于一些比较大的表,几百G甚至T的表,如果使用这种在线重定义的方式,就会遇到各种各样的bug。后来坑太多了。现在对于大表的分表改造,我们先同步历史数据层面的改造,然后再做一次数据增量。方法会复杂很多。但实际上,如果我们在开始阶段就已经为这种大表设计好分区,尤其是在时间索引上,那么按照时间来分区可以避免很多问题。为什么我们的历史库中有这么多时间索引?一个很大的原因是很多报表是按时间查询的。比如你想查看本月或这一天的一些新数据,就需要通过时间字段来访问。之前看过很多时间索引,但是因为时间索引的特性,导致系统不断出现各种问题。如果在设计阶段就把这些大表提前设计成分区表,就可以完全避免这些不必要的问题。运维管理由于每个公司的具体情况不同,我简单介绍一下我公司的一些运维管理做法,供大家参考。变更管理相对来说,我们公司的变更管理是比较严格的,以后可能会更加严格。变更控制以变更控制为例,白天严禁进行任何变更,工作时间也不能进行任何变更。甚至一些紧急或故障维修,也需要部门负责人确认,领导批准,以确保风险可控。变革过程也许每个公司都有一个变革过程,但我们公司有一个特殊的地方。因为一些兼容数据库的要求可能会更高,所以流程控制的每一部分都必须到位。变更计划我们的变更计划需要大家提前做一个回顾和验证,包括制定计划的同事和执行操作的同事。变更实施者需要提前在一个环境中做一个完整的验证,以确保每一步都得到验证。通过。Specificationmanagementarchitecturespecification在我做架构师的时候,制定各种规范是一项重点工作。可能是自己养成了习惯,现在正在和大家一起制定各种运维规范。但是我感受最深的是:规范要有一个统一的标准,如果不统一,以后可能会出问题。比如我们之前的一些开发和测试环境就没有那么规范。现在我们要改造做自动化的时候,发现根本做不到,因为每个库的情况不一样,自动化脚本不可能适应所有情况去做这种标准化的改造.把它变成不标准很简单,但是把不标准变成标准就比较难了,尤其是在我们养成了习惯之后。运维规范2014年之前,我们做的是纯Oracle数据库运维,因为我们之前建立的是传统金融企业,运维都是Oracle数据库,但是2014年之后,我们逐渐转向互联网金融。因此,我们先后研究了MySQL、PG、Redis、MongDB、SQLServer、HBase等7、8个数据库,在运维过程中遇到了更多的坑。一开始有很多标准,但没有一个是最佳实践,很多都是根据行业和自己的经验制定的;还有各种数据库,不同的团队制定了不同的标准,***也有自己的标准。各种标准。所以,我们在运维中会发现各种各样的问题,在这方面强行规范整改是绝对必要的。而且,以往的标准大多没有经过大规模使用和大规模负载的验证,很多标准没有那么统一、规范和有效。因此,我们在运维过程中不断优化和完善这个规范。毕竟你遇到的情况不多的时候,你真的是没有办法解决这个问题的。规范优化比如一开始我们没有规定Redis一定要和应用放在同一个网络区域,但是随着Redis负载的增加,我们发现防火墙已经承受不住了。当时平安的WiFi刚刚上线,但是关于Redis的接入,有几个实例每秒调用数万次,整个防火墙无法支撑,差点造成严重故障。解决这个问题后,我们制定了一个强制规范:Redis这种高并发访问的数据库,必须和应用放在一起,不能出现跨墙访问。所以这个规范也在不断的优化,包括一些我们运维的标准。因为在标准刚制定的时候,我们可能没有考虑到一些问题,因为它已经很久没有使用了。印象最深的是MySQL刚推出时的一个问题。软件版本对小版本不明确。后来甚至出现关机时MySQL版本是5.6.22,维护完成后启动为5.6.16版本。诀窍是通过不断优化确保我们的规范和现实相结合,以避免出现此类问题。人员发展团队意识关于团队,要提高团队中每个人的作用,确保团队中的每个人都有后盾。如果变得不能留下任何人,对于球队的整体发展来说都是不正常的。所以,我们在安排工作的时候,对于比较重要的工作,我会尽量不让熟悉的同事重复去做,而是尽量让一些不熟悉的同事参与其中。每次高级成员离开时,团队的整体技能或知识就会明显缺失。为了避免类似的问题,我们从2017年开始制定了一些策略,让大家分享知识和技能。每周选择两个下午,每个下午选择一到两个小时进行分享。另一种策略是技能的积累,即把我们在工作中遇到和解决的一些问题录入到问题管理系统中。这样有两个好处:可以记录重复的问题,因为我们要分析哪些问题是重复的,哪些是共性的,就需要有这样一个系统拉取对应的问题列表,**来解决问题。就算人走丢了,之前解决的一些问题和技巧也可以被团队其他人发现,这样每一个离开的人都不会留下一个坑。所以,我们采用这种方式,是为了尽量避免人员流失或者团队变动带来的一些问题。但最终还是没办法完全避免这种事情,因为数据库运维有一定的复杂性,需要靠不断的失败和解决,包括一些人为的失误来改善。权责明确我们的轮班人员每天24小时工作,即三班倒。该团队之前遇到了严重的问题。当有事情发生时,值班人员可能会把问题交给下一个班次;或者升级给别人后,他们觉得这与他们无关。另外,风险意识不强,有些操作在生产仓库操作前没有经过评估。那时候,我们也有很多问题。后来我们在内部重点提升了两点意识:责任人意识和风险意识。首先,在生产中采取措施之前,您需要确保操作的影响和后果。这个问题不能做完了再想:比如我们每天都在变,需要提前准备好脚本和手册,让值班人员熟悉。手术的后果是什么?后面会出现什么异常?对策是什么?……这些都需要提前评估。技能提升关于技能的提升,虽然必要的训练必不可少,但我们认为关键还是要靠自己在实践中的学习、理解和积累。没有好的捷径可以实现它。很多时候,还是需要不断的去解决问题,发现问题,甚至包括犯错的代价都可以得到提升。刘栓,平安科技数据库技术部运维团队经理,目前在平安科技数据库技术部运维团队工作。十余年数据库管理经验,多年数据库架构设计和运维管理经验,熟悉性能调优和故障处理。目前主要负责平安科技数据库运维管理工作。团队负责大部分数据库的运维,包括Oracle、PostgreSQL、MySQL、Redis、MongoDB。
