Java 分库分表——Mango 初体验

  • Java 分库分表——Mango 初体验
  • 一、Mango 简介
  • 二、SpringBoot 集成 Mango
  • 2.1 获取 Mango
  • 2.2 配置 xml
  • 2.3 分库、分表规则配置
  • 2.4 Dao 层配置
  • 2.5 测试
  • 三、总结


Java 分库分表——Mango 初体验

众所周知,随着系统的使用,数据库数据量不断上升,当单表数据量达到1000W以后,很多数据库操作都会比较吃力,这个时候数据库层面的分库、分表就显得迫在眉睫了。接下来,介绍一种 ORM 框架 Mango。

一、Mango 简介

首先,我们来看看 mango 官网关于 mango 的简介:

mango的中文名是“芒果”,它是一个极速分布式ORM框架。目前已有十多个大型线上项目在使用mango,在某一支付系统中,更是利用mango,承载了每秒12万的支付下单请求。

下面是mango的一些特性:

  • 超高性能,响应速度接近直接使用JDBC
  • 采用接口与注解的形式定义DAO,完美结合db与cache操作
  • 支持动态sql,可以构造任意复杂的sql语句
  • 支持多数据源,分表,分库,事务
  • 提供拦截器功能,利用拦截器可为mango框架扩展各种自定义插件
  • 独创“函数式调用”功能,能将任意复杂的对象,映射到数据库的表中
  • 高效详细的实时统计系统,方便开发者随时了解自己的系统
  • 独立jar包,不依赖其它jar包
  • 提供便捷的spring插件,与spring无缝集成

    乐视集团的订单系统便是Mango搭建,具体参见:乐视秒杀架构解读:从零开始搭建百万每秒订单系统

二、SpringBoot 集成 Mango

SpringBoot 近年大热的微服务架构的重要一员,相信各位看官早已经使用过,如果你还没有入门或者刚刚入门,建议可以看看翟永超大大的SpringBoot系列教程,相信你回受益良多。

2.1 获取 Mango

由于mango不依赖其它jar包,所以可以直接 下载mango-1.6.0.jar ,并将它放在工程的classpath下。

当然mango也已经上传到 maven中心库 中,如果您的工程在使用maven,那么只需要在pom.xml文件中添加下面的依赖就能使用mango的功能。

<dependency>
    <groupId>org.jfaster</groupId>
    <artifactId>mango</artifactId>
    <version>1.6.0</version>
</dependency>

需要注意的是,只使用mango是无法连接数据库成功的,对于连接不同的数据库,您还需要添加相应的JDBC驱动,以连接MySQL数据库为例,您还需要用到 mysql-connector-java

<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.29</version>
</dependency>
2.2 配置 xml

这里直接放上我的 xml 文件,相信各位看管都看得懂是怎么配置的。

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN 2.0//EN" "http://www.springframework.org/dtd/spring-beans-2.0.dtd">

<beans>
    <!-- 配置简单数据源工厂 -->
    <bean id="dataSourceFactory" class="org.jfaster.mango.datasource.SimpleDataSourceFactory">
        <property name="name" value="dsf0" />
        <property name="dataSource">
            <bean class="org.jfaster.mango.datasource.DriverManagerDataSource">
                <property name="driverClassName" value="com.mysql.jdbc.Driver" />
                <property name="url" value="jdbc:mysql://localhost:3306/mango_example_0" />
                <property name="username" value="root" />
                <property name="password" value="TW123" />
            </bean>
        </property>
    </bean>

    <!-- 配置简单数据源工厂 -->
    <bean id="dataSourceFactory1" class="org.jfaster.mango.datasource.SimpleDataSourceFactory">
        <property name="name" value="dsf1" />
        <property name="dataSource">
            <bean class="org.jfaster.mango.datasource.DriverManagerDataSource">
                <property name="driverClassName" value="com.mysql.jdbc.Driver" />
                <property name="url" value="jdbc:mysql://localhost:3306/mango_example_1" />
                <property name="username" value="root" />
                <property name="password" value="TW123" />
            </bean>
        </property>
    </bean>

    <!-- 配置mango对象 -->
    <bean id="mango" class="org.jfaster.mango.operator.Mango" factory-method="newInstance">
        <property name="dataSourceFactories">
            <list>
                <ref bean="dataSourceFactory" />
                <ref bean="dataSourceFactory1" />
            </list>
        </property>
    </bean>

    <!-- 配置扫描使用@DB注解修饰的DAO类 -->
    <bean class="org.jfaster.mango.plugin.spring.MangoDaoScanner">
        <property name="packages">
            <list>
                <!-- 扫描包名 -->
              	<!-- 这里要注意,包下的类名要以 DAO 或者 Dao 结尾,否则无法被扫描到-->
                <value>com.zhou.mango.dao</value> 
                <!-- <value>其他需要扫描的包</value> -->
            </list>
        </property>
    </bean>
</beans>
2.3 分库、分表规则配置

分库: 需要实现 DatabaseShardingStrategy

public class OrderDatabaseShardingStrategy implements DatabaseShardingStrategy<Integer> {
    @Override
    public String getDataSourceFactoryName(Integer uid) {
      // 根据 uid 计算使用的数据库,返回的也就是 2.2 里 xml 配置的  dataSourceFactory name
      return "dsf" + uid % 2;
    }
}

public class OrderDatabaseShardingStrategy implements DatabaseShardingStrategy<Integer> {
    @Override
    public String getDataSourceFactoryName(Integer uid) {
      // 根据 uid 计算使用的数据库,返回的也就是 2.2 里 xml 配置的  dataSourceFactory name
      return "dsf" + uid % 2;
    }
}

分表: 需要实现 TableShardingStrategy

public class OrderTableShardingStrategy implements TableShardingStrategy<Integer> {
    @Override
    public String getTargetTable(String table, Integer uid) {
      	// 返回表名
        int num = uid <= 1000 ? 1 : 2;
        return table + "_" + num;
    }
}

public class OrderTableShardingStrategy implements TableShardingStrategy<Integer> {
    @Override
    public String getTargetTable(String table, Integer uid) {
      	// 返回表名
        int num = uid <= 1000 ? 1 : 2;
        return table + "_" + num;
    }
}
2.4 Dao 层配置

废话不多说,相信大家都看得懂

@DB(table = "t_order")
@Sharding(
        databaseShardingStrategy = OrderDatabaseShardingStrategy.class,	// 分库规则
        tableShardingStrategy = OrderTableShardingStrategy.class	//分表规则
)
public interface ShardingOrderDao {
    @SQL("insert into #table(id, uid, price, status) values(:id, :uid, :price, :status)")
    public void addOrder(@DatabaseShardingBy("uid") @TableShardingBy("uid") Order order);

    @SQL("select id, uid, price, status from #table where uid = :1")
    public List<Order> getOrdersByUid(@DatabaseShardingBy @TableShardingBy int uid);
}

@DB(table = "t_order")
@Sharding(
        databaseShardingStrategy = OrderDatabaseShardingStrategy.class,	// 分库规则
        tableShardingStrategy = OrderTableShardingStrategy.class	//分表规则
)
public interface ShardingOrderDao {
    @SQL("insert into #table(id, uid, price, status) values(:id, :uid, :price, :status)")
    public void addOrder(@DatabaseShardingBy("uid") @TableShardingBy("uid") Order order);

    @SQL("select id, uid, price, status from #table where uid = :1")
    public List<Order> getOrdersByUid(@DatabaseShardingBy @TableShardingBy int uid);
}
2.5 测试
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = Application.class)
public class ShardingTest {
    @Autowired
    private OrderService orderService;


    @Test
    public void test1() {
        List<Integer> uids = Lists.newArrayList(111, 221, 323, 30014, 30015, 30016);
        for (Integer uid : uids) {
            String id = RandomUtils.randomStringId(10); // 随机生成10位字符串ID
            Order order = new Order();
            order.setId(id);
            order.setUid(uid);
            order.setPrice(100);
            order.setStatus(1);

            orderService.addOrder(order);
            System.out.println(orderService.getOrdersByUid(uid));
        }
    } 
}

@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = Application.class)
public class ShardingTest {
    @Autowired
    private OrderService orderService;


    @Test
    public void test1() {
        List<Integer> uids = Lists.newArrayList(111, 221, 323, 30014, 30015, 30016);
        for (Integer uid : uids) {
            String id = RandomUtils.randomStringId(10); // 随机生成10位字符串ID
            Order order = new Order();
            order.setId(id);
            order.setUid(uid);
            order.setPrice(100);
            order.setStatus(1);

            orderService.addOrder(order);
            System.out.println(orderService.getOrdersByUid(uid));
        }
    } 
}

三、总结

通过观察数据库、控制台我们可以发现,数据已经按照我们配置的规则存入了对应的数据库以及表中,亲自动手试试吧。

示例已上传 码云