背景
项目中应用服务直接通过jdbc连接impala做数据查询,其他遇到一个问题,查询impala时因为没有设置查询超时,有些大sql一直占用连接,同时这个sql在impala集群中执行着,也占用了impala集群的资源,这样挤压了其他sql的响应。所以这时候设置查询超时,让连接断开,空闲出集群资源能够很大程度上提升服务的稳定性。
解决过程
连接impala的jdbc主要有cloudera jdbc和hive jdbc。因为需要使用kerberos认证来连接impala,最开始同事为了简单话选择了hive jdbc,配置kerberos也方便(官方推荐 Cloudera JDBC Connector)。开始用的很爽,但后面发现并不能设置查询超时(Statement.setQueryTime()无效,但是如果通过hive jdbc连接查询hive的话是能够生效的---HIVE-4924,查询impala却不行)。最后想查询下hive jdbc是否还有其他参数能够设置,通过几天的寻找,最终却无果。
后来把目光放在了cloudera jdbc上,通过文档中的参数,发现一个SocketTimeout参数,并在本地尝试了cloudera jdbc配置上SocketTimeout这个参数,在自测过程上都出现了大的查询没有执行完,因为SocketTimeout时间到了而断开并抛出socket timeout exception,当时很开心,以为问题解决了,当把这个拿给同事时,同事试了几次,有时候会timeout断开,有时候却根本不断开,直到sql执行成功返回,基本宣布这个参数失败。
再一次失败后,都快觉得这个问题搞不定了,我觉得不太可能是官方定义了一个不靠谱的参数,更可能是自己理解上面的错误,后来又反复查看了这个参数的解释(The number of seconds after which Impala closes the connection with the client application if the connection is idle),一旦连接空闲超过这个时长,impala就会关闭应该客户端的连接。什么叫connection is idle,以及它为何叫socketTimeout却不是查询超时的英文呢?socket是网络层,而且在参数定义中,说连接空闲,而不是连接占用的时间。有了这些疑问后,又搜索查询了一番,最终在一篇文章中,很详细的解释了jdbc中的各种timeout。
The higher level timeout is dependent on the lower level timeout. The higher level timeout will operate normally only if the lower level timeout operates normally as well. If the JDBC driver socket timeout does not work properly, then higher level timeouts such as statement timeout and transaction timeout will not work properly either.
Even after the statement timeout was configured, the application still did not recover from the error because the statement timeout did not work at the time of network failure.
什么是JDBC的socket timeout?
第4种类型的JDBC使用socket与数据库连接,数据库并不对应用与数据库间的连接超时进行处理。
JDBC的socket timeout在数据库被突然停掉或是发生网络错误(由于设备故障等原因)时十分重要。由于TCP/IP的结构原因,socket没有办法探测到网络错误,因此应用也无法主动发现数据库连接断开。如果没有设置socket timeout的话,应用在数据库返回结果前会无期限地等下去,这种连接被称为dead connection。
为了避免dead connections,socket必须要有超时配置。socket timeout可以通过JDBC设置,socket timeout能够避免应用在发生网络错误时产生无休止等待的情况,缩短服务失效的时间。
不推荐使用socket timeout来限制statement的执行时长,因此socket timeout的值必须要高于statement timeout,否则,socket timeout将会先生效,这样statement timeout就变得毫无意义,也无法生效。什么是Statement Timeout?
statement timeout用来限制statement的执行时长,timeout的值通过调用JDBC的java.sql.Statement.setQueryTimeout(int timeout) API进行设置。不过现在开发者已经很少直接在代码中设置,而多是通过框架来进行设置。
不同的关系型数据库,以及不同的JDBC驱动,其statement timeout处理过程会有所不同。
通过这篇文章的讲解,我知道我之前在hive jdbc配置的Statement.setQueryTime()是statement timeout,它是依赖于更低基本的socket timeout, impala没有生效的原因很大可能是对于impala的查询statement并没有去处理超时(Statement Timeout Execution Process for JDBC Driver),而对hive查询却做了处理。
而cloudera jdbc提供了socket timeout参数,也在statement timeout提供了超时的处理,即statement查询超时后会中断查询并抛出java.sql.SQLTimeoutException。所以配置上Statement.setQueryTime(),就能够在查询超过timeout值后抛出异常,关闭连接。
最后讲queryTimeOut配置入DataSource,与orm结合,查询超时问题算告一段落。
参考
cloudera jdbc: https://www.cloudera.com/documentation/enterprise/5-11-x/topics/impala_jdbc.html#jdbc_driver_choice
query timeout: https://www.cubrid.org/blog/understanding-jdbc-internals-and-timeout-configuration
带有kerberos验证连接impala: http://blog.csdn.net/tlqfreedom/article/details/75220058