记一次D腾的现网数据库问题排查经历,真相是万万没想到!!
春节临近,团队小伙伴们工作的斗志正在逐渐涣散,期待这时光快些吧,早发年终,安安心心过个年,以为这几天就能安安稳稳度过了,但直到x日x团队x同学传来捷报:你们提供的敏感数据审核功能出现问题了,任务已经执行了几个小时了,都没处理完,赶紧看看是怎么回事吧。
听了这话,我是又惊又怕,项目隶属的集团属于国企,对用户的各种数据安全管控非常高,日常用户发布的数据有专门的审核团队在负责,但随着 吴签等众多艺人沦陷,需要对涉及到的各项数据实现动态下线,总体是由系统运营部门发起下线任务,我们这面根据需求处理并上报结果,然后他们再根据结果来决定哪些需要真正下线。流程就是这么个流程:
No,这绝对不可能是我这面的问题,我自信满满,因为这个服务是去年7月份投入了近半个月时间从需求对接到开发测试再到上线,虽然要匹配几千万的数据,性能不说快,但整体试基本能在20分钟左右完成匹配和结果数据生成,但他们说的也是事实,还是开始定位问题吧。
前两天我们这面服务上过线,先排查一下是不是其他同学代码改动影响到了,尤其是涉及到数据库配置方面的,因为这种事情说不好,有时候开发影响范围没评估好,一些改动可能会把看似无关的功能给毙了,仔细核查了代码,发现基本就是业务性质的代码变更,那业务应该可以排出了。 突然想到还有一个可能,是不是运维他们升级tomcat启动账号导致的(集团要求,这个是基于传统tomcat的war项目,需要改成非root启动),难道是这个影响到了?因为他们改了tomcat启动账号后,导致一些业务涉及到临时目录没权限而导致业务异常了,遂找运维确认,运维立马给出了否定:我们这个账号安全升级,是不会影响到业务系统读取数据库的,这些都是基于内网的服务连接,可以基本忽略网络开销。 我:是不是可能是数据库的问题? 运维:不可能,数据库有问题早就告警了,你还是先从业务上考虑问题出在那儿吧。
难了,业务上未做改动,数据库没问题,这咋定位呢,那就把日志打详细点,看每步的性能消耗,发布观察,单次数据库查询耗时超过100s了,甚至慢的时候将近200s,业务处理部分很快10-50ms,这什么情况?
我:杰哥,这个数据库查询怎么耗时这么多啊,业务这块没改动过啊,是不是数据库有什么问题或者是数据库做了什么配置上的变更? 运维:不会,数据库同事那面有操作会给我们说的,你把查询语句给我一下,我执行一下。 于是按业务查询逻辑拼接了查询脚本给到运维。
运维:很快啊,基本是ms级,主从库都查询了一下,都很快。
我不禁对自我产生了怀疑,难道业务的服务版本太旧了?抱着试一试的心态,我把业务代码迁移到一个SpringBoot项目中,验证单轮数据读取100-500ms,业务处理部分不超过50ms,这个数据就恢复了之前的状态了,甚至还要快一点。 我:杰哥,这个我用war很慢,jar就很快,这个感觉是数据库的问题吧,毕竟这块的业务没变动过啊(解释不清这现象)。 运维:那你只配置xxx.62这个IP呢,这个是物理节点,其它两个是虚拟机的。
遂再验证,果然还是失望了,war的性能基本没任何变化。 不禁又怀疑是tomcat启动账号问题了,后面运维切换回root账号测试了,一样!随着时间一点一点流逝,我有点焦急了,已经找不到方法了,只有向老大请教了。
我们一起先看了数据库,在三台服务器上分别执行这个查询脚本和运维的测试结果一样,是很快的,然后在Studio 3T(Mongo客户端)上翻页,有差异了,主库的翻页很慢,两个从库的很快,这就感觉有点玄学了(这块就只有数据库同学去定位了),不禁产生一丝敬意。 然后让我这面把主库的IP去掉,只留两个从库的IP呢,你以为这样就行?还是还是一样。
然后又轮番从多个角度进行测试!!!
idea | result |
---|---|
1. 操作小数集合,单次1W条 | 100+s |
2. 每次读取5000条 | 80s附近浮动 |
3. 每次读取1000条 | 10~20s |
4. 不要条件操作一个小集合,单次1W条 | 100+s |
5. 使用阿里arthas分析 | 测试一半有其它事情中断了 |
...... | ...... |
我向老大提建议:实在定位不到,就切换到基于SpringBoot项目里面去吧,可能是war服务的组件问题(有些东西就是玄学不好解释)。老大也同意了,我们还是在deadline前再分析一下吧
继续探索吧,上帝不会辜负一个有决心的人! ..... 好像忽略了一个问题,SpringBoot项目的Mongo连接配置是读写分离的,任务的处理过程和主库没关系,就是从库读取:
uri: mongodb://账号:密码@副本集列表/库实例?authSource=admin&slaveOk=true&replicaSet=shard3&write=1&readPreference=secondaryPreferred&connectTimeoutMS=300000
去掉readPreference=secondaryPreferred试试呢,去掉后默认主库优先,果不其然,性能慢了,它慢了。 那就沿着这个方向向度娘请教了一下,更新了war的读写分离配置,like this:
<!-- mongodb 查询模板-->
<mongo:mongo replica-set="${mongo.replica-set}" id="mongo">
<mongo:options
connections-per-host="${mongo.connections}" max-wait-time="10000"
threads-allowed-to-block-for-connection-multiplier="${mongo.threads}"/>
</mongo:mongo>
<mongo:db-factory authentication-dbname="${mongo.replica-set.authentication}" dbname="${mongo.name}" mongo-ref="mongo" id="mongoFactory" username="${mongo.replica-set.username}" password="${mongo.replica-set.password}"/>
>
<!-- 将mongoTemplate的定义配置上优先由从库读取-->
<!-- 改前配置-->
<bean id="mongoTemplate" class="org.springframework.data.mongodb.core.MongoTemplate">
<constructor-arg name="mongoDbFactory" ref="mongoFactory"/>
</bean>
<!-- 改后配置-->
<!-- 主要用于处理大数据读写分离:优先从secondary节点进行读取操作,secondary节点不可用时从主节点读取数据,-->
<bean id="secondaryPreferredReadPreference" class="com.mongodb.TaggableReadPreference.SecondaryPreferredReadPreference"/>
<bean id="mongoTemplate" class="org.springframework.data.mongodb.core.MongoTemplate">
<constructor-arg name="mongoDbFactory" ref="mongoFactory"/>
<property name="readPreference" ref="secondaryPreferredReadPreference"/>
</bean>
嗯,部署测试,ok,性能果然上来了,性能基本恢复和之前一致了。那这么说起来,还是主Mongo库的问题了(这个后面和运维沟通了,只有再观察观察,只有你的这块业务目前有问题,先优化业务代码吧),诶,和老大汇报了这个情况,那最终就按照业务代码优化来做吧。 为了这次改动的影响范围,之前的业务还是保留保留之前的主库读写,先优化我这个业务由从库读优先吧,于是重新向Spring容器中配置一个新的bean: acBackMongoTemplate,配置如上。
和x团队x同学反馈了,让他们重新发起基于准现网环境的测试任务,验证改动的有效性和准确性,结果15分钟,问题就完美解决了。1天后的服务上线顺带将这个优化需求给上了。
总结: 这次,虽然不是业务这面的问题,但是定位问题的过程还是比较曲折,作为一个5年的Javaer,缺少对这个问题根源的准确定位,中间虽然思考过是数据库的问题,但还是没找准具体的问题点,导致处理周期蛮长的(其实花费了2-3天才解决),并且虽然通过优化数据库读写分离实现了这次Bug的修复,但问题的根源依旧还在,只有依赖数据库DBA大佬了,还是太弱了,学习路途蛮长,继续吧。
后续补充: 时隔几日,具体问题找到了,数据库人员给出的解释:
**mongodb的网卡水晶头松了,从1000M降成了100M,正好遇到定时数据库把宽带占满,导致应用大部分连接没法连接数据库。业务大面积无法访问!!!!
转载自:https://juejin.cn/post/7187695989300396089