m-shard是本人近日完成一个基于mybatis、spring的分表分库插件,以下将对该插件做详细说明。
MShard是什么
MShard是一个定位于orm层shard方案,支持分表分库(跨库分表)、读写分离,支持分布式事务,它是一个基于于mybatis3.x的扩展,依赖于spring。
功能要点
1、分库分表(跨库)
2、读取分享
3、事务控制
基于spring管理事务,处理方式:将一个大的事务拆成多个小的事务
4、主键策略
mysql 利用第三方表、函数来完成,因为我们可能会在同一个mysql实例中部署多个库,而increment_offset、increment_increment是全局性的,所以mysql不能用自带的auto_increment方案;
相对而言,oracle就好办了,为每个库、每张库定义一个sequence,保证increment一致,错开offset即可
基础流程
MShard 被设计成一个 mybatis 的插件,重写了 MethodProxy、Environment、SqlSessionFactory等类,总体的sql执行步骤如下:
1、根据mapper xml生成sql
由于自定义了 methodProxy 和 MapperFactoryBean,所以我们可以在不依赖于sqlSession的情况下根据mapper.xml生成待执行的sql语句
2、sql 解析
利用 mybatis 自身的 api 获取分表字段的值
3、sql 路由
根据分表字段的值取模,找到对应的数据源
4、到指定的数据源执行sql
此时才根据数据源生成sqlSession,然后执行相应的语句
使用说明
1、shard-config.xml
该配置文件位于 classpath 根路径,用于配置数据源信息、分库的表及字段信息、数据源的读写分离信息。
a、数据源配置如下,目前只直接mysql,name为唯一标识符
b、以下是数据结点配置,用于将数据源分组,并区分哪些源用于读,哪些源用于写
(注意:每个节点对应逻辑上的一个数据库;如果某结点物理部署时采用多主热备,即多master,则在writeNodes将master一一配上即可;main=true代表该结点为主库,因为不是所有的表都需要分库处理;readNodes中的数据源将不会用写操作,同一事务中如有先写后读取的操作,那么该操作会在同一个writeNode上执行,从而保证一致性)
c、最后就是分库的表、字段配置了,注意:字段类型必须是数值型,且不能为空
2、建立数据库表、函数
本文以3个节点为例,每个节点有1个读库,2个写库。
MShard是什么
MShard是一个定位于orm层shard方案,支持分表分库(跨库分表)、读写分离,支持分布式事务,它是一个基于于mybatis3.x的扩展,依赖于spring。
功能要点
1、分库分表(跨库)
2、读取分享
3、事务控制
基于spring管理事务,处理方式:将一个大的事务拆成多个小的事务
4、主键策略
mysql 利用第三方表、函数来完成,因为我们可能会在同一个mysql实例中部署多个库,而increment_offset、increment_increment是全局性的,所以mysql不能用自带的auto_increment方案;
相对而言,oracle就好办了,为每个库、每张库定义一个sequence,保证increment一致,错开offset即可
基础流程
MShard 被设计成一个 mybatis 的插件,重写了 MethodProxy、Environment、SqlSessionFactory等类,总体的sql执行步骤如下:
1、根据mapper xml生成sql
由于自定义了 methodProxy 和 MapperFactoryBean,所以我们可以在不依赖于sqlSession的情况下根据mapper.xml生成待执行的sql语句
2、sql 解析
利用 mybatis 自身的 api 获取分表字段的值
3、sql 路由
根据分表字段的值取模,找到对应的数据源
4、到指定的数据源执行sql
此时才根据数据源生成sqlSession,然后执行相应的语句
使用说明
1、shard-config.xml
该配置文件位于 classpath 根路径,用于配置数据源信息、分库的表及字段信息、数据源的读写分离信息。
a、数据源配置如下,目前只直接mysql,name为唯一标识符
<dataSource name="ds1">
<url>jdbc:mysql://10.1.4.241:3306/sample_db1?useUnicode=true&characterEncoding=UTF-8</url>
<userName>root</userName>
<password>123456</password>
</dataSource>
<dataSource name="ds1_rep1">
<url>jdbc:mysql://10.1.4.242:3306/sample_db1?useUnicode=true&characterEncoding=UTF-8</url>
<userName>root</userName>
<password>123456</password>
</dataSource>
....
b、以下是数据结点配置,用于将数据源分组,并区分哪些源用于读,哪些源用于写
(注意:每个节点对应逻辑上的一个数据库;如果某结点物理部署时采用多主热备,即多master,则在writeNodes将master一一配上即可;main=true代表该结点为主库,因为不是所有的表都需要分库处理;readNodes中的数据源将不会用写操作,同一事务中如有先写后读取的操作,那么该操作会在同一个writeNode上执行,从而保证一致性)
<dataNodes>
<dataNode main="true">
<writeNodes>ds1</writeNodes>
<readNodes>ds1_rep1, ds1_rep2</readNodes>
</dataNode>
<dataNode>
<writeNodes>ds2</writeNodes>
<readNodes>ds2_rep1, ds2_rep2</readNodes>
</dataNode>
<dataNode>
<writeNodes>ds3</writeNodes>
<readNodes>ds3_rep1, ds3_rep2</readNodes>
</dataNode>
</dataNodes>
c、最后就是分库的表、字段配置了,注意:字段类型必须是数值型,且不能为空
<rules>
<table name="sample_blog" column="user_id"></table>
</rules>
2、建立数据库表、函数
本文以3个节点为例,每个节点有1个读库,2个写库。
注意:sequence表为主键辅助表,需设置为 MyIsam 引擎,因为该引擎下锁是以表为单位的,故不会出现并发问题。
CREATE TABLE `sample_blog` (
`id` int(11) NOT NULL DEFAULT '0' COMMENT '主键',
`user_id` int(11) DEFAULT NULL COMMENT '用户ID',
`title` varchar(255) DEFAULT NULL COMMENT '标题',
`context` mediumtext COMMENT '内容',
`create_time` datetime DEFAULT NULL COMMENT '发表时间',
`is_use` tinyint(1) DEFAULT NULL COMMENT '是否启用',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='博文';
CREATE TABLE `sequence` (
`tbl_name` varchar(32) NOT NULL DEFAULT '',
`next_id` int(11) DEFAULT NULL,
PRIMARY KEY (`tbl_name`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
CREATE FUNCTION `get_new_id`( tblName varchar(32) ) RETURNS int(11)
BEGIN
DECLARE idOffset int default 1;
DECLARE idIncrement int default 3;
DECLARE nextID int default 0;
select next_id into nextID from sequence where tbl_name=tblName limit 1;
if(nextID<=0) then
insert into sequence(tbl_name, next_id) values(tblName, idOffset+idIncrement) ;
return idOffset;
else
update sequence set next_id=nextID+idIncrement where tbl_name=tblName;
RETURN nextID;
end if;
END;
3、编写测试代码
本例中采用mapper映射机制。
测试类 BlogMapper.java 映射文件 BlogMapper.xml
以数据插入为例,
int insert(Blog record);
<insert id="insert" parameterType="sample.dal.entity.Blog">
insert into sample_blog (id, user_id, title,
create_time, is_use, context
)
values (get_new_id('sample_blog'), #{userId,jdbcType=INTEGER},
#{title,jdbcType=VARCHAR},
#{createTime,jdbcType=TIMESTAMP}, #{isUse,jdbcType=BIT}, #{context,jdbcType=LONGVARCHAR}
)
</insert>
测试代码:
@Test
public void testMapper() {
BlogMapper mapper = factory.getBean(BlogMapper.class);
BlogExample ex = new BlogExample();
ex.createCriteria().andTitleLike("%").andIdIsNotNull();
Blog record = new Blog();
record.setContext("aaaaaa");
for(int i=0 ; i< 100; i++){
mapper.insert(record);
}
}
执行该测试用例后,100条数据以userId为分库字段,分别存储到了 3个数据源中。
4、事务控制
a、在 spring 配置中加入以下配置即:
<bean id="transactionManager" class="org.meteor.transaction.ShardDataSourceTrasactionManager"></bean>
<tx:annotation-driven transaction-manager="transactionManager" />
b、在需要用到事务的业务方法头部或者类的头部加上 @Transactional 即可
写在最后
本插件目前还处于测试阶段,不能用于生产环境;
在这里写这篇文章只是想把自己的设计思想传达给大家,与大家一起分享一下,希望能让大家眼前一亮,但不足之处难免,也希望得到指证;
至于代码,目前只是为了完成基础功能,还有很大的优化空间。
依赖包:
aopalliance-1.0.jar
asm-3.3.jar
asm-commons-3.3.jar
aspectj-1.6.12.jar
commons-beanutils-1.8.3.jar
commons-codec-1.4.jar
commons-collections-3.2.1.jar
commons-dbcp-1.4.jar
commons-io-2.0.1.jar
commons-logging-1.1.1.jar
commons-pool-1.5.4.jar
junit-4.9-SNAPSHOT-20100512-0041.jar
spring.aop-3.0.5.RELEASE.jar
spring.asm-3.0.5.RELEASE.jar
spring.beans-3.0.5.RELEASE.jar
spring.context-3.0.5.RELEASE.jar
spring.context.support-3.0.5.RELEASE.jar
spring.core-3.0.5.RELEASE.jar
spring.expression-3.0.5.RELEASE.jar
spring.jdbc-3.0.5.RELEASE.jar
spring.transaction-3.0.5.RELEASE.jar
aspectjweaver-1.6.9.jar
asm-3.3.jar
asm-commons-3.3.jar
aspectj-1.6.12.jar
commons-beanutils-1.8.3.jar
commons-codec-1.4.jar
commons-collections-3.2.1.jar
commons-dbcp-1.4.jar
commons-io-2.0.1.jar
commons-logging-1.1.1.jar
commons-pool-1.5.4.jar
junit-4.9-SNAPSHOT-20100512-0041.jar
spring.aop-3.0.5.RELEASE.jar
spring.asm-3.0.5.RELEASE.jar
spring.beans-3.0.5.RELEASE.jar
spring.context-3.0.5.RELEASE.jar
spring.context.support-3.0.5.RELEASE.jar
spring.core-3.0.5.RELEASE.jar
spring.expression-3.0.5.RELEASE.jar
spring.jdbc-3.0.5.RELEASE.jar
spring.transaction-3.0.5.RELEASE.jar
aspectjweaver-1.6.9.jar
下载链接:
mshard-demo-0.1.jar
http://pan.baidu.com/s/1dDj6QBn
mshard-0.1-beta.jar
http://pan.baidu.com/s/1kTqMnor