注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

being23

写给未来的自己

 
 
 

日志

 
 
关于我

真正的坚定,就是找到力量去做自己喜欢的事情,并为之努力,这样才会觉得生活是幸福的。

网易考拉推荐

STATEMENT对象优化  

2013-02-06 21:59:57|  分类: work@oppo |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |

重用statement对象

每次创建statement对象时,需要在客户端和服务端分配资源,如内存和游标(cursor),用于存储对象及数据。为了不必要的内存重分配,应重用statement对象。statement对象创建后,可以使用setSQL方法进行重用,例如:

Connection* conn = env->createConnection(); 
Statement* stmt = conn->createStatement(); 
stmt->setSQL(“INSERT INTO fruit_basket_tab VALUES(‘Apples’, 3)”); 
stmt->executeUpdate(); 
stmt->setSQL(“INSERT INTO fruit_basket_tab VALUES(‘Oranges’, 4)”); 
stmt->executeUpdate(); 
stmt->setSQL(“INSERT INTO fruit_basket_tab VALUES(‘Bananas’, 1)”); 
stmt->executeUpdate();' 
stmt->setSQL(“SELECT * FROM fruit_basket_tab WHERE quantity > 2”); 
ResultSet* rs = stmt->executeQuery();

statement参数化

为了进一步控制内存重新分配,可以通过参数化将前面3条SQL语句变成1条,然后设置参数,再执行。注意输入参数的类型变化,因为,每次改变参数类型都会触发重绑定。参数化示例如下:

stmt->setSQL(“INSERT INTO fruit_basket_tab VALUES(:1, :2)”); 
stmt->setString( 1, “Apples” ); 
stmt->setInt( 2, 3 ); 
stmt->executeUpdate(); 
stmt->setString( 1, “Oranges” ); 
stmt->setInt( 2, 4 ); 
stmt->executeUpdate(); 
stmt->setString( 1, “Bananas” ); 
stmt->setInt( 2, 1 ); 
stmt->executeUpdate();

批量更新

对于那些经常发生的操作,很多时间都浪费在与服务器网络通信中。OCCI提供了有效的机制用于在单次网络通信中发送多行信息。该优化可用于INSERTs,UPDATEsDELETEs。首先,设置最大迭代次数,然后设置可变长度参数的最大长度。在迭代过程中参数类型不可变。具体细节参考OCCI Programmers Guide,第二章。下例是上面的INSERTs的优化:

//prepare the batching process 
stmt->setMaxIterations( 3 ); 
stmt->setMaxParamSize( 1, 8 ); //”Bananas” is longest param 
//batch the statements 
stmt->setSQL(“INSERT INTO fruit_basket_tab VALUES(:1, :2)”); 
stmt->setString( 1, “Apples” ); 
stmt->setInt( 2, 3 ); 
stmt->addIteration(); 
stmt->setString( 1, “Oranges” ); 
stmt->setInt( 2, 4 ); 
stmt->addIteration(); 
stmt->setString( 1, “Bananas” ); 
stmt->setInt( 2, 1 ); 
//execute the statements 
stmt->executeUpdate();

Statement::setDataBuffer方法

绑定值到参数化statements的参数时需要内存拷贝,因为为了避免信息在中间执行过程中被覆盖,所以必须拷贝到内部的缓冲区中。拷贝的代价对于大字符串尤其明显,内存的消耗以及拷贝所花的时间。如果应用可以自己管理内存,就可以通过OCCI提供的方法最小化上述开销。

虽然许多OCI开发者使用OCCI简明的创建environmentsstatement对象,但仍然使用许多OCI中的类型。setDataBuffer方法允许OCI开发者执行数组更新,最小化网络通信次数。setDataBuffer方法与setXXX方法工作方法不同。一般说来,setXXX方法会将传过来的数据拷贝到内部缓冲区中,只要setXXX返回后参数值就可以被改变。然而,使用setDataBuffer方法可以避免将数据拷贝到内部缓冲区中。代价是应用程序在执行完statement之前不可以修改缓冲区。例如:

// insert Bananas 
char buf[BUF_SIZE] = "Bananas"; 
int quantity = 1; 
ub2 buflen = strlen( buf ) + 1; 
ub2 quantlen = sizeof(int); 
stmt->setDataBuffer(1, (dvoid*)buf, OCCI_SQLT_STR, buflen, &buflen); 
stmt->setDataBuffer(2, (dvoid*)&quantity, OCCIINT, quantlen, 
&quantlen); 
stmt->executeUpdate(); // executeArrayUpdate(1) also would work. 
// insert Apples 
strcpy( buf, “Apples” ); 
quantity = 3; 
buflen = strlen( buf ) + 1; 
quantlen = sizeof( int ); 
stmt->setDataBuffer(1, (dvoid*)buf, OCCI_SQLT_STR, buflen, &buflen); 
stmt->setDataBuffer(2, (dvoid*)&quantity, OCCIINT, quantlen, 
&quantlen); 
stmt->executeUpdate(); // executeArrayUpdate(1) also would work. 
//commit the transaction 
conn->commit();

setDataBuffer方法可以与迭代执行(iterative executes)和executeArrayUpdate方法结合使用。

executeArrayUpdate方法

当进行大量INSERTsUPDATEs操作时,可以通过executeArrayUpdate方法和setDataBuffer方法批量处理。这可以节省网络通信,提高吞吐量。示例如下:

char fruit[][BUF_SIZE] = { "Apples","Oranges","Bananas","Grapes" }; 
int int_arr[]={ 3,4,1,5 }; 
ub2 fruitlen[4]; // array of size of individual elements 
ub2 intsize[4]; 
for(int i=0 ; i<4 ; i++) 
{ 
intsize[i] = sizeof(int); 
fruitlen[i] = strlen( fruit[i] ) + 1 ; // include the null 
} 
stmt->setDataBuffer(1, (dvoid*)fruit, OCCI_SQLT_STR, BUF_SIZE, 
fruitlen); 
stmt->setDataBuffer(2, (dvoid*)int_arr, OCCIINT, sizeof(int), intsize); 
stmt->executeArrayUpdate(4); 
conn->commit();

executeArrayUpdate方法不会执行,直到所有缓冲区均通过setDataBuffer方法设置。如果有参数需要调用setXXX方法赋值,可以调用setMaxIterationssetMaxParamSize方法,以及addIteration方法。具体如下:

char fruits[][BUF_SIZE] = {“Apples”, “Oranges”, “Bananas”}; 
ub2 fruitLen[3]; 
for( int j=0; j<3; j++ ) 
{ 
fruitLen[j] = strlen( fruits[j] ) + 1; //include the null 
} 
stmt->setMaxIterations(3); 
//setDataBuffer only needs to be executed once 
//while all the other variables need to be set for each iteration 
stmt->setDataBuffer( 1, fruits, OCCI_SQLT_STR, sizeof(fruits[0]), 
fruitLen ); 
stmt->setInt(2, 3); //Apple’s quantity 
stmt->addIteration(); 
stmt->setInt(2, 4); //Orange’s quantity 
stmt->addIteration(); 
stmt->setInt(2, 1); //Banana’s quantity 
//execute the iterative update 
stmt->executeUpdate(3);

使用合适的Accessors和字符集

对操作的列使用合适的setXXXgetXXX方法,而非统一作为string处理,可以省去不必要的转换。
NLS_LANG环境设置中使用合适的字符集,以避免获取字符串时不必要的字符集转换。

自动提交模式

由于所有的SQL DML都是在事务中执行,所以需要确认所有的DML。可以根据具体情况使用“Connection::commit”和“Connection::rollback”方法。“Statement::setAutoCommit”方法可以用来确认其后的每条语句。使用该方法可节省网络传输时间。

//code with AutoCommit 
//transaction 1 
stmt->executeUpdate(“INSERT INTO fruit_basket_tab VALUES(“Apples”,3)); 
stmt->executeUpdate(“INSERT INTO fruit_basket_tab VALUES(“Oranges”,4)); 
stmt->setAutoCommit( TRUE ); 
stmt->executeUpdate(“INSERT INTO fruit_basket_tab VALUES(“Bananas”,1)); 
stmt->setAutoCommit( FALSE ); 
//transaction 2 
stmt->executeUpdate(“INSERT INTO fruit_basket_tab VALUES(“Apples”,5)); 
stmt->executeUpdate(“INSERT INTO fruit_basket_tab VALUES(“Oranges”,6)); 
stmt->setAutoCommit( TRUE ); 
stmt->executeUpdate(“INSERT INTO fruit_basket_tab VALUES(“Bananas”,2)); 
stmt->setAutoCommit( FALSE );

这与下面的语句是等价的,但是2次网络传输,每个事务1次

//code without AutoCommit 
//transaction 1 
stmt->executeUpdate(“INSERT INTO fruit_basket_tab VALUES(“Apples”,3)); 
stmt->executeUpdate(“INSERT INTO fruit_basket_tab VALUES(“Oranges”,4)); 
stmt->executeUpdate(“INSERT INTO fruit_basket_tab VALUES(“Bananas”,1)); 
conn->commit(); 
//transaction 2 
stmt->executeUpdate(“INSERT INTO fruit_basket_tab VALUES(“Apples”,5)); 
stmt->executeUpdate(“INSERT INTO fruit_basket_tab VALUES(“Oranges”,6)); 
stmt->executeUpdate(“INSERT INTO fruit_basket_tab VALUES(“Bananas”,2)); 
conn->commit();

建议AutoCommit只在每个事务的最后一条SQL语句前面使用。

结果集对象的优化

结果集作为请求的响应返回。可以使用nextstatus方法处理结果集:

ResultSet* rs = stmt->executeQuery( “SELECT * FROM fruit_basket_tab” ); 
ResultSet::Status stat = rs->status(); //status is DATA_AVAILABLE 
while( rs->next() ) { //process data }

setPrefetchRowCount and setPrefetchMemorySize

虽然rs->next()每次只能返回一行,但是可以在一次网络中预取多行放到客户端的缓存中。使用类StatementsetPrefetchRowCount方法和setPrefetchMemorySize方法,每次可以取得不止一行。上例优化后如下:

stmt->setPrefetchRowCount( 3 ); 
ResultSet* rs = stmt->executeQuery( “SELECT * FROM fruit_basket_tab” ); 
while ( rs->next() ) { //process data }

使用上述代码,在一次网络通信中就能取得3条记录。默认情形中,预取功能是启用的,每次多取一条。要想关闭预取功能,必须同时调用方法setPrefetchRowCountsetPrefetchMemorySize,参数为0。如果两个setPrefetchXXX方法都被调用了,那么实际预取的数目是这两个方法参数中较小的那个。

setMaxColumnSize

当取得的结果中有些列较大时,可以使用ResultSet::setMaxColumnSize方法限制从指定的列可以获取多少数据。当只对部分数据感兴趣或者缓存大小有限时,这就有用了。

ResultSet *rs = stmt->executeQuery( “SELECT description FROM 
fruit_basket_tab” ); 
//want only first 80 characters from the description column 
rs->setMaxColumnSize( 1, 80 );

关闭结果集

当对结果集的处理结束后,调用Statement::closeResultSet方法手动强制关闭,这样数据库和OCCI的使用的资源就不必等待自动释放。

终止Statement

最后,为了确保没有内存泄漏,并关闭相关的服务器端的游标,需要释放所有的statement对象。

conn->terminateStatement( stmt );

参考资料

  评论这张
 
阅读(1629)| 评论(0)
推荐 转载

历史上的今天

在LOFTER的更多文章

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2017