2017-08-08 112 views
2

查詢大Oracle數據庫表的CLOB和LONG時,出現性能問題。如何提高Oracle-DB中的CLOB和LONG值的查詢性能(cx_Oracle vs OJDBC)?

到目前爲止,我寫了下面的單元測試與cx_Oracle(蟒蛇)和JDBC(Java):

Python代碼使用cx_Oracle:使用ojdbc7.jar

class CXOraclePerformanceTest(TestCase): 
    def test_cx_oracle_performance_with_clob(self): 
     self.execute_cx_oracle_performance("CREATE TABLE my_table (my_text CLOB)") 

    def test_cx_oracle_performance_with_long(self): 
     self.execute_cx_oracle_performance("CREATE TABLE my_table (my_text LONG)") 

    def execute_cx_oracle_performance(self, create_table_statement): 
     # prepare test data 
     current_milli_time = lambda: int(round(time.time() * 1000)) 
     db = cx_Oracle.connect(CONNECT_STRING) 

     db.cursor().execute(create_table_statement) 
     db.cursor().execute("INSERT INTO my_table (my_text) VALUES ('abc')") 

     for i in range(13): 
      db.cursor().execute("INSERT INTO my_table (my_text) SELECT 'abc' FROM my_table") 

     row_count = db.cursor().execute("SELECT count(*) FROM my_table").fetchall()[0][0] 
     self.assertEqual(8192, row_count) 

     # execute query with big result set 
     timer = current_milli_time() 

     rows = db.cursor().execute("SELECT * FROM my_table") 
     for row in rows: 
      self.assertEqual("abc", str(row[0])) 

     timer = current_milli_time() - timer 
     print("{} -> duration: {} ms".format(create_table_statement, timer)) 

     # clean-up 
     db.cursor().execute("DROP TABLE my_table") 
     db.close() 

Java代碼:

public class OJDBCPerformanceTest { 

    @Test public void testOJDBCPerformanceWithCLob() throws Exception { 
     testOJDBCPerformance("CREATE TABLE my_table (my_text CLOB)"); 
    } 

    @Test public void testOJDBCPerformanceWithLong() throws Exception { 
     testOJDBCPerformance("CREATE TABLE my_table (my_text LONG)"); 
    } 

    private void testOJDBCPerformance(String createTableStmt) throws Exception { 
     // prepare connection 
     OracleConnection connection = (OracleConnection) DriverManager.getConnection(connectionString); 
     connection.setAutoCommit(false); 
     connection.setDefaultRowPrefetch(512); 

     // prepare test data 
     Statement stmt = connection.createStatement(); 
     stmt.execute(createTableStmt); 
     stmt.execute("INSERT INTO my_table (my_text) VALUES ('abc')"); 

     for (int i = 0; i < 13; i++) 
      stmt.execute("INSERT INTO my_table (my_text) SELECT 'abc' FROM my_table"); 

     ResultSet resultSet = stmt.executeQuery("SELECT count(*) FROM my_table"); 
     resultSet.next(); 
     Assert.assertEquals(8192, resultSet.getInt(1)); 

     // execute query with big result set 
     long timer = new Date().getTime(); 

     stmt = connection.createStatement(); 
     resultSet = stmt.executeQuery("SELECT * FROM my_table"); 
     while (resultSet.next()) 
      Assert.assertEquals("abc", resultSet.getString(1)); 

     timer = new Date().getTime() - timer; 
     System.out.println(String.format("%s -> duration: %d ms", createTableStmt, timer)); 

     // clean-up 
     stmt = connection.createStatement(); 
     stmt.execute("DROP TABLE my_table"); 
    } 

} 

Python的測試輸出:

CREATE TABLE my_table (my_text CLOB) -> duration: 31186 ms 
CREATE TABLE my_table (my_text LONG) -> duration: 218 ms 

Java測試輸出:

CREATE TABLE my_table (my_text CLOB) -> duration: 359 ms 
CREATE TABLE my_table (my_text LONG) -> duration: 14174 ms 
  • 爲什麼既是持續時間如此之高的區別?
  • 如何改善一個或兩個程序的性能?
  • 是否有任何Oracle特定的選項或參數可用於提高查詢性能?

回答

0

經過一番研究,我可以部分回答我的問題。

我設法提高了OJDBC的性能。 OJDBC API提供了屬性useFetchSizeWithLongColumn,您可以通過它快速查詢LONG列。

新建查詢時間: CREATE TABLE my_table (my_text LONG) -> duration: 134 ms

Oracle文檔:

它是一個很小的唯一屬性。它不應該與任何其他驅動程序一起使用。 如果設置爲「true」,則在「SELECT」中檢索數據時的性能將得到改進,但處理LONG列的默認行爲將更改爲獲取多行(預取大小)。這意味着將分配足夠的內存來讀取這些數據。因此,如果您想使用此屬性,請確保您檢索的LONG列不會太大,否則可能會導致內存不足。這個屬性也可以設置爲Java屬性:

java -Doracle.jdbc.useFetchSizeWithLongColumn=true myApplication

或通過API:

Properties props = new Properties(); 
props.setProperty("useFetchSizeWithLongColumn", "true"); 
OracleConnection connection = (OracleConnection) DriverManager.getConnection(connectionString, props); 

我還沒有cx_Oracle的解決方案。這就是爲什麼我開了一個github上的問題:

https://github.com/oracle/python-cx_Oracle/issues/63

1

要獲得相同的性能,長,你需要告訴cx_Oracle以那種方式來獲取的CLOB。你可以看看這個例子: https://github.com/oracle/python-cx_Oracle/blob/master/samples/ReturnLongs.py

在代碼中,我添加了這個方法:

def output_type_handler(self, cursor, name, defaultType, size, precision, scale): 
    if defaultType == cx_Oracle.CLOB: 
     return cursor.var(cx_Oracle.LONG_STRING, arraysize = cursor.arraysize) 

然後,已創建數據庫連接後,我加入這個代碼:

db.outputtypehandler = self.output_type_handler 

這些變化,性能幾乎完全相同。

需要注意的是在幕後,cx_Oracle使用動態獲取和分配。這種方法對於小的CLOB(小的一般意味着幾兆字節或更少)非常適用。在這種情況下,數據庫可以直接發送數據,而當使用LOB的,只是定位器返回到客戶端,則需要另一輪的訪問數據庫獲取數據。你可以想像,那顯著減慢運行,特別是在數據庫和客戶端在網絡上分離出來!