zoukankan      html  css  js  c++  java
  • 求生欲很强的数据库

     

    最近一个月来一直在做某局的项目,涉及到了微信支付相关的业务。业务本身也是一套完整的从下单到支付到退款的全流程,我司和三方联调开发,个中滋味不表。

    其中从最开始就遇到一个反复出现(偶发性)的问题,有时候数据库无法快速且正常返回查询结果。我先后从多个方面进行尝试调试,寻求解决办法。

    我先还原一下场景。

    服务器:2台a和b。

    负载均衡:访问任意到打到a或者b。

    数据库:前期没有使用持续化存储,用的热数据缓存。此次使用mongo,最开始版本3.0.x,后升级到我司其他线上项目运行版本3.2.6。a和b公用同一台database service,且mongo安装在b上。

    现象:数据库有时候能返回查询结果,有时候好像卡死在那里,从接口调用到最终数据库服务本身皆无错误日志。我查询的那个集合只有50多条记录,并且我增加了索引在我的查询字段上,按照道理来说应该是毫秒级别的耗时。

    首先我先从项目数据库调用入手,写了最小化的脚本,单独去读取某一条订单记录,能快速且正常返回。至此说明项目是用的mongo driver是能正常调用到mongo的。后我开启mongoose debug模式,开启方式为在我操作类里添加如下语句:

    mongoose.set('debug', true);

    开启后,每当调用到数据库操作会输出类似mysql的sql语句,更方便调试跟踪问题。然而我看到的现象是,有时候能正常执行查询的时候就有该debug日志吞吐,而有时候没有任何输出,也无数据返回。

    期间,我反复尝试安装并部署不同版本的mongo和对应版本的mongoose,甚至调整了项目框架版本,先排除了各模块之间兼容性问题。

    后我怀疑到是否是负载均衡到了a(mongo是安装在b上),而a不能访问到b上的mongo呢?

    我查询相关资料,修改了mongo的配置文件,将bind_ip从127.0.0.1改为0.0.0.0,且把maxConns改为2000。试探性地临时关闭了iptables服务,然而没有解决该问题。

    在这个过程里面我有意识到,每当重启服务之后短暂的十几秒里面,可以正常进行查询,最多能成功2次,到第三次的时候就会前端调用超时报错。

    后为了能顺利进行后续开发(因为很多接口是用到数据库存储本地数据的),我暂时关闭了a上的项目服务,先排除因为网络不通造成的问题。到这个时候数据库查询就开始正常了一段时间,我的开发也能顺利进行下去。

    我那个时候暂时把问题归结于网络,因为脚本本地调用是ok的,关闭了负载,b上本地部署mongo调用也是正常的。我和某科的老师一起排查问题,a和b两台机子相互ping和telnet均成功,说明数据链路层是通的,问题应该是在应用层上的。通过尝试添加output设置,将27017端口暴露出去。然后重启a上的服务,再次进行负载均衡,惊喜地发现两边都可以正常使用mongo查询了。到这个时候,我再次把问题归结于网络设置导致每次请求转到a的时候无法连接到mongo,而有时候负载到b是正常的,所以才会偶发性地出现查询结果无法返回导致超时的问题。

    Xx大会如期上线了我们的项目,大会期间非常稳定,此后的一周我再也没想起这段为了解决诡异bug而烦恼的时光。直到后面和某海联调退费接口的时候,再次出现该问题。

    说实话,到这次复现问题的时候,我是有点没有思路。因为之前几乎把所有可能性都尝试过了。在同事探讨和帮助的过程,我想到了会不会是有其他代码引起mongo操作阻塞呢?

    在网上也有人遇到过类似问题,讨论回复如下:

    有个比较常见的坑是mongodb使用数据库级的锁,在写数据的时候要快,否则很容易把其他mongodb的请求阻塞。你可以尝试在mongodb起来的时候是就开起mongostat,然后查看locked%和qr|qw列的值是否出现异常,飙升。 

    还有如果collection中记录很多的话,一定要建立索引。你可以直接在mongo shell下验证你的查询是否有用到索引,我记得mongo语句后跟explain()可以显示索引是否起作用的。

    简单说就是有频繁写入的操作和频繁未命中索引(包括无索造成引)查询(被查询的集合数据量较大)的逻辑代码导致数据库级别锁,让其他请求一直排队等待。我想起了每次查看日志的时候总有调用人社接口的日志大量输出,而在那个公共调用的代码里面有一段读写mongo的操作代码。

    我仔细看了一下,这段历史遗留代码是为了缓存从人社接口拉取到的数据,其他的我不说了,缓存这段代码毫无意义,首先从量级来看全cd人的数据有无必要存是其一,其二查询没有索引,业务场景上是存储的数据是只增不减的,数据数量越来越大,查询会非常慢。其三我全文搜索也没有找到用到这个数据的其他地方。经过查询,该集合数据量已经超过200万条。

    我先屏蔽了该段代码,重启服务后发现之前timeout的接口马上毫秒级返回了数据。多次尝试跑通了全支付流程,再无mongo查询异常出现。之后和写这段代码的前同事沟通后确认该代码是可以删除的,至此该问题尘埃落地。

    所以,之前时好时坏,只是因为数据库顽强的求生欲而已。本质是因为那段历史遗留代码会非常频繁地读写数据库,导致其他请求排队等待。通过在这次排查问题,我又一次对分析问题有了新的思考:

    1. 对一些临时接手后有大量历史遗留问题的项目,一定要深入细节,不能因为是继承的代码,默认它是正常无错的,关于那段存储人社数据的代码我也曾经怀疑过,但是没有去深究,默认了它一直使用就是不能轻易改动的。
    2. 结对编程是很重要的。这在一些其他公司已经很常见了,对于小的创业公司,我们考虑更多的是一个工程师去support一个或者多个完整的项目,有时候个人的思路是非常有限的,常见的问题是很容易通过自身努力解决,而一些思路有死角的疑难杂症可能需要群策。我这里说的结对编程特指对该项目负责且全程参与的人。因为往往一些google都很难找到特别接近的解决方案的问题,往往有项目自身特殊原因。

    God is in details.God bless my code.

  • 相关阅读:
    再次或多次格式化导致namenode的ClusterID和datanode的ClusterID之间不一致的问题解决办法
    Linux安装aria2
    POJ 3335 Rotating Scoreboard 半平面交
    hdu 1540 Tunnel Warfare 线段树 区间合并
    hdu 3397 Sequence operation 线段树 区间更新 区间合并
    hud 3308 LCIS 线段树 区间合并
    POJ 3667 Hotel 线段树 区间合并
    POJ 2528 Mayor's posters 贴海报 线段树 区间更新
    POJ 2299 Ultra-QuickSort 求逆序数 线段树或树状数组 离散化
    POJ 3468 A Simple Problem with Integers 线段树成段更新
  • 原文地址:https://www.cnblogs.com/freephp/p/8997457.html
Copyright © 2011-2022 走看看