在Oracle 9i JDBC驱动程序使用过程中,发现在处理时间参数上存在一个致命的问题,有时候不能正确处理时间参数,导致不能有效的利用时间索引,而生成错误的执行计划,导致系统额外的开销。
数据库中
定义分区表 dyn_dayahead_bid及本地分区Unique索引ind_dyn_daybid_store。
在java
程序中,使用如下的代码去访问数据库: java.sql.Timestamp m_time = .... PreparedStatement m_qry = m_conn.prepareStatement("update DYN_DAYAHEAD_BID set Value_0 = 1 , Value_1 = 2 , Value_2 = 3 , Value_3 = 4 , Value_4 = 5 , Value_5 = 6 , Value_6 = 7 , Value_7 = 8 , Value_8 = 9 , Value_9 = 10 where Data_Time= ? and Tag_Phy= ? and Tag_App= ? and Version= ?");
m_qry.setDate(1, m_time);
m_qry.setDate(1, m_time);
通过跟踪执行计划,发现该语句的执行计划如下面所示:
Rows Row Source Operation
------- ---------------------------------------------------
0 UPDATE
1 PARTITION RANGE ALL PARTITION: 1 5
1 TABLE ACCESS FULL OBJ#(30385) PARTITION: 1 5
在这里可以明显的看出,Oracle没有正确的考虑分区因素,也没有选择使用分区索引,而选择了最差的全表扫描方式。
为了查清原因,在sqlplus下执行以下语句:
SQL> update DYN_DAYAHEAD_BID set Value_0 = 1 , Value_1 = 2 , Value_2 = 3 , Value_3 = 4 , Value_4 = 5 , Value_5 = 6 , Value_6 = 7 , Value_7 = 8 , Value_8 = 9 , Value_9 = 10
where data_time=to_date('2006-04-14 0:15:00','yyyy-mm-dd hh24:mi:ss') and tag_phy='303101120211' and tag_app='5TMS01DBS07' and version=1
SQL> /
已更新 1 行。
Execution Plan
----------------------------------------------------------
0 UPDATE STATEMENT Optimizer=CHOOSE (Cost=2 Card=1 Bytes=51)
1 0 UPDATE OF 'DYN_DAYAHEAD_BID'
2 1 INDEX (UNIQUE SCAN) OF 'IND_DYN_DAYBID_STORE' (UNIQUE) (
Cost=1 Card=1 Bytes=51)
显然,在sql/plus下,正确的考虑了分区因素,并且使用了索引,采取了最优的索引唯一扫描方式定位记录。
在java程序中,将sql语句修改为:
update DYN_DAYAHEAD_BID set Value_0 = 1 , Value_1 = 2 , Value_2 = 3 , Value_3 = 4 , Value_4 = 5 , Value_5 = 6 , Value_6 = 7 , Value_7 = 8 , Value_8 = 9 , Value_9 = 10 where Data_Time= to_date(:?,'yyyy-mm-dd hh24:mi:ss') and Tag_Phy= ? and Tag_App= ?
and Version= ?后,使用字符串做参数后,执行计划就正确了,
Rows
Row Source Operation
-------
---------------------------------------------------
0
UPDATE
0
PARTITION RANGE SINGLE PARTITION: KEY KEY
0
INDEX UNIQUE SCAN OBJ#(30391) PARTITION: KEY KEY (object id 30391)
后来发现,如果将sql语句改成update DYN_DAYAHEAD_BID set Value_0 = 1 , Value_1 = 2 , Value_2 = 3 , Value_3 = 4 , Value_4 = 5 , Value_5 = 6 , Value_6 = 7 , Value_7 = 8 , Value_8 = 9 , Value_9 = 10 where Data_Time= ? +0 and Tag_Phy= ? and Tag_App= ? and Version= ?,还是使用Timestamp类型变量,这时候的执行计划也是正确的。
在我们的应用中,有一个操作需要一起修改约500条记录,当时在数据库中不到10天的数据量(约15万条)的情况下,这个操作需要近2分钟才能完成,可以预料,在正式运行中,随着数据量的激增,用户根本不可能接受这样的结果。通过上面的优化,这个操作现在可以在小于1秒的时间内完成,取得了惊人的效果。
这个问题只所以发生,应该还是Oracle和JDBC在处理时间参数方面存在瑕疵所导致的。通常情况下这个问题很难被发现,这次由于一次操作的记录比较多,才使这个问题暴露出来。