数据库分片思想

垂直切分

按照业务拆分的方式称为垂直分片,又称为纵向拆分,它的核心理念是专库专用。

水平切分

水平分片又称为横向拆分。 相对于垂直分片,它不再将数据根据业务逻辑分类,而是通过某个字段(或某几个字段),根据某种规则将数据分散至多个库或表中,每个分片仅包含数据的一部分。 例如:根据主键分片,偶数主键的记录放入0库(或表),奇数主键的记录放入1库(或表)

Sharding-JDBC简介

定位为轻量级Java框架,在Java的JDBC层提供的额外服务。 它使用客户端直连数据库,以jar包形式提供服务,无需额外部署和依赖,可理解为增强版的JDBC驱动,完全兼容JDBC和各种ORM框架。

  • 适用于任何基于Java的ORM框架,如:JPA, Hibernate, Mybatis, Spring JDBC Template或直接使用JDBC。
  • 基于任何第三方的数据库连接池,如:DBCP, C3P0, BoneCP, Druid, HikariCP等。
  • 支持任意实现JDBC规范的数据库。目前支持MySQL,Oracle,SQLServer和PostgreSQL。

Sharding-JDBC采用无中心化架构,适用于Java开发的高性能的轻量级OLTP应用;

功能列表

  • 分库 & 分表
  • 读写分离
  • 分布式主键

引入依赖

     <dependency><groupId>io.shardingjdbc</groupId><artifactId>sharding-jdbc-core</artifactId><version>2.0.3</version></dependency>

选择2.0.3作为实践版本,是因为1.4.2之前版本需要依赖druid解析,1.5.2之后采用自研解析方式,并且版本对比来说1.5.2之后性能更加稳定。不采用最新版本3.1.0.M1的原因是由于新版本性能相对而言还不太稳定,不适合在生产环境使用。

规则配置

Sharding-JDBC可以通过Java,YAML,Spring命名空间和Spring Boot Starter四种方式配置,开发者可根据场景选择适合的配置方式。详情请参见配置手册。

分片算法

通过分片算法将数据分片,支持通过=、BETWEEN和IN分片。分片算法需要应用方开发者自行实现,可实现的灵活度非常高。

目前提供4种分片算法。由于分片算法和业务实现紧密相关,因此并未提供内置分片算法,而是通过分片策略将各种场景提炼出来,提供更高层级的抽象,并提供接口让应用开发者自行实现分片算法。

精确分片算法
对应PreciseShardingAlgorithm,用于处理使用单一键作为分片键的=与IN进行分片的场景。需要配合StandardShardingStrategy使用。

范围分片算法
对应RangeShardingAlgorithm,用于处理使用单一键作为分片键的BETWEEN AND进行分片的场景。需要配合StandardShardingStrategy使用。

复合分片算法
对应ComplexKeysShardingAlgorithm,用于处理使用多键作为分片键进行分片的场景,包含多个分片键的逻辑较复杂,需要应用开发者自行处理其中的复杂度。需要配合ComplexShardingStrategy使用。

Hint分片算法
对应HintShardingAlgorithm,用于处理使用Hint行分片的场景。需要配合HintShardingStrategy使用。

项目结构

jpa配置

spring.jpa.database=mysql
spring.jpa.show-sql=true
spring.jpa.hibernate.ddl-auto=none

分表分库数据源

@Configuration
public class DataSourceConfig {/*** 分表** @return 数据源* @throws SQLException 数据库异常*/@Beanpublic DataSource userShardingDataSource() throws SQLException {ShardingRuleConfiguration shardingRuleConfig = new ShardingRuleConfiguration();//  单库分表TableRuleConfiguration result = new TableRuleConfiguration();result.setLogicTable("t_user");result.setActualDataNodes("ds0.t_user${0..2}");result.setKeyGeneratorColumnName("user_id");result.setKeyGeneratorClass(MySqlKeyGenerator.class.getName());result.setDatabaseShardingStrategyConfig(new StandardShardingStrategyConfiguration("user_id",ModuloShardingDatabaseAlgorithm.class.getName(),ModuloShardingDatabaseAlgorithm.class.getName()));result.setTableShardingStrategyConfig(new StandardShardingStrategyConfiguration("user_id",ModuloShardingTableAlgorithm.class.getName(),ModuloShardingTableAlgorithm.class.getName()));TableRuleConfiguration result2 = new TableRuleConfiguration();result2.setLogicTable("t_order");result2.setActualDataNodes("ds${0..1}.t_order${[0,1]}");result2.setKeyGeneratorColumnName("order_id");result2.setKeyGeneratorClass(RedisKeyGenerator.class.getName());result2.setDatabaseShardingStrategyConfig(new StandardShardingStrategyConfiguration("user_id",ModuloShardingDatabaseAlgorithm.class.getName(),ModuloShardingDatabaseAlgorithm.class.getName()));result2.setTableShardingStrategyConfig(new StandardShardingStrategyConfiguration("order_id",ModuloShardingTableAlgorithm.class.getName(),ModuloShardingTableAlgorithm.class.getName()));TableRuleConfiguration result3 = new TableRuleConfiguration();result3.setLogicTable("t_order_item");result3.setActualDataNodes("ds${0..1}.t_order_item${[0,1]}");result3.setKeyGeneratorColumnName("order_item_id");result3.setKeyGeneratorClass(RedisKeyGenerator.class.getName());result3.setDatabaseShardingStrategyConfig(new StandardShardingStrategyConfiguration("user_id",ModuloShardingDatabaseAlgorithm.class.getName(),ModuloShardingDatabaseAlgorithm.class.getName()));result3.setTableShardingStrategyConfig(new StandardShardingStrategyConfiguration("order_id",ModuloShardingTableAlgorithm.class.getName(),ModuloShardingTableAlgorithm.class.getName()));shardingRuleConfig.getTableRuleConfigs().add(result);shardingRuleConfig.getTableRuleConfigs().add(result2);shardingRuleConfig.getTableRuleConfigs().add(result3);// 配置表关联shardingRuleConfig.getBindingTableGroups().add("t_order, t_order_item");Map<String, DataSource> dbMap = new HashMap<>(2);dbMap.put("ds0", DataSourceUtil.dataSource("ds0"));dbMap.put("ds1", DataSourceUtil.dataSource("ds1"));Properties properties = new Properties();properties.put("sql.show", true);return ShardingDataSourceFactory.createDataSource(dbMap, shardingRuleConfig, new HashMap<>(1), properties);}}

选库算法

@Slf4j
public final class ModuloShardingDatabaseAlgorithm implements PreciseShardingAlgorithm<Long>, RangeShardingAlgorithm<Long> {/*** 选库*/@Overridepublic String doSharding(final Collection<String> databaseNames, final PreciseShardingValue<Long> shardingValue) {log.info("databaseNames:{}", JSON.toJSONString(databaseNames));log.info("shardingValue:{}", JSON.toJSONString(shardingValue));String databaseName = "";// 只做单库分表if ("t_user".equalsIgnoreCase(shardingValue.getLogicTableName())) {databaseName = (String) databaseNames.toArray()[0];}// 分库分表if ("t_order".equalsIgnoreCase(shardingValue.getLogicTableName()) ||"t_order_item".equalsIgnoreCase(shardingValue.getLogicTableName())) {for (String each : databaseNames) {if (each.endsWith(shardingValue.getValue() % databaseNames.size() + "")) {databaseName = each;break;}}}log.info("databaseName:{}", databaseName);if (StringUtils.isNotEmpty(databaseName)) {return databaseName;}throw new UnsupportedOperationException();}/*** 实现between and查询*/@Overridepublic Collection<String> doSharding(Collection<String> collection, RangeShardingValue<Long> rangeShardingValue) {log.info("collection:{}", JSON.toJSONString(collection));log.info("rangeShardingValue:{}", JSON.toJSONString(rangeShardingValue));Collection<String> collect = new ArrayList<>();Range<Long> valueRange = rangeShardingValue.getValueRange();log.info("valueRange:{}", JSON.toJSONString(valueRange));log.info("lowerEndpoint:{}", valueRange.lowerEndpoint());log.info("upperEndpoint:{}", valueRange.upperEndpoint());for (Long i = valueRange.lowerEndpoint(); i <= valueRange.upperEndpoint(); i++) {for (String each : collection) {if (each.endsWith(i % collection.size() + "")) {collect.add(each);}}}log.info("collect:{}", JSON.toJSONString(collect));return collect;}
}

选表算法

@Slf4j
public final class ModuloShardingTableAlgorithm implements PreciseShardingAlgorithm<Long>, RangeShardingAlgorithm<Long> {/*** 选表*/@Overridepublic String doSharding(final Collection<String> tableNames, final PreciseShardingValue<Long> shardingValue) {log.info("tableNames:{}", JSON.toJSONString(tableNames));log.info("shardingValue:{}", JSON.toJSONString(shardingValue));String tableName = "";if ("t_user".equalsIgnoreCase(shardingValue.getLogicTableName())) {for (String each : tableNames) {if (each.endsWith(shardingValue.getValue() % tableNames.size() + "")) {log.info("table:{}", each);tableName = each;break;}}}// 分库分表if ("t_order".equalsIgnoreCase(shardingValue.getLogicTableName()) ||"t_order_item".equalsIgnoreCase(shardingValue.getLogicTableName())) {for (String each : tableNames) {if (each.endsWith(shardingValue.getValue() % tableNames.size() + "")) {tableName = each;break;}}}log.info("tableName:{}", tableName);if (StringUtils.isNotEmpty(tableName)) {return tableName;}throw new UnsupportedOperationException();}/*** 实现between and查询*/@Overridepublic Collection<String> doSharding(Collection<String> collection, RangeShardingValue<Long> rangeShardingValue) {log.info("collection:{}", JSON.toJSONString(collection));log.info("rangeShardingValue:{}", JSON.toJSONString(rangeShardingValue));Collection<String> collect = new ArrayList<>();Range<Long> valueRange = rangeShardingValue.getValueRange();for (Long i = valueRange.lowerEndpoint(); i <= valueRange.upperEndpoint(); i++) {for (String each : collection) {if (each.endsWith(i % collection.size() + "")) {collect.add(each);}}}return collect;}
}

主键策略

默认使用雪花算法(snowflake)生成64bit的长整型数据。如果在请求并发小的情况下会出现所生产的主键都为偶数。有时候我们需要自增主键,就需要自定义主键成策略。

自定义分布式redis主键

public class RedisKeyGenerator implements KeyGenerator {private RedisClient client = RedisClient.create(RedisURI.Builder.redis("192.168.97.57",6379).withTimeout(Duration.ofMillis(6000)).withPassword("666666").withDatabase(1).build());private GenericObjectPool<StatefulRedisConnection<String, String>> pool = ConnectionPoolSupport.createGenericObjectPool(() -> client.connect(), getGenericObjectPoolConfig());public RedisKeyGenerator() {}private GenericObjectPoolConfig getGenericObjectPoolConfig() {GenericObjectPoolConfig config = new GenericObjectPoolConfig();config.setMaxIdle(8);config.setMinIdle(0);config.setMaxTotal(8);config.setMaxWaitMillis(-1);return config;}@Overridepublic synchronized Number generateKey() {try (StatefulRedisConnection<String, String> connection = pool.borrowObject()) {RedisAsyncCommands<String, String> commands = connection.async();RedisFuture<Long> future = commands.incr("id");return future.get();} catch (Exception e) {e.printStackTrace();return System.currentTimeMillis();}}@Overrideprotected void finalize() throws Throwable {if (!pool.isClosed()){pool.close();}super.finalize();}
}

自定义mysql自增主键

建立表t_generate_key

CREATE TABLE
IF NOT EXISTS ds0.t_generate_key (user_id BIGINT NOT NULL AUTO_INCREMENT,PRIMARY KEY (user_id)
);

代码

public class MySqlKeyGenerator implements KeyGenerator {private DataSource dataSource;private String sql = "insert into t_generate_key()values();";public MySqlKeyGenerator() {this.dataSource = DataSourceUtil.dataSource("ds0");}@Overridepublic synchronized Number generateKey() {try (Connection conn = dataSource.getConnection();Statement statement = conn.createStatement()) {statement.executeUpdate(sql, Statement.RETURN_GENERATED_KEYS);ResultSet resultSet = statement.getGeneratedKeys();if (resultSet.next()) {return resultSet.getLong(1);}} catch (Exception e) {return System.currentTimeMillis();}return System.currentTimeMillis();}
}

源码下载

spring-boot-jpa-sharding-jdbc

参考

  1. 性能测试报告
  2. 源码示例参考

sharding-jdbc分库分表相关推荐

  1. Spring boot + Sharding JDBC 分库分表 及 分布式事务处理

    Sharding JDBC 基础概念 Apache ShardingSphere 是一套开源的分布式数据库解决方案组成的生态圈,它由 JDBC.Proxy 和 Sidecar(规划中)这 3 款既能够 ...

  2. SSM项目引入sharding JDBC进行分表

    SSM项目引入sharding JDBC进行分表 注意点: 本次集成sharing-jdbc 4.1.1,由于各个版本差别比较大,配置方式差别也特别大,请根据官方文档进行配置! 官方配置路径:http ...

  3. ShardingSphere JDBC 分库分表 读写分离 数据加密

    简介 在上篇文章中,在本地搭建了运行环境,本地来体验下ShardingSphere JDBC的三个功能:分库分表.读写分离.数据加密 示例运行 首先把概念先捋一捋,参考下面的文档: 数据分片 读写分离 ...

  4. 使用sharding做分库分表,使用jpa,发生的save不报错,数据库缺插不进去数据的问题

    先讲讲问题的诞生,我们项目起初没有引进 sharding分库,而是在项目上线前,才做的分库分表.也就是之前的业务都写好的,所以知道业务代码没有任何问题. 然后引入 sharding 的相关的依赖以后, ...

  5. mysql sharding 方案_mysql sharding 方案 分库分表(sharding)系列(4)

    图1. Sharding实现层面与相关框架/产品 在DAO层实现 当团队决定自行实现sharding的时候,DAO层可能是嵌入sharding逻辑的首选位置,因为在这个层面上,每一个DAO的方法都明确 ...

  6. MySQL数据库性能优化--数据分库分表

    目录 前言 1.什么时候需要分库分表? 1.1.第一次改造 1.2.分库分表的必要性 1.3.第二次改造 2.分库分表应该怎么分? 3.垂直分库会带来哪些问题? 3.1.跨库的关联查询 3.2.分布式 ...

  7. sharding-jdbc4.1.1 分库分表后 mysql查询优化(count)

    sharding jdbc分库分表之后查询优化 背景 需求 研发历程 1.单线程(sharding jdbc 内置查询机制) 2.多线程(sharding jdbc 内置查询机制) 3.sql调整 结 ...

  8. mysql分库分表方案之sharding-jdbc使用(非demo示例)

    选择开源核心组件的一个非常重要的考虑通常是社区活跃性,一旦项目团队无法进行自己后续维护和扩展的情况下更是如此. 关于分库分表和读写分离.主从 一般来说,需要分库分表的系统是流量比较大的,而且比较容易出 ...

  9. Shardingsphere的分库分表+读写分离+分页条件查询

    Shardingsphere的分库分表与读写分离 导入依赖 <dependencies><dependency><groupId>org.springframewo ...

  10. 【数据库与事务系列】分库分表中间件

    前面讲了利用mybatis插件进行多数据源切换和分表的方案,但是对业务侵入性较强,当然给予mybatis-plus的对业务侵入性还好,但是支持的策略有限.场景有限. 所以业界诞生了很多分库分表中间件来 ...

最新文章

  1. C/C++包管理工具Conan简介
  2. Machine Learning week 2 quiz: Linear Regression with Multiple Variables
  3. emwin修改text字体颜色_Rggplot2 绘制带颜色条的相关性散点图
  4. Learning to rank基本算法小结
  5. 转载——CVE-2019-0807
  6. 机器学习算法之线性回归
  7. Get shell By Powershell
  8. 计算机二级安装64位的还是,电脑操作系统安装,该选择32位还是64位?
  9. Queue.ArrayBlockQueue
  10. excel单元格内加空格_Excel基础知识,你懂多少?
  11. 门窗计算机公式,门窗天使软件怎么编辑公式 公式输入方法
  12. Windows下如何强制删除文件夹及文件的命令
  13. 74hc595级联c语言程序,stm32使用三片74HC595级联程序代码
  14. 分享11个网页游戏和9个黑客源码,总有一款适合你
  15. 智能快递柜的电气特性
  16. 谷歌开发者大会焦点:TensorFlow.js可制作微信小程序,Android 10原生支持5G,TF2.0大更新...
  17. iphone电压测试软件,新款iPhone SE充电兼容性大测试之45W篇
  18. php神盾解密工具 注册码_教你如何解密 “ PHP 神盾解密工具 ”
  19. Spring Boot 之 Spring Data JPA(一)
  20. 食堂团餐预定小程序开发制作功能介绍

热门文章

  1. 吴裕雄--天生自然 高等数学学习:数量积、向量积和混合积
  2. 使用数据挖掘软件Rapidminer进行关联规则分析
  3. 分销系统商城小程序业务逻辑功能设计_OctShop
  4. Magento高级产品订阅
  5. 无领导小组讨论面试真题解析(八)—— 是什么影响了利润
  6. python ipaddress模块简介
  7. 【强化学习】确定性策略强化学习-DPGDDPG算法推导及分析
  8. 中国科学院大学计算机研究所,武延军 - 中国科学院大学 - 计算机科学与技术学院...
  9. 绝地求生进游戏显示服务器未正常运行,绝地求生大逃杀BE服务器未正常运行简单解决办法一览...
  10. Java性能调优工具:MAT内存分析工具,上万字带你彻底了解