Sharding-JDBC+MyBatis分库分表


  随着项目功能越来越多业务越来越复杂,数据库存储的数据逐渐庞大,当mysql单表存储数据过千万的时候,对该表的操作变得缓慢,这时候就需要通过分库分表对数据库优化。

 

水平分库:是把同一个表的数据按一定规则拆到不同的数据库中,每个库可以放在不同的服务器上。

  • 解决了单库大数据,高并发的性能瓶颈
  • 提高了系统的稳定性及可用性

水平分表:是在同一个数据库内,把同一个表的数据按一定规则拆到多个表中。

  • 优化单一表数据量过大而产生的性能问题
  • 避免IO争抢并减少锁表的几率

 

  本文采用 Sharding-JDBC 分库分表,它是一个优秀的开源框架,使用客户端直连数据库,以 jar 包形式提供服务,无需额外部署和依赖,可理解为增强版的 JDBC 驱动,完全兼容 JDBC 和各种 ORM 框架。

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

一、建库建表

CREATE DATABASE ds_0;
USE ds_0;

DROP TABLE IF EXISTS `user_0`;
CREATE TABLE `user_0` (
  `id` bigint(64) NOT NULL,
  `city` varchar(20) NOT NULL,
  `name` varchar(20) NOT NULL,
  `sex` tinyint(4) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

DROP TABLE IF EXISTS `user_1`;
CREATE TABLE `user_1` (
  `id` bigint(64) NOT NULL,
  `city` varchar(20) NOT NULL,
  `name` varchar(20) NOT NULL,
  `sex` tinyint(4) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

CREATE DATABASE ds_1;
USE ds_1;

DROP TABLE IF EXISTS `user_0`;
CREATE TABLE `user_0` (
  `id` bigint(64) NOT NULL,
  `city` varchar(20) NOT NULL,
  `name` varchar(20) NOT NULL,
  `sex` tinyint(4) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

DROP TABLE IF EXISTS `user_1`;
CREATE TABLE `user_1` (
  `id` bigint(64) NOT NULL,
  `city` varchar(20) NOT NULL,
  `name` varchar(20) NOT NULL,
  `sex` tinyint(4) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

 

二、创建SpringBoot项目,引入依赖

<dependency>
     <groupId>org.mybatis.spring.boot</groupId>
     <artifactId>mybatis-spring-boot-starter</artifactId>
     <version>1.3.1</version>
</dependency>
<dependency>
    <groupId>io.shardingjdbc</groupId>
    <artifactId>sharding-jdbc-core</artifactId>
    <version>2.0.3</version>
</dependency>
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
    <version>1.1.3</version>
</dependency>
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.44</version>
</dependency>

三、创建mapper文件、类、实体

UserMapper.xml
<?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="law.mapper.UserMapper">

    <resultMap id="baseResultMap" type="law.entity.User">
        <result column="id" property="id"/>
        <result column="city" property="city"/>
        <result column="name" property="name"/>
        <result column="sex" property="sex"/>
    </resultMap>

    <insert id="addUser" parameterType="law.entity.User">
        INSERT INTO user (id, city, name, sex)
        VALUES (#{id}, #{city}, #{name}, #{sex})
    </insert>

    <select id="list" resultMap="baseResultMap" parameterType="java.util.ArrayList">
        SELECT * FROM user
        <where>
            id in (
            <foreach collection="list" item="id" index="index" separator=",">
                #{id}
            </foreach>
            )
        </where>
    </select>

    <select id="query" resultMap="baseResultMap">
        SELECT * FROM user order by id asc
    </select>

</mapper>
package law.mapper;

import law.entity.User;
import org.springframework.stereotype.Repository;
import java.util.List;

@Repository
public interface UserMapper {

    void addUser(User user);

    List<User> list(List<Long> ids);

    List<User> query();
}
package law.entity;

import lombok.Data;
import java.io.Serializable;

@Data
public class User implements Serializable {

    private static final long serialVersionUID = -1205226416664488559L;

    private Long id;

    private String city;

    private String name;

    private Long sex;
}

四、加载数据库链接、创建分库分表策略

 
 
package law.configs;

import com.alibaba.druid.pool.DruidDataSource;
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;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
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.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;

import javax.sql.DataSource;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;

@Configuration
@MapperScan(basePackages = "law.mapper", sqlSessionTemplateRef = "testSqlSessionTemplate")
public class DataSourceConfig {

/**
* 配置数据源
*/
@Bean(name = "shardingDataSource")
DataSource getShardingDataSource() throws SQLException {
ShardingRuleConfiguration shardingRuleConfig = new ShardingRuleConfiguration();
shardingRuleConfig.getTableRuleConfigs().add(userTableRuleConfiguration());
shardingRuleConfig.getTableRuleConfigs().add(caipiaoTableRuleConfiguration());
return new ShardingDataSource(shardingRuleConfig.build(createDataSourceMap()));
}

/**
* 设置单表的分库分表策略
* 虚拟表与实际表的映射关系
*/
@Bean
TableRuleConfiguration userTableRuleConfiguration() {
TableRuleConfiguration configuration = new TableRuleConfiguration();
configuration.setLogicTable("user");
configuration.setActualDataNodes("ds_${0..1}.user_${0..1}");
configuration.setDatabaseShardingStrategyConfig(new StandardShardingStrategyConfiguration("id", DatabaseShardingAlgorithm.class.getName()));
configuration.setTableShardingStrategyConfig(new StandardShardingStrategyConfiguration("sex", DatabaseShardingAlgorithm.class.getName()));
return configuration;
}

/**
* 设置不分库分表的映射关系
*/
@Bean
TableRuleConfiguration caipiaoTableRuleConfiguration() {
TableRuleConfiguration configuration = new TableRuleConfiguration();
configuration.setLogicTable("caipiao");
configuration.setActualDataNodes("wms.caipiao");
return configuration;
}

/**
* 手动配置事务管理器
*/
@Bean
public DataSourceTransactionManager transactitonManager(DataSource shardingDataSource) {
return new DataSourceTransactionManager(shardingDataSource);
}

@Bean
@Primary
public SqlSessionFactory sqlSessionFactory(DataSource shardingDataSource) throws Exception {
SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
bean.setDataSource(shardingDataSource);
bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:law/mapper/*.xml"));
return bean.getObject();
}

@Bean
@Primary
public SqlSessionTemplate testSqlSessionTemplate(SqlSessionFactory sqlSessionFactory) throws Exception {
return new SqlSessionTemplate(sqlSessionFactory);
}

/**
* 加载数据库
*/
private Map<String, DataSource> createDataSourceMap() {
Map<String, DataSource> result = new HashMap<>();
result.put("ds_0", createDataSource("ds_0"));
result.put("ds_1", createDataSource("ds_1"));
result.put("wms", createDataSource("wms"));
return result;
}

private DataSource createDataSource(final String dataSourceName) {
DruidDataSource result = new DruidDataSource();
result.setDriverClassName(com.mysql.jdbc.Driver.class.getName());
result.setUrl(String.format("jdbc:mysql://localhost:3306/%s", dataSourceName) + "?useUnicode=true&characterEncoding=utf8&allowMultiQueries=true&zeroDateTimeBehavior=convertToNull&useSSL=false");
result.setUsername("root");
result.setPassword("123456");

return result;
}
}
 
package law.configs;

import io.shardingjdbc.core.api.algorithm.sharding.PreciseShardingValue;
import io.shardingjdbc.core.api.algorithm.sharding.standard.PreciseShardingAlgorithm;
import java.util.Collection;

/**
*分库分表算法,分为2个库每个库2个表
*以id字段分库,用id值除2取余,分配到对应库
*以sex字段分表,用sex值除2取余,分配到对应表
*/
public class DatabaseShardingAlgorithm implements PreciseShardingAlgorithm<Long> { @Override public String doSharding(Collection<String> collection, PreciseShardingValue<Long> preciseShardingValue) { for (String each : collection) { if (each.endsWith(Long.parseLong(preciseShardingValue.getValue().toString()) % 2 + "")) { return each; } } throw new IllegalArgumentException(); } }

五、启动测试

package law.controller;

import com.google.common.collect.Lists;
import law.entity.Lottery;
import law.entity.User;
import law.mapper.LotteryMapper;
import law.mapper.UserMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

@RestController
public class UserController {

    @Autowired
    private UserMapper userMapper;
    @Autowired
    private LotteryMapper lotteryMapper;

    @Transactional
    @RequestMapping("/addUsers")
    public Object addUsers() {
        for (long i = 1; i < 10; i++) {
            User user = new User();
            user.setId(i);
            user.setCity("北京");
            user.setName("" + i);
            user.setSex((long) 0);
            userMapper.addUser(user);
        }
//        //测试事务
//        if (1 == 1) {
//            throw new RuntimeException();
//        }
        for (long i = 11; i < 20; i++) {
            User user = new User();
            user.setId(i);
            user.setCity("成都");
            user.setName("" + i);
            user.setSex((long) 1);
            userMapper.addUser(user);
        }
        return "success";
    }
  //测试单表 @RequestMapping(
"/addLotteries") public Object addLotteries() { for (int i = 1; i < 3; i++) { Lottery lottery = new Lottery(); lottery.setCpNum(i); lottery.setCpValue("" + i); lotteryMapper.addLottery(lottery); } return "success"; } @RequestMapping("/selectByIds") public Object selectByIds() { List<Long> ids = Lists.newArrayList(); ids.add(1L); ids.add(3L); return userMapper.list(ids); } @RequestMapping("/selectSort") public Object selectSort() { return userMapper.query(); } }
package law;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.transaction.annotation.EnableTransactionManagement;

@MapperScan(value = {"law.mapper"})
@EnableTransactionManagement(proxyTargetClass = true)
@SpringBootApplication
public class Demo {
    public static void main(String[] args) {
        SpringApplication.run(Demo.class, args);
    }
}

 


免责声明!

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



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