SharedingSphere原理


 
  1. 背景

采用了SharedingSphere进行分表处理、数据脱敏、读写分离。
  1. 核心功能

SharedingSphere-JDBC:
数据分片:分库分表
读写分离:读写操作分别路由到主库与从库
数据脱敏:对敏感字段进行加密存储,使用时解密
分布式事务:基于XA协议的两阶段提交
ShardingSphere-Proxy:数据库代理
...
  1. 优缺点(对比Mycat)

SharedingSphere-JDBC基于jdbc驱动,以jar包形式提供轻量级插件,运维成本较低;Mycat是基于Proxy,以中间件的形式提供服务,本身类似于一个Mysql数据库,所以需要关注Mycat服务的高可用,运维成本较高,需要有一定的运维实力去维护各种配置。
SharedingSphere-JDBC只提供java语言的jar包,不能够跨平台;
SharedingSphere-JDBC不支持部分sql语句
  1. 配置(基于版本5.0.x)

1. spring.shardingsphere.rules : 规则配置
2. spring.shardingsphere.rules.sharding.sharding-algorithms : 分库分表规则配置
3. spring.shardingsphere.rules.sharding.tables : 分库分表对象配置
4. spring.shardingsphere.rules.sharding.key-generators : 分布式序列配置
5. spring.shardingsphere.rules.replica-query : 读写分离配置
6. spring.shardingsphere.rules.encrypt : 数据脱敏配置
 
@SpringBootApplication(exclude = {DataSourceAutoConfiguration. class})
@EnableConfigurationProperties
public class SharedingApplication {
  • 分库分表配置

#数据分片 #YamlShardingRuleSpringBootConfiguration
#数据源
spring.shardingsphere.datasource.names=test1,test2
spring.shardingsphere.datasource.common.type=com.alibaba.druid.pool.DruidDataSource
spring.shardingsphere.datasource.common.driver-class-name=com.mysql.jdbc.Driver
 
spring.shardingsphere.datasource.test1.url=jdbc:mysql://localhost:3306/testdb1?useUnicode=true&characterEncoding=utf-8&useSSL=false
spring.shardingsphere.datasource.test1.username=root
spring.shardingsphere.datasource.test1.password=
 
spring.shardingsphere.datasource.test2.url=jdbc:mysql://localhost:3306/testdb2?useUnicode=true&characterEncoding=utf-8&useSSL=false
spring.shardingsphere.datasource.test2.username=root
spring.shardingsphere.datasource.test2.password=
 
#分片配置
spring.shardingsphere.rules.sharding.tables.order.actual-data-nodes=testdb$->{1..2}.order_$->{0..1}
 
##分布式序列配置(该项为必需,不是很友好)
spring.shardingsphere.rules.sharding.key-generators.snowflake.type=SNOWFLAKE
spring.shardingsphere.rules.sharding.key-generators.snowflake.props.max-vibration-offset=2048
spring.shardingsphere.rules.sharding.key-generators.snowflake.props.worker-id=1
 
##分库规则
spring.shardingsphere.rules.sharding.sharding-algorithms.dbstrategy.type=INLINE
spring.shardingsphere.rules.sharding.sharding-algorithms.dbstrategy.props.algorithm-expression=testdb$->{ Long.parseLong(sku.replaceAll('[a-z]','').replaceAll('[A-Z]','')) % 2 + 1}
##分表规则
spring.shardingsphere.rules.sharding.sharding-algorithms.tbstrategy.type=INLINE
spring.shardingsphere.rules.sharding.sharding-algorithms.tbstrategy.props.algorithm-expression=order_$->{ Long.parseLong(sku.replaceAll('[a-z]','').replaceAll('[A-Z]','')) % 2}
 
##库配置
spring.shardingsphere.rules.sharding.tables.order.database-strategy.standard.sharding-column=sku
spring.shardingsphere.rules.sharding.tables.order.database-strategy.standard.sharding-algorithm-name=dbstrategy
##分表配置
spring.shardingsphere.rules.sharding.tables.order.table-strategy.standard.sharding-column=sku
spring.shardingsphere.rules.sharding.tables.order.table-strategy.standard.sharding-algorithm-name=tbstrategy
##分布式ID
spring.shardingsphere.rules.sharding.tables.order.key-generate-strategy.column=order_no
spring.shardingsphere.rules.sharding.tables.order.key-generate-strategy.key-generator-name=snowflake
  • 读写分离配置

#读写分离 # YamlReplicaQueryRuleSpringBootConfiguration
#数据源
spring.shardingsphere.datasource.names=master,slave
spring.shardingsphere.datasource.common.type=com.alibaba.druid.pool.DruidDataSource
spring.shardingsphere.datasource.common.driver-class-name=com.mysql.jdbc.Driver
 
spring.shardingsphere.datasource.master.url=jdbc:mysql://localhost:3306/testdb1?useUnicode=true&characterEncoding=utf-8&useSSL=false
spring.shardingsphere.datasource.master.username=root
spring.shardingsphere.datasource.master.password=
 
spring.shardingsphere.datasource.slave.url=jdbc:mysql://localhost:3306/testdb2?useUnicode=true&characterEncoding=utf-8&useSSL=false
spring.shardingsphere.datasource.slave.username=root
spring.shardingsphere.datasource.slave.password=
 
#读写分离
spring.shardingsphere.rules.replica-query.data-sources.readwrite.name=readwrite
spring.shardingsphere.rules.replica-query.data-sources.readwrite.primary-data-source-name=master
spring.shardingsphere.rules.replica-query.data-sources.readwrite.replica-data-source-names=slave
spring.shardingsphere.rules.replica-query.load-balancers.balancers.type=ROUND_ROBIN
spring.shardingsphere.rules.replica-query.load-balancers.balancers.props.value=value
  • 数据脱敏

#数据加密 #YamlEncryptRuleSpringBootConfiguration
#数据源
spring.shardingsphere.datasource.names=testdb1
spring.shardingsphere.datasource.common.type=com.alibaba.druid.pool.DruidDataSource
spring.shardingsphere.datasource.common.driver-class-name=com.mysql.jdbc.Driver
 
spring.shardingsphere.datasource.test1.url=jdbc:mysql://localhost:3306/testdb1?useUnicode=true&characterEncoding=utf-8&useSSL=false
spring.shardingsphere.datasource.test1.username=root
spring.shardingsphere.datasource.test1.password=
 
#数据加密
##加密算法
spring.shardingsphere.rules.encrypt.encryptors.aesencrypt.type=AES
spring.shardingsphere.rules.encrypt.encryptors.aesencrypt.props.aes-key-value=123456
 
spring.shardingsphere.rules.encrypt.tables.order.name=orderEncryptMobile
spring.shardingsphere.rules.encrypt.tables.order.columns.mobile.cipher-column=mobile
#spring.shardingsphere.rules.encrypt.tables.order.columns.mobile.logic-column=mobile
#spring.shardingsphere.rules.encrypt.tables.order.columns.mobile.plain-column=mobile
#spring.shardingsphere.rules.encrypt.tables.order.columns.mobile.assisted-query-column=mobile
spring.shardingsphere.rules.encrypt.tables.order.columns.mobile.encryptor-name=aesencrypt
  • 综合配置

#数据源
spring.shardingsphere.datasource.names=test1,test2,slave1,slave2
spring.shardingsphere.datasource.common.type=com.alibaba.druid.pool.DruidDataSource
spring.shardingsphere.datasource.common.driver-class-name=com.mysql.jdbc.Driver
 
spring.shardingsphere.datasource.test1.url=jdbc:mysql://localhost:3306/testdb1?useUnicode=true&characterEncoding=utf-8&useSSL=false
spring.shardingsphere.datasource.test1.username=root
spring.shardingsphere.datasource.test1.password=
 
spring.shardingsphere.datasource.test2.url=jdbc:mysql://localhost:3306/testdb2?useUnicode=true&characterEncoding=utf-8&useSSL=false
spring.shardingsphere.datasource.test2.username=root
spring.shardingsphere.datasource.test2.password=
 
spring.shardingsphere.datasource.slave1.url=jdbc:mysql://localhost:3306/testdb1?useUnicode=true&characterEncoding=utf-8&useSSL=false
spring.shardingsphere.datasource.slave1.username=root
spring.shardingsphere.datasource.slave1.password=
 
spring.shardingsphere.datasource.slave2.url=jdbc:mysql://localhost:3306/testdb2?useUnicode=true&characterEncoding=utf-8&useSSL=false
spring.shardingsphere.datasource.slave2.username=root
spring.shardingsphere.datasource.slave2.password=
 
#分片配置
spring.shardingsphere.rules.sharding.tables.order.actual-data-nodes=testdb$->{1..2}.order_$->{0..1}
 
##分布式序列配置
spring.shardingsphere.rules.sharding.key-generators.snowflake.type=SNOWFLAKE
spring.shardingsphere.rules.sharding.key-generators.snowflake.props.max-vibration-offset=2
spring.shardingsphere.rules.sharding.key-generators.snowflake.props.worker-id=2
 
##分库规则
spring.shardingsphere.rules.sharding.sharding-algorithms.dbstrategy.type=INLINE
spring.shardingsphere.rules.sharding.sharding-algorithms.dbstrategy.props.algorithm-expression=testdb$->{ Long.parseLong(sku.replaceAll('[a-z]','').replaceAll('[A-Z]','')) % 2 + 1}
##分表规则
spring.shardingsphere.rules.sharding.sharding-algorithms.tbstrategy.type=INLINE
spring.shardingsphere.rules.sharding.sharding-algorithms.tbstrategy.props.algorithm-expression=order_$->{ Long.parseLong(sku.replaceAll('[a-z]','').replaceAll('[A-Z]','')) % 2}
 
##库配置
spring.shardingsphere.rules.sharding.tables.order.database-strategy.standard.sharding-column=sku
spring.shardingsphere.rules.sharding.tables.order.database-strategy.standard.sharding-algorithm-name=dbstrategy
##分表配置
spring.shardingsphere.rules.sharding.tables.order.table-strategy.standard.sharding-column=sku
spring.shardingsphere.rules.sharding.tables.order.table-strategy.standard.sharding-algorithm-name=tbstrategy
##分布式ID
spring.shardingsphere.rules.sharding.tables.order.key-generate-strategy.column=order_no
spring.shardingsphere.rules.sharding.tables.order.key-generate-strategy.key-generator-name=snowflake
 
## 默认分片配置
spring.shardingsphere.rules.sharding.default-database-strategy.standard.sharding-column=sku
spring.shardingsphere.rules.sharding.default-database-strategy.standard.sharding-algorithm-name=dbstrategy
 
#读写分离
spring.shardingsphere.rules.replica-query.data-sources.readwrite.name=readwrite
spring.shardingsphere.rules.replica-query.data-sources.readwrite.primary-data-source-name=test1
spring.shardingsphere.rules.replica-query.data-sources.readwrite.replica-data-source-names=slave1
spring.shardingsphere.rules.replica-query.data-sources.readwrite1.name=readwrite1
spring.shardingsphere.rules.replica-query.data-sources.readwrite1.primary-data-source-name=test2
spring.shardingsphere.rules.replica-query.data-sources.readwrite1.replica-data-source-names=slave2
#负载均衡
spring.shardingsphere.rules.replica-query.load-balancers.balancers.type=ROUND_ROBIN
spring.shardingsphere.rules.replica-query.load-balancers.balancers.props.value=value
 
#数据加密
##加密算法
spring.shardingsphere.rules.encrypt.encryptors.aesencrypt.type=AES
spring.shardingsphere.rules.encrypt.encryptors.aesencrypt.props.haes-key-value=123456
 
spring.shardingsphere.rules.encrypt.tables.order.name=orderEncryptMobile
spring.shardingsphere.rules.encrypt.tables.order.columns.mobile.cipher-column=mobile
spring.shardingsphere.rules.encrypt.tables.order.columns.mobile.encryptor-name=aesencrypt
  • 分布式序列

    • SharedingSphere提供内置分布式序列算法:雪花算法、UUID
    • 自定义分布式序列算法(SPI加载方式)
resources 文件夹下增加 META-INF.services org.apache.shardingsphere.sharding.spi.KeyGenerateAlgorithm
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
org.apache.shardingsphere.sharding.algorithm.keygen.SnowflakeKeyGenerateAlgorithm
org.apache.shardingsphere.sharding.algorithm.keygen.UUIDKeyGenerateAlgorithm
com.bruce.common.algorithm.MyKeyGenerateAlgorithm
import lombok.Getter;
import lombok.Setter;
import org.apache.shardingsphere.sharding.spi.KeyGenerateAlgorithm;
 
import java.util.Properties;
import java.util.UUID;
 
@Getter
@Setter
public final class MyKeyGenerateAlgorithm implements KeyGenerateAlgorithm {
 
private Properties props = new Properties();
 
@Override
public void init() {
}
 
@Override
public synchronized Comparable<?> generateKey() {
return 自定义分布式ID;
}
 
@Override
public String getType() {
return "自定义分布式ID算法名称";
}
}

数据脱敏方式

  • SharedingSphere提供内置加密算法:MD5 AES RC4
  • 自定义数据脱敏算法(SPI加载方式):
resources 文件夹下增加 META-INF.services org.apache.shardingsphere.encrypt.spi.EncryptAlgorithm
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
 
org.apache.shardingsphere.encrypt.algorithm.MD5EncryptAlgorithm
org.apache.shardingsphere.encrypt.algorithm.AESEncryptAlgorithm
org.apache.shardingsphere.encrypt.algorithm.RC4EncryptAlgorithm
com.bruce.common.encrypt.MyEncryptAlgorithm
import lombok.Getter;
import lombok.Setter;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.binary.StringUtils;
import org.apache.shardingsphere.encrypt.spi.EncryptAlgorithm;
 
import java.nio.charset.StandardCharsets;
import java.util.Properties;
 
@Getter
@Setter
public final class MyEncryptAlgorithm implements EncryptAlgorithm {
 
private static final String HAES_KEY = "自定义属性";
 
private Properties props = new Properties();
 
private byte[] secretKey;
 
@Override
public void init() {
secretKey = createSecretKey();
}
 
private byte[] createSecretKey() {
return HAES_KEY.getBytes();
}
 
@Override
public String encrypt(final Object plaintext) {
if (null == plaintext) {
return null;
}
return 自定义加密算法;
}
 
@Override
public Object decrypt(final String ciphertext) {
if (null == ciphertext) {
return null;
}
return 自定义解密算法;
}
 
@Override
public String getType() {
return "自定义脱敏算法名称";
}
}
  1. 运行原理

SharedingSphere内核包括解析引擎、路由引擎、改写引擎、执行引擎、归并引擎
ShardingSphere -> ShardingSphereConnection -> ShardingSpherePreparedStatement -> ShardingSphereResultSet
private ShardingSpherePreparedStatement( final ShardingSphereConnection connection, final String sql,
final int resultSetType, final int resultSetConcurrency, final int resultSetHoldability, final boolean returnGeneratedKeys) throws SQLException {
if (Strings. isNullOrEmpty(sql)) {
throw new SQLException(SQLExceptionConstant. SQL_STRING_NULL_OR_EMPTY);
}
this. connection = connection;
schemaContexts = connection.getSchemaContexts();
this. sql = sql;
statements = new ArrayList<>();
parameterSets = new ArrayList<>();
//解析引擎
ShardingSphereSQLParserEngine sqlStatementParserEngine = new ShardingSphereSQLParserEngine(DatabaseTypeRegistry. getTrunkDatabaseTypeName( schemaContexts.getDatabaseType()));
sqlStatement = sqlStatementParserEngine.parse(sql, true);
parameterMetaData = new ShardingSphereParameterMetaData( sqlStatement);
statementOption = returnGeneratedKeys ? new StatementOption( true) : new StatementOption(resultSetType, resultSetConcurrency, resultSetHoldability);
SQLExecutor sqlExecutor = new SQLExecutor( schemaContexts.getExecutorKernel(), connection.isHoldTransaction());
preparedStatementExecutor = new PreparedStatementExecutor(connection.getDataSourceMap(), schemaContexts, sqlExecutor);
rawExecutor = new RawJDBCExecutor( schemaContexts.getExecutorKernel(), connection.isHoldTransaction());
batchPreparedStatementExecutor = new BatchPreparedStatementExecutor( schemaContexts, sqlExecutor);
kernelProcessor = new KernelProcessor();
}
 
public ResultSet executeQuery( final String sql) throws SQLException {
if (Strings. isNullOrEmpty(sql)) {
throw new SQLException(SQLExceptionConstant. SQL_STRING_NULL_OR_EMPTY);
}
ResultSet result;
try {
executionContext = createExecutionContext(sql);
List<QueryResult> queryResults;
//执行引擎
if (ExecutorConstant. MANAGED_RESOURCE) {
Collection<InputGroup<StatementExecuteUnit>> inputGroups = getInputGroups();
cacheStatements(inputGroups);
queryResults = statementExecutor.executeQuery(inputGroups);
} else {
queryResults = rawExecutor.executeQuery(getRawInputGroups(), new RawSQLExecutorCallback());
}
//归并引擎
MergedResult mergedResult = mergeQuery(queryResults);
result = new ShardingSphereResultSet( statements.stream().map( this::getResultSet).collect(Collectors. toList()), mergedResult, this, executionContext);
} finally {
currentResultSet = null;
}
currentResultSet = result;
return result;
}
 
private ExecutionContext createExecutionContext( final String sql) throws SQLException {
clearStatements();
LogicSQL logicSQL = createLogicSQL(sql);
ExecutionContext result = kernelProcessor.generateExecutionContext(logicSQL, schemaContexts.getDefaultSchema(), schemaContexts.getProps());
logSQL(logicSQL, schemaContexts.getProps(), result);
return result;
}
 
public ExecutionContext generateExecutionContext( final LogicSQL logicSQL, final ShardingSphereSchema schema, final ConfigurationProperties props) {
Collection<ShardingSphereRule> rules = schema.getRules();
//路由引擎
SQLRouteEngine sqlRouteEngine = new SQLRouteEngine(rules, props);
SQLStatementContext<?> sqlStatementContext = logicSQL.getSqlStatementContext();
RouteContext routeContext = sqlRouteEngine.route(logicSQL, schema);
//改写引擎
SQLRewriteEntry rewriteEntry = new SQLRewriteEntry(schema.getMetaData().getSchemaMetaData().getConfiguredSchemaMetaData(), props, rules);
SQLRewriteResult rewriteResult = rewriteEntry.rewrite(logicSQL.getSql(), logicSQL.getParameters(), sqlStatementContext, routeContext);
Collection<ExecutionUnit> executionUnits = ExecutionContextBuilder. build(schema.getMetaData(), rewriteResult, sqlStatementContext);
return new ExecutionContext(sqlStatementContext, executionUnits, routeContext);
}
 
//执行引擎
public <I, O> List<O> execute( final Collection<InputGroup<I>> inputGroups,
final ExecutorCallback<I, O> firstCallback, final ExecutorCallback<I, O> callback, final boolean serial) throws SQLException {
if (inputGroups.isEmpty()) {
return Collections. emptyList();
}
return serial ? serialExecute(inputGroups, firstCallback, callback) : parallelExecute(inputGroups, firstCallback, callback);
}
 
private <I, O> List<O> serialExecute( final Collection<InputGroup<I>> inputGroups, final ExecutorCallback<I, O> firstCallback, final ExecutorCallback<I, O> callback) throws SQLException {
Iterator<InputGroup<I>> inputGroupsIterator = inputGroups.iterator();
InputGroup<I> firstInputs = inputGroupsIterator.next();
List<O> result = new LinkedList<>(syncExecute(firstInputs, null == firstCallback ? callback : firstCallback));
for (InputGroup<I> each : Lists. newArrayList(inputGroupsIterator)) {
result.addAll(syncExecute(each, callback));
}
return result;
}
 
private <I, O> List<O> parallelExecute(final Collection<InputGroup<I>> inputGroups, final ExecutorCallback<I, O> firstCallback, final ExecutorCallback<I, O> callback) throws SQLException {
Iterator<InputGroup<I>> inputGroupsIterator = inputGroups.iterator();
InputGroup<I> firstInputs = inputGroupsIterator.next();
Collection<ListenableFuture<Collection<O>>> restResultFutures = asyncExecute(Lists. newArrayList (inputGroupsIterator), callback);
return getGroupResults(syncExecute(firstInputs, null == firstCallback ? callback : firstCallback), restResultFutures);
}
 
private <I, O> ListenableFuture<Collection<O>> asyncExecute(final InputGroup<I> inputGroup, final ExecutorCallback<I, O> callback) {
Map<String, Object> dataMap = ExecutorDataMap. getValue ();
return executorService.getExecutorService().submit(() -> callback.execute(inputGroup.getInputs(), false, dataMap));
}
ConnectionMode connectionMode = maxConnectionsSizePerQuery < sqlUnits.size() ? ConnectionMode. CONNECTION_STRICTLY : ConnectionMode. MEMORY_STRICTLY;
  1. 不支持sql

  • 不支持存储过程,函数,游标的操作
  • CASE WHEN 中包含子查询不支持
  • CASE WHEN 中使用逻辑表名不支持(请使用表别名) 不支持 HAVING、UNION (ALL)
  • 不支持部分子查询
  • 子查询中包含聚合函数目前无法支持
  • ... 


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM