Sharding-JDBC系列
2、Sharding-JDBC分库分表之SpringBoot分片策略
3、Sharding-JDBC分库分表之SpringBoot主从配置
4、SpringBoot集成Sharding-JDBC-5.3.0分库分表
5、SpringBoot集成Sharding-JDBC-5.3.0实现按月动态建表分表
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:数据库的表元数据。包括表元数据、表定义的字段、主键、索引等;
关于本篇内容你有什么自己的想法或独到见解,欢迎在评论区一起交流探讨下吧。