【分库分表(垂直/水平)】

关系型数据库本身比较容易成为系统瓶颈,单机存储容量、连接数、处理能力都有限。当单表的数据量达到1000W或100G以后,由于查询维度较多,即使添加从库、优化索引,做很多操作时性能仍下降严重。此时就要考虑对其进行切分了,切分的目的就在于减少数据库的负担,缩短查询时间。

数据库分布式核心内容无非就是数据切分(Sharding),以及切分后对数据的定位、整合。数据切分就是将数据分散存储到多个数据库中,使得单一数据库中的数据量变小,通过扩充主机的数量缓解单一数据库的性能问题,从而达到提升数据库操作性能的目的。

数据切分根据其切分类型,可以分为两种方式:垂直(纵向)切分和水平(横向)切分

 

1、垂直(纵向)切分

垂直切分常见有垂直分库和垂直分表两种。

垂直分库就是根据业务耦合性,将关联度低的不同表存储在不同的数据库。做法与大系统拆分为多个小系统类似,按业务分类进行独立划分。与"微服务治理"的做法相似,每个微服务使用单独的一个数据库。如图:

 

垂直分表是基于数据库中的"列"进行,某个表字段较多,可以新建一张扩展表,将不经常用或字段长度较大的字段拆分出去到扩展表中。在字段很多的情况下(例如一个大表有100多个字段),通过"大表拆小表",更便于开发与维护,也能避免跨页问题,MySQL底层是通过数据页存储的,一条记录占用空间过大会导致跨页,造成额外的性能开销。另外数据库以行为单位将数据加载到内存中,这样表中字段长度较短且访问频率较高,内存能加载更多的数据,命中率更高,减少了磁盘IO,从而提升了数据库性能。

 

垂直切分的优点:

· 解决业务系统层面的耦合,业务清晰

· 与微服务的治理类似,也能对不同业务的数据进行分级管理、维护、监控、扩展等

· 高并发场景下,垂直切分一定程度的提升IO、数据库连接数、单机硬件资源的瓶颈

缺点:

· 部分表无法join,只能通过接口聚合方式解决,提升了开发的复杂度

· 分布式事务处理复杂

· 依然存在单表数据量过大的问题(需要水平切分)

 

2、水平(横向)切分

当一个应用难以再细粒度的垂直切分,或切分后数据量行数巨大,存在单库读写、存储性能瓶颈,这时候就需要进行水平切分了。

水平切分分为库内分表和分库分表,是根据表内数据内在的逻辑关系,将同一个表按不同的条件分散到多个数据库或多个表中,每个表中只包含一部分数据,从而使得单个表的数据量变小,达到分布式的效果。如图所示: 

 

库内分表只解决了单一表数据量过大的问题,但没有将表分布到不同机器的库上,因此对于减轻MySQL数据库的压力来说,帮助不是很大,大家还是竞争同一个物理机的CPU、内存、网络IO,最好通过分库分表来解决。

水平切分的优点:

· 不存在单库数据量过大、高并发的性能瓶颈,提升系统稳定性和负载能力

· 应用端改造较小,不需要拆分业务模块

缺点:

· 跨分片的事务一致性难以保证

· 跨库的join关联查询性能较差

· 数据多次扩展难度和维护量极大

水平切分后同一张表会出现在多个数据库/表中,每个库/表的内容不同。

 

mysql的水平分表和垂直分表的区别 

 

1,水平分割:

例:QQ的登录表。假设QQ的用户有100亿,如果只有一张表,每个用户登录的时候数据库都要从这100亿中查找,会很慢很慢。如果将这一张表分成100份,每张表有1亿条,就小了很多,比如qq0,qq1,qq1...qq99表。

用户登录的时候,可以将用户的id%100,那么会得到0-99的数,查询表的时候,将表名qq跟取模的数连接起来,就构建了表名。比如123456789用户,取模的89,那么就到qq89表查询,查询的时间将会大大缩短。

这就是水平分割。

2,垂直分割:

垂直分割指的是:表的记录并不多,但是字段却很长,表占用空间很大,检索表的时候需要执行大量的IO,严重降低了性能。这时需要把大的字段拆分到另一个表,并且该表与原表是一对一的关系。

例如学生答题表tt:有如下字段:

Id name 分数 题目 回答

其中题目和回答是比较大的字段,id name 分数比较小。

如果我们只想查询id为8的学生的分数:select 分数 from tt where id = 8;虽然知识查询分数,但是题目和回答这两个大字段也是要被扫描的,很消耗性能。但是我们只关心分数,并不想查询题目和回答。这就可以使用垂直分割。我们可以把题目单独放到一张表中,通过id与tt表建立一对一的关系,同样将回答单独放到一张表中。这样我们插叙tt中的分数的时候就不会扫描题目和回答了。

3,其他要点:

1)存放图片、文件等大文件用文件系统存储。数据库只存储路径,图片和文件存放在文件系统,甚至单独存放在一台服务器(图床)。

2)数据参数配置。

最重要的参数就是内存,我们主要用的innodb引擎,所以下面两个参数调的很大:

innodb_additional_mem_pool_size=64M

innodb_buffer_pool_size=1G

对于MyISAM,需要调整key_buffer_size,当然调整参数还是要看状态,用show status语句可以看到当前状态,以决定该调整哪些参数。

 

分库分表之后全局id的生成

1. 数据库自增ID——来自Flicker的解决方案

因为MySQL本身支持auto_increment操作,很自然地,我们会想到借助这个特性来实现这个功能。Flicker在解决全局ID生成方 案里就采用了MySQL自增长ID的机制(auto_increment + replace into + MyISAM)。一个生成64位ID方案具体就是这样的: 先创建单独的数据库(eg:ticket),然后创建一个表:

CREATE TABLE Tickets64 (

            id bigint(20) unsigned NOT NULL auto_increment,

            stub char(1) NOT NULL default '',

            PRIMARY KEY  (id),

            UNIQUE KEY stub (stub)

    ) ENGINE=MyISAM

当我们插入记录后,执行SELECT * from Tickets64,查询结果就是这样的:

+-------------------+------+| id                | stub |

+-------------------+------+| 72157623227190423 |    a |

+-------------------+------+

在我们的应用端需要做下面这两个操作,在一个事务会话里提交:

REPLACE INTO Tickets64 (stub) VALUES ('a');SELECT LAST_INSERT_ID();

这样我们就能拿到不断增长且不重复的ID了。 到上面为止,我们只是在单台数据库上生成ID,从高可用角度考虑,接下来就要解决单点故障问题:Flicker启用了两台数据库服务器来生成ID,通过区分auto_increment的起始值和步长来生成奇偶数的ID。

TicketServer1:auto-increment-increment = 2auto-increment-offset = 1

 

TicketServer2:auto-increment-increment = 2auto-increment-offset = 2

最后,在客户端只需要通过轮询方式取ID就可以了。

· 优点:充分借助数据库的自增ID机制,提供高可靠性,生成的ID有序。

· 缺点:占用两个独立的MySQL实例,有些浪费资源,成本较高。

2. 独立的应用程序——来自Twitter的解决方案

Twitter在把存储系统从MySQL迁移到Cassandra的过程中由于Cassandra没有顺序ID生成机制,于是自己开发了一套全局唯一ID生成服务:Snowflake。根据twitter的业务需求,snowflake系统生成64位的ID。由3部分组成:

41位的时间序列(精确到毫秒,41位的长度可以使用69年)

10位的机器标识(10位的长度最多支持部署1024个节点)

12位的计数顺序号(12位的计数顺序号支持每个节点每毫秒产生4096个ID序号)

最高位是符号位,始终为0。

· 优点:高性能,低延迟;独立的应用;按时间有序。

· 缺点:需要独立的开发和部署。

Spring Boot集成sharding-jdbc实现按时间分库分表

 

1

前篇内容已经实现了spring boot集成mybatis访问MySQL,本篇在此基础上集成sharding-jdbc实现分库分表等功能。

集成sharding-jdbc

首先创建创建数据库和表。这是sharding-jdbc所要求的。



  • create database db_201810;


  • create database db_201811;


  •  


  • use db_201810;


  • create table gps_20181014(id int not null auto_increment,gprs varchar(16) NOT NULL,sys_time datetime,PRIMARY KEY (id));


  • create table gps_20181015(id int not null auto_increment,gprs varchar(16) NOT NULL,sys_time datetime,PRIMARY KEY (id));


  • insert into gps_20181014 values(0,'0123456789012345','2018-10-14 0:0:0');


  • insert into gps_20181015 values(0,'0123456789012345','2018-10-15 0:0:0');


  •  


  • use db_201811;


  • create table gps_20181114(id int not null auto_increment,gprs varchar(16) NOT NULL,sys_time datetime,PRIMARY KEY (id));


  • create table gps_20181115(id int not null auto_increment,gprs varchar(16) NOT NULL,sys_time datetime,PRIMARY KEY (id));


  • insert into gps_20181114 values(0,'0123456789012345','2018-11-14 0:0:0');


  • insert into gps_20181115 values(0,'0123456789012345','2018-11-15 0:0:0');

接着是pom.xml文件,添加sharding-jdbc到工程中。主要是下面两个依赖:

        <!--sharding-jdbc --> 

        <dependency>

            <groupId>io.shardingjdbc</groupId>

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

            <version>2.0.3</version>

        </dependency>

        <dependency>

            <groupId>commons-dbcp</groupId>

            <artifactId>commons-dbcp</artifactId>

            <version>1.3</version>

        </dependency>

完整的pom.xml文件内容如下



  • <?xml version="1.0" encoding="UTF-8"?>


  • <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</groupId>


  • <artifactId>SpringBootDemo</artifactId>


  • <version>0.0.1-SNAPSHOT</version>


  • <packaging>jar</packaging>


  •  


  • <name>SpringBootDemo</name>


  • <description>Demo project for Spring Boot</description>


  •  


  • <parent>


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


  • <artifactId>spring-boot-starter-parent</artifactId>


  • <version>2.0.5.RELEASE</version>


  • <relativePath/> <!-- lookup parent from repository -->


  • </parent>


  •  


  • <properties>


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


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


  • <java.version>1.8</java.version>


  • </properties>


  •  


  • <dependencies>


  • <dependency>


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


  • <artifactId>spring-boot-starter-data-redis</artifactId>


  • </dependency>


  • <dependency>


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


  • <artifactId>spring-boot-starter-jdbc</artifactId>


  • </dependency>


  • <dependency>


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


  • <artifactId>spring-boot-starter-web</artifactId>


  • </dependency>


  • <dependency>


  • <groupId>org.mybatis.spring.boot</groupId>


  • <artifactId>mybatis-spring-boot-starter</artifactId>


  • <version>1.3.2</version>


  • </dependency>


  •  


  • <dependency>


  • <groupId>mysql</groupId>


  • <artifactId>mysql-connector-java</artifactId>


  • <scope>runtime</scope>


  • </dependency>


  • <dependency>


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


  • <artifactId>spring-boot-starter-test</artifactId>


  • <scope>test</scope>


  • </dependency>


  •  


  •  


  • <!--sharding-jdbc -->


  • <dependency>


  • <groupId>io.shardingjdbc</groupId>


  • <artifactId>sharding-jdbc-core</artifactId>


  • <version>2.0.3</version>


  • </dependency>


  •  


  • <dependency>


  •     <groupId>commons-dbcp</groupId>


  •     <artifactId>commons-dbcp</artifactId>


  •     <version>1.3</version>


  • </dependency>


  •  


  • </dependencies>


  •  


  • <build>


  • <plugins>


  • <plugin>


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


  • <artifactId>spring-boot-maven-plugin</artifactId>


  • </plugin>


  • </plugins>


  • </build>


  •  


  •  


  • </project>

 

剩下就是代码了。首先是数据源配置和库策略、表策略:

DataSourceConfig.java



  • package com.net.config;


  •  


  • import java.sql.SQLException;


  • import java.util.HashMap;


  • import java.util.Map;


  •  


  • import javax.sql.DataSource;


  •  


  • import org.apache.commons.dbcp.BasicDataSource;


  • import org.apache.ibatis.session.SqlSessionFactory;


  • import org.mybatis.spring.SqlSessionFactoryBean;


  • import org.mybatis.spring.annotation.MapperScan;


  • import org.springframework.context.annotation.Bean;


  • import org.springframework.context.annotation.Configuration;


  • import org.springframework.context.annotation.Primary;


  • import org.springframework.jdbc.datasource.DataSourceTransactionManager;


  •  


  • import com.net.config.DatabaseShardingAlgorithm;


  • import com.net.config.TableShardingAlgorithm;


  •  


  • import io.shardingjdbc.core.api.config.ShardingRuleConfiguration;


  • import io.shardingjdbc.core.api.config.TableRuleConfiguration;


  • import io.shardingjdbc.core.api.config.strategy.StandardShardingStrategyConfiguration;


  • import io.shardingjdbc.core.jdbc.core.datasource.ShardingDataSource;


  •  


  •  


  • @Configuration


  • @MapperScan(basePackages = "com.net.domain", sqlSessionFactoryRef = "sqlSessionFactory")


  • public class DataSourceConfig {


  • //配置sharding-jdbcDataSource,给上层应用使用,这个DataSource包含所有的逻辑库和逻辑表,应用增删改查时,修改对应sql


  • //然后选择合适的数据库继续操作。因此这个DataSource创建很重要。


  • @Bean


  • @Primary


  • public DataSource shardingDataSource() throws SQLException {


  •  


  • ShardingRuleConfiguration shardingRuleConfig = new ShardingRuleConfiguration();


  •      


  • //用户表配置,可以添加多个配置


  • shardingRuleConfig.getTableRuleConfigs().add(getUserTableRuleConfiguration());


  • shardingRuleConfig.getTableRuleConfigs().add(getUserTableRuleConfiguration1());


  • shardingRuleConfig.getBindingTableGroups().add("gps");


  •      


  • //设置数据库策略,传入的是sys_time


  • shardingRuleConfig.setDefaultDatabaseShardingStrategyConfig(new StandardShardingStrategyConfiguration("sys_time", DatabaseShardingAlgorithm.class.getName()));


  • //设置数据表策略,传入的是sys_time


  • shardingRuleConfig.setDefaultTableShardingStrategyConfig(new StandardShardingStrategyConfiguration("sys_time", TableShardingAlgorithm.class.getName()));


  •      


  •      


  • return new ShardingDataSource(shardingRuleConfig.build(createDataSourceMap()));


  • }


  •  


  • //创建用户表规则


  • @Bean


  • TableRuleConfiguration getUserTableRuleConfiguration() {


  • TableRuleConfiguration orderTableRuleConfig = new TableRuleConfiguration();


  •  


  • orderTableRuleConfig.setLogicTable("gps");


  • //设置数据节点,格式为dbxx.tablexx。这里的名称要和map的别名一致。下面两种方式都可以


  • //orderTableRuleConfig.setActualDataNodes("db_${0..1}.gps_${0..1}");


  • orderTableRuleConfig.setActualDataNodes("db_201810.gps_20181014,db_201810.gps_20181015,db_201811.gps_20181114,db_201811.gps_20181115");


  • //设置纵列名称


  • orderTableRuleConfig.setKeyGeneratorColumnName("sys_time");


  • return orderTableRuleConfig;


  • }


  •  


  • @Bean


  • TableRuleConfiguration getUserTableRuleConfiguration1() {


  • TableRuleConfiguration orderTableRuleConfig = new TableRuleConfiguration();


  • //设置用户表逻辑


  • orderTableRuleConfig.setLogicTable("tb_user");


  • //设置数据节点,格式为dbxx.tablexx。这里的名称要和map的别名一致


  • orderTableRuleConfig.setActualDataNodes("gps_com.tb_user");


  •      


  • return orderTableRuleConfig;


  • }


  •  


  •  


  • //下面函数是获取数据源,即包含有多少个数据库,读入到系统中存放于map


  • private Map<String, DataSource> createDataSourceMap() {


  • Map<String, DataSource> result = new HashMap<>();


  •  


  • result.put("gps_com", createDataSource("jdbc:mysql://localhost:3306/gps_com?characterEncoding=utf8&useSSL=false"));


  • result.put("db_201810", createDataSource("jdbc:mysql://localhost:3306/db_201810?characterEncoding=utf8&useSSL=false"));


  • result.put("db_201811", createDataSource("jdbc:mysql://localhost:3306/db_201811?characterEncoding=utf8&useSSL=false"));


  • return result;


  • }


  •  


  • private DataSource createDataSource(final String dataSourceName) {


  • //使用默认连接池


  • BasicDataSource result = new BasicDataSource();


  • result.setDriverClassName(com.mysql.jdbc.Driver.class.getName());


  • //设置数据库路径


  • result.setUrl(dataSourceName);


  • //设置数据库用户名


  • result.setUsername("root");


  • //设置数据密码


  • result.setPassword("123456");


  • return result;


  • }


  •  


  •  


  •  


  •  


  •  


  •  


  • /**


  • * 需要手动配置事务管理器


  • */


  • @Bean


  • public DataSourceTransactionManager transactitonManager(DataSource shardingDataSource) {


  • return new DataSourceTransactionManager(shardingDataSource);


  • }


  •  


  •  


  • //下面是SessionFactory配置,二选一,由类前语句选择  @MapperScan(basePackages = "com.net.domain", sqlSessionFactoryRef = "sqlSessionFactory")


  • @Bean


  • @Primary


  • public SqlSessionFactory sqlSessionFactory(DataSource shardingDataSource) throws Exception {


  • SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();


  • sessionFactory.setDataSource(shardingDataSource);


  • return sessionFactory.getObject();


  • }


  •  


  • }


  •  

DatabaseShardingAlgorithm.java



  • package com.net.config;


  •  


  • import java.text.ParseException;


  • import java.text.SimpleDateFormat;


  • import java.util.Collection;


  • import java.util.Date;


  •  


  • import io.shardingjdbc.core.api.algorithm.sharding.PreciseShardingValue;


  • import io.shardingjdbc.core.api.algorithm.sharding.standard.PreciseShardingAlgorithm;


  •  


  •  


  •  


  • public class DatabaseShardingAlgorithm implements PreciseShardingAlgorithm<String> {


  •     @Override


  •     public String doSharding(Collection<String> collection, PreciseShardingValue<String> preciseShardingValue) {


  •  


  •     String db_name="db_";


  •     try {


  • Date date = (Date) new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(preciseShardingValue.getValue());


  •  


  • String year = String.format("%tY", date);


  • String mon  = String.format("%tm",date);


  • db_name=db_name+year+mon;


  • System.out.println("db_name:" + db_name);


  • } catch (ParseException e) {


  • // TODO Auto-generated catch block


  • e.printStackTrace();


  • }


  •    


  •     for (String each : collection) {


  •         System.out.println("db:" + each);


  •         if (each.equals(db_name)) {


  •                 return each;


  •             }


  •         }


  •    


  •     throw new IllegalArgumentException();


  •     }


  • }

TableShardingAlgorithm.java



  • package com.net.config;


  •  


  • import java.text.ParseException;


  • import java.text.SimpleDateFormat;


  • import java.util.Collection;


  • import java.util.Date;


  •  


  • import io.shardingjdbc.core.api.algorithm.sharding.PreciseShardingValue;


  • import io.shardingjdbc.core.api.algorithm.sharding.standard.PreciseShardingAlgorithm;


  •  


  •  


  • public class TableShardingAlgorithm implements PreciseShardingAlgorithm<String> {


  •     @Override


  •     public String doSharding(Collection<String> collection, PreciseShardingValue<String> preciseShardingValue) {


  •     String tb_name=preciseShardingValue.getLogicTableName() + "_";


  •     try {


  • Date date = (Date) new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(preciseShardingValue.getValue());


  •  


  • String year = String.format("%tY", date);


  • String mon  = String.format("%tm",date);


  • String dat  = String.format("%td",date);


  • tb_name=tb_name+year+mon+dat;


  • System.out.println("tb_name:" + tb_name);


  • } catch (ParseException e) {


  • // TODO Auto-generated catch block


  • e.printStackTrace();


  • }


  •    


  •     for (String each : collection) {


  •         System.out.println("tb:" + each);


  •        


  •         if (each.equals(tb_name)) {


  •                 return each;


  •             }


  •         }


  •    


  •     throw new IllegalArgumentException();


  •     }


  • }

这时工程结构如图所示:

 

运行程序,没有问题,浏览器触发访问数据库也能正常访问gps_com的tb_user表。

 

以上说明集成sharding-jdbc没有问题。至于能访问数据库是由于DataSourceConfig.java中加上了下面代码。

shardingRuleConfig.getTableRuleConfigs().add(getUserTableRuleConfiguration1());

@Bean

TableRuleConfiguration getUserTableRuleConfiguration1() {

        TableRuleConfiguration orderTableRuleConfig = new TableRuleConfiguration();

        //设置用户表逻辑

        orderTableRuleConfig.setLogicTable("tb_user");

        //设置数据节点,格式为dbxx.tablexx。这里的名称要和map的别名一致

        orderTableRuleConfig.setActualDataNodes("gps_com.tb_user");

      

        return orderTableRuleConfig;

}

 

测试并使用sharding-jdbc

上面完成了sharding-jdbc的集成,但是还没有真正发挥作用,也就是分库分表的功能。

继续添加代码,最后工程结构图是:

 

GpsData.java



  • package com.net.domain;


  •  


  • import java.io.Serializable;


  •  


  • public class GpsData implements Serializable{ 


  •  


  • private static final long serialVersionUID = 1L;


  • String gprs;


  • String sys_time;


  •  


  •  


  • public void setGprs(String gprs)


  • {


  • this.gprs = gprs;


  • }


  • public String getGprs()


  • {


  • return this.gprs;


  • }


  •  


  • public void setSystime(String sys_time)


  • {


  • this.sys_time = sys_time;


  • }


  • public String getSystime()


  • {


  • return this.sys_time;


  • }


  •  


  • }

GpsDataMapper.java



  • package com.net.domain;


  •  


  • import java.util.List;


  •  


  • import org.apache.ibatis.annotations.Mapper;


  • import org.apache.ibatis.annotations.Param;


  • import org.apache.ibatis.annotations.Select;


  •  


  • import com.net.domain.GpsData;


  •  


  • @Mapper


  • public interface GpsDataMapper {


  •  


  • @Select("select * from gps where sys_time = #{sys_time}")


  • List<GpsData> findAll(@Param("sys_time") String sys_time);


  • }

GpsDataService.java



  • package com.net.service;


  •  


  • import java.util.List;


  •  


  • import com.net.domain.GpsData;


  •  


  • public interface GpsDataService {


  •  


  • List<GpsData> getAll(String sys_time);


  • }

GpsDataServiceImpl.java



  • package com.net.service.impl;


  •  


  • import java.util.List;


  •  


  • import org.springframework.beans.factory.annotation.Autowired;


  • import org.springframework.stereotype.Service;


  •  


  • import com.net.domain.GpsData;


  • import com.net.domain.GpsDataMapper;


  • import com.net.service.GpsDataService;


  •  


  • @Service("gpsDataService")


  • public class GpsDataServiceImpl implements GpsDataService{


  •  


  • @Autowired


  • private GpsDataMapper gpsDataMapper;


  •  


  • @Override


  • public List<GpsData> getAll(String sys_time)


  • {


  • return gpsDataMapper.findAll(sys_time);


  • }


  • }

最后是测试代码

TestWithWeb.java



  • package com.net.web;


  •  


  • import java.util.List;


  •  


  • import org.springframework.beans.factory.annotation.Autowired;


  • import org.springframework.beans.factory.annotation.Qualifier;


  • import org.springframework.web.bind.annotation.RequestMapping;


  • import org.springframework.web.bind.annotation.RestController;


  •  


  • import com.net.domain.User;


  • import com.net.domain.GpsData;


  • import com.net.service.GpsDataService;


  • import com.net.service.UserService;


  •  


  •  


  • @RestController


  • public class TestWithWeb {


  •  


  • @Autowired


  • @Qualifier("userService")


  • private UserService userService;


  •  


  • @Autowired


  • @Qualifier("gpsDataService")


  • private GpsDataService gpsDataService;


  •  


  • @RequestMapping("/log")


  • public String login()


  • {


  • System.out.println("hello");


  •  


  • User user = userService.login("root", "123456");


  • System.out.println(user.toString());


  •  


  • List<GpsData> GpsData_list = gpsDataService.getAll("2018-11-14 0:0:0");


  •  


  • System.out.println(GpsData_list.get(0).getGprs());


  • System.out.println(GpsData_list.get(0).getSystime());


  •  


  • return "OK";


  • }


  • }

代码展示结束,开始运行测试。

测试语句:gpsDataService.getAll("2018-11-14 0:0:0");

结果如下:

 

 

由于在代码里面直接配置了数据源,所以application.properties里面关于数据库的信息不起作用,可以直接去掉。

  • 背景

最近在研究Mysql的分库分表,前面的博客已经详细介绍了分库分表!

由于sharding-jdbc是不支持动态进行建库的SQL,那么就需要一次把需要的数据库和数据表都建好

  • 建库、建表

考虑到这只是一个测试的demo,所以,只建了两个库和两个表



  • CREATE TABLE `t_order_0` (


  • `id` BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT '主键id',


  • `order_id` VARCHAR(32) NULL DEFAULT NULL COMMENT '顺序编号',


  • `user_id` VARCHAR(32) NULL DEFAULT NULL COMMENT '用户编号',


  • `userName` VARCHAR(32) NULL DEFAULT NULL COMMENT '用户名',


  • `passWord` VARCHAR(32) NULL DEFAULT NULL COMMENT '密码',


  • `nick_name` VARCHAR(32) NULL DEFAULT NULL,
  • SpringBoot+Mybatis+sharding-jdbc框架搭建(pom文件)


  • <dependencies>


  •         <dependency>


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


  •             <artifactId>spring-boot-starter</artifactId>


  •         </dependency>


  •         <dependency>


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


  •             <artifactId>spring-boot-starter-test</artifactId>


  •             <scope>test</scope>


  •         </dependency>


  •         <dependency>


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


  •             <artifactId>spring-boot-starter-web</artifactId>


  •         </dependency>


  •         <dependency>


  •             <groupId>org.mybatis.spring.boot</groupId>


  •             <artifactId>mybatis-spring-boot-starter</artifactId>


  •             <version>1.1.1</version>


  •         </dependency>


  •         <dependency>


  •             <groupId>mysql</groupId>


  •             <artifactId>mysql-connector-java</artifactId>


  •         </dependency>


  •         <dependency>


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


  •             <artifactId>spring-boot-devtools</artifactId>


  •             <optional>true</optional>


  •         </dependency>


  •  


  •         <dependency>


  •             <groupId>org.projectlombok</groupId>


  •             <artifactId>lombok</artifactId>


  •         </dependency>


  •  


  •         <!--sharding-jdbc -->


  •         <dependency>


  •             <groupId>com.dangdang</groupId>


  •             <artifactId>sharding-jdbc-core</artifactId>


  •             <version>1.5.4</version>


  •         </dependency>


  •     </dependencies>
  • 配置文件


  • mybatis.config-locations=classpath:mybatis/mybatis-config.xml


  •  


  • #datasource


  • spring.devtools.remote.restart.enabled=false


  •  


  • #data source1


  • spring.datasource.test1.driverClassName=com.mysql.jdbc.Driver


  • spring.datasource.test1.jdbcUrl=jdbc:mysql://127.0.0.1:3306/test_msg1?serverTimezone=UTC


  • spring.datasource.test1.username=root


  • spring.datasource.test1.password=123456


  •  


  • #data source2


  • spring.datasource.test2.driverClassName=com.mysql.jdbc.Driver


  • spring.datasource.test2.jdbcUrl=jdbc:mysql://127.0.0.1:3306/test_msg2?serverTimezone=UTC


  • spring.datasource.test2.username=root


  • spring.datasource.test2.password=123456
  • 启动文件


  • @SpringBootApplication


  • @EnableAutoConfiguration(exclude={DataSourceAutoConfiguration.class}) //排除DataSourceConfiguratrion


  • @EnableTransactionManagement(proxyTargetClass = true)   //开启事物管理功能


  • public class ShardingJdbcApplication {


  •    


  •     public static void main(String[] args) {


  •         SpringApplication.run(ShardingJdbcApplication.class, args);


  •     }


  • }
  • 实体类(Data注解可免去set/get方法)


  • @Data


  • public class User {


  •  


  •     private Long id;


  •     private Long order_id;


  •     private Long user_id;


  •     private String userName;


  •     private String passWord;


  •     private String nickName;


  • }
  • Service


  • @Slf4j


  • @Service


  • public class UserService {


  •  


  •     @Resource


  •     private UserMapper userMapper;


  •  


  •     public void insert(User user) {


  •         userMapper.insert(user);


  •     }


  • }
  • Mapper


  • public interface UserMapper {


  •  


  •     void insert(User user);


  • }
  • 数据源配置和Mybatis配置和分库分表规则(重要)

这里,我们是将多个数据源交给sharding-jdbc进行管理,并且有默认的数据源,当没有设置分库分表规则的时候就可以使用默认的数据源

分表:user_id%2 = 0的数据存储到test_msg1,为1的存储到test_msg0

分表:order_id%2 = 0的数据存储到t_order_0,为1的存储到t_order_1



  • /**


  • * @Auther: Tinko


  • * @Date: 2018/12/19 16:27


  • * @Description: 数据源配置和Mybatis配置和分库分表规则


  • */


  • @Configuration


  • @MapperScan(basePackages = "com.example.shardingjdbc.mapper", sqlSessionTemplateRef  = "test1SqlSessionTemplate")


  • public class DataSourceConfig {


  •  


  •     /**


  •      * 配置数据源0,数据源的名称最好要有一定的规则,方便配置分库的计算规则


  •      * @return


  •      */


  •     @Bean(name="dataSource0")


  •     @ConfigurationProperties(prefix = "spring.datasource.test1")


  •     public DataSource dataSource0(){


  •         return DataSourceBuilder.create().build();


  •     }


  •     /**


  •      * 配置数据源1,数据源的名称最好要有一定的规则,方便配置分库的计算规则


  •      * @return


  •      */


  •     @Bean(name="dataSource1")


  •     @ConfigurationProperties(prefix = "spring.datasource.test2")


  •     public DataSource dataSource1(){


  •         return DataSourceBuilder.create().build();


  •     }


  •  


  •     /**


  •      * 配置数据源规则,即将多个数据源交给sharding-jdbc管理,并且可以设置默认的数据源,


  •      * 当表没有配置分库规则时会使用默认的数据源


  •      * @param dataSource0


  •      * @param dataSource1


  •      * @return


  •      */


  •     @Bean


  •     public DataSourceRule dataSourceRule(@Qualifier("dataSource0") DataSource dataSource0,


  •                                          @Qualifier("dataSource1") DataSource dataSource1){


  •         Map<String, DataSource> dataSourceMap = new HashMap<>(); //设置分库映射


  •         dataSourceMap.put("dataSource0", dataSource0);


  •         dataSourceMap.put("dataSource1", dataSource1);


  •         return new DataSourceRule(dataSourceMap, "dataSource0"); //设置默认库,两个库以上时必须设置默认库。默认库的数据源名称必须是dataSourceMap的key之一


  •     }


  •  


  •     /**


  •      * 配置数据源策略和表策略,具体策略需要自己实现


  •      * @param dataSourceRule


  •      * @return


  •      */


  •     @Bean


  •     public ShardingRule shardingRule(DataSourceRule dataSourceRule){


  •         //具体分库分表策略


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


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


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


  •                 .dataSourceRule(dataSourceRule)


  •                 .build();


  •  


  •         //绑定表策略,在查询时会使用主表策略计算路由的数据源,因此需要约定绑定表策略的表的规则需要一致,可以一定程度提高效率


  •         List<BindingTableRule> bindingTableRules = new ArrayList<BindingTableRule>();


  •         bindingTableRules.add(new BindingTableRule(Arrays.asList(orderTableRule)));


  •         return ShardingRule.builder()


  •                 .dataSourceRule(dataSourceRule)


  •                 .tableRules(Arrays.asList(orderTableRule))


  •                 .bindingTableRules(bindingTableRules)


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


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


  •                 .build();


  •     }


  •  


  •     /**


  •      * 创建sharding-jdbc的数据源DataSource,MybatisAutoConfiguration会使用此数据源


  •      * @param shardingRule


  •      * @return


  •      * @throws SQLException


  •      */


  •     @Bean(name="dataSource")


  •     public DataSource shardingDataSource(ShardingRule shardingRule) throws SQLException {


  •         return ShardingDataSourceFactory.createDataSource(shardingRule);


  •     }


  •  


  •     /**


  •      * 需要手动配置事务管理器


  •      * @param dataSource


  •      * @return


  •      */


  •     @Bean


  •     public DataSourceTransactionManager transactitonManager(@Qualifier("dataSource") DataSource dataSource){


  •         return new DataSourceTransactionManager(dataSource);


  •     }


  •  


  •     @Bean(name = "test1SqlSessionFactory")


  •     @Primary


  •     public SqlSessionFactory testSqlSessionFactory(@Qualifier("dataSource") DataSource dataSource) throws Exception {


  •         SqlSessionFactoryBean bean = new SqlSessionFactoryBean();


  •         bean.setDataSource(dataSource);


  •         bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mybatis/*.xml"));


  •         return bean.getObject();


  •     }


  •  


  •     @Bean(name = "test1SqlSessionTemplate")


  •     @Primary


  •     public SqlSessionTemplate testSqlSessionTemplate(@Qualifier("test1SqlSessionFactory") SqlSessionFactory sqlSessionFactory) throws Exception {


  •         return new SqlSessionTemplate(sqlSessionFactory);


  •     }


  • }
  • 分库规则


  • /**


  • * @Auther: Tinko


  • * @Date: 2018/12/19 16:31


  • * @Description: 分库规则


  • */


  • public class ModuloDatabaseShardingAlgorithm implements SingleKeyDatabaseShardingAlgorithm<Long> {


  •  


  •     @Override


  •     public String doEqualSharding(Collection<String> databaseNames, ShardingValue<Long> shardingValue) {


  •         for (String each : databaseNames) {


  •             if (each.endsWith(Long.parseLong(shardingValue.getValue().toString()) % 2 + "")) {


  •                 return each;


  •             }


  •         }


  •         throw new IllegalArgumentException();


  •     }


  •  


  •     @Override


  •     public Collection<String> doInSharding(Collection<String> databaseNames, ShardingValue<Long> shardingValue) {


  •         Collection<String> result = new LinkedHashSet<>(databaseNames.size());


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


  •             for (String tableName : databaseNames) {


  •                 if (tableName.endsWith(value % 2 + "")) {


  •                     result.add(tableName);


  •                 }


  •             }


  •         }


  •         return result;


  •     }


  •  


  •     @Override


  •     public Collection<String> doBetweenSharding(Collection<String> databaseNames, ShardingValue<Long> shardingValue) {


  •         Collection<String> result = new LinkedHashSet<>(databaseNames.size());


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


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


  •             for (String each : databaseNames) {


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


  •                     result.add(each);


  •                 }


  •             }


  •         }


  •         return result;


  •     }


  • }

 

  • 分表规则


  • /**


  • * @Auther: Tinko


  • * @Date: 2018/12/19 16:30


  • * @Description: 分表规则


  • */


  • public class ModuloTableShardingAlgorithm implements SingleKeyTableShardingAlgorithm<Long> {


  •  


  •     @Override


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


  •         for (String each : tableNames) {


  •             if (each.endsWith(shardingValue.getValue() % 2 + "")) {


  •                 return each;


  •             }


  •         }


  •         throw new IllegalArgumentException();


  •     }


  •  


  •     @Override


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


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


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


  •             for (String tableName : tableNames) {


  •                 if (tableName.endsWith(value % 2 + "")) {


  •                     result.add(tableName);


  •                 }


  •             }


  •         }


  •         return result;


  •     }


  •  


  •     @Override


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


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


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


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


  •             for (String each : tableNames) {


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


  •                     result.add(each);


  •                 }


  •             }


  •         }


  •         return result;


  •     }


  • }
  • Mysql交互的配置的文件


  • <?xml version="1.0" encoding="UTF-8" ?>


  • <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >


  • <mapper namespace="com.example.shardingjdbc.mapper.UserMapper" >


  •  


  •     <sql id="Base_Column_List" >


  •         id, userName, passWord, user_sex, nick_name


  •     </sql>


  •  


  •     <insert id="insert" parameterType="com.example.shardingjdbc.entity.User" >


  •         INSERT INTO


  •         t_order


  •         (order_id,user_id,userName,passWord)


  •         VALUES


  •         (#{order_id},#{user_id},#{userName}, #{passWord})


  •     </insert>


  •  


  • </mapper>

git传送门

然后,测试可行!!!

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值