背景
项目中Oracle使用druid数据库连接池,为了提高访问数据库的效率,开启了PreparedStatement Cache。但是随着项目的运行,也出现了一些问题,在项目运行的过程中,给表添加字段后,会一直出现"ORA-17401:违反协议"异常。应用重启后,不再抛出。
问题描述
在应用持续运行中,给生产库中的某张表添加字段,然后涉及该表查询的地方不断抛出"ORA-17401:违反协议"异常,直到重启引用。
异常描述
"ORA-17401:违反协议"是TTC(Two-Task Common)的一个错误。TTC是oracle通信协议里较为靠上的一层,用于处理客户端与数据库服务器通信时的协议差异,比如转换字符集和数据类型。用以确保Oracle的客户端和服务器在不同的网络交互模式(比如TCP/IP、共享内存)下可以透明地交互。
问题定位
- 重启应用异常不再抛出,反应出重新建立数据库链接能够修复该错误;
- 部分项目使用其他连接池没有此问题,而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.OracleDriver@H_301_188@rather than
oracle.jdbc.driver.OracleDriver@H_301_188@as Oracle have stated that
oracle.jdbc.driver.OracleDriver@H_301_188@is 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版本中已经兼容了两种写法,不过我还是按要求改成了正确的。
以上基本就是整个问题的查找过程,不过最后因为种种原因,暂时只是采取了一种比较不合理的方法来规避,尽量减少用户来触发从而降低体验。
原文链接:https://www.f2er.com/oracle/209225.html