sharding-jdbc分库分表

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/scgyus/article/details/84975328

当当开源的sharding-jdbc,官方网址:https://github.com/dangdangdotcom/sharding-jdbc

好了,看了这么多的介绍,感觉还是很高大上的,注意点有:
①对JDBC API进行了原生态的分装,这是与cobar-client不一样的地方,这就是他可以支持多个第三方ORM框架的关键
②可支持=,BETWEEN,IN等操作,说明,JDBC返回结果后,sharding进行了合并操作,这里面肯定会有性能损耗
③支持分表,这也是cobar-client不支持的地方


好了,先简单的按照官方网址的demo实践一发

先在MySQL中建2个库

分别在这2个库中运行:

/*
Navicat MySQL Data Transfer

Source Server         : localhost_3306
Source Server Version : 50550
Source Host           : localhost:3306
Source Database       : sharding_1

Target Server Type    : MYSQL
Target Server Version : 50550
File Encoding         : 65001

Date: 2018-02-25 12:33:38
*/

SET FOREIGN_KEY_CHECKS=0;

-- ----------------------------
-- Table structure for t_student_00
-- ----------------------------
DROP TABLE IF EXISTS `t_student_00`;
CREATE TABLE `t_student_00` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `student_id` int(11) NOT NULL,
  `name` varchar(255) NOT NULL,
  `age` int(11) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

-- ----------------------------
-- Table structure for t_student_01
-- ----------------------------
DROP TABLE IF EXISTS `t_student_01`;
CREATE TABLE `t_student_01` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `student_id` int(11) NOT NULL,
  `name` varchar(255) NOT NULL,
  `age` int(11) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8;

-- ----------------------------
-- Table structure for t_user_00
-- ----------------------------
DROP TABLE IF EXISTS `t_user_00`;
CREATE TABLE `t_user_00` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `user_id` int(11) NOT NULL,
  `name` varchar(255) NOT NULL,
  `age` int(11) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

-- ----------------------------
-- Table structure for t_user_01
-- ----------------------------
DROP TABLE IF EXISTS `t_user_01`;
CREATE TABLE `t_user_01` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `user_id` int(11) NOT NULL,
  `name` varchar(255) NOT NULL,
  `age` int(11) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8;

-- ----------------------------
-- Table structure for t_user_02
-- ----------------------------
DROP TABLE IF EXISTS `t_user_02`;
CREATE TABLE `t_user_02` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `user_id` int(11) NOT NULL,
  `name` varchar(255) NOT NULL,
  `age` int(11) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;

新建maven项目

Maven依赖的pom.xml

<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>org.study</groupId>  
	    <artifactId>sharding-jdbc</artifactId>  
	    <version>0.0.1-SNAPSHOT</version>  
	    <packaging>jar</packaging>  
	  
	    <name>sharding-jdbc</name>  
	    <url>http://maven.apache.org</url>  
	  
	    <properties>  
	        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>  
	        <spring.version>3.2.5.RELEASE</spring.version>  
	        <mybatis.version>3.2.4</mybatis.version>  
	    </properties>  
	  
	    <dependencies>  
	        <dependency>  
	            <groupId>junit</groupId>  
	            <artifactId>junit</artifactId>  
	            <version>4.10</version>  
	        </dependency>  
	        <dependency>  
	            <groupId>com.dangdang</groupId>  
	            <artifactId>sharding-jdbc-core</artifactId>  
	            <version>1.0.0</version>  
	        </dependency>  
	        <dependency>  
	            <groupId>org.springframework</groupId>  
	            <artifactId>spring-orm</artifactId>  
	            <version>${spring.version}</version>  
	        </dependency>  
	        <dependency>  
	            <groupId>commons-dbcp</groupId>  
	            <artifactId>commons-dbcp</artifactId>  
	            <version>1.4</version>  
	        </dependency>  
	        <dependency>  
	            <groupId>org.mybatis</groupId>  
	            <artifactId>mybatis-spring</artifactId>  
	            <version>1.2.2</version>  
	        </dependency>  
	        <dependency>  
	            <groupId>org.mybatis</groupId>  
	            <artifactId>mybatis</artifactId>  
	            <version>${mybatis.version}</version>  
	        </dependency>  
	  
	        <dependency>  
	            <groupId>org.springframework</groupId>  
	            <artifactId>spring-expression</artifactId>  
	            <version>${spring.version}</version>  
	        </dependency>  
	        <dependency>  
	            <groupId>org.springframework</groupId>  
	            <artifactId>spring-aop</artifactId>  
	            <version>${spring.version}</version>  
	        </dependency>  
	        <dependency>  
	            <groupId>org.springframework</groupId>  
	            <artifactId>spring-beans</artifactId>  
	            <version>${spring.version}</version>  
	        </dependency>  
	        <dependency>  
	            <groupId>org.springframework</groupId>  
	            <artifactId>spring-context</artifactId>  
	            <version>${spring.version}</version>  
	        </dependency>  
	        <dependency>  
	            <groupId>org.springframework</groupId>  
	            <artifactId>spring-context-support</artifactId>  
	            <version>${spring.version}</version>  
	        </dependency>  
	        <dependency>  
	            <groupId>org.springframework</groupId>  
	            <artifactId>spring-test</artifactId>  
	            <version>${spring.version}</version>  
	        </dependency>  
	        <dependency>  
	            <groupId>org.springframework</groupId>  
	            <artifactId>spring-tx</artifactId>  
	            <version>${spring.version}</version>  
	        </dependency>  
	        <dependency>  
	            <groupId>mysql</groupId>  
	            <artifactId>mysql-connector-java</artifactId>  
	            <version>5.1.28</version>  
	        </dependency>  
	        <dependency>  
	            <groupId>log4j</groupId>  
	            <artifactId>log4j</artifactId>  
	            <version>1.2.16</version>  
	        </dependency>  
	        <dependency>  
	            <groupId>org.slf4j</groupId>  
	            <artifactId>slf4j-log4j12</artifactId>  
	            <version>1.7.5</version>  
	        </dependency>  
	    </dependencies>  
	</project>  

UserSingleKeyTableShardingAlgorithm


package com.lyz.sharding.algorithm;  

import java.util.Collection;  
import java.util.LinkedHashSet;  

import com.dangdang.ddframe.rdb.sharding.api.ShardingValue;  
import com.dangdang.ddframe.rdb.sharding.api.strategy.table.SingleKeyTableShardingAlgorithm;  
import com.google.common.collect.Range;  
/** 
 * 因为t_student实际表在每个库中只有3个,所以 %3 
 * @author iuyazhuang
 * 
 */  
public class UserSingleKeyTableShardingAlgorithm implements SingleKeyTableShardingAlgorithm<Integer>{  

    /** 
     * sql 中 = 操作时,table的映射 
     */  
    public String doEqualSharding(Collection<String> tableNames, ShardingValue<Integer> shardingValue) {  
        for (String each : tableNames) {  
            if (each.endsWith(("0".concat(String.valueOf(shardingValue.getValue() % 3))))) {  
                return each;  
            }  
        }  
        throw new IllegalArgumentException();  
    }  

    /** 
     * sql 中 in 操作时,table的映射 
     */  
    public Collection<String> doInSharding(Collection<String> tableNames, ShardingValue<Integer> shardingValue) {  
        Collection<String> result = new LinkedHashSet<String>(tableNames.size());  
        for (Integer value : shardingValue.getValues()) {  
            for (String tableName : tableNames) {  
                if (tableName.endsWith(("0".concat(String.valueOf(value % 3))))) {  
                    result.add(tableName);  
                }  
            }  
        }  
        return result;  
    }  

    /** 
     * sql 中 between 操作时,table的映射 
     */  
    public Collection<String> doBetweenSharding(Collection<String> tableNames,  
            ShardingValue<Integer> shardingValue) {  
        Collection<String> result = new LinkedHashSet<String>(tableNames.size());  
        Range<Integer> range = (Range<Integer>) shardingValue.getValueRange();  
        for (Integer i = range.lowerEndpoint(); i <= range.upperEndpoint(); i++) {  
            for (String each : tableNames) {  
                if (each.endsWith(("0".concat(String.valueOf(i % 3))))) {  
                    result.add(each);  
                }  
            }  
        }  
        return result;  
    }  

}  

UserSingleKeyDatabaseShardingAlgorithm


package com.lyz.sharding.algorithm;  

import java.util.Collection;  
import java.util.LinkedHashSet;  

import com.dangdang.ddframe.rdb.sharding.api.ShardingValue;  
import com.dangdang.ddframe.rdb.sharding.api.strategy.database.SingleKeyDatabaseShardingAlgorithm;  
import com.google.common.collect.Range;  

/** 
 * user表分库的逻辑函数 
 * @author liuyazhuang
 * 
 */  
public class UserSingleKeyDatabaseShardingAlgorithm implements SingleKeyDatabaseShardingAlgorithm<Integer>{  

    /** 
     * sql 中关键字 匹配符为 =的时候,表的路由函数 
     */  
    public String doEqualSharding(Collection<String> availableTargetNames, ShardingValue<Integer> shardingValue) {  
        for (String each : availableTargetNames) {  
            if (each.endsWith(shardingValue.getValue() % 2 + "")) {  
                return each;  
            }  
        }  
        throw new IllegalArgumentException();  
    }  

    /** 
     * sql 中关键字 匹配符为 in 的时候,表的路由函数 
     */  
    public Collection<String> doInSharding(Collection<String> availableTargetNames, ShardingValue<Integer> shardingValue) {  
        Collection<String> result = new LinkedHashSet<String>(availableTargetNames.size());  
        for (Integer value : shardingValue.getValues()) {  
            for (String tableName : availableTargetNames) {  
                if (tableName.endsWith(value % 2 + "")) {  
                    result.add(tableName);  
                }  
            }  
        }  
        return result;  
    }  

    /** 
     * sql 中关键字 匹配符为 between的时候,表的路由函数 
     */  
    public Collection<String> doBetweenSharding(Collection<String> availableTargetNames,  
            ShardingValue<Integer> shardingValue) {  
        Collection<String> result = new LinkedHashSet<String>(availableTargetNames.size());  
        Range<Integer> range = (Range<Integer>) shardingValue.getValueRange();  
        for (Integer i = range.lowerEndpoint(); i <= range.upperEndpoint(); i++) {  
            for (String each : availableTargetNames) {  
                if (each.endsWith(i % 2 + "")) {  
                    result.add(each);  
                }  
            }  
        }  
        return result;  
    }  

}  

spring-database.xml配置

<?xml version="1.0" encoding="UTF-8"?>  
<beans xmlns="http://www.springframework.org/schema/beans"  
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"  
    xmlns:mybatis-spring="http://mybatis.org/schema/mybatis-spring"  
    xmlns:tx="http://www.springframework.org/schema/tx"  
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd  
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd  
        http://mybatis.org/schema/mybatis-spring http://mybatis.org/schema/mybatis-spring-1.2.xsd  
        http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd">  
          
    <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">  
        <property name="locations">  
            <list>  
                <value>classpath:config/resource/jdbc_dev.properties</value>  
            </list>  
        </property>  
    </bean>  
          
    <bean name="sharding_0" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">  
        <property name="url" value="${jdbc_url0}" />  
        <property name="username" value="${jdbc_username0}" />  
        <property name="password" value="${jdbc_password0}" />  
<!--         <property name="driverClass" value="${jdbc_driver0}" /> -->  
        <!-- 初始化连接大小 -->  
        <property name="initialSize" value="0" />  
        <!-- 连接池最大使用连接数量 -->  
        <property name="maxActive" value="20" />  
        <!-- 连接池最小空闲 -->  
        <property name="minIdle" value="0" />  
        <!-- 获取连接最大等待时间 -->  
        <property name="maxWait" value="60000" />  
        <property name="validationQuery" value="${validationQuery}" />  
        <property name="testOnBorrow" value="false" />  
        <property name="testOnReturn" value="false" />  
        <property name="testWhileIdle" value="true" />  
        <!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 -->  
        <property name="timeBetweenEvictionRunsMillis" value="60000" />  
        <!-- 配置一个连接在池中最小生存的时间,单位是毫秒 -->  
        <property name="minEvictableIdleTimeMillis" value="25200000" />  
        <!-- 打开removeAbandoned功能 -->  
        <property name="removeAbandoned" value="true" />  
        <!-- 1800秒,也就是30分钟 -->  
        <property name="removeAbandonedTimeout" value="1800" />  
        <!-- 关闭abanded连接时输出错误日志 -->  
        <property name="logAbandoned" value="true" />  
        <property name="filters" value="stat" />  
    </bean>  
      
    <bean name="sharding_1" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">  
        <property name="url" value="${jdbc_url1}" />  
        <property name="username" value="${jdbc_username1}" />  
        <property name="password" value="${jdbc_password1}" />  
<!--         <property name="driverClass" value="${jdbc_driver1}" /> -->  
        <!-- 初始化连接大小 -->  
        <property name="initialSize" value="0" />  
        <!-- 连接池最大使用连接数量 -->  
        <property name="maxActive" value="20" />  
        <!-- 连接池最小空闲 -->  
        <property name="minIdle" value="0" />  
        <!-- 获取连接最大等待时间 -->  
        <property name="maxWait" value="60000" />  
        <property name="validationQuery" value="${validationQuery}" />  
        <property name="testOnBorrow" value="false" />  
        <property name="testOnReturn" value="false" />  
        <property name="testWhileIdle" value="true" />  
        <!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 -->  
        <property name="timeBetweenEvictionRunsMillis" value="60000" />  
        <!-- 配置一个连接在池中最小生存的时间,单位是毫秒 -->  
        <property name="minEvictableIdleTimeMillis" value="25200000" />  
        <!-- 打开removeAbandoned功能 -->  
        <property name="removeAbandoned" value="true" />  
        <!-- 1800秒,也就是30分钟 -->  
        <property name="removeAbandonedTimeout" value="1800" />  
        <!-- 关闭abanded连接时输出错误日志 -->  
        <property name="logAbandoned" value="true" />  
        <property name="filters" value="stat" />  
    </bean>  
      
  
</beans>  

spring-sharding.xml

<?xml version="1.0" encoding="UTF-8"?>  
<beans xmlns="http://www.springframework.org/schema/beans"  
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"  
    xmlns:mybatis-spring="http://mybatis.org/schema/mybatis-spring"  
    xmlns:tx="http://www.springframework.org/schema/tx"  
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd  
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd  
        http://mybatis.org/schema/mybatis-spring http://mybatis.org/schema/mybatis-spring-1.2.xsd  
        http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd">  
          
    <context:component-scan base-package="com.lyz.sharding" /> 
     
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">  
        <property name="basePackage" value="com.lyz.sharding.mapper"/>  
        <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>  
    </bean>  
      
    <!-- 配置sqlSessionFactory -->  
     <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">  
        <property name="dataSource" ref="shardingDataSource"/>  
        <property name="mapperLocations" value="classpath*:config/mapper/*Mapper.xml"/>  
    </bean>  
      
      
    <!-- 配置好dataSourceRulue,即对数据源进行管理 -->  
    <bean id="dataSourceRule" class="com.dangdang.ddframe.rdb.sharding.api.rule.DataSourceRule">  
        <constructor-arg>  
            <map>  
                <entry key="sharding_0" value-ref="sharding_0"/>  
                <entry key="sharding_1" value-ref="sharding_1"/>  
            </map>  
        </constructor-arg>  
    </bean>  
      
    <!-- 对t_user表的配置,进行分库配置,逻辑表名为t_user,每个库有实际的三张表 -->  
    <bean id="userTableRule" class="com.dangdang.ddframe.rdb.sharding.api.rule.TableRule">  
        <constructor-arg value="t_user" index="0"/>  
        <constructor-arg index="1">  
            <list>  
                <value>t_user_00</value>  
                <value>t_user_01</value>  
                <value>t_user_02</value>  
            </list>  
        </constructor-arg>  
        <constructor-arg index="2" ref="dataSourceRule"/>  
        <constructor-arg index="3" ref="userDatabaseShardingStrategy"/>  
        <constructor-arg index="4" ref="userTableShardingStrategy"/>  
    </bean>  
      
    <!-- t_user分库策略 -->  
    <bean id="userDatabaseShardingStrategy" class="com.dangdang.ddframe.rdb.sharding.api.strategy.database.DatabaseShardingStrategy">  
        <constructor-arg index="0" value="user_id"/>  
        <constructor-arg index="1">  
            <bean class="com.lyz.sharding.algorithm.UserSingleKeyDatabaseShardingAlgorithm" />  
        </constructor-arg>  
    </bean>  
      
    <!-- t_user 分表策略 -->  
    <bean id="userTableShardingStrategy" class="com.dangdang.ddframe.rdb.sharding.api.strategy.table.TableShardingStrategy">  
        <constructor-arg index="0" value="user_id"/>  
        <constructor-arg index="1">  
            <bean class="com.lyz.sharding.algorithm.UserSingleKeyTableShardingAlgorithm" />  
        </constructor-arg>  
    </bean>  
      
      
      
      <!-- 对t_student表的配置,进行分库配置,逻辑表名为t_student,每个库有实际的三张表 -->  
    <bean id="studentTableRule" class="com.dangdang.ddframe.rdb.sharding.api.rule.TableRule">  
        <constructor-arg value="t_student" index="0"/>  
        <constructor-arg index="1">  
            <list>  
                <value>t_student_00</value>  
                <value>t_student_01</value>  
            </list>  
        </constructor-arg>  
        <constructor-arg index="2" ref="dataSourceRule"/>  
        <constructor-arg index="3" ref="studentDatabaseShardingStrategy"/>  
        <constructor-arg index="4" ref="studentTableShardingStrategy"/>  
    </bean>  
      
     <!-- t_student分库策略 -->  
    <bean id="studentDatabaseShardingStrategy" class="com.dangdang.ddframe.rdb.sharding.api.strategy.database.DatabaseShardingStrategy">  
        <constructor-arg index="0" value="student_id"/>  
        <constructor-arg index="1">  
            <bean class="com.lyz.sharding.algorithm.StudentSingleKeyDatabaseShardingAlgorithm" />  
        </constructor-arg>  
    </bean>  
      
    <!-- t_student 分表策略 -->  
    <bean id="studentTableShardingStrategy" class="com.dangdang.ddframe.rdb.sharding.api.strategy.table.TableShardingStrategy">  
        <constructor-arg index="0" value="student_id"/>  
        <constructor-arg index="1">  
            <bean class="com.lyz.sharding.algorithm.StudentSingleKeyTableShardingAlgorithm" />  
        </constructor-arg>  
    </bean>  
      
      
    <!-- 构成分库分表的规则 传入数据源集合和每个表的分库分表的具体规则 -->  
    <bean id="shardingRule" class="com.dangdang.ddframe.rdb.sharding.api.rule.ShardingRule">  
        <constructor-arg index="0" ref="dataSourceRule"/>  
        <constructor-arg index="1">  
            <list>  
                <ref bean="userTableRule"/>  
                <ref bean="studentTableRule"/>  
            </list>  
        </constructor-arg>  
    </bean>  
      
    <!-- 对datasource进行封装 -->  
    <bean id="shardingDataSource" class="com.dangdang.ddframe.rdb.sharding.api.ShardingDataSource">  
        <constructor-arg ref="shardingRule"/>  
    </bean>  
  
    <!-- 事务 -->  
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">  
        <property name="dataSource" ref="shardingDataSource" />  
    </bean>  
  
    <tx:annotation-driven transaction-manager="transactionManager" />  
          
</beans>  

ShardingJdbcMybatisTest.java


package com.lyz.sharding.test;  

import java.util.Arrays;
import java.util.List;

import javax.annotation.Resource;

import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import com.lyz.sharding.entity.Student;
import com.lyz.sharding.entity.User;
import com.lyz.sharding.service.StudentService;
import com.lyz.sharding.service.UserService;

/**
 * 测试分库分表规则
 * @author liuyazhuang
 *
 */
@RunWith(SpringJUnit4ClassRunner.class)  
@ContextConfiguration(locations = { "classpath*:config/spring/spring-database.xml", "classpath*:config/spring/spring-sharding.xml" })  
public class ShardingJdbcMybatisTest {  

    @Resource  
    public UserService userService;  
      
    @Resource  
    public StudentService studentService;  

    @Test  
    public void testUserInsert() {  
        User u = new User();  
        u.setUserId(11);  
        u.setAge(25);  
        u.setName("github");  
        Assert.assertEquals(userService.insert(u), true);  
    }  
      
    @Test  
    public void testStudentInsert() {  
        Student student = new Student();  
        student.setStudentId(21);  
        student.setAge(21);  
        student.setName("hehe");  
        Assert.assertEquals(studentService.insert(student), true);  
    }  

    @Test  
    public void testFindAll(){  
        List<User> users = userService.findAll();  
        if(null != users && !users.isEmpty()){  
            for(User u :users){  
                System.out.println(u);  
            }  
        }  
    }  
      
    @Test  
    public void testSQLIN(){  
        List<User> users = userService.findByUserIds(Arrays.asList(13,11));  
        if(null != users && !users.isEmpty()){  
            for(User u :users){  
                System.out.println(u);  
            }  
        }  
    }  
      
    @Test  
    public void testTransactionTestSucess(){  
        userService.transactionTestSucess();  
    }  
      
    @Test(expected = IllegalAccessException.class)  
    public void testTransactionTestFailure() throws IllegalAccessException{  
        userService.transactionTestFailure();  
    }  
}  

猜你喜欢

转载自blog.csdn.net/scgyus/article/details/84975328