【问题标题】:How can I improve query performance for CLOB and LONG values in Oracle-DB (cx_Oracle vs OJDBC)?如何提高 Oracle-DB(cx_Oracle 与 OJDBC)中 CLOB 和 LONG 值的查询性能?
【发布时间】:2018-01-16 04:57:35
【问题描述】:

我在查询大型 Oracle 数据库表的 CLOB 和 LONG 时遇到性能问题。

到目前为止,我使用 cx_Oracle (python) 和 JDBC (java) 编写了以下单元测试:

使用 cx_Oracle 的 Python 代码:

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()

使用 ojdbc7.jar 的 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 特定选项或参数可用于提高查询性能?

【问题讨论】:

    标签: java oracle performance cx-oracle ojdbc


    【解决方案1】:

    要获得与 LONG 相同的性能,您需要告诉 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 时,只需将定位器返回给客户端,然后需要再次往返数据库来获取数据。可以想象,这会显着减慢操作速度,尤其是在数据库和客户端在网络上分开的情况下!

    【讨论】:

      【解决方案2】:

      经过一番研究,我可以部分回答我的问题。

      我设法提高了 OJDBC 性能。 OJDBC API 提供了useFetchSizeWithLongColumn 属性,您可以使用它非常快速地查询 LONG 列。

      新查询时长: CREATE TABLE my_table (my_text LONG) -&gt; 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

      【讨论】:

        猜你喜欢
        • 2019-05-23
        • 1970-01-01
        • 2013-01-16
        • 2012-01-28
        • 2011-10-14
        • 2012-10-27
        • 1970-01-01
        • 2017-01-13
        • 1970-01-01
        相关资源
        最近更新 更多