历经一个月的项目,使用Crate提升搜索速度终于要上线.先交代一下背景,我们使用的crate相关的依赖的版本:
1.crate-client 0.47.8
2.crate-jdbc 1.5.1
发布之后,验证阶段一切正常,等把流量入口打开,一段时间之后,猛然发现crate的一个查询服务所在的机器load飙到20往上,机器是4核CPU,4G内存的虚拟机.
top -H发现有大量的tomcat线程(10+),每个线程占用的CPU都达到20%,并且这种线程有增多的趋势.
vmstat命令发现r数字特别高,即正在等待CPU的线程非常多.
jstack的结果发现runnable的线程多达300多.
看现象,感觉问题是线程池分配没有回收,但是定位不到问题在哪.
最终定位的问题是在特定场景下存在死循环,并且直接才crate服务器执行相同的查询逻辑正常返回,因此基本定位在crate-jdbc中.
1.死循环场景
1.crate中数据为空,这是很从crate中查询数据;
2.crate中存在满足查询条件数据,我们分页查询,假设总数据量为10页,我们因为代码问题,直接查询第11页的内容(实际上肯定不存在);
2.原因
原因是这个版本crate-jdbc和mybatis一起存在死循环,如下,我们对crate的查询首先经过mybatis,从
1.MapperProxy.invoke
2.MapperMethod.execute
3.SqlSessionTemplate.selectList, SqlSessionTemplate.invoke
4.DefaultSqlSession.selectList
5.BaseExecutor.query, BaseExecutor.queryFromDatabase
6.ReuseExecutor.doQuery
7.RoutingStatementHandler.query
8.PreparedStatementHandler.query
9.CratePreparedStatement.execute()
10.DefaultResultSetHandler.handleResultSets, DefaultResultSetHandler.getFirstResultSet
问题的代码就在DefaultResultSetHandler.getFirstResultSet中:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
|
其中stmt为CratePreparedStatement,其getMoreResults()实现:
1 2 3 4 |
|
因此逻辑进入到else中,stmt.getUpdateCount()的实现:
1 2 3 4 5 6 7 8 |
|
其中CratePreparedStatement的resultSet为null,并且sqlResponse不为空,sqlResponse的rowCount为0,因此getUpdateCount()返回值为0.因此getFirstResultSet中继续在while循环中,无法跳出.
3.绕过
归结起来产生问题的很简单,就是查询的结果集为空的时候,就会产生死循环,针对两种死循环场景,我们都可以绕过:
1.查询之前,先执行count查询,当count为0,即crate中没有满足我们查询条件的数据,则直接返回空数据集;
2.分页查询的时候,先执行count查询,当分页的offset大于等于count,则值额节返回空数据集;