和其他應(yīng)對(duì)高并發(fā),數(shù)據(jù)量大的方案比若皱,分庫(kù)分表通常是最實(shí)用,最樸素的一個(gè)方案尘颓,既簡(jiǎn)單又有效走触。但是分庫(kù)分表后怎么可以對(duì)業(yè)務(wù)層代碼影響降到最小,是程序員們需要解決的問(wèn)題疤苹,下邊看一下一個(gè)開(kāi)源組件 sharding-jdbc互广,應(yīng)用程序只需引入jar然后通過(guò)編寫(xiě)分片策略方法、和相關(guān)的配置卧土,即可實(shí)現(xiàn)分庫(kù)分表惫皱。
本篇文章主要解釋以下內(nèi)容,基于 4.0.1 版本
研究源碼前尤莺,首先思考幾個(gè)問(wèn)題
1旅敷、sharding-jdbc 為什么可以和 Mybatis\ibaits\hibernate\jpa\spring data template 結(jié)合使用,換句話說(shuō):為什么不依賴orm
2颤霎、內(nèi)部運(yùn)行流程分別是怎么實(shí)現(xiàn)的
(1)初始化過(guò)程
(2)sql 解析
(3)sql 提取
(4)sql 路由
(5)sql 替換
(6)sql 執(zhí)行
1媳谁、sharding-jdbc 為什么不依賴orm
sharding-jdbc 的執(zhí)行入口是 ShardingDataSource 此類實(shí)現(xiàn)了 javax.sql.DataSource 涂滴,javax 的 x 是extension 的意思,也就是擴(kuò)展包,為了使 java 基礎(chǔ)包更加通用撼玄,在上邊加了一層擴(kuò)展唆缴。javax.sql.DataSource 接口只有一個(gè)方法 getConnection , 幾乎所有的 orm 框架都是用 DataSource 接口獲取數(shù)據(jù)庫(kù)連接的搁料。 所以才使得 sharding-jdbc 不依賴具體的 orm 層框架成為可能。
其中 ShardingDataSource 構(gòu)造函數(shù)就是进苍,初始化的入口加缘。
其中 getConnection 方法,是執(zhí)行階段的入口觉啊。
public class ShardingDataSource extends AbstractDataSourceAdapter {
private final ShardingRuntimeContext runtimeContext;
//初始化階段
public ShardingDataSource(final Map<String, DataSource> dataSourceMap, final ShardingRule shardingRule, final Properties props) throws SQLException {
//匹配數(shù)據(jù)庫(kù)類型
super(dataSourceMap);
checkDataSourceType(dataSourceMap);
runtimeContext = new ShardingRuntimeContext(dataSourceMap, shardingRule, props, getDatabaseType());
}
private void checkDataSourceType(final Map<String, DataSource> dataSourceMap) {
for (DataSource each : dataSourceMap.values()) {
Preconditions.checkArgument(!(each instanceof MasterSlaveDataSource), "Initialized data sources can not be master-slave data sources.");
}
}
//執(zhí)行階段
@Override
public final ShardingConnection getConnection() {
return new ShardingConnection(getDataSourceMap(), runtimeContext, TransactionTypeHolder.get());
}
}
2拣宏、內(nèi)部運(yùn)行流程分別是怎么實(shí)現(xiàn)的
(1)初始化階段
涉及的相關(guān)類 uml 圖
初始化分為3個(gè)步驟:
- 根據(jù)配置信息 shardingRuleConfiguration 創(chuàng)建分片規(guī)則 ShardingRule
- 匹配數(shù)據(jù)庫(kù)類型 databaseType
- 創(chuàng)建 sql 解析引擎 SQLParseEngine 、sql 執(zhí)行引擎 ShardingExecuteEngine
- 保存數(shù)據(jù)庫(kù)杠人、數(shù)據(jù)表信息勋乾。
下邊我們一個(gè)一個(gè)分析,以 spring namespace 方法創(chuàng)建為例嗡善。
1辑莫、根據(jù)配置信息 shardingRuleConfiguration 創(chuàng)建分片規(guī)則 ShardingRule
public class SpringShardingDataSource extends ShardingDataSource {
public SpringShardingDataSource(final Map<String, DataSource> dataSourceMap, final ShardingRuleConfiguration shardingRuleConfiguration, final Properties props) throws SQLException {
//new ShardingRule 創(chuàng)建分片規(guī)則
super(dataSourceMap, new ShardingRule(shardingRuleConfiguration, dataSourceMap.keySet()), props);
}
}
spring namespace 會(huì)根據(jù) xml 的配置創(chuàng)建好用戶配置信息類ShardingRuleConfiguration 包括:
- TableRuleConfiguration:邏輯表名 + 可路由到的數(shù)據(jù)數(shù)據(jù)節(jié)點(diǎn)名(數(shù)據(jù)源名+表名)支持表達(dá)式
- bindingTableGroups 綁定表組:例如order + order_item 表
- broadcastTables 廣播表:例如配置信息需要每個(gè)數(shù)據(jù)庫(kù)都有一張這樣的表,需要修改的時(shí)候罩引,那么需要廣播對(duì)吧!
- defaultDataSourceName 默認(rèn)數(shù)據(jù)源名稱
- defaultDatabaseShardingStrategyConfig 默認(rèn)分庫(kù)策略
- defaultTableShardingStrategyConfig 默認(rèn)分表策略
其中分片策略配置包括:暗示策略Hint基于ThraedLocal
表達(dá)式分片策略Inline基于groovy 的 Inline 表達(dá)式
復(fù)合分片策略 Complex 基于多列 各吨、不分庫(kù)策略None,標(biāo)準(zhǔn)策略Standard基于單列袁铐。
然后根據(jù) shardingRuleConfiguration和數(shù)據(jù)源集合揭蜒,創(chuàng)建分片規(guī)則 ShardingRule
public ShardingRule(final ShardingRuleConfiguration shardingRuleConfig, final Collection<String> dataSourceNames) {
Preconditions.checkArgument(null != shardingRuleConfig, "ShardingRuleConfig cannot be null.");
Preconditions.checkArgument(null != dataSourceNames && !dataSourceNames.isEmpty(), "Data sources cannot be empty.");
this.ruleConfiguration = shardingRuleConfig;
//根據(jù)配置創(chuàng)建所有數(shù)據(jù)源集合,主從復(fù)制需要特殊處理(只是保留一個(gè)主從公共數(shù)據(jù)源)
shardingDataSourceNames = new ShardingDataSourceNames(shardingRuleConfig, dataSourceNames);
//根據(jù)分片配置剔桨,創(chuàng)建分片規(guī)則
tableRules = createTableRules(shardingRuleConfig);
broadcastTables = shardingRuleConfig.getBroadcastTables();
//創(chuàng)建 表分片key與分片規(guī)則完全一致 的規(guī)則
bindingTableRules = createBindingTableRules(shardingRuleConfig.getBindingTableGroups());
//創(chuàng)建默認(rèn)的分庫(kù)策略
defaultDatabaseShardingStrategy = createDefaultShardingStrategy(shardingRuleConfig.getDefaultDatabaseShardingStrategyConfig());
defaultTableShardingStrategy = createDefaultShardingStrategy(shardingRuleConfig.getDefaultTableShardingStrategyConfig());
defaultShardingKeyGenerator = createDefaultKeyGenerator(shardingRuleConfig.getDefaultKeyGeneratorConfig());
//主從規(guī)則
masterSlaveRules = createMasterSlaveRules(shardingRuleConfig.getMasterSlaveRuleConfigs());
encryptRule = createEncryptRule(shardingRuleConfig.getEncryptRuleConfig());
}
其中最重要的方法 createTableRules 根據(jù)分片配置以邏輯表為單位創(chuàng)建數(shù)據(jù)節(jié)點(diǎn)屉更,通常一個(gè)TableRule(一個(gè)邏輯表規(guī)則) 對(duì)應(yīng)多個(gè)分表,分庫(kù)洒缀。
//以每個(gè)表為單位創(chuàng)建分片規(guī)則
private Collection<TableRule> createTableRules(final ShardingRuleConfiguration shardingRuleConfig) {
Collection<TableRuleConfiguration> tableRuleConfigurations = shardingRuleConfig.getTableRuleConfigs();
Collection<TableRule> result = new ArrayList<>(tableRuleConfigurations.size());
for (TableRuleConfiguration each : tableRuleConfigurations) {
//創(chuàng)建分片規(guī)則
result.add(new TableRule(each, shardingDataSourceNames, getDefaultGenerateKeyColumn(shardingRuleConfig)));
}
return result;
}
然后創(chuàng)建初始化 TableRule
1瑰谜、將邏輯表名轉(zhuǎn)成小寫(xiě)
2、獲取用戶配置的真實(shí)表名树绩,因?yàn)榭梢耘渲帽磉_(dá)式所以這里需要計(jì)算一下萨脑。
3、將真實(shí) datasource + 表名 (逗號(hào)分隔) 封裝成 DataNode
如果用戶沒(méi)有配置真實(shí)表葱峡,那么默認(rèn)使用邏輯表名充當(dāng)真實(shí)表名
4砚哗、根據(jù)分庫(kù)策略配置項(xiàng),創(chuàng)建分庫(kù)策略砰奕。根據(jù)分表策略配置項(xiàng)蛛芥,創(chuàng)建分表策略
5提鸟、非默認(rèn)策略必須配置一個(gè)想要路由到的真實(shí)表
//tableRuleConfig 為分片規(guī)則配置項(xiàng)
public TableRule(final TableRuleConfiguration tableRuleConfig, final ShardingDataSourceNames shardingDataSourceNames, final String defaultGenerateKeyColumn) {
//將邏輯表名轉(zhuǎn)成小寫(xiě)
logicTable = tableRuleConfig.getLogicTable().toLowerCase();
//將真實(shí)表名表達(dá)式,轉(zhuǎn)換為多個(gè) datasource +表名
List<String> dataNodes = new InlineExpressionParser(tableRuleConfig.getActualDataNodes()).splitAndEvaluate();
//按照順序存儲(chǔ) DataNode (datasource +表名)
dataNodeIndexMap = new HashMap<>(dataNodes.size(), 1);
//將真實(shí) datasource + 表名 封裝成 DataNode
actualDataNodes = isEmptyDataNodes(dataNodes)
? generateDataNodes(tableRuleConfig.getLogicTable(), shardingDataSourceNames.getDataSourceNames()) : generateDataNodes(dataNodes, shardingDataSourceNames.getDataSourceNames());
//單純存放真實(shí)表名仅淑,去重
actualTables = getActualTables();
//根據(jù)分庫(kù)策略配置項(xiàng)称勋,創(chuàng)建分庫(kù)策略
databaseShardingStrategy = null == tableRuleConfig.getDatabaseShardingStrategyConfig() ? null : ShardingStrategyFactory.newInstance(tableRuleConfig.getDatabaseShardingStrategyConfig());
//根據(jù)分表策略配置項(xiàng),創(chuàng)建分表策略
tableShardingStrategy = null == tableRuleConfig.getTableShardingStrategyConfig() ? null : ShardingStrategyFactory.newInstance(tableRuleConfig.getTableShardingStrategyConfig());
generateKeyColumn = getGenerateKeyColumn(tableRuleConfig.getKeyGeneratorConfig(), defaultGenerateKeyColumn);
shardingKeyGenerator = containsKeyGeneratorConfiguration(tableRuleConfig)
? new ShardingKeyGeneratorServiceLoader().newService(tableRuleConfig.getKeyGeneratorConfig().getType(), tableRuleConfig.getKeyGeneratorConfig().getProperties()) : null;
//非默認(rèn)策略必須配置一個(gè)想要路由到的真實(shí)表
checkRule(dataNodes);
}
將真實(shí) datasource + 表名 (逗號(hào)分隔) 封裝成 DataNode
private List<DataNode> generateDataNodes(final String logicTable, final Collection<String> dataSourceNames) {
List<DataNode> result = new LinkedList<>();
int index = 0;
//為每個(gè)數(shù)據(jù)源創(chuàng)建一套
for (String each : dataSourceNames) {
DataNode dataNode = new DataNode(each, logicTable);
result.add(dataNode);
//保存數(shù)據(jù)節(jié)點(diǎn)的順序
dataNodeIndexMap.put(dataNode, index);
actualDatasourceNames.add(each);
//按照數(shù)據(jù)源維度涯竟,存放真實(shí)數(shù)據(jù)表
addActualTable(dataNode.getDataSourceName(), dataNode.getTableName());
index++;
}
return result;
}
public DataNode(final String dataNode) {
if (!isValidDataNode(dataNode)) {
throw new ShardingConfigurationException("Invalid format for actual data nodes: '%s'", dataNode);
}
//根據(jù).分隔 前半部分為數(shù)據(jù)源赡鲜,后邊為真實(shí)表
List<String> segments = Splitter.on(DELIMITER).splitToList(dataNode);
dataSourceName = segments.get(0);
tableName = segments.get(1);
}
根據(jù)分庫(kù)策略配置項(xiàng),創(chuàng)建分庫(kù)策略庐船。根據(jù)分表策略配置項(xiàng)银酬,創(chuàng)建分表策略,這里分表策略和分庫(kù)策略的創(chuàng)建邏輯都是一樣的筐钟。包括默認(rèn)的分庫(kù)分表策略揩瞪。
1、標(biāo)準(zhǔn)分片策略
2篓冲、Inline表達(dá)式分片策略
3李破、復(fù)合分片策略
4、threadLocal 分片策略
public static ShardingStrategy newInstance(final ShardingStrategyConfiguration shardingStrategyConfig) {
//標(biāo)準(zhǔn)分片策略
if (shardingStrategyConfig instanceof StandardShardingStrategyConfiguration) {
return new StandardShardingStrategy((StandardShardingStrategyConfiguration) shardingStrategyConfig);
}
//Inline表達(dá)式分片策略
if (shardingStrategyConfig instanceof InlineShardingStrategyConfiguration) {
return new InlineShardingStrategy((InlineShardingStrategyConfiguration) shardingStrategyConfig);
}
//復(fù)合分片策略
if (shardingStrategyConfig instanceof ComplexShardingStrategyConfiguration) {
return new ComplexShardingStrategy((ComplexShardingStrategyConfiguration) shardingStrategyConfig);
}
//threadLocal 分片策略
if (shardingStrategyConfig instanceof HintShardingStrategyConfiguration) {
return new HintShardingStrategy((HintShardingStrategyConfiguration) shardingStrategyConfig);
}
return new NoneShardingStrategy();
}
//標(biāo)準(zhǔn)分片策略
public StandardShardingStrategy(final StandardShardingStrategyConfiguration standardShardingStrategyConfig) {
Preconditions.checkNotNull(standardShardingStrategyConfig.getShardingColumn(), "Sharding column cannot be null.");
Preconditions.checkNotNull(standardShardingStrategyConfig.getPreciseShardingAlgorithm(), "precise sharding algorithm cannot be null.");
//用于分片的列
shardingColumn = standardShardingStrategyConfig.getShardingColumn();
//是必選的 用于處理=和IN的分片
preciseShardingAlgorithm = standardShardingStrategyConfig.getPreciseShardingAlgorithm();
//是可選的 用于處理BETWEEN AND分片壹将,如果不配置 RangeShardingAlgorithm嗤攻,SQL中的 BETWEEN AND 將按照全庫(kù)路由處理
rangeShardingAlgorithm = standardShardingStrategyConfig.getRangeShardingAlgorithm();
}
/**
* Inline表達(dá)式分片策略。使用Groovy的Inline表達(dá)式诽俯,提供對(duì)SQL語(yǔ)句中的=和IN的分片操作支持妇菱。
* InlineShardingStrategy只支持單分片鍵,對(duì)于簡(jiǎn)單的分片算法暴区,可以通過(guò)簡(jiǎn)單的配置使用恶耽,從而避免繁瑣的Java代碼開(kāi)發(fā),
* 如: tuser${user_id % 8} 表示t_user表按照user_id按8取模分成8個(gè)表颜启,表名稱為t_user_0 到 t_user_7。
*/
public InlineShardingStrategy(final InlineShardingStrategyConfiguration inlineShardingStrategyConfig) {
Preconditions.checkNotNull(inlineShardingStrategyConfig.getShardingColumn(), "Sharding column cannot be null.");
Preconditions.checkNotNull(inlineShardingStrategyConfig.getAlgorithmExpression(), "Sharding algorithm expression cannot be null.");
shardingColumn = inlineShardingStrategyConfig.getShardingColumn();
String algorithmExpression = InlineExpressionParser.handlePlaceHolder(inlineShardingStrategyConfig.getAlgorithmExpression().trim());
closure = new InlineExpressionParser(algorithmExpression).evaluateClosure();
}
//復(fù)合分片策略浪讳。提供對(duì)SQL語(yǔ)句中的 =, IN 和 BETWEEN AND 的分片操作支持缰盏。
public ComplexShardingStrategy(final ComplexShardingStrategyConfiguration complexShardingStrategyConfig) {
Preconditions.checkNotNull(complexShardingStrategyConfig.getShardingColumns(), "Sharding columns cannot be null.");
Preconditions.checkNotNull(complexShardingStrategyConfig.getShardingAlgorithm(), "Sharding algorithm cannot be null.");
shardingColumns = new TreeSet<>(String.CASE_INSENSITIVE_ORDER);
shardingColumns.addAll(Splitter.on(",").trimResults().splitToList(complexShardingStrategyConfig.getShardingColumns()));
shardingAlgorithm = complexShardingStrategyConfig.getShardingAlgorithm();
}
//通過(guò)Hint而非SQL解析的方式分片的策略
public HintShardingStrategy(final HintShardingStrategyConfiguration hintShardingStrategyConfig) {
Preconditions.checkNotNull(hintShardingStrategyConfig.getShardingAlgorithm(), "Sharding algorithm cannot be null.");
shardingColumns = new TreeSet<>(String.CASE_INSENSITIVE_ORDER);
shardingAlgorithm = hintShardingStrategyConfig.getShardingAlgorithm();
}
這里可能有個(gè)誤區(qū),分片策略不是自定義的嗎淹遵?怎么會(huì)出現(xiàn)在源碼中口猜。sharding jdbc 分片策略不能自定義,分片算法是自定義的透揣,上邊代碼中济炎,getShardingAlgorithm() 就是在獲取用戶自定義的算法。
2辐真、匹配數(shù)據(jù)庫(kù)類型 databaseType
在初始化 ShardingDataSource 時(shí)候會(huì)調(diào)用父類AbstractDataSourceAdapter 構(gòu)造方法须尚,根據(jù)數(shù)據(jù)庫(kù)連接的url進(jìn)行匹配數(shù)據(jù)庫(kù)類型
public AbstractDataSourceAdapter(final Map<String, DataSource> dataSourceMap) throws SQLException {
this.dataSourceMap = dataSourceMap;
databaseType = createDatabaseType();
}
//根據(jù)數(shù)據(jù)數(shù)據(jù)庫(kù)連接 url 匹配找到數(shù)據(jù)庫(kù)類型
private DatabaseType createDatabaseType(final DataSource dataSource) throws SQLException {
if (dataSource instanceof AbstractDataSourceAdapter) {
return ((AbstractDataSourceAdapter) dataSource).databaseType;
}
try (Connection connection = dataSource.getConnection()) {
return DatabaseTypes.getDatabaseTypeByURL(connection.getMetaData().getURL());
}
}
private static boolean matchStandardURL(final String url, final DatabaseType databaseType) {
return url.startsWith(String.format("jdbc:%s:", databaseType.getName().toLowerCase()));
}
3崖堤、創(chuàng)建 sql 解析引擎 SQLParseEngine 、sql 執(zhí)行引擎 ShardingExecuteEngine
1耐床、創(chuàng)建 ShardingRuntimeContext 調(diào)用父類 AbstractRuntimeContext 的構(gòu)造方法
2密幔、創(chuàng)建運(yùn)行時(shí)候需要的一些配置信息,包括是否打印sql,單次執(zhí)行消耗的數(shù)據(jù)庫(kù)連接數(shù)撩轰、并行執(zhí)行線程池大小等等
3胯甩、創(chuàng)建 sql 執(zhí)行引擎
4、創(chuàng)建 sql 解析引擎
protected AbstractRuntimeContext(final T rule, final Properties props, final DatabaseType databaseType) {
this.rule = rule;
//運(yùn)行時(shí)候的一些配置信息
this.props = new ShardingProperties(null == props ? new Properties() : props);
this.databaseType = databaseType;
//創(chuàng)建 sql 執(zhí)行
executeEngine = new ShardingExecuteEngine(this.props.<Integer>getValue(ShardingPropertiesConstant.EXECUTOR_SIZE));
//根據(jù)數(shù)據(jù)庫(kù)類型初始化堪嫂,sql 解析引擎
parseEngine = SQLParseEngineFactory.getSQLParseEngine(DatabaseTypes.getTrunkDatabaseTypeName(databaseType));
ConfigurationLogger.log(rule.getRuleConfiguration());
ConfigurationLogger.log(props);
}
創(chuàng)建 sql 執(zhí)行引擎
sql 執(zhí)行引擎 ShardingExecuteEngine 復(fù)合了sql 執(zhí)行服務(wù)ShardingExecutorService偎箫,在創(chuàng)建ShardingExecuteEngine 時(shí)同時(shí)創(chuàng)建了 ShardingExecutorService 一對(duì)一的關(guān)系,這里涉及到了guava 的 MoreExecutors 對(duì)jdk Executor 的增強(qiáng)
public ShardingExecutorService(final int executorSize, final String nameFormat) {
executorService = MoreExecutors.listeningDecorator(getExecutorService(executorSize, nameFormat));
//java 系統(tǒng)默認(rèn)創(chuàng)建的線程都是用戶線程,也就是說(shuō)如果 用戶線程不結(jié)束皆串,main 方法不會(huì)結(jié)束的淹办。
//在線程 start 之前當(dāng)調(diào)用 setDaemon 時(shí),此線程就是守護(hù)線程了愚战,main 方法不會(huì)等待此線程運(yùn)行結(jié)束
//非主動(dòng)結(jié)束進(jìn)程 或調(diào)用 System.exit 時(shí)無(wú)論什么線程都會(huì)結(jié)束
//MoreExecutors 是 gava 對(duì) jdk 線程池的增強(qiáng)
//加 60 秒的 jvm 關(guān)閉鉤子, 在 jvm 中已經(jīng)沒(méi)有用戶線程在運(yùn)行了,那么 等待 60 秒后關(guān)閉線程池
MoreExecutors.addDelayedShutdownHook(executorService, 60, TimeUnit.SECONDS);
}
//創(chuàng)建線程池娇唯,如果配置參數(shù) executorSize 為0那么創(chuàng)建 CachedThreadPool 核心線程數(shù)量為0 最大線程數(shù)量為 Integer.MAX_VALUE
private ExecutorService getExecutorService(final int executorSize, final String nameFormat) {
//線程名字為 ShardingSphere 開(kāi)頭
ThreadFactory shardingThreadFactory = ShardingThreadFactoryBuilder.build(nameFormat);
return 0 == executorSize ? Executors.newCachedThreadPool(shardingThreadFactory) : Executors.newFixedThreadPool(executorSize, shardingThreadFactory);
}
sql 解析引擎
1、根據(jù)數(shù)據(jù)庫(kù)名稱創(chuàng)建 SQLParseEngine
這里還有一個(gè)誤區(qū)寂玲,不是每個(gè)數(shù)據(jù)都應(yīng)該有對(duì)應(yīng)的 SQLParseEngine 嗎塔插?其實(shí)類都是同一個(gè) SQLParseEngine 只是其中 String databaseTypeName 不同而已,雖然調(diào)用了構(gòu)造函數(shù)但是此處的初始化還沒(méi)有完成拓哟,真正完成在第一次解析sql的時(shí)候想许。
2、第一次解析sql断序。SQLParseEngine.parse0
(1)是否用緩存中已解析好的結(jié)果
(2)創(chuàng)建 SQLParseKernel 并且調(diào)用了 ParseRuleRegistry.getInstance() 完整初始化過(guò)程流纹。
public static SQLParseEngine getSQLParseEngine(final String databaseTypeName) {
if (ENGINES.containsKey(databaseTypeName)) {
return ENGINES.get(databaseTypeName);
}
//這里同步保證每個(gè)數(shù)據(jù)庫(kù)只有一個(gè) SQLParseEngine 實(shí)例
synchronized (ENGINES) {
if (ENGINES.containsKey(databaseTypeName)) {
return ENGINES.get(databaseTypeName);
}
SQLParseEngine result = new SQLParseEngine(databaseTypeName);
ENGINES.put(databaseTypeName, result);
return result;
}
}
第一次解析sql。SQLParseEngine.parse0
因?yàn)榈谝淮螆?zhí)行解析 sql 所以虛擬機(jī)會(huì)加載 ParseRuleRegistry 到內(nèi)存违诗,并且調(diào)用 ParseRuleRegistry 中的靜態(tài)代碼塊漱凝。
1、利用 java spi 機(jī)制加載 SQLParserEntry 的實(shí)現(xiàn)類到內(nèi)存诸迟,SQLParserEntry 的實(shí)現(xiàn)類包括MySQLParserEntry,OracleParserEntry 茸炒,分別在不同的模塊中,所以可以按需引入阵苇。
2壁公、加載 sql 解析規(guī)范例如 sql-statement-rule-definition.xml 文件并且解析存放在Map<String, SQLStatementRuleDefinition> sqlStatementRuleDefinitions 中。
private SQLStatement parse0(final String sql, final boolean useCache) {
//是否用緩存中已解析好的結(jié)果
if (useCache) {
Optional<SQLStatement> cachedSQLStatement = cache.getSQLStatement(sql);
if (cachedSQLStatement.isPresent()) {
return cachedSQLStatement.get();
}
}
// ParseRuleRegistry.getInstance() 獲取定的一些規(guī)范绅项,和配置信息
SQLStatement result = new SQLParseKernel(ParseRuleRegistry.getInstance(), databaseTypeName, sql).parse();
if (useCache) {
cache.put(sql, result);
}
return result;
}
ParseRuleRegistry.getInstance()
static {
//spi 機(jī)制加載注冊(cè)的 sql 語(yǔ)法解析類型紊册,例如 oracl mysql
NewInstanceServiceLoader.register(SQLParserEntry.class);
instance = new ParseRuleRegistry();
}
//key 接口類型, value 實(shí)現(xiàn)類集合
private static final Map<Class, Collection<Class<?>>> SERVICE_MAP = new HashMap<>();
/**
* 根據(jù) ServiceLoader java util spi 機(jī)制獲取相應(yīng)的 service 目錄默認(rèn)在 META-INF/services/
*/
public static <T> void register(final Class<T> service) {
for (T each : ServiceLoader.load(service)) {
registerServiceClass(service, each);
}
}
加載 sql 解析規(guī)范例如 sql-statement-rule-definition.xml 文件并且解析存放在Map<String, SQLStatementRuleDefinition> sqlStatementRuleDefinitions 中快耿,key 為數(shù)據(jù)庫(kù)名稱囊陡,value 為規(guī)則類芳绩。
/**
* load sql 解析規(guī)范 例如sql-statement-rule-definition.xml
*/
private void initParseRuleDefinition() {
ExtractorRuleDefinitionEntity generalExtractorRuleEntity = extractorRuleLoader.load(RuleDefinitionFileConstant.getExtractorRuleDefinitionFile());
FillerRuleDefinitionEntity generalFillerRuleEntity = fillerRuleLoader.load(RuleDefinitionFileConstant.getFillerRuleDefinitionFile());
for (SQLParserEntry each : NewInstanceServiceLoader.newServiceInstances(SQLParserEntry.class)) {
String databaseTypeName = each.getDatabaseTypeName();
fillerRuleDefinitions.put(databaseTypeName, createFillerRuleDefinition(generalFillerRuleEntity, databaseTypeName));
sqlStatementRuleDefinitions.put(databaseTypeName, createSQLStatementRuleDefinition(generalExtractorRuleEntity, databaseTypeName));
}
}
那隨后的執(zhí)行階段就可以根據(jù)數(shù)據(jù)庫(kù)名取到對(duì)應(yīng)的解析方法了。
public SQLAST parse() {
//根據(jù)數(shù)據(jù)庫(kù)名獲取关斜,對(duì)應(yīng)的解析類
SQLParser sqlParser = SQLParserFactory.newInstance(databaseTypeName, sql);
....略
//根據(jù)數(shù)據(jù)庫(kù)名獲取示括,對(duì)應(yīng)的解析規(guī)則類
SQLStatementRule rule = parseRuleRegistry.getSQLStatementRule(databaseTypeName, parseTree.getClass().getSimpleName());
if (null == rule) {
throw new SQLParsingException(String.format("Unsupported SQL of `%s`", sql));
}
return new SQLAST((ParserRuleContext) parseTree, getParameterMarkerIndexes((ParserRuleContext) parseTree), rule);
}
至此 sharding jdbc 4.0.1 初始化階段已經(jīng)完成,關(guān)于
2痢畜、內(nèi)部運(yùn)行流程分別是怎么實(shí)現(xiàn)的
(1)初始化過(guò)程
(2)sql 解析
(3)sql 提取
(4)sql 路由
(5)sql 替換
(6)sql 執(zhí)行
中的 2垛膝、3、4丁稀、5吼拥、6 下次分析。