事情是这样的,我们的系统经常出现莫名其妙的卡顿现象.一个正常情况下40ms就完成的请求,有时候会出现短则几秒钟,长则几十秒的延时.经过排查,排除了mongodb慢查询,道理上讲同一个请求,同样的参数数据库查询不可能一会儿快一会儿慢.然后在mongo驱动里打日志分析,确定不是发生了数据库重连.那进一步推断只能是mongo驱动回调机制出现了问题.经过N个小时的问题排查,手段包括加连接数,加服务器,升级mongo驱动到最新版本.这其中只有加连接数奏效了.
还有一个奇怪的现象.在压力测试的时候,查询同一个接口,压到1000qps也不会出现卡顿的,然而放到生产环境后,就会有卡顿.综合这几个分析结果,我猜测是有慢查询影响了快的查询,于是进行了测试.
在测试中,我把连接数限制到2.同时启动两个查询,一块一慢,两个都是每隔一秒查询一次,这个时候能发现快的那个查询存在明显的卡顿现象,一会儿几十毫秒,一会儿就飙到几秒十几秒去了.然而单独执行快查询,时间就很平均且都是几十毫秒就执行完了.更夸张的是,如果连接数调成1,这俩查询是一起回调的,都是十几秒才完成.而如果把这两种查询任务分配独立的连接池,它们之间就没有影响了.
这样就基本得出了结论.node-mongodb-native这个驱动每个连接上的查询是会排队的,并不像熟悉nodejs的人想象的那样基于事件驱动,IO完成就回调,是坑爹的排队执行的,同一个连接前面有慢查询的话,慢查询结束之前后面的查询必须等待!!!
这也解释了为什么加连接数会有效果,因为连接数多了以后,同一个连接数上排队的查询就会少一点,那队列前面存在慢查询的几率也小了.然而连接数优化只能缓解问题,不能从根本上解决问题,当我们把每个进程的连接数从10加到50后,卡顿现象少了一半.从50加到100,没有变化.
解决办法还没有定下来,我测试了java驱动,不存在快慢查询排队现象.先把问题分享出来,看看大家有遇到过这个问题没有.实在不行,打算把数据库查询功能用java写成一个代理服务,不用node这个驱动了.