Oracle 使用druid连接池,发生“违反协议”异常

前端之家收集整理的这篇文章主要介绍了Oracle 使用druid连接池,发生“违反协议”异常前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

背景

项目中Oracle使用druid数据库连接池,为了提高访问数据库的效率,开启了PreparedStatement Cache。但是随着项目的运行,也出现了一些问题,在项目运行的过程中,给表添加字段后,会一直出现"ORA-17401:违反协议"异常。应用重启后,不再抛出。

问题描述

在应用持续运行中,给生产库中的某张表添加字段,然后涉及该表查询的地方不断抛出"ORA-17401:违反协议"异常,直到重启引用。

异常描述

"ORA-17401:违反协议"是TTC(Two-Task Common)的一个错误。TTC是oracle通信协议里较为靠上的一层,用于处理客户端与数据库服务器通信时的协议差异,比如转换字符集和数据类型。用以确保Oracle的客户端和服务器在不同的网络交互模式(比如TCP/IP、共享内存)下可以透明地交互。

问题定位

  1. 重启应用异常不再抛出,反应出重新建立数据库链接能够修复该错误
  2. 部分项目使用其他连接池没有此问题,而druid与之最大的差异就是开启了PreparedStatement Cache。PreparedStatement实际上避免了相同sql语句重复解析和建立游标,同时也复用了相同的数据结构。那么当服务器端的表结构即数据结构发生改变时,很有可能TTC就会无法处理这种改变。而使用普通的Statement时,因为不存在数据结构的复用,所以也就不会出现"违反协议异常"。

所以,根据以上两点,关闭PreparedStatement Cache后,重复之前错误的流程,不再抛出该异常。

但是选择druid就是为了其PreparedStatement Cache这个特性,然后只能继续找问题,最后查阅代码,发现之前的mapper.xml中,查询都是直接使用的select *,对部分改为select [字段] 后,发现问题不再出现,但是和其他开发人人员沟通后,认为将目前项目所有的查询修改,工作量较大,风险较高,而且同样的查询在每个项目中都有,也不想进行重构,所以,只能用其他的办法来解决

最终解决方

在和其他人员沟通后,只能将修改后的影响降为最低,所以只能通过如下代码,在每次修改表结构后,主动清除每个应用服务器上每个数据库连接的PreparedStatement Cache,以此来尽量避免由用户行为触发异常的出现。

    @Resource(name = "druidDataSource")
    private DruidDataSource druidDataSource;

    public void clearDruidStatementCache() {
        Log.info("DruidDataSource","清除Druid的Statement Cache Start......");
        if (druidDataSource != null) {
            try {
                druidDataSource.clearStatementCache();
            } catch (sqlException e) {
                Log.error("DruidDataSource","清除Druid的Statement Cache Error",e);
            }
        }
        Log.info("DruidDataSource","清除Druid的Statement Cache End......");
    }

问题的查找过程

最初用的是druid1.0.25版本,根据http://www.oschina.net/news/79452/druid-1-0-27中的信息,在1.0.27版本中,进行了如下修改


根据其中所说,如果执行出错,可以从PSCache中移除,所以将版本换到了1.0.27,但是问题还是依然存在,只好继续查。


根据http://tomcat.apache.org/tomcat-5.5-doc/jndi-datasource-examples-howto#Oracle_8i,_9i_&_10g,注意如下内容

For Oracle 9i onwards you should useoracle.jdbc.OracleDriverrather thanoracle.jdbc.driver.OracleDriveras Oracle have stated thatoracle.jdbc.driver.OracleDriveris deprecated and support for this driver class will be discontinued in the next major release.

而我们用的是oracle.jdbc.driver.OracleDriver,并不是oracle.jdbc.OracleDriver,但是并不一定要进行修改

这是因为根据https://github.com/alibaba/druid/issues/1598中所说,在1.0.29版本中已经兼容了两种写法,不过我还是按要求改成了正确的

以上基本就是整个问题的查找过程,不过最后因为种种原因,暂时只是采取了一种比较不合理的方法来规避,尽量减少用户来触发从而降低体验。

猜你在找的Oracle相关文章