Sahrding-jdbc分表中间件整合说明
Sharding-JDBC是当当应用框架ddframe中,从关系型数据库模块dd-rdb中分离出来的数据库水平分片框架,实现透明化数据库分库分表访问。
Sharding-JDBC直接封装JDBC协议,可以理解为增强版的JDBC驱动,旧代码迁移成本几乎为零。 Sharding-JDBC定位为轻量级java框架,使用客户端直连数据库,以jar包形式提供服务,无proxy代理层,无需额外部署,无其他依赖,DBA也无需改变原有的运维方式。
此案例为sharding-jdbc整合mybatis 的简单实现,其中sharding-jdbc的数据源配置及分表方式通过编写java代码并以注解的形式进行注册。
单库分表,在数据库中新建t_order_1、t_order_2、t_order_3、t_order_4四张表,根据表中时间字段creat_time按照月份分表。
其中sharding-jdbc的依赖包使用1.x版本
以上Java文件均为正常java类,和普通项目相同,无需特殊处理;
编写分表策略的java类
配置sharding-jdbc数据源的类
在application.properties:中配置数据库连接信息
详情见:springboot-mybatis-demo01
此案例为jpa整合sharding-jdbc的简单实现,依旧为单库分表,所用到的数据库表以及分表规则与demo1中的相同;不同的是这个项目中主要通过增加配置文件的形式来完成分表策略的实现,需要增加的java类仅为分表规则的自定义实现类。具体方法详见如下步骤。
与demo1中java代码实现的方式不同,在使用配置文件实现分表时,除了sharding-jdbc-care核心jar,还需要sharding-jdbc-config-sprinng 的jar
详情见 文件
无需特殊改动,与普通不分表的项目相同
需要在下面的配置文件中引入
在项目中引入配置文件,详细内容:
详情见:sharding-jdbc-jpa
此案例是针对项目中实际存在的一张大表做的拆分,在实际项目中关于此表的sql中涉及到分页、left join关联、时间字段格式化函数、查询条件中包含子查询等复杂的sql,下面会针对具体的sql进行改写,以达到直接查询目标表并返回正确结果的目的。
针对t_info这张表,根据时间字段input_time取其月份值,在数据库中创建t_info_01、t_info_02、t_info_03、t_info_04表;与其关联的main_user表不做拆分。
ModloTableShardingAlgorithm.java是实现了有sharding-jdbc的jar包中提供的SingleKeyTableShardingAlgorithm接口,实现三个方法,根据实际需要自定义编写分表规则
针对项目中所用到的t_info表,分表后sql需改造的情况如下:
1. 使用sharding-jdb后不支持distinct操作
2. 原本的查询SQL中,用到了left join,会导致扫描所有表
3. 涉及分页查询,针对子查询结果在再分页会导致扫描所有表
4. 因为t_info表是按照时间字段input_time进行拆分的,所以为了达到直接扫描目标表的效果,需要在查询条件中传入input_time的条件值,以达到提高查询速度的效果
5. 针对分片字段input_time 在使用日期格式化函数时也会导致扫描所有表,所以建议在代码中格式化后再传入。
详情见:sharding-jdbc-mybatis-demo02
在使用分表设计的项目中,想达到直接扫描目标表的效果前提条件是where条件中必须传入分表字段。
下面提到的表t_order为逻辑表,实际的物理表为t_order_1...t_order_4,分片字段为creat_time,分片规则为取其月份分表)
1. SQL中针对分表字段使用LIKE、to_days()函数、<=,扫描所有表
例:SELECT*FROM t_order WHERE creat_time like #{creat_time}
SELECT*FROM t_order WHERE to_days(creat_time)= to_days(#{creat_time})
SELECT*FROM t_order WHERE creat_time <= #{creat_time}
1. 针对分片字段使用between and 并直接传值,可以扫描对应的表
例:SELECT*FROM t_order WHERE creat_time BETWEEN #{creat_time} AND #{creat_time1}
2.关于针对单表使用COUNT、MAX、MIN,并对分表字段传入条件值,可以直接扫描目标表
例:select count(*) from t_order where 1=1 and creat_time BETWEEN ? and ?
select max(id) from t_order where 1=1 and creat_time BETWEEN ? and ?
select min(id) from t_order where 1=1 and creat_time BETWEEN ? and ?
3.sql中使用group by 分组函数时,并对分表字段出入条件值,可以直接扫描目标表
例:select * from t_order where creat_time =? group by id
1. SQL语句中针对所有字段使用OR 、DISTINCT、UNION、having,会报错
例:select distinct t_order.id,t_order.name .....from t_order.....
2. SQL语句中针对分表字段使用between()and时,其中的参数包含子查询,会报错
例:SELECT*FROM t_order WHERE creat_time BETWEEN(select creat_time from t_order_1 where user_id=”6”) AND #{creat_time}
1. SQL语句的where 条件没有传入分表的字段值,扫描所有表
例:SELECT * FROM t_order
2. SQL语句中包含冗余括号的子查询,扫描所有表
例:select*from(
注:selectSql为“SELECT * FROM t_order where creat_time=”20180430””
1.在查询条件中针对非分表字段含有子查询、分表字段传入条件值,可以直接扫描目标表
例:SELECT*FROM t_order WHERE 1=1 AND userName in (select userName from t_order_1 where user_id="6")AND creat_time BETWEEN '20180419' and '20180420'
1.SQL中使用left join进行联表查询,并传入了分表字段,扫描所有表
例:from t_order LEFT JOIN main_users on t_order.u_ch_id = main_users.uid where 1=1
and creat_time BETWEEN#{creatTime,jdbcType=TIMESTAMP}and #{creatTime1,jdbcType=TIMESTAMP}
1.拆分的表与未进行拆分的表进行联表查,并传入分表字段值,可以直接扫描目标表
例:From t_order,main_users where t_order.u_ch_id=main_users.uid
and t_order.creat_time BETWEEN #{creatTime,jdbcType=TIMESTAMP}and #{creatTime1,jdbcType=TIMESTAMP}
1.insert语句针对分表字段使用SYSDATE()获取数据库系统时间,会报错
例:INSERT INTO t_order
(order_id,user_id,userName,passWord,user_sex,description,creat_time)
VALUES
(#{order_id},#{user_id},#{userName},#{passWord},#{userSex},#{description},SYSDATE())
2. insert语句中,针对分表字段必须传入正确的值,才可以直接插入到目标表
例:insert into t_order (id,name,creat_time) values ( “2”,”啊啊啊”,”2018-04-27”);
1.update语句中,如果where条件中没有分表字段,则会修改所有分表中满足条件的数据
例:update t_order set name=? where id=?
2.update语句中,如果where条件含有分表字段,则只修改目标表的数据
例:update t_order set name=? where id=? and creat_time=?
1.delete语句中,如果where条件中没有分表字段,则会删除所有分表中满足条件的数据
例:delete from t_order where id = ?
2.delete语句中,如果where条件含有分表字段,则只修改目标表的数据
例:delete from t_order where id=? and creat_time=?
针对上述DML语句案例分析,各个分表间的主键id不应有重复(例如不能使用int自增型)。
针对数据库事务得的操作,只需自爱配置事务的数据源时引用shardingDataSource
在需要事务的地方使用@Transactional注解
注:
在使用Transactional注解时,数据库引擎要支持事务,如果是MySQL,注意表要使用支持事务的引擎,比如innodb,如果是myisam,事务是不起作用的