【问题标题】:How to improve activejdbc startup performance?如何提高activejdbc启动性能?
【发布时间】:2021-10-04 00:39:28
【问题描述】:

我的组织在我们的 Java Web 应用程序中使用带有 javalite + activejdbc 的遗留组件作为 ORM。我正在为开发创建一个本地 docker 数据库(oracle 12c)。当我启动指向本地数据库的本地码头服务器时,启动需要 1 个多小时。原因是活动的 jdbc 正在查看所有表的所有实体类,并在循环中为每个表获取元数据。查看活动的 JDBC 注册表类 (org.javalite.activejdbc.Registry) 它这样做:

Connection c = ConnectionsAccess.getConnection(dbName);
java.sql.DatabaseMetaData databaseMetaData = c.getMetaData();
String[] tables = metaModels.getTableNames(dbName);
for (String table : tables) {
    ResultSet rs = databaseMetaData.getColumns(null, schema, tableName, null);
    ...
}

每个调用都需要大约 15-30 秒,并且有数百个实体类。当我将本地服务器指向我们的测试数据库时,它的速度要快得多(但仍然非常慢)。无论如何我可以调整我的本地 docker 数据库,以便这些元数据调用更快?或者我可以设置任何activejdb配置来使初始化变得懒惰?与我们的测试数据库相比,这些调用在本地数据库上花费的时间要长得多,这一定是有原因的。我不认为这是因为我们的测试数据库如此强大 - 测试数据库真的很慢而且资源很少。

编辑/澄清: 这似乎不是一个活跃的 jdbc 问题,而是一个问题,为什么元数据查询在我的本地 docker 数据库上需要这么长时间。以下代码使用本地数据库 URL 需要 16 秒,指向测试时需要 356 毫秒。我还可以在 docker 映像中看到本地 CPU 峰值达到 100%。

public class DatabaseMetaDataTest {

     public static void main(String args[]) throws SQLException {
          //Registering the Driver
          DriverManager.registerDriver(new oracle.jdbc.driver.OracleDriver());
          //Getting the connection
          String url = "jdbc:oracle:thin:@localhost:1521/ORCLCDB.localdomain";
          //String url = "jdbc:oracle:thin:@test:1532:xe";
          
          Connection con = DriverManager.getConnection(url, "user", "pass");
          System.out.println("Connection established......");
          //Retrieving the meta data object
          DatabaseMetaData metaData = con.getMetaData();
          //Retrieving the columns in the database
          long start = System.currentTimeMillis();
          ResultSet columns = metaData.getColumns(null, "SCHEMA", "TABLE", null);
          long end = System.currentTimeMillis();
          System.out.println("duration:" + (end-start));
          //Printing the column name and size

       }

}

进一步更新: 我反编译了 oracle 驱动程序,发现这是永远占用的 SQL:

SELECT  NULL AS table_cat,
       t.owner AS table_schem,
       t.table_name AS table_name,
       t.column_name AS column_name,
       DECODE (t.data_type, 'CHAR', 1, 'VARCHAR2', 12, 'NUMBER', 3,
               'LONG', -1, 'DATE', 93, 'RAW', -3, 'LONG RAW', -4,  
               'BLOB', 2004, 'CLOB', 2005, 'BFILE', -13, 'FLOAT', 6, 
               'TIMESTAMP(6)', 93, 'TIMESTAMP(6) WITH TIME ZONE', -101, 
               'TIMESTAMP(6) WITH LOCAL TIME ZONE', -102, 
               'INTERVAL YEAR(2) TO MONTH', -103, 
               'INTERVAL DAY(2) TO SECOND(6)', -104, 
               'BINARY_FLOAT', 100, 'BINARY_DOUBLE', 101, 
               'XMLTYPE', 2009, 
               1111)
              AS data_type,
       t.data_type AS type_name,
       DECODE (t.data_precision,                null, DECODE(t.data_type,                        'NUMBER', DECODE(t.data_scale,                                    null, 0                                   , 38),          DECODE (t.data_type, 'CHAR', t.char_length,                   'VARCHAR', t.char_length,                   'VARCHAR2', t.char_length,                   'NVARCHAR2', t.char_length,                   'NCHAR', t.char_length,                   'NUMBER', 0,           t.data_length)                           ),         t.data_precision)
              AS column_size,
       0 AS buffer_length,
       DECODE (t.data_type,                'NUMBER', DECODE(t.data_precision,                                 null, DECODE(t.data_scale,                                              null, -127                                             , t.data_scale),                                  t.data_scale),                t.data_scale) AS decimal_digits,
       10 AS num_prec_radix,
       DECODE (t.nullable, 'N', 0, 1) AS nullable,
       NULL AS remarks,
       t.data_default AS column_def,
       0 AS sql_data_type,
       0 AS sql_datetime_sub,
       t.data_length AS char_octet_length,
       t.column_id AS ordinal_position,
       DECODE (t.nullable, 'N', 'NO', 'YES') AS is_nullable,
         null as SCOPE_CATALOG,
       null as SCOPE_SCHEMA,
       null as SCOPE_TABLE,
       null as SOURCE_DATA_TYPE,
       'NO' as IS_AUTOINCREMENT
FROM all_tab_columns t
WHERE t.owner LIKE 'SCHEMA' ESCAPE '/'
  AND t.table_name LIKE 'TABLE' ESCAPE '/'
  AND t.column_name LIKE '%' ESCAPE '/'

ORDER BY table_schem, table_name, ordinal_position

我可以看到,当我在 oracle sql developer 中运行它时,我的 sysdba 用户需要 0.5 秒,但其他用户需要 16 秒。仍在调查这些用户之间的区别。

进一步更新... 这似乎是由于 12c 中的一些 oracle 错误造成的。 select * from all_tab_columns 在以普通用户身份运行时执行计划不佳。它抱怨一些晦涩的表“X$KZSRO”进行全表扫描并永远进行排序(表有 2 行 ffs)。当我以 sysdba 身份连接时,它运行得更快。我猜普通用户访问此表存在一些问题。现在,因为这只是开发数据库,​​我只是将 sysdba 角色授予我的用户并稍后找出一些 sql 配置文件。我知道它不是很好的解决方案,但它修复了 oracle 中的性能错误。启动时间从 1 小时缩短到 1 分钟。

【问题讨论】:

    标签: oracle orm activejdbc


    【解决方案1】:

    首先,如果从您的数据库中获取每个表的元数据需要 15 到 30 秒,那么该数据库一定存在非常错误。 ActiveJDBC 使用动态发现以便在每次启动时与数据库同步。这是默认行为。

    但是,如果您愿意,可以使用Static Metadata Generation。 使用此方法,将在构建期间收集所有数据库元数据,并将其作为文件打包到您的 jar 中。 ActiveJDBC 将在所有其他环境中立即启动,因为它将从该文件而不是数据库中读取元数据。

    显然,您必须确保构建时的数据库与您的其他数据库具有完全相同的架构。否则,您将遇到一些映射问题。

    虽然静态元数据生成将解决您的启动性能问题,但您的数据库仍然存在问题,我强烈建议您对此进行调查。

    注意:ActiveJDBC 的第一个实现是在 2009 年为 Humana 实现的,我们也使用了 Oracle 数据库。我们当时的架构大约有 120 个表,而 ActiveJDBC 总是以闪电般的速度启动。

    【讨论】:

    • 感谢您的回复。你是在重申问题。我的本地数据库有问题。如何调整它以使活动的 jdbc 启动得更快。我不需要本地数据库快如闪电——只要足够快就可以进行开发工作。当我指向测试数据库时,它可能在 1 分钟内开始,指向 localhost docker 数据库需要 1 小时。我不清楚 metadata.getcolumns 是如何工作的(例如它是否使用 oracle 数据字典表)以及为什么这些查询如此缓慢。我想继续使用动态发现并避免回归测试成本。
    • 因为你是对的,所以给 +1 - oracle 数据库有一些非常错误的地方;)特别是 12c 中的性能错误。
    • 感谢您将其跟踪到 oracle 驱动程序。请尝试使用静态元数据方法,因为它可以让您解决 Oracle 驱动程序问题。
    • 给你 +1 磨练司机
    【解决方案2】:

    这个问题实际上并不是一个 activejdbc 问题,而是一个 oracle 12c 问题。除非您以 sysdba 身份登录,否则似乎从 12c 上的 ALL_TAB_COLUMNS 表读取性能不佳/查询计划不佳。它不是一个很好的解决方案,但它适用于本地 docker dev 数据库,所以我只是将我的用户设置为 sysdba。有一天会找到一些 sql 配置文件作为真正的解决方案。 sysdba 不能用于任何 prod 环境,但它适用于 localhost dev 数据库。

    grant sysdba to my_user;
    

    jetty-env.xml:

    <New class="oracle.jdbc.pool.OracleDataSource">
      <Set name="URL">jdbc:oracle:thin:@localhost:1521/ORCLCDB.localdomain</Set>
      <Set name="user">my_user as sysdba</Set>
      <Set name="password">my_password</Set>
    </New>
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2023-01-22
      • 2017-06-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-09-19
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多