SpringBoot使用sharding-jdbc分库分表

一、前言

一般来说,随着业务的发展数据库的数据量会越来越多,当单表数据超过上千万时执行一些查询sql语句就会遇到性能问题。一开始可以用主从复制读写分离来减轻db压力,但是后面还是要用分库分表把数据进行水平拆分和垂直拆分。

实现分库分表目前我知道的方式有两种,第一种是使用mycat中间件实现,第二种是使用sharding-jdbc实现。相比较而言,sharding-jdbc引入一个jar包即可使用更轻量级一些,它们之间的优缺点这里也不做比较,有兴趣的可以自己搜索相关资料。

不清楚分库分表原理的可以参考这篇博客,数据库之分库分表-垂直?水平?

二、使用当当网的sharding-jdbc分库分表

2.1新建SpringBoot项目

新建项目sharding-jdbc-first,并在pom文件添加如下内容:

  1. <parent>


  2. <groupId>org.springframework.boot</groupId> 

  3. <artifactId>spring-boot-starter-parent</artifactId> 

  4. <version>1.5.16.RELEASE</version> 

  5. <relativePath/> <!-- lookup parent from repository --> 

  6. </parent> 


  7. <properties> 

  8. <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> 

  9. <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> 

  10. <java.version>1.8</java.version> 

  11. </properties> 


  12. <dependencies> 

  13. <dependency> 

  14. <groupId>org.springframework.boot</groupId> 

  15. <artifactId>spring-boot-starter-data-jpa</artifactId> 

  16. </dependency> 

  17. <dependency> 

  18. <groupId>org.springframework.boot</groupId> 

  19. <artifactId>spring-boot-starter-web</artifactId> 

  20. </dependency> 


  21. <dependency> 

  22. <groupId>mysql</groupId> 

  23. <artifactId>mysql-connector-java</artifactId> 

  24. <scope>runtime</scope> 

  25. </dependency> 

  26. <dependency> 

  27. <groupId>org.springframework.boot</groupId> 

  28. <artifactId>spring-boot-starter-test</artifactId> 

  29. <scope>test</scope> 

  30. </dependency> 

  31. <dependency> 

  32. <groupId>com.dangdang</groupId> 

  33. <artifactId>sharding-jdbc-core</artifactId> 

  34. <version>1.4.2</version> 

  35. </dependency> 

  36. <dependency> 

  37. <groupId>com.alibaba</groupId> 

  38. <artifactId>druid</artifactId> 

  39. <version>1.0.12</version> 

  40. </dependency> 


  41. <dependency> 

  42. <groupId>com.dangdang</groupId> 

  43. <artifactId>sharding-jdbc-self-id-generator</artifactId> 

  44. <version>1.4.2</version> 

  45. </dependency> 



  46. </dependencies> 

目前好像不支持SpringBoot2.0以上的版本。

2.2编写实体类及建库建表

目标:

db0

├── t_order_0 user_id为偶数 order_id为偶数

├── t_order_1 user_id为偶数 order_id为奇数

db1

├── t_order_0 user_id为奇数 order_id为偶数

├── t_order_1 user_id为奇数 order_id为奇数


  1. 创建两个数据库 ds_0 和 ds_1,编码类型UTF-8。
  2. 每个库分表创建两个表t_order_0和t_order_1,sql语句如下:

    DROP TABLE IF EXISTS t_order_0;

    CREATE TABLE t_order_0 (

    order_id bigint(20) NOT NULL,

    user_id bigint(20) NOT NULL,

    PRIMARY KEY (order_id)

    ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;

  3. 新建类Order,代码如下
  1. package cn.sp.bean;



  2. import javax.persistence.Entity; 

  3. import javax.persistence.Id; 

  4. import javax.persistence.Table; 


  5. /** 

  6. * Created by 2YSP on 2018/9/23. 

  7. */ 

  8. @Entity 

  9. @Table(name="t_order") 

  10. public class Order { 

  11. @Id 

  12. private Long orderId; 


  13. private Long userId; 


  14. public Long getOrderId() { 

  15. return orderId; 




  16. public void setOrderId(Long orderId) { 

  17. this.orderId = orderId; 




  18. public Long getUserId() { 

  19. return userId; 




  20. public void setUserId(Long userId) { 

  21. this.userId = userId; 






这里需要注意 @Id注解不要导错包,之前我就遇到过这个问题。

4.配置文件application.yml

  1. server:


  2. port: 8000 

  3. spring: 

  4. jpa: 

  5. database: mysql 

  6. show-sql: true 

  7. hibernate: 

  8. ## 自己建表 

  9. ddl-auto: none 

  10. application: 

  11. name: sharding-jdbc-first 

这里要注意的是spring-data-jpa默认会自己建表,这里我们要手动建立,所以需要将ddl-auto属性设置为none。

2.3自定义分库分表算法

1.分库算法类需要实现SingleKeyDatabaseShardingAlgorithm<T>接口,这是一个泛型接口,T代表分库依据的字段的类型,比如我们根据userId%2来分库,userId是Long型的,这里的T就是Long。

  1. public class ModuloDatabaseShardingAlgorithm implements SingleKeyDatabaseShardingAlgorithm<Long> {


  2. @Override 

  3. public String doEqualSharding(Collection<String> availableDatabaseNames, ShardingValue<Long> shardingValue) { 

  4. for(String databaseName : availableDatabaseNames){ 

  5. if (databaseName.endsWith(shardingValue.getValue() % 2 + "")){ 


  6. return databaseName; 





  7. throw new IllegalArgumentException(); 




  8. @Override 

  9. public Collection<String> doInSharding(Collection<String> availableDatabaseNames, ShardingValue<Long> shardingValue) { 

  10. Collection<String> result = new LinkedHashSet<>(availableDatabaseNames.size()); 

  11. for(Long value : shardingValue.getValues()){ 

  12. for(String name : availableDatabaseNames){ 

  13. if (name.endsWith(value%2 + "")){ 

  14. result.add(name); 







  15. return result; 




  16. @Override 

  17. public Collection<String> doBetweenSharding(Collection<String> availableDatabaseNames, ShardingValue<Long> shardingValue) { 

  18. Collection<String> result = new LinkedHashSet<>(availableDatabaseNames.size()); 

  19. Range<Long> range = shardingValue.getValueRange(); 

  20. for(Long i = range.lowerEndpoint() ; i < range.upperEndpoint();i++){ 

  21. for(String each : availableDatabaseNames){ 

  22. if (each.endsWith( i % 2+"")){ 

  23. result.add(each); 








  24. return result; 






2.分表算法类需要实现SingleKeyTableShardingAlgorithm<T>接口。

  1. /**


  2. * 表分片算法 

  3. * Created by 2YSP on 2018/9/23. 

  4. */ 

  5. public class ModuloTableShardingAlgorithm implements SingleKeyTableShardingAlgorithm<Long> { 


  6. /** 

  7. * select * from t_order from t_order where order_id = 11 

  8. * └── SELECT * FROM t_order_1 WHERE order_id = 11 

  9. * select * from t_order from t_order where order_id = 44 

  10. * └── SELECT * FROM t_order_0 WHERE order_id = 44 

  11. */ 

  12. @Override 

  13. public String doEqualSharding(Collection<String> tableNames, ShardingValue<Long> shardingValue) { 

  14. for (String tableName : tableNames) { 

  15. if (tableName.endsWith(shardingValue.getValue() % 2 + "")) { 

  16. return tableName; 






  17. throw new IllegalArgumentException(); 




  18. /** 

  19. * select * from t_order from t_order where order_id in (11,44) 

  20. * ├── SELECT * FROM t_order_0 WHERE order_id IN (11,44) 

  21. * └── SELECT * FROM t_order_1 WHERE order_id IN (11,44) 

  22. * select * from t_order from t_order where order_id in (11,13,15) 

  23. * └── SELECT * FROM t_order_1 WHERE order_id IN (11,13,15) 

  24. * select * from t_order from t_order where order_id in (22,24,26) 

  25. * └──SELECT * FROM t_order_0 WHERE order_id IN (22,24,26) 

  26. */ 

  27. @Override 

  28. public Collection<String> doInSharding(Collection<String> tableNames, ShardingValue<Long> shardingValue) { 

  29. Collection<String> result = new LinkedHashSet<>(tableNames.size()); 

  30. for (Long value : shardingValue.getValues()) { 

  31. for (String table : tableNames) { 

  32. if (table.endsWith(value % 2 + "")) { 

  33. result.add(table); 







  34. return result; 




  35. /** 

  36. * select * from t_order from t_order where order_id between 10 and 20 

  37. * ├── SELECT * FROM t_order_0 WHERE order_id BETWEEN 10 AND 20 

  38. * └── SELECT * FROM t_order_1 WHERE order_id BETWEEN 10 AND 20 

  39. */ 

  40. @Override 

  41. public Collection<String> doBetweenSharding(Collection<String> tableNames, ShardingValue<Long> shardingValue) { 

  42. Collection<String> result = new LinkedHashSet<>(tableNames.size()); 

  43. Range<Long> range = shardingValue.getValueRange(); 

  44. for (Long i = range.lowerEndpoint(); i < range.upperEndpoint(); i++) { 

  45. for (String each : tableNames) { 

  46. if (each.endsWith(i % 2 + "")) { 

  47. result.add(each); 








  48. return result; 





2.4配置数据源

数据源配置类DataSourceConfig

  1. @Configuration


  2. public class DataSourceConfig { 

  3. @Bean 

  4. public IdGenerator getIdGenerator(){ 

  5. return new CommonSelfIdGenerator(); 




  6. @Bean 

  7. public DataSource getDataSource() { 

  8. return buildDataSource(); 





  9. private DataSource buildDataSource() { 

  10. //1.设置分库映射 

  11. Map<String, DataSource> dataSourceMap = new HashMap<>(2); 

  12. dataSourceMap.put("ds_0", createDataSource("ds_0")); 

  13. dataSourceMap.put("ds_1", createDataSource("ds_1")); 

  14. //设置默认db为ds_0,也就是为那些没有配置分库分表策略的指定的默认库 

  15. //如果只有一个库,也就是不需要分库的话,map里只放一个映射就行了,只有一个库时不需要指定默认库, 

  16. // 但2个及以上时必须指定默认库,否则那些没有配置策略的表将无法操作数据 

  17. DataSourceRule rule = new DataSourceRule(dataSourceMap, "ds_0"); 


  18. //2.设置分表映射,将t_order_0和t_order_1两个实际的表映射到t_order逻辑表 

  19. TableRule orderTableRule = TableRule.builder("t_order") 

  20. .actualTables(Arrays.asList("t_order_0", "t_order_1")) 

  21. .dataSourceRule(rule) 

  22. .build(); 

  23. //3.具体的分库分表策略 

  24. ShardingRule shardingRule = ShardingRule.builder() 

  25. .dataSourceRule(rule) 

  26. .tableRules(Arrays.asList(orderTableRule)) 

  27. .databaseShardingStrategy(new DatabaseShardingStrategy("user_id", new ModuloDatabaseShardingAlgorithm())) 

  28. .tableShardingStrategy(new TableShardingStrategy("order_id", new ModuloTableShardingAlgorithm())) 

  29. .build(); 


  30. DataSource dataSource = ShardingDataSourceFactory.createDataSource(shardingRule); 

  31. return dataSource; 




  32. private static DataSource createDataSource(String dataSourceName) { 

  33. //使用druid连接数据库 

  34. DruidDataSource druidDataSource = new DruidDataSource(); 

  35. druidDataSource.setDriverClassName("com.mysql.jdbc.Driver"); 

  36. druidDataSource.setUrl(String.format("jdbc:mysql://localhost:3306/%s?characterEncoding=utf-8", dataSourceName)); 

  37. druidDataSource.setUsername("root"); 

  38. druidDataSource.setPassword("1234"); 

  39. return druidDataSource; 





这里的一些配置信息url,username,password等可以优化下,从配置文件读取。

2.5测试

1.新建OrderRepository

  1. public interface OrderRepository extends CrudRepository<Order,Long> {





2.controller层

  1. /**


  2. * Created by 2YSP on 2018/9/23. 

  3. */ 

  4. @RestController 

  5. @RequestMapping("/order") 

  6. public class OrderController { 


  7. @Autowired 

  8. private OrderRepository repository; 


  9. @Autowired 

  10. private IdGenerator idGenerator; 


  11. @RequestMapping("/add") 

  12. public String add(){ 

  13. for(int i=0;i<10;i++){ 

  14. Order order = new Order(); 

  15. order.setOrderId((long) i); 

  16. order.setUserId((long) i); 

  17. repository.save(order); 



  18. // Order order = new Order(); 

  19. // order.setUserId(1L); 

  20. // order.setOrderId(idGenerator.generateId().longValue()); 

  21. // repository.save(order); 

  22. return "success"; 




  23. @RequestMapping("/query") 

  24. public List<Order> queryAll(){ 

  25. List<Order> orders = (List<Order>) repository.findAll(); 

  26. return orders; 






3.访问http://localhost:8080/order/add,即可在数据库ds_0,ds_1发现多了一些数据。

访问http://localhost:8080/order/query可以查询刚刚添加的订单数据。

完整代码地址:https://github.com/2YSP/sharding-jdbc-first

三、使用sharding-jdbc-spring-boot-starter分库分表

暂时省略,可以访问github地址了解更多。

四、总结

在分库分表的时候要根据实际情况来决定根据哪个字段来分(不一定都是主键),需要分几个库几张表。

分库分表后遇到的问题:

1.不能像以前一样使用数据库自增的主键了,会出现主键重复的问题(可以使用分布式主键来代替)。

2.不支持一些关键字。

3.在做一些统计查询的时候也更加困难,那时候可能需要引入搜索引擎ES了。

4.之前以为sharding-jdbc不支持分页操作,那天测试了下竟然可以。

原文地址:https://www.cnblogs.com/2YSP/p/9746981.html

时间: 2024-10-28 21:14:32

SpringBoot使用sharding-jdbc分库分表的相关文章

SpringBoot+Mybatis+Sharding-JDBC实现分库分表

项目里面一直用Sharding-JDBC,今天整理一下,就当温故而知新了,也是稳固而知新了. 一.整体介绍 项目采用的框架是SpringBoot+Mybatis+Sharding-JDBC,采用的是properties的形式: 分为两个数据库sharding_0,sharding_1.每个库三个表,t_user_00,t_user_01,t_user_02: 分库策略:age % 2 = 0的数据存储到sharding_0 ,为1的数据存储到sharding_1: 分表策略:user_id %

数据库-sharding之分库分表

1.分库分表(sharding)的原因 在网站初期,为了节约资源,常常部署单点(单一服务器)mysql作为数据存储.随着网站业务量增大,机器的性能有限,单点mysql的I/O性能瓶颈马 上达到高潮. ?为了突破单节点I/O的性能,聪明的架构师开始引入多节点,这就是所谓分库.如果说单节点是单线程串行计算,那么多节点就是多线程并行计算.多节 ?点的mysql服务借鉴了并行的思想,将原本对单点的操作分散到N台机器上,提升的效果明显. ?有可能我们的网站发展很迅速,每天的注册用户很多,然而这些注册用户都

Sharding-Sphere 3.X 与spring与mybatis集成(分库分表)demo

最近在弄这个sharding-sphere,公司内部分库分表是在此业务代码上进行逻辑分库分表,但是这种总是不好,也调研了几款分库分表中间件.mycat.网易cetus.阿里DRDS.这几种就是背景强大,大公司经过大量的实战,成熟度很高,而框架sharding-sphere比较轻量级,最近比较火,它是以jar包形式提供服务,可以无缝连接ORM框架,并不需要额外的部署,不需要依赖,运维可以不需要改动,很多人都把sharding-sphere当成增强版的jdbc驱动,迁移代码其实没那么复杂.对于巨头公

Sharding JDBC如何分库分表?看完你就会了

Sharding JDBC的操作分为配置使用.读写分离.分库分表以及应用等,今天我们主要来了解一下关于分库分表的操作,如果你对此感兴趣的话,那我们就开始吧. 环境准备 pom.xml <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.1.3.RELEASE

一小时读懂Sharding JDBC之分库分表

作为轻量级java框架,sharding JDBC在Java的jdbc层提供了额外的服务,可以理解为增强版的jdbc驱动.其中,分库分表的操作是其中的重要一环,接下来就跟随我来看一看,这一操作如何进行. 环境准备 pom.xml <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <v

分库分表(4) ---SpringBoot + ShardingSphere 实现分表

分库分表(4)--- ShardingSphere实现分表 有关分库分表前面写了三篇博客: 1.分库分表(1) --- 理论 2.分库分表(2) --- ShardingSphere(理论) 3.分库分表(3) ---SpringBoot + ShardingSphere实现读写分离 这篇博客通过ShardingSphere实现分表不分库,并在文章最下方附上项目Github地址. 一.项目概述 1.技术架构 项目总体技术选型 SpringBoot2.0.6 + shardingsphere4.0

分库分表(5) ---SpringBoot + ShardingSphere 实现分库分表

分库分表(5)--- ShardingSphere实现分库分表 有关分库分表前面写了四篇博客: 1.分库分表(1) --- 理论 2.分库分表(2) --- ShardingSphere(理论) 3.分库分表(3) ---SpringBoot + ShardingSphere实现读写分离 4.分库分表(4) ---SpringBoot + ShardingSphere 实现分表 这篇博客通过ShardingSphere实现分库分表,并在文章最下方附上项目Github地址. 一.项目概述 1.技术

分库分表(6)--- SpringBoot+ShardingSphere实现分表+ 读写分离

分库分表(6)--- ShardingSphere实现分表+ 读写分离 有关分库分表前面写了五篇博客: 1.分库分表(1) --- 理论 2.分库分表(2) --- ShardingSphere(理论) 3.分库分表(3) ---SpringBoot + ShardingSphere实现读写分离 4.分库分表(4) ---SpringBoot + ShardingSphere 实现分表 5.分库分表(5) ---SpringBoot + ShardingSphere 实现分库分表 这篇博客通过S

转数据库分库分表(sharding)系列(二) 全局主键生成策略

本文将主要介绍一些常见的全局主键生成策略,然后重点介绍flickr使用的一种非常优秀的全局主键生成方案.关于分库分表(sharding)的拆分策略和实施细则,请参考该系列的前一篇文章:数据库分库分表(sharding)系列(一) 拆分实施策略和示例演示 本文原文连接: http://blog.csdn.net/bluishglc/article/details/7710738 ,转载请注明出处! 第一部分:一些常见的主键生成策略 一旦数据库被切分到多个物理结点上,我们将不能再依赖数据库自身的主键

数据库分库分表(sharding)系列(三) 关于使用框架还是自主开发以及sharding实现层面的考量

当团队对系统业务和数据库进行了细致的梳理,确定了切分方案后,接下来的问题就是如何去实现切分方案了,目前在sharding方面有不少的开源框架和产 品可供参考,同时很多团队也会选择自主开发实现,而不管是选择框架还是自主开发,都会面临一个在哪一层上实现sharding逻辑的问题,本文会对这一系 列的问题逐一进行分析和考量.本文原文连接: http://blog.csdn.net/bluishglc/article/details/7766508转载请注明出处! 一.sharding逻辑的实现层面 从