`
76756154
  • 浏览: 34930 次
  • 性别: Icon_minigender_1
  • 来自: 深圳
文章分类
社区版块
存档分类
最新评论

Mybatis分库分表扩展插件

    博客分类:
  • Java
 
阅读更多

 

       对于一个刚上线的互联网项目来说,由于前期活跃用户数量并不多,并发量也相对较小,所以此时企业一般都会选择将所有数据存放在一个数据库中进行访问操作。但随着后续的市场推广力度不断加强,用户数量和并发量不断上升,这时如果仅靠一个数据库来支撑所有访问压力,几乎是在自寻死路。所以一旦到了这个阶段,大部分Mysql DBA就会将数据库设置成读写分离状态,也就是一个Master节点对应多个Salve节点。经过Master/Salve模式的设计后,完全可以应付单一数据库无法承受的负载压力,并将访问操作分摊至多个Salve节点上,实现真正意义上的读写分离。但大家有没有想过,单一的Master/Salve模式又能抗得了多久呢?如果用户数量和并发量出现量级上升,单一的Master/Salve模式照样抗不了多久,毕竟一个Master节点的负载还是相对比较高的。为了解决这个难题,Mysql DBA会在单一的Master/Salve模式的基础之上进行数据库的垂直分区(分库)。所谓垂直分区指的是可以根据业务自身的不同,将原本冗余在一个数据库内的业务表拆散,将数据分别存储在不同的数据库中,同时仍然保持Master/Salve模式。经过垂直分区后的Master/Salve模式完全可以承受住难以想象的高并发访问操作,但是否可以永远高枕无忧了?答案是否定的,一旦业务表中的数据量大了,从维护和性能角度来看,无论是任何的CRUD操作,对于数据库而言都是一件极其耗费资源的事情。即便设置了索引,仍然无法掩盖因为数据量过大从而导致的数据库性能下降的事实,因此这个时候Mysql DBA或许就该对数据库进行水平分区(分表,sharding),所谓水平分区指的是将一个业务表拆分成多个子表,比如user_table0、user_table1、user_table2。子表之间通过某种契约关联在一起,每一张子表均按段位进行数据存储,比如user_table0存储1-10000的数据,而user_table1存储10001-20000的数据,最后user_table3存储20001-30000的数据。经过水平分区设置后的业务表,必然能够将原本一张表维护的海量数据分配给N个子表进行存储和维护,这样的设计在国内一流的互联网企业比较常见,如图所示:

 

水平分区

 

       以上是数据库分库分表的原理,但是如果每次在数据访问层的设计中实现分库分表,将会显得非常麻烦。笔者参考了了下code google shardbatis的设计方式,发现googlecode上的shardbatis无法做到分库,只能做到分表,而且配置相当不灵活,源码很难找到。所以笔者重新设计了一套mybatis插件,相比google code的那套,该框架可以做到完整的分库分表和友好的配置。组件结构如图所示:

组件结构图

 

使用方法:

1.准备数据库集群配置文件

 

 

<?xml version="1.0" encoding="UTF-8"?>
<databases xmlns="http://fangjialong.iteye/schema/snsprod"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.vivo.com.cn/schema/snsprod
    http://fangjialong.iteye/schema/snsprod
	http://fangjialong.iteye/schema/snsprod/shardbatis-db.xsd">
	<!-- 全局配置 -->
	<logicName value="test"/>
	<configs>
		<property name="minPoolSize" value="4" />
		<property name="minPoolSize" value="8" />
		<property name="driverClass" value="com.mysql.jdbc.Driver" />
		<property name="maxIdleTime" value="900" />
		<property name="idleConnectionTestPeriod" value="1800" />
	</configs>
	<database suffix="_00" username="root" password=""
		jdbcUrl="jdbc:mysql://localhost:3306/test_00?useUnicode=true&amp;characterEncoding=UTF-8&amp;zeroDateTimeBehavior=convertToNull">
		<property name="minPoolSize" value="4" />
		<property name="minPoolSize" value="8" />
		<property name="driverClass" value="com.mysql.jdbc.Driver" />
		<property name="maxIdleTime" value="900" />
		<property name="idleConnectionTestPeriod" value="1800" />
	</database>
	<database suffix="_01" username="root" password=""
		jdbcUrl="jdbc:mysql://localhost:3306/test_01?useUnicode=true&amp;characterEncoding=UTF-8&amp;zeroDateTimeBehavior=convertToNull" />
</databases>

 

 

2.准备分库分表规则配置

 

<configs xmlns="http://fangjialong.iteye.com/schema/snsprod"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://fangjialong.iteye.com/schema/snsprod
    http://fangjialong.iteye.com/schema/snsprod
	http://fangjialong.iteye.com/schema/snsprod/shardbatis-config.xsd">
	
	<strategy logicTable="t_student" class="com.cannon.prod.dal.strategy.StudentShardStrategy"/>
</configs>

 

3.为mybatis配置扩展插件

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
	<typeAliases>
		<typeAlias alias="Student" type="com.cannon.prod.dal.model.Student" />
	</typeAliases>
	<plugins>
		<plugin interceptor="com.cannon.prod.dal.shardbatis.ShardPlugin">
			<property name="configsLocation" value="META-INF/mybatis/shardbatis-config.xml" />
		</plugin>
	</plugins>
	<mappers>
		<mapper resource="META-INF/mybatis/mapper/mybatis-mapper-student.xml" />
	</mappers>
</configuration>

 

4.为mybatis配置分库分表数据源

<?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"
	xsi:schemaLocation="http://www.springframework.org/schema/beans 
	http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
	http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context-3.2.xsd">

	<!-- 配置数据源 -->
	<bean id="distributeDefaultDataSource" class="com.cannon.prod.dal.shardbatis.ShardDataSource"
		init-method="init" destroy-method="destroy">
		<property name="configsLocation" value="META-INF/database/distributed-default-db.xml"></property>
	</bean>

	<bean id="distributeDefaultTransactionManager"
		class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
		<property name="dataSource" ref="distributeDefaultDataSource" />
	</bean>

	<bean id="distributeDefaultTransactionTemplate"
		class="org.springframework.transaction.support.TransactionTemplate">
		<property name="transactionManager" ref="distributeDefaultTransactionManager" />
	</bean>

	<bean id="distributeDefaultSqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
		<property name="configLocation">
			<bean class="org.springframework.core.io.ClassPathResource">
				<constructor-arg index="0"
					value="META-INF/mybatis/mybatis-config.xml" />
			</bean>
		</property>
		<property name="dataSource" ref="distributeDefaultDataSource" />
	</bean>

</beans>

 

5.使用逻辑表配置mybatis mapper

<?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.cannon.prod.dal.mapper.Student">
	<resultMap type="Student" id="StudentResultMap">
		<id property="no" column="no" />
		<result property="name" column="name" />
		<result property="sex" column="sex" />
	</resultMap>
	<!-- 查询学生,根据id -->
	<select id="getByNo" parameterType="String" resultType="Student"
		resultMap="StudentResultMap">  
        <![CDATA[ 
            SELECT * FROM `t_student` WHERE `no` = #{no}  
        ]]>
	</select>
	<delete id="deleteByNo" parameterType="String">
		<![CDATA[ 
            DELETE FROM `t_student` WHERE `no` = #{no}  
        ]]>
	</delete>
	<insert id="create" parameterType="Student">
		INSERT INTO t_student(no, name, sex) VALUES(#{no}, #{name}, #{sex})
	</insert>
	<update id="update" parameterType="Student">
		UPDATE t_student
		<trim prefix="SET" suffixOverrides=",">
			<if test="name != null">name=#{name},</if>
		</trim>
		WHERE no=#{no}
	</update>
</mapper>

 

6.编写分库分表规则

public class StudentShardStrategy implements ShardStrategy {
	@Override
	public ShardCondition parse(Map<String, Object> params) {
		String no = (String) params.get("no");
		char c = no.charAt(0);
		ShardCondition condition = new ShardCondition();
		if (c == '1') {
			condition.setDatabaseSuffix("_01");
			condition.setTableSuffix("_01");
		} else {
			condition.setDatabaseSuffix("_00");
			condition.setTableSuffix("_00");
		}
		return condition;
	}
}

 

 

全部配置好了之后就可以按照以前使用mybatis的方式将数据库集群当做一个逻辑表来操作了。剩余的转换操作就交给中间扩展层插件来转换吧。

以下是转换结果:

Shard Original SQL:SELECT * FROM `t_student` WHERE `no` = ?

 

Shard Convert SQL:SELECT * FROM `test_00`.`t_student_00` WHERE `no` = ?

 

Shard Original SQL:DELETE FROM `t_student` WHERE `no` = ?

 

Shard Convert SQL:DELETE FROM `test_00`.`t_student_00` WHERE `no` = ?

 

Shard Original SQL:INSERT INTO t_student(no, name, sex) VALUES(?, ?, ?)

 

Shard Convert SQL:INSERT INTO test_00.t_student_00 (no, name, sex) VALUES (?, ?, ?)

 

Shard Original SQL:UPDATE t_student SET name=?  WHERE no=?

 

Shard Convert SQL:UPDATE test_00.t_student_00 SET name = ? WHERE no = ?

 

注意:分库分表从原则上是不支持跨库事物的,如果需要使用事务必须保证在多个表在同一个库中。

以下是事物支持的测试用例:

 

/**
 * @author fangjialong
 * @date 2015年9月5日 下午4:27:50
 */
public class StudentDAOTransactionTest {
	private DalSpringContext context;
	private StudentDAO studentDAO;
	private TransactionTemplate tt;
	@Test
	public void test() {
		LogFactory.useNoLogging();
		this.context = new DalSpringContext();
		this.context.refresh();
		this.studentDAO = context.getBean(StudentDAO.class);
		this.tt = context.getBean("distributeDefaultTransactionTemplate", TransactionTemplate.class);
		tt.execute(new TransactionCallbackWithoutResult() {
			@Override
			protected void doInTransactionWithoutResult(TransactionStatus status) {
				Student s = new Student();
				s.setNo("0001");
				s.setName("房佳龙");
				studentDAO.update(s);
				status.setRollbackOnly();
			}
		});
		context.close();
	}
}

 

 change log:

2015-09-08:1、增加指定库表查询方案,2、增加分库分表Sequence创建方案

 

  • 大小: 99 KB
  • 大小: 19.3 KB
分享到:
评论
8 楼 longqian8866 2017-04-12  
你好, 2015-09-08的shardDataSource类实现Datasource类报错。
7 楼 pyzheng 2016-05-12  
分库不支持事务, 不考虑用atomikos来解决么  这玩意可以解决分库事务吧
6 楼 kyosing 2016-03-29  
有测试过了吗?性能如何?
5 楼 mingkongeye 2015-12-24  
你在并发下测试过吗?我并发测了下,循环1000次往表里insert,有问题。
4 楼 mingkongeye 2015-12-24  
非常感谢分享。
3 楼 lujicong 2015-12-09  
SqlParser解析mysql函数是报错,例如:IF (X is NULL,0,1),该如何解决,谢谢!
2 楼 inotgaoshou 2015-11-24  
可以考虑放到github或oschina之类
1 楼 samm 2015-09-11  

相关推荐

    转:Mybatis分库分表扩展插件

    总的来说,Mybatis分库分表扩展插件是应对大数据量挑战的有效工具,它通过智能化的数据路由和管理机制,实现了数据库的水平扩展,提高了系统的可扩展性和性能。然而,使用分库分表插件也需要注意数据一致性、事务...

    sharding + mybatis-plus 分库分表

    "Sharding + Mybatis-Plus 分库分表"的主题就是针对这个问题提出的解决方案。Sharding-JDBC是一个轻量级的Java框架,它可以在不改变任何数据库语义和业务代码的情况下,实现数据库的水平拆分,从而提高系统的并行...

    应对sharding-jdbc结合mybatis实现分库分表功能 分表的联合查询采用将mysql的数据同步到elasticsearch进行筛选

    标题中的“应对sharding-jdbc结合mybatis实现分库分表功能”涉及到以下几个核心概念: 1. **Sharding-JDBC**:Sharding-JDBC提供了一种透明化的数据库分片解决方案,它作为一个JDBC的驱动,使得应用程序无须修改...

    sharding-jdbc-4.0.0-rc1 整合spring boot 2+mybatis plus 分库分表Demo

    在本项目中,我们主要探讨如何使用`sharding-jdbc-4.0.0-rc1`这个版本的ShardingSphere来整合`Spring Boot 2`框架和`MyBatis Plus`,实现数据库的分库分表功能。下面将详细阐述这一过程中的关键知识点。 1. **...

    spring+mybatis下分表插件shardbatis

    而Shardbatis则是一个专门针对Spring和MyBatis设计的分库分表插件,用于简化数据库分片的实现过程。 首先,我们需要理解Spring的核心概念。Spring是一个开源的Java平台,它提供了全面的软件基础设施服务,用于构建...

    基于mybatis插件实现轻量级分库分表方案-亿级数据mysql存储解决方案-mybatis-sharding.zip

    MyBatis-Sharding 是 MyBatis 的扩展插件,它提供了对数据库分片的功能,支持水平分库分表,旨在简化数据库分片的复杂性。通过对 SQL 进行拦截、解析和改写,实现在不改变原有业务代码的情况下,实现数据的分布式...

    分库分表demo项目.zip

    本项目"分库分表demo项目.zip"就是一个使用SpringBoot、ShardingSphere、Mybatis-Plus以及MySQL进行集成演示的例子,旨在帮助开发者理解并掌握这一关键的数据库扩展策略。 **1. SpringBoot框架** SpringBoot是基于...

    基于mybatis框架,数据库垂直、水平拆分及读写分离实现

    在MyBatis中,可以自定义插件实现分片逻辑,例如根据用户的ID模某个数进行分片,确保同一用户的数据始终在同一个分片上。 **四、Spring整合MyBatis的SqlSessionTemplate** Spring的SqlSessionTemplate是MyBatis与...

    sharding-sphere 分库分表 (springboot + mybat-sharding-sphere.zip

    3. **MyBatis集成**:MyBatis-Sharding-Sphere作为MyBatis的插件,无缝集成到MyBatis的SQL执行环境中,无需修改原有SQL语句,即可实现分库分表的效果。 **压缩包子文件的文件名称列表分析** 由于给出的文件名称是...

    Apache ShardingSphere 中文文档 分库分表

    ShardingSphere 的优势在于它的灵活性和可扩展性,可以适应不同的数据库环境,同时通过插件机制,用户可以根据需要添加或定制功能。其路线规划涵盖了更多的数据库适配、优化性能、增强生态等方面,以满足日益复杂的...

    shardbatis-2.0.0B.jar.zip

    Shardbatis是一款针对Mybatis框架的扩展插件,它的主要功能是实现数据库的水平切分,从而有效地解决大数据量下的性能问题。在大型系统中,随着数据量的不断增长,单个数据库可能无法承载所有的数据,这时就需要进行...

    shardbatis-2.0.0B

    在这个版本中,Shardbatis引入了更先进的路由算法和更友好的配置方式,使得开发者能够更加轻松地将Mybatis与分库分表相结合。 1. **分库分表原理**: 分库分表是将一个大的数据库拆分为多个小型数据库,将一张大表...

    springboot + mybatis(通用mapper) + druid多数据源

    这在处理分布式系统、读写分离、分库分表等场景下非常有用。 在`demo-boot-multdata`这个项目中,开发者可能创建了两个或多个数据源,每个数据源对应不同的数据库实例。同时,他们使用通用Mapper来简化MyBatis的SQL...

    sharding-jdbc-master.zip 附完整代码可供参考

    【描述】中提到的“mybatis的sharding分库分表处理”是指在数据量庞大,单表操作性能下降时,通过Sharding-JDBC插件来实现数据库的水平扩展,将单一的大表拆分成多个小表,从而提高数据处理效率。MyBatis作为常用的...

    SpringBoot+gradle+mybatis多数据源动态配置

    本项目结合了SpringBoot、Gradle和MyBatis,旨在实现多数据源的动态配置,这对于处理多个数据库或者分库分表的场景尤其有用。以下是对这个项目的详细解析: **SpringBoot** SpringBoot是由Pivotal团队提供的全新...

    mybatis generator

    - 在大型项目中,考虑分库分表的情况,MBG配置可以按数据库或模块进行细分。 - 将MBG集成到持续集成流程中,每次数据库变更后自动更新代码,确保代码与数据库同步。 总之,MyBatis Generator是MyBatis框架下的一...

Global site tag (gtag.js) - Google Analytics