今天早上,领导给了我一个任务:在老的系统运行中,DBA反馈说获取database TableMeta操作有点慢,让我分析下基于oracle driver驱动是否可以做下优化。由此引出了本文,仅仅做一个记录。
在补充几点背景知识:
1. 老系统介绍
2. table meta操作原理
jdbcTemplate.execute(new ConnectionCallback() { public Object doInConnection(Connection c) throws SQLException, DataAccessException { DatabaseMetaData meta = c.getMetaData(); meta.getTables(catalog, schemaPattern, tableNamePattern, types); meta.getColumns(catalog, schemaPattern, tableNamePattern, columnNamePattern); meta.getPrimaryKeys(catalog, schema, table); return null; } });
简单一点说,就是利用java.sql.DatabaseMetaData接口中定义的meta信息获取接口进行处理。
主要是通过构造对应的SQL进行查询,主要是关联了all_objects 和 all_tab_comments, all_synonyms
SELECT NULL AS table_cat, o.owner AS table_schem, o.object_name AS table_name, o.object_type AS table_type, c.comments AS remarks FROM all_objects o, all_tab_comments c WHERE o.owner LIKE #schema# ESCAPE '/' AND o.object_name LIKE #table# ESCAPE '/' AND o.object_type IN ('TABLE', 'SYNONYM', 'VIEW') AND o.owner = c.owner (+) AND o.object_name = c.table_name (+) UNION SELECT NULL AS table_cat, s.owner AS table_schem, s.synonym_name AS table_name, 'SYNONYM' AS table_table_type, c.comments AS remarks FROM all_synonyms s, all_objects o, all_tab_comments c WHERE s.owner LIKE #schema# ESCAPE '/' AND s.synonym_name LIKE #table# ESCAPE '/' AND s.table_owner = o.owner AND s.table_name = o.object_name AND o.object_type IN ('TABLE', 'VIEW') AND o.owner = c.owner (+) AND o.object_name = c.table_name (+) ORDER BY table_type, table_schem, table_name
注意一下#schema# , #table#的替换,可以使用%进行模糊匹配
主要是通过构造对应的SQL进行查询,主要关联了all_tab_comments, all_synonyms, all_col_comments
SELECT NULL AS table_cat, DECODE(s.table_owner, NULL, t.owner, s.table_owner) AS table_schem, DECODE(s.synonym_name, NULL, t.table_name, s.synonym_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, 1111) AS data_type, t.data_type AS type_name, DECODE (t.data_precision, null, t.data_length, t.data_precision) AS column_size, 0 AS buffer_length, t.data_scale AS decimal_digits, 10 AS num_prec_radix, DECODE (t.nullable, 'N', 0, 1) AS nullable, c.comments 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 FROM all_tab_columns t , all_col_comments c , all_synonyms s WHERE (t.owner LIKE #schema# ESCAPE '/' OR (s.owner LIKE #schema# ESCAPE '/' AND t.owner = s.table_owner)) AND (t.table_name LIKE #table# ESCAPE '/' OR s.synonym_name LIKE #table# ESCAPE '/') AND t.column_name LIKE #column# ESCAPE '/' AND t.owner = c.owner (+) AND t.table_name = c.table_name (+) AND t.column_name = c.column_name (+) AND s.table_name (+) = t.table_name AND ((DECODE(s.owner, t.owner, 'OK','PUBLIC', 'OK',NULL, 'OK','NOT OK') = 'OK') OR (s.owner LIKE 'SRF' AND t.owner = s.table_owner)) ORDER BY table_schem, table_name, ordinal_position
注意一下#schema# , #table# , #column#的替换,可以使用%进行模糊匹配
主要是通过构造对应的SQL进行查询,主要关联了 all_cons_columns, all_constraints
SELECT NULL AS table_cat, c.owner AS table_schem, c.table_name, c.column_name, c.position AS key_seq, c.constraint_name AS pk_name FROM all_cons_columns c, all_constraints k WHERE k.constraint_type = 'P' AND k.table_name = #table# AND k.owner like #schema# escape '/' AND k.constraint_name = c.constraint_name AND k.table_name = c.table_name AND k.owner = c.owner ORDER BY column_name
注意一下#schema# , #table# 的替换,可以使用%进行模糊匹配
有两种获取meta信息的方式
SELECT TABLE_SCHEMA AS TABLE_CAT, NULL AS TABLE_SCHEM, TABLE_NAME, CASE WHEN TABLE_TYPE='BASE TABLE' THEN 'TABLE' WHEN TABLE_TYPE='TEMPORARY' THEN 'LOCAL_TEMPORARY' ELSE TABLE_TYPE END AS TABLE_TYPE, TABLE_COMMENT AS REMARKS FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA LIKE #schema# AND TABLE_NAME LIKE #table# AND TABLE_TYPE IN ('BASE TABLE','VIEW','TEMPORARY') ORDER BY TABLE_TYPE, TABLE_SCHEMA, TABLE_NAME
SHOW TABLES from retl like 'columns'; show full columns from columns from retl like 'text%' ;
具体的getTables,getColumns,getPrimaryKeys的实现就不一一贴了,有兴趣的可以自己去看看
目前我们新老系统分别使用了ddlutils和schemacrawler两种tablemeta分析方案,最终都是基于DatabaseMetaData进行数据获取。
两者在实现上没有本质的区别,只不过在schemacrawler在meta信息的获取上可自定义性更强,比如你只关注table,不关注columns,primarykeys,foreignkey等,都可以通过SchemaCrawlerOptions进行指定
schemaCrawler例子:
Connection connection = dataSource.getConnection(); DatabaseMetaData databaseMetaData = connection.getMetaData(); String nameSpace = dataMedia.getNamespace(); String name = dataMedia.getName(); if (databaseMetaData.storesUpperCaseIdentifiers()) {// 识别大小写 nameSpace = nameSpace.toUpperCase(); name = name.toUpperCase(); } if (databaseMetaData.storesLowerCaseIdentifiers()) { nameSpace = nameSpace.toLowerCase(); name = name.toLowerCase(); } final SchemaCrawlerOptions options = new SchemaCrawlerOptions(); options.setSchemaInfoLevel(SchemaInfoLevel.standard()); options.setSchemaInclusionRule(new InclusionRule(nameSpace, InclusionRule.NONE)); options.setTableInclusionRule(new InclusionRule(nameSpace + "." + name, InclusionRule.NONE)); Database database = SchemaCrawlerUtility.getDatabase(connection, options); Schema[] schemas = database.getSchemas(); for(Schema schema : schemas) { for (Table table: Schema.getTables()) { Column[] columns = table.getColumns(); } }
分析本质,主要还是调用DatabaseMediaData进行操作
MetadataResultSet results = null; results = new MetadataResultSet(getMetaData() .getTables(unquotedName(catalogName), unquotedName(schemaName), tableNamePattern, TableType.toStrings(tableTypes)));// 调用getTables方法 while (results.next()) { // "TABLE_CAT", "TABLE_SCHEM" final String tableName = quotedName(results.getString("TABLE_NAME")); final TableType tableType = results.getEnum("TABLE_TYPE",TableType.unknown); final String remarks = results.getString("REMARKS"); final MutableSchema schema = lookupSchema(catalogName, schemaName); ..... if (tableInclusionRule.include(table.getFullName())) { table.setType(tableType); table.setRemarks(remarks); schema.addTable(table); } }
通过代码分析,可以看到获取meta信息的方式,总共有3次SQL查询.
因此总结一下优化方案:
本文可能对他人借鉴意义并不是非常大,只为自己做一下记录,项目第一个版本上线后再来做一下对应的优化方案。优化无止境,fighting!!!!!