前言
Sharding-JDBC是一個開源的分佈式數據庫中間件,它無需額外部署和依賴,完全兼容JDBC和各種ORM框架。Sharding-JDBC作爲面向開發的微服務雲原生基礎類庫,完整的實現了分庫分表、讀寫分離和分佈式主鍵功能,並初步實現了柔性事務。
以2.0.3爲例maven包依賴如下
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.dongpeng</groupId>
<artifactId>sharding-jdbc</artifactId>
<version>1.0.0</version>
<packaging>jar</packaging>
<name>sharding-jdbc</name>
<url>http://maven.apache.org</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>io.shardingjdbc</groupId>
<artifactId>sharding-jdbc-core</artifactId>
<version>2.0.3</version>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>23.0</version>
</dependency>
<dependency>
<groupId>com.mchange</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.5.2</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.46</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.21</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.21</version>
</dependency>
</dependencies>
<build>
<resources>
<resource>
<directory>src/main/java</directory>
</resource>
<resource>
<directory>src/main/resources</directory>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
</plugins>
</build>
</project>
簡單的分庫demo介紹如下
package com.dongpeng.sharding.jdbc;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import javax.sql.DataSource;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import io.shardingjdbc.core.api.ShardingDataSourceFactory;
import io.shardingjdbc.core.api.config.ShardingRuleConfiguration;
import io.shardingjdbc.core.api.config.TableRuleConfiguration;
import io.shardingjdbc.core.api.config.strategy.InlineShardingStrategyConfiguration;
/**
* sharding-jdbc分庫的demo
* @author Admin
*
*/
public class ShardingDataDemo {
public static void main(String[] args) throws Exception{
Map<String, DataSource> dataSourceMap = new HashMap<String, DataSource>();
ComboPooledDataSource dataSource1 = new ComboPooledDataSource();
dataSource1.setDriverClass("com.mysql.jdbc.Driver"); // loads the jdbc driver
dataSource1.setJdbcUrl("jdbc:mysql://localhost:3306/db_0?useSSL=false");
dataSource1.setUser("root");
dataSource1.setPassword("root");
dataSourceMap.put("db_0", dataSource1);
ComboPooledDataSource dataSource2 = new ComboPooledDataSource();
dataSource2.setDriverClass("com.mysql.jdbc.Driver"); // loads the jdbc driver
dataSource2.setJdbcUrl("jdbc:mysql://localhost:3306/db_1?useSSL=false");
dataSource2.setUser("root");
dataSource2.setPassword("root");
dataSourceMap.put("db_1", dataSource1);
/**
* 配置分庫規則
*/
TableRuleConfiguration orderTableRuleConfig = new TableRuleConfiguration();
orderTableRuleConfig.setLogicTable("t_order");
orderTableRuleConfig.setActualDataNodes("db_${0..1}.t_order_0");
orderTableRuleConfig.setDatabaseShardingStrategyConfig(new InlineShardingStrategyConfiguration("user_id", "db_${user_id % 2}"));
/**
* 分片規則配置
*/
ShardingRuleConfiguration shardingRuleConfig = new ShardingRuleConfiguration();
shardingRuleConfig.getTableRuleConfigs().add(orderTableRuleConfig);
Properties properties = new Properties();
properties.put("sql.show", "true");
DataSource dataSource = ShardingDataSourceFactory.createDataSource(dataSourceMap, shardingRuleConfig,new HashMap<String,Object>(),properties);
Connection connection = dataSource.getConnection();
PreparedStatement statement = connection.prepareStatement("select * from t_order where user_id=?");
statement.setInt(1, 1);
ResultSet rs = statement.executeQuery();
while(rs.next()) {
System.out.println(rs.getString("user_id"));
}
rs.close();
statement.close();
connection.close();
}
}
幾個重要的類
ShardingRuleConfiguration
ShardingDataSourceFactory
ShardingDataSource
ShardingConnection
ShardingPreparedStatement
ShardingStatement
源碼分析
ShardingRuleConfiguration
分片的規則配置類主要的屬性如下
private String defaultDataSourceName;
private Collection<TableRuleConfiguration> tableRuleConfigs = new LinkedList<>();
private Collection<String> bindingTableGroups = new LinkedList<>();
private ShardingStrategyConfiguration defaultDatabaseShardingStrategyConfig;
private ShardingStrategyConfiguration defaultTableShardingStrategyConfig;
private String defaultKeyGeneratorClass;
private Collection<MasterSlaveRuleConfiguration> masterSlaveRuleConfigs = new LinkedList<>();
defaultDataSourceName指定沒有分片規則庫使用的數據源
tableRuleConfigs 配置指定表的分片規則
TableRuleConfiguration的屬性如下
private String logicTable;
private String actualDataNodes;
private ShardingStrategyConfiguration databaseShardingStrategyConfig;
private ShardingStrategyConfiguration tableShardingStrategyConfig;
private String keyGeneratorColumnName;
private String keyGeneratorClass;
private String logicIndex;
logicTable配置邏輯表比如數據庫中有t_order_0,t_order_1兩張表,邏輯表設置爲t_order
actualDataNodes邏輯節點配置,用的類InlineExpressionParser作爲解析器,配置如 db_${0..1}.t_order_0
databaseShardingStrategyConfig配置數據庫的分片規則
tableShardingStrategyConfig 表的分片規則
兩個分片規則都實現了ShardingStrategyConfiguration接口,用於構建ShardingStrategy分片類,以下是sharding-jdbc提供的分片配置類,具體實現分片規則可以查看源碼
keyGeneratorColumnName配置分佈式主鍵
keyGeneratorClass配置id生成類
logicIndex配置邏輯分片索引位置
還有properties,map的方式配置一些信息這個可參考文檔,如sql.show在properties中配置等等
ShardingDataSourceFactory
shardingDataSourceFactory是ShardingDataSource的工廠類,用於創建ShardingDataSource生成方式
支持文件,自定義等等如下
public static DataSource createDataSource(final Map<String, DataSource> dataSourceMap, final ShardingRuleConfiguration shardingRuleConfig,
final Map<String, Object> configMap, final Properties props) throws SQLException {
return new ShardingDataSource(shardingRuleConfig.build(dataSourceMap), configMap, props);
}
/**
* Create sharding data source.
*
* @param yamlFile yaml file for rule configuration of databases and tables sharding with data sources
* @return sharding data source
* @throws SQLException SQL exception
* @throws IOException IO exception
*/
public static DataSource createDataSource(final File yamlFile) throws SQLException, IOException {
YamlShardingConfiguration config = unmarshal(yamlFile);
return new ShardingDataSource(config.getShardingRule(Collections.<String, DataSource>emptyMap()), config.getShardingRule().getConfigMap(), config.getShardingRule().getProps());
}
ShardingDataSource
主要用於初始化構建dataSource的一些配置信息封裝成ShardingContext提供給ShardingConnection使用
ShardingConnection
繼承自AbstractConnectionAdapter同時實現了connection接口,封了preparedStatement和statement的調用方法,ShardingPreparedStatement和ShardingStatement類來實現,主要的方法是獲取connection的方法如下
public Connection getConnection(final String dataSourceName, final SQLType sqlType) throws SQLException {
if (getCachedConnections().containsKey(dataSourceName)) {
return getCachedConnections().get(dataSourceName);
}
DataSource dataSource = shardingContext.getShardingRule().getDataSourceMap().get(dataSourceName);
Preconditions.checkState(null != dataSource, "Missing the rule of %s in DataSourceRule", dataSourceName);
String realDataSourceName;
if (dataSource instanceof MasterSlaveDataSource) {
NamedDataSource namedDataSource = ((MasterSlaveDataSource) dataSource).getDataSource(sqlType);
realDataSourceName = namedDataSource.getName();
if (getCachedConnections().containsKey(realDataSourceName)) {
return getCachedConnections().get(realDataSourceName);
}
dataSource = namedDataSource.getDataSource();
} else {
realDataSourceName = dataSourceName;
}
Connection result = dataSource.getConnection();
getCachedConnections().put(realDataSourceName, result);
replayMethodsInvocation(result);
return result;
}
這個方法的作用是,如果拿到的數據源是一個MasterSalveDataSource的數據源,需要進行讀寫分離的判斷,並最終返回執行的connection
ShardingPreparedStatement和ShardingStatement
具體的執行類,都有對應的route方法封裝最終都交由SQLRouter來解析sql獲取最終的路由信息,最後執行相應的sql