Sharding-JDBC 数据库中间件
简介:Sharding-JDBC是一个开源的分布式数据库中间件,此处以2.x版本为目标讲解。
1. 特点
A. Sharding-JDBC直接封装JDBC API,可适用于任何基于java的ORM框架(Mybatis/JPA),可基于任何第三方的数据库连接池(C3P0/Durid),理论上可支持任意实现JDBC规范的数据库(MySQL/Oracle);
B. 功能方面:包括分库分表、读写分离、柔性事务、分布式主键和分布式治理;
C. 配置多样化:支持Java编码和YAML的配置方式、支持自定义Spring命名空间与Spring boot starter;
D. 性能方面:性能高,单库查询QPS为原生JDBC的99.8%,双库查询QPS比单库增加94%。
2. 核心概念
A. 逻辑表(LogicTable):进行水平拆分的时候同一类型(逻辑、数据结构相同)的表的总称;
B. 真实表(ActualTable):在分片的数据库中真实存在的物理表;
C. 数据节点(DataNode):数据分片的最小单元,由数据源名称和真实表组成;
D. 绑定表(BindingTable):指分片规则一致的主表和子表;
E. 分片键(ShardingColumn):分片键用于将数据表水平拆分的字段,是指SQL语句中WHERE中的条件列;
F. 分片算法(ShardingAlgorithm):进行水平拆分时采用的算法,分片算法需要应用方自行实现;
G. 分片策略
按策略类型分:
StandardShardingStrategy(标准分片策略):只支持单分片键,提供PreciseShardingAlgorithm(精确分片算法,用于处理=和in的分片)和RangeShardingAlgorithm(范围分片算法,用于处理BETWEEN AND分片)两个分片算法;
ComplexShardingStrategy(复合分片策略):支持多分片键,其余类似StandardShardingStrategy;
InlineShardingStrategy(行表达式分片策略)
HintShardingStrategy(Hint分片策略)
NoneShardingStrategy(不分片策略)
按策略配置分:
数据源分片策略(DatabaseShardingStrategy):数据被分配的目标数据源;
表分片策略(TableShardingStrategy):数据被分配的目标表,该目标表存在与该数据的目标数据源内;
注意:如果分片规则中的所有表或大部分表的分片策略相同,可以使用默认策略来简化配置,如设置在ShardingRuleConfiguration类下,否则设置在TableRuleConfiguration类下。
3. Maven依赖
<!-- 引入sharding-jdbc核心模块(基于java和YAML配置) --> <dependency> <groupId>io.shardingjdbc</groupId> <artifactId>sharding-jdbc-core</artifactId> </dependency>
<!-- 引入sharding-jdbc核心模块(基于java和YAML配置) --> <dependency>
<groupId>io.shardingjdbc</groupId>
<artifactId>sharding-jdbc-core-spring-namespace</artifactId>
</dependency>
4. 分库分表
A. 基于java代码
package com.ruhuanxingyun.sharding; import java.util.Collection; import com.ruhuanxingyun.sharding.datasource.DynamicDataSourceContextHolder; import io.shardingjdbc.core.api.algorithm.sharding.PreciseShardingValue; import io.shardingjdbc.core.api.algorithm.sharding.standard.PreciseShardingAlgorithm; public class ModuloDatabaseShardingAlgorithm implements PreciseShardingAlgorithm<Long> { @Override public String doSharding(Collection<String> availableTargetNames, PreciseShardingValue<Long> shardingValue) { Long databaseId = shardingValue.getValue(); if (DynamicDataSourceContextHolder.containsDataSource("ds" + databaseId)) { return "ds" + databaseId; } else { return "ds1"; } } }
package com.ruhuanxingyun.sharding; import io.shardingjdbc.core.api.config.TableRuleConfiguration; import io.shardingjdbc.core.api.config.strategy.StandardShardingStrategyConfiguration; public class ShardingJdbcConfiguration { public TableRuleConfiguration ruleConfig(String tableName, String nodes, String column) { // 配置表规则 TableRuleConfiguration tableRuleConfig = new TableRuleConfiguration(); tableRuleConfig.setLogicTable(tableName); tableRuleConfig.setActualDataNodes(nodes); tableRuleConfig.setKeyGeneratorColumnName(column); // 自定义的分片算法实现 StandardShardingStrategyConfiguration standardStrategy = new StandardShardingStrategyConfiguration(column, ModuloDatabaseShardingAlgorithm.class.getName()); // 配置分库策略 tableRuleConfig.setDatabaseShardingStrategyConfig(standardStrategy); return tableRuleConfig; } }
private DataSource buildShardingDataSource(Map<String, DataSource> dataSourceMap){ // 配置分片规则 ShardingRuleConfiguration shardingRuleConfig = new ShardingRuleConfiguration(); StringBuilder dbSb = new StringBuilder(); if (dataSourceMap.size() != 0) { for (String str: dataSourceMap.keySet()) { dbSb.append(str+".user_info"); dbSb.append(","); } String dbNodes = dbSb.toString().substring(0, dbSb.toString().length() - 1); shardingRuleConfig.getTableRuleConfigs().add(shardingJdbcConfiguration.ruleConfig(dbNodes, "user_info", "database_id")); // 默认设置db为ds1,也就是为那些没有配置分库分表策略的指定默认库 shardingRuleConfig.setDefaultDataSourceName("ds1"); } Properties properties = new Properties(); properties.setProperty("sql.show", Boolean.FALSE.toString()); DataSource dataSource = null; try { // 获取数据源对象 dataSource = ShardingDataSourceFactory.createDataSource(dataSourceMap, shardingRuleConfig, new ConcurrentHashMap(), properties); } catch (SQLException e) { e.printStackTrace(); } return dataSource; }
B. 基于yaml文件
5. 读写分离
A. 基于java代码
B. 基于yaml文件
# 数据源配置,同分库分表
dataSources:
# !!表示实现类 db_master: !!com.alibaba.druid.pool.DruidDataSource name: master driverClassName: com.mysql.jdbc.Driver url: jdbc:mysql://172.17.0.1:3306/ruphie?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&failOverReadOnly=false&useSSL=false username: ruhuanxingyun password: 123456 minIdle: 0 maxActive: 200 initialSize: 0 timeBetweenEvictionRunsMillis: 60000 minEvictableIdleTimeMillis: 300000 validationQuery: SELECT 'x' validationQueryTimeout: 10000 testWhileIdle: true testOnBorrow: true testOnReturn: false maxWait: 60000 logAbandoned: true removeAbandoned: true removeAbandonedTimeout: 1800 db_slave: !!com.alibaba.druid.pool.DruidDataSource name: slave driverClassName: com.mysql.jdbc.Driver url: jdbc:mysql://172.17.0.2:3306/ruphie?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&failOverReadOnly=false&useSSL=false username: ruhuanxingyun password: 123456 minIdle: 200 maxActive: 300 initialSize: 100 timeBetweenEvictionRunsMillis: 60000 minEvictableIdleTimeMillis: 300000 validationQuery: SELECT 'x' validationQueryTimeout: 10000 testWhileIdle: true testOnBorrow: true testOnReturn: false maxWait: 60000 logAbandoned: true removeAbandoned: true removeAbandonedTimeout: 1800 masterSlaveRule:
# 分库分表数据源名称 name: db_ms
# master数据源名称 masterDataSourceName: db_master
# slave数据源名称,多个用数组表示[db_slave_0, db_slave_1] slaveDataSourceNames: db_slave
@Value("${sharding-jdbc}") private Resource resourceFile; @Bean public DataSource dataSource() throws SQLException, IOException { byte[] bs = new byte[resourceFile.getInputStream().available()]; resourceFile.getInputStream().read(bs); // 读写分离数据源构建方式 return MasterSlaveDataSourceFactory.createDataSource(bs); }
6. SQL语句限制
1. 不支持or/having/distinct关键字;
2. 不支持多条语句insert和case when等。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 零经验选手,Compose 一天开发一款小游戏!
· 通过 API 将Deepseek响应流式内容输出到前端
· AI Agent开发,如何调用三方的API Function,是通过提示词来发起调用的吗