【源码】Sharding-JDBC源码分析之配置数据库定义的表的元数据解析原理

 Sharding-JDBC系列

1、Sharding-JDBC分库分表的基本使用

2、Sharding-JDBC分库分表之SpringBoot分片策略

3、Sharding-JDBC分库分表之SpringBoot主从配置

4、SpringBoot集成Sharding-JDBC-5.3.0分库分表

5、SpringBoot集成Sharding-JDBC-5.3.0实现按月动态建表分表

6、【源码】Sharding-JDBC源码分析之JDBC

7、【源码】Sharding-JDBC源码分析之SPI机制

8、【源码】Sharding-JDBC源码分析之Yaml分片配置文件解析原理

9、【源码】Sharding-JDBC源码分析之Yaml分片配置原理(一)

10、【源码】Sharding-JDBC源码分析之Yaml分片配置原理(二)

11、【源码】Sharding-JDBC源码分析之Yaml分片配置转换原理

12、【源码】Sharding-JDBC源码分析之ShardingSphereDataSource的创建原理

13、【源码】Sharding-JDBC源码分析之ContextManager创建中mode分片配置信息的持久化存储的原理

14、【源码】Sharding-JDBC源码分析之ContextManager创建中ShardingSphereDatabase的创建原理

15、【源码】Sharding-JDBC源码分析之分片规则生成器DatabaseRuleBuilder实现规则配置到规则对象的生成原理

16、【源码】Sharding-JDBC源码分析之配置数据库定义的表的元数据解析原理

前言

【源码】Sharding-JDBC源码分析之ContextManager创建中ShardingSphereDatabase的创建原理-CSDN博客

中介绍ShardingSphereDatabase的创建。ShardingSphereDatabase是通过create()方法创建,创建时,传入Map<String, ShardingSphereSchema>集合。此处的ShardingSphereSchema为数据库的元数据,本篇从源码分析数据库元数据的解析过程。

ShardingSphereDatabase回顾

ShardingSphereDatabase的相关源码如下:

/**
 * 数据库信息
 */
@Getter
public final class ShardingSphereDatabase {

    // 默认logic_db
    private final String name;

    // 数据库类型。如MySQLDatabaseType
    private final DatabaseType protocolType;

    // 资源元数据。数据源、数据库类型、数据源的元数据(hostname、port等)
    private final ShardingSphereResourceMetaData resourceMetaData;

    // 配置的规则的元数据集合,线程安全
    private final ShardingSphereRuleMetaData ruleMetaData;

    // schema中定义的表和视图的信息。key默认为logic_db
    private final Map<String, ShardingSphereSchema> schemas;
    
    public ShardingSphereDatabase(final String name, final DatabaseType protocolType, final ShardingSphereResourceMetaData resourceMetaData,
                                  final ShardingSphereRuleMetaData ruleMetaData, final Map<String, ShardingSphereSchema> schemas) {
        this.name = name;
        this.protocolType = protocolType;
        this.resourceMetaData = resourceMetaData;
        this.ruleMetaData = ruleMetaData;
        this.schemas = new ConcurrentHashMap<>(schemas.size(), 1);
        schemas.forEach((key, value) -> this.schemas.put(key.toLowerCase(), value));
    }

    /**
     * 创建一个ShardingSphereDatabase
     * @param name 默认为logic_db
     * @param protocolType 默认为MySQLDatabaseType
     * @param storageTypes 对应逻辑数据源及协议类型。如:order_ds: MySQLDatabaseType
     * @param databaseConfig 数据源配置。默认为DataSourceProvidedDatabaseConfiguration对象,数据源提供数据库配置。从数据库中获取的数据库配置信息
     * @param props 配置的props
     * @param instanceContext
     * @return
     * @throws SQLException
     */
    public static ShardingSphereDatabase create(final String name, final DatabaseType protocolType, final Map<String, DatabaseType> storageTypes,
                                                final DatabaseConfiguration databaseConfig, final ConfigurationProperties props, final InstanceContext instanceContext) throws SQLException {
        // 获取配置的数据源规则,如ShardingRule(分片的规则。将ShardingRuleConfiguration中的规则配置项转化成对应规则的对象)等
        // 创建数据源的规则。根据配置的规则对象(xxxConfiguration)转化成对应规则对象(xxxRule)
        Collection<ShardingSphereRule> databaseRules = DatabaseRulesBuilder.build(name, databaseConfig, instanceContext);
        Map<String, ShardingSphereSchema> schemas = new ConcurrentHashMap<>();
        // 从数据源中组装数据库元数据(表元数据、表中列元数据)
        schemas.putAll(GenericSchemaBuilder.build(new GenericSchemaBuilderMaterial(protocolType, storageTypes,
                // 获取可用的DataSource的Map
                DataSourceStateManager.getInstance().getEnabledDataSourceMap(name, databaseConfig.getDataSources()), databaseRules, props,
                DatabaseTypeEngine.getDefaultSchemaName(protocolType, name))));
        // 生成系统的schema。获取databaseType定义的databaseName的默认表集合,从包中获取对应表的yaml文件,解析成ShardingSphereSchema
        schemas.putAll(SystemSchemaBuilder.build(name, protocolType));
        return create(name, protocolType, databaseConfig, databaseRules, schemas);
    }
    
    // 省略其他
}

create()方法执行如下:

1)执行DatabaseRulesBuilder.build(),将配置的规则转换为ShardingSphereRule;

详见:【源码】Sharding-JDBC源码分析之分片规则生成器DatabaseRuleBuilder实现规则配置到规则对象的生成原理

2)执行GenericSchemaBuilder.build(),从配置的数据源中获取数据库的ShardingSphereSchema元数据(定义的表元数据、表定义的字段、主键、索引等);

3)执行SystemSchemaBuilder.build(),根据DatabaseType中定义的系统默认库、默认表,从jar包中获取对应表的Yaml文件,解析Yaml中配置的表、字段等为ShardingSphereSchema元数据对象;

4)执行同名create()方法,传入上面的ShardingSphereRule集合、ShardingSphereSchema集合,最后创建ShardingSphereDatabase对象,保存如上的信息;

GenericSchemaBuilder

GenericSchemaBuilder的源码如下:

package org.apache.shardingsphere.infra.metadata.database.schema.builder;

/**
 * 普通schema的生成器
 */
public final class GenericSchemaBuilder {
    

    /**
     * 创建通用的schema
     */
    public static Map<String, ShardingSphereSchema> build(final GenericSchemaBuilderMaterial material) throws SQLException {
        // 获取规则中的所有表名。包括逻辑表名和实际表名
        return build(getAllTableNames(material.getRules()), material);
    }

    /**
     * 创建通用的schema
     * @param tableNames
     * @param material
     * @return
     * @throws SQLException
     */
    public static Map<String, ShardingSphereSchema> build(final Collection<String> tableNames, final GenericSchemaBuilderMaterial material) throws SQLException {
        // 获取表的元数据
        Map<String, SchemaMetaData> result = loadSchemas(tableNames, material);
        // 判断协议类型是否与存储类型相同,如果不同,需要先翻译转换
        if (!isProtocolTypeSameWithStorageType(material)) {
            result = translate(result, material);
        }
        // 将SchemaMetaData装饰为ShardingSphereSchema
        return decorate(result, material);
    }

    /**
     * 判断配置的数据源的数据库类型是否和数据源的数据库类型一致。默认protocolType为配置的第一个数据源的数据库类型
     * @param material
     * @return
     */
    private static boolean isProtocolTypeSameWithStorageType(final GenericSchemaBuilderMaterial material) {
        for (DatabaseType each : material.getStorageTypes().values()) {
            if (!material.getProtocolType().equals(each)) {
                return false;
            }
        }
        return true;
    }

    /**
     * 获取规则中的所有表名。包括逻辑表名和实际表名,以及存储配置信息的repository表
     */
    private static Collection<String> getAllTableNames(final Collection<ShardingSphereRule> rules) {
        return rules.stream().filter(each -> each instanceof TableContainedRule).flatMap(each -> ((TableContainedRule) each).getTables().stream()).collect(Collectors.toSet());
    }

    /**
     * 加载Schema,返回Map对象。key为logic_db,value为SchemaMetaData。SchemaMetaData记录库中对应的表、视图集合
     * @param tableNames 规则中的所有表名。包括逻辑表名和实际表名,以及存储配置信息的repository表
     * @param material
     * @return
     * @throws SQLException
     */
    private static Map<String, SchemaMetaData> loadSchemas(final Collection<String> tableNames, final GenericSchemaBuilderMaterial material) throws SQLException {
        // 判断应用程序启动或更新时是否验证表元数据一致性,默认为false
        boolean checkMetaDataEnable = material.getProps().getValue(ConfigurationPropertyKey.CHECK_TABLE_META_DATA_ENABLED);
        // 获取模式元数据加载器材料。根据数据源分类,创建SchemaMetaDataLoaderMaterial,记录schema名(默认logic_db)、真实表名、数据源、数据类型
        Collection<SchemaMetaDataLoaderMaterial> schemaMetaDataLoaderMaterials = SchemaMetaDataUtil.getSchemaMetaDataLoaderMaterials(tableNames, material, checkMetaDataEnable);
        if (schemaMetaDataLoaderMaterials.isEmpty()) {
            return Collections.emptyMap();
        }
        // 加载SchemaMetaData
        return SchemaMetaDataLoaderEngine.load(schemaMetaDataLoaderMaterials);
    }

    /**
     * 解析翻译。整合schemaMetaDataMap中同一个schema(默认为数据库名)的SchemaMetaData
     * @param schemaMetaDataMap
     * @param material
     * @return
     */
    private static Map<String, SchemaMetaData> translate(final Map<String, SchemaMetaData> schemaMetaDataMap, final GenericSchemaBuilderMaterial material) {
        Collection<TableMetaData> tableMetaDataList = new LinkedList<>();
        // 遍历数据源的数据库类型
        for (DatabaseType each : material.getStorageTypes().values()) {
            // 获取默认的schema名称,如MySQL,则为数据库名称,默认为logic_db
            String defaultSchemaName = DatabaseTypeEngine.getDefaultSchemaName(each, material.getDefaultSchemaName());
            // 添加schema中的TableMetaData对象
            tableMetaDataList.addAll(Optional.ofNullable(schemaMetaDataMap.get(defaultSchemaName)).map(SchemaMetaData::getTables).orElseGet(Collections::emptyList));
        }
        // 获取默认的schema名称,如MySQL,则为数据库名称,默认为logic_db
        String frontendSchemaName = DatabaseTypeEngine.getDefaultSchemaName(material.getProtocolType(), material.getDefaultSchemaName());
        Map<String, SchemaMetaData> result = new LinkedHashMap<>();
        result.put(frontendSchemaName, new SchemaMetaData(frontendSchemaName, tableMetaDataList));
        return result;
    }

    /**
     * 装饰。先对SchemaMetaData进行深拷贝,然后将SchemaMetaData转换为ShardingSphereSchema
     * @param schemaMetaDataMap
     * @param material
     * @return
     */
    @SuppressWarnings({"unchecked", "rawtypes"})
    private static Map<String, ShardingSphereSchema> decorate(final Map<String, SchemaMetaData> schemaMetaDataMap, final GenericSchemaBuilderMaterial material) {
        // 添加所有的schemaMetaDataMap
        Map<String, SchemaMetaData> result = new LinkedHashMap<>(schemaMetaDataMap);
        // 获取规则基础schema数据源装饰器
        // 深拷贝result
        for (Entry<ShardingSphereRule, RuleBasedSchemaMetaDataDecorator> entry : OrderedSPIRegistry.getRegisteredServices(RuleBasedSchemaMetaDataDecorator.class, material.getRules()).entrySet()) {
            if (!(entry.getKey() instanceof TableContainedRule)) {
                continue;
            }
            result.putAll(entry.getValue().decorate(result, (TableContainedRule) entry.getKey(), material));
        }
        // 将SchemaMetaData转换为ShardingSphereSchema
        return convertToSchemaMap(result, material);
    }

    /**
     * 将SchemaMetaData转换为ShardingSphereSchema
     * @param schemaMetaDataMap
     * @param material
     * @return
     */
    private static Map<String, ShardingSphereSchema> convertToSchemaMap(final Map<String, SchemaMetaData> schemaMetaDataMap, final GenericSchemaBuilderMaterial material) {
        if (schemaMetaDataMap.isEmpty()) {
            return Collections.singletonMap(material.getDefaultSchemaName(), new ShardingSphereSchema());
        }
        Map<String, ShardingSphereSchema> result = new ConcurrentHashMap<>(schemaMetaDataMap.size(), 1);
        for (Entry<String, SchemaMetaData> entry : schemaMetaDataMap.entrySet()) {
            Map<String, ShardingSphereTable> tables = convertToTableMap(entry.getValue().getTables());
            result.put(entry.getKey().toLowerCase(), new ShardingSphereSchema(tables, new LinkedHashMap<>()));
        }
        return result;
    }

    /**
     * 将TableMetaData转化为ShardingSphereTable。key为表名,value为ShardingSphereTable
     */
    private static Map<String, ShardingSphereTable> convertToTableMap(final Collection<TableMetaData> tableMetaDataList) {
        Map<String, ShardingSphereTable> result = new LinkedHashMap<>(tableMetaDataList.size(), 1);
        for (TableMetaData each : tableMetaDataList) {
            Collection<ShardingSphereColumn> columns = convertToColumns(each.getColumns());
            Collection<ShardingSphereIndex> indexes = convertToIndexes(each.getIndexes());
            Collection<ShardingSphereConstraint> constraints = convertToConstraints(each.getConstrains());
            result.put(each.getName(), new ShardingSphereTable(each.getName(), columns, indexes, constraints));
        }
        return result;
    }
    
    private static Collection<ShardingSphereColumn> convertToColumns(final Collection<ColumnMetaData> columnMetaDataList) {
        Collection<ShardingSphereColumn> result = new LinkedList<>();
        for (ColumnMetaData each : columnMetaDataList) {
            result.add(new ShardingSphereColumn(each.getName(), each.getDataType(), each.isPrimaryKey(), each.isGenerated(), each.isCaseSensitive(), each.isVisible(), each.isUnsigned()));
        }
        return result;
    }
    
    private static Collection<ShardingSphereIndex> convertToIndexes(final Collection<IndexMetaData> indexMetaDataList) {
        Collection<ShardingSphereIndex> result = new LinkedList<>();
        for (IndexMetaData each : indexMetaDataList) {
            result.add(new ShardingSphereIndex(each.getName()));
        }
        return result;
    }
    
    private static Collection<ShardingSphereConstraint> convertToConstraints(final Collection<ConstraintMetaData> constraintMetaDataList) {
        Collection<ShardingSphereConstraint> result = new LinkedList<>();
        for (ConstraintMetaData each : constraintMetaDataList) {
            result.add(new ShardingSphereConstraint(each.getName(), each.getReferencedTableName()));
        }
        return result;
    }
}

build()方法执行如下:

1)执行getAllTableNames()方法,从ShardingSphereRule集合中获取规则中的所有表名;

https://blog.csdn.net/JingAi_jia917/article/details/141862121 中介绍了会自动添加SingleRule,所以此处的表包括逻辑表名、实际表名,以及数据源中未添加分片配置的单表;

2)执行同名build()方法,返回Map<String, ShardingSphereSchema>对象。执行如下:

2.1)执行loadSchemas()方法,返回Map<String, SchemaMetaData>,其中key为数据库名称,value为SchemaMetaData。SchemaMetaData中保存对应数据库中的表的元数据,如表名、表定义的字段、主键、约束等;

a)执行SchemaMetaDataUtil.getSchemaMetaDataLoaderMaterials()方法,根据配置的dataSource分类,每个dataSource创建一个SchemaMetaDataLoaderMaterial对象。对象中保存dataSouce对应的schema(默认为logic_db)、DataSource、DatabaseType数据库类型、真实表集合;

b)执行SchemaMetaDataLoaderEngine.load(schemaMetaDataLoaderMaterials),根据对应的DatabaseType,找到对应的DialectSchemaMetaDataLoader,通过配置的数据源,获取Connection连接,查找对应表的元数据信息。如字段、字段类型、主键、外键等;

c)按schema分组,封装元数据为SchemaMetaData对象;

2.2)执行decorate(),将Map<String, SchemaMetaData>装饰为Map<String, ShardingSphereSchema>对象;

遍历Map<String, SchemaMetaData>,解析SchemaMetaData信息,逐层解析table、column、index、constraint,创建对应的ShardingSphereTable、ShardingSphereTColumn、ShardingSphereIndex、ShardingSphereConstraint,然后创建创建ShardingSphereSchema对象;

SystemSchemaBuilder

SystemSchemaBuilder的源码如下:

package org.apache.shardingsphere.infra.metadata.database.schema.builder;

@NoArgsConstructor(access = AccessLevel.PRIVATE)
public final class SystemSchemaBuilder {

    /**
     * 生成系统的schema。获取databaseType定义的databaseName的默认表集合,从包中获取对应表的yaml文件,解析成ShardingSphereSchema
     * @param databaseName
     * @param databaseType
     * @return
     */
    public static Map<String, ShardingSphereSchema> build(final String databaseName, final DatabaseType databaseType) {
        Map<String, ShardingSphereSchema> result = new LinkedHashMap<>(databaseType.getSystemSchemas().size(), 1);
        YamlTableSwapper swapper = new YamlTableSwapper();
        // 获取对应databaseType中的默认databaseName库,并遍历
        for (String each : getSystemSchemas(databaseName, databaseType)) {
            // getSchemaStreams:从系统获取对应库的表所配置的yaml文件流
            // 解析yaml文件中的信息为ShardingSphereSchema对象
            result.put(each.toLowerCase(), createSchema(getSchemaStreams(each, databaseType), swapper));
        }
        return result;
    }

    /**
     * 通过databaseType对象定义的系统默认的数据库,获取对应originalDatabaseName的schema。如MySQLDatabaseType,传入mysql时,获取mysql系统库
     * @param originalDatabaseName
     * @param databaseType
     * @return
     */
    private static Collection<String> getSystemSchemas(final String originalDatabaseName, final DatabaseType databaseType) {
        String databaseName = databaseType instanceof PostgreSQLDatabaseType || databaseType instanceof OpenGaussDatabaseType ? "postgres" : originalDatabaseName;
        return databaseType.getSystemDatabaseSchemaMap().getOrDefault(databaseName, Collections.emptyList());
    }
    
    private static Collection<InputStream> getSchemaStreams(final String schemaName, final DatabaseType databaseType) {
        // 获取对应数据库类型的对应数据库要获取的表集合信息SystemSchemaBuilderRule对象
        SystemSchemaBuilderRule builderRule = SystemSchemaBuilderRule.valueOf(databaseType.getType(), schemaName);
        Collection<InputStream> result = new LinkedList<>();
        // 遍历表,获取对应表的yaml文件输入流。文件定义在schema/mysql/sys/...
        for (String each : builderRule.getTables()) {
            result.add(SystemSchemaBuilder.class.getClassLoader().getResourceAsStream("schema/" + databaseType.getType().toLowerCase() + "/" + schemaName + "/" + each + ".yaml"));
        }
        return result;
    }
    
    private static ShardingSphereSchema createSchema(final Collection<InputStream> schemaStreams, final YamlTableSwapper swapper) {
        Map<String, ShardingSphereTable> tables = new LinkedHashMap<>(schemaStreams.size(), 1);
        for (InputStream each : schemaStreams) {
            YamlShardingSphereTable metaData = new Yaml().loadAs(each, YamlShardingSphereTable.class);
            tables.put(metaData.getName(), swapper.swapToObject(metaData));
        }
        return new ShardingSphereSchema(tables, Collections.emptyMap());
    }
}

build()方法执行如下:

1)通过getSystemSchemas()方法,获取对应databaseType中的默认databaseName库;

在DatabaseType中,定义了系统默认的数据库信息。如MySQLDatabaseType,相关源码如下:

public final class MySQLDatabaseType implements DatabaseType {
    
    // 系统数据库的schema
    private static final Map<String, Collection<String>> SYSTEM_DATABASE_SCHEMA_MAP = new HashMap<>();
    
    static {
        SYSTEM_DATABASE_SCHEMA_MAP.put("information_schema", Collections.singletonList("information_schema"));
        SYSTEM_DATABASE_SCHEMA_MAP.put("performance_schema", Collections.singletonList("performance_schema"));
        SYSTEM_DATABASE_SCHEMA_MAP.put("mysql", Collections.singletonList("mysql"));
        SYSTEM_DATABASE_SCHEMA_MAP.put("sys", Collections.singletonList("sys"));
        SYSTEM_DATABASE_SCHEMA_MAP.put("shardingsphere", Collections.singletonList("shardingsphere"));
    }

    @Override
    public Map<String, Collection<String>> getSystemDatabaseSchemaMap() {
        return SYSTEM_DATABASE_SCHEMA_MAP;
    }
}

2)遍历默认库,执行createScema(),创建对应schema的ShardingSphereSchema;

a)执行getSchemaStreams()方法,先从SystemSchemaBuilderRule枚举类中获取定义的某个schema包含的表名集合。之后遍历表名,从对应包的路径中查找schema/目录下对应数据库类型的对应表的Yaml文件,获取Yaml的输入流。如MySQL的sys库,则有一个sys表,表对应的文件为schema/mysql/sys/sys.yaml。文件信息如下:

name: sys_config
columns:
  variable:
    caseSensitive: false
    dataType: 12
    generated: false
    name: variable
    primaryKey: true
    visible: true
  value:
    caseSensitive: false
    dataType: 12
    generated: false
    name: value
    primaryKey: false
    visible: true

# 省略其他

b)遍历Yaml输入流,使用SnakeYaml的Yaml对象,解析对应的输入流为YamlShardingSphereTable对象;

c)通过YamlTableSwapper,将YamlShardingSphereTable转换为ShardingSphereTable;

d)创建ShardingSphereSchema对象;

3)返回按schema分组的ShardingSphereSchema,返回Map<String, ShardingSphereSchema>对象;

小结

限于篇幅,本篇就分享到这里。以下做一个小结:

1)ShardingSphereDatabase通过内部静态create()方法创建,create()执行如下:

1.1)通过DatabaseRulesBuilder.build(),将配置的rules规则的RuleConfiguration转换为ShardingSphereRule对象;

详见:【源码】Sharding-JDBC源码分析之分片规则生成器DatabaseRuleBuilder实现规则配置到规则对象的生成原理

1.2)执行GenericSchemaBuilder.build(),从配置的数据源中获取数据库的ShardingSphereSchema元数据(定义的表元数据、表定义的字段、主键、索引等);

a)从ShardingSphereRule从获取所有表名,包括配置的分片表以及数据源中的未配置的单表;

b)通过数据源的Connection对象,查找对应表的元数据信息。如字段、字段类型、主键、约束等;

c)最后按schema分组,将对应信息封装成ShardingSphereSchema对象;

1.3)执行SystemSchemaBuilder.build(),根据DatabaseType中定义的系统默认库、默认表,从jar包中获取对应表的Yaml文件,解析Yaml中配置的表、字段等为ShardingSphereSchema元数据对象;

a)根据DatabaseType中定义的系统默认库、默认表,从jar包中获取对应表的Yaml文件。文件中定义了对应表的字段、字段类型、主键、约束等;

b)使用SnakeYaml的Yaml对象,解析对应的输入流为YamlShardingSphereTable对象。通过YamlTableSwapper转换为ShardingSphereTable,创建ShardingSphereSchema对象;

1.4)执行同名create()方法,传入上面的ShardingSphereRule集合、ShardingSphereSchema集合,最后创建ShardingSphereDatabase对象;

2)ShardingSphereDatabase对象包含的信息为:

2.1)String name:数据库名称,默认为logic_db;

2.2)DatabaseType databaseType:数据库对应的类型;

2.3)ShardingSphereResourceMetaData resourceMetaData:资源元数据。包括:数据源对象、数据源对应的数据库类型、数据源的配置元数据;

2.4)ShardingSphereRuleMetaData ruleMetaData:配置的规则ShardingSphereRule集合;

2.5)Map<String, ShardingSphereSchema> schemas:数据库的表元数据。包括表元数据、表定义的字段、主键、索引等;

关于本篇内容你有什么自己的想法或独到见解,欢迎在评论区一起交流探讨下吧。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值