精华帖 (7) :: 良好帖 (12) :: 新手帖 (0) :: 隐藏帖 (8)
|
||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
作者 | 正文 | |||||||||||||||
发表时间:2010-08-31
最后修改:2010-09-06
Shardbatis是在mybatis 2.3.5代码的基础上进行一些扩展实现数据水平切分功能。 数据的水平切分包括多数据库的切分和多表的数据切分。目前shardbatis已经实现了单数据库的数据多表水平切分 mybatis2.3.5的核心类图(包含了spring对ibatis的封装SqlMapClientTemplate)如下(其他版本的ibatis的类图有略微不同) 改造后的类图 从这两张图上可以看出shardbatis里新增了接口SqlMapShardingExt,SqlMapShardingExt中具体的方法如下 public interface SqlMapShardingExt extends SqlMapExecutor{ /** * 带有sharding功能的insert * @param id * @param parameterObject * @param groups * @return * @throws SQLException */ Object insertWithSharding(String id, Object parameterObject, ShardingFactorGroup... groups) throws SQLException; /** * 带有sharding功能的insert * @param id * @param groups * @return * @throws SQLException */ Object insertWithSharding(String id, ShardingFactorGroup... groups) throws SQLException; /** * 带有sharding功能的update * @param id * @param parameterObject * @param groups * @return * @throws SQLException */ int updateWithSharding(String id, Object parameterObject, ShardingFactorGroup... groups) throws SQLException; /** * 带有sharding功能的update * @param id * @param groups * @return * @throws SQLException */ int updateWithSharding(String id, ShardingFactorGroup... groups) throws SQLException; /** * 带有sharding功能的delete * @param id * @param parameterObject * @param groups * @return * @throws SQLException */ int deleteWithSharding(String id, Object parameterObject, ShardingFactorGroup... groups) throws SQLException; /** * 带有sharding功能的delete * @param id * @param groups * @return * @throws SQLException */ int deleteWithSharding(String id, ShardingFactorGroup... groups) throws SQLException; /** * 带有sharding功能的单记录查询 * @param id * @param parameterObject * @param groups * @return * @throws SQLException */ Object queryForObjectWithSharding(String id, Object parameterObject, ShardingFactorGroup... groups) throws SQLException; /** * 带有sharding功能的单记录查询 * @param id * @param groups * @return * @throws SQLException */ Object queryForObjectWithSharding(String id, ShardingFactorGroup... groups) throws SQLException; /** * 带有sharding功能的单记录查询 * @param id * @param parameterObject * @param resultObject * @param groups * @return * @throws SQLException */ Object queryForObjectWithSharding(String id, Object parameterObject, Object resultObject, ShardingFactorGroup... groups) throws SQLException; /** * 带有sharding功能的查询 * @param id * @param parameterObject * @param groups * @return * @throws SQLException */ List queryForListWithSharding(String id, Object parameterObject, ShardingFactorGroup... groups) throws SQLException; /** * 带有sharding功能的查询 * @param id * @param groups * @return * @throws SQLException */ List queryForListWithSharding(String id, ShardingFactorGroup... groups) throws SQLException; /** * 带有sharding功能的查询 * @param id * @param parameterObject * @param skip * @param max * @param groups * @return * @throws SQLException */ List queryForListWithSharding(String id, Object parameterObject, int skip, int max, ShardingFactorGroup... groups) throws SQLException; /** * 带有sharding功能的查询 * @param id * @param skip * @param max * @param groups * @return * @throws SQLException */ List queryForListWithSharding(String id, int skip, int max, ShardingFactorGroup... groups) throws SQLException; /** * * @param id * @param parameterObject * @param rowHandler * @param groups * @throws SQLException */ void queryWithRowHandlerWithSharding(String id, Object parameterObject, RowHandler rowHandler, ShardingFactorGroup... groups) throws SQLException; /** * * @param id * @param rowHandler * @param groups * @throws SQLException */ void queryWithRowHandlerWithSharding(String id, RowHandler rowHandler, ShardingFactorGroup... groups) throws SQLException; /** * * @param id * @param parameterObject * @param keyProp * @param groups * @return * @throws SQLException */ Map queryForMapWithSharding(String id, Object parameterObject, String keyProp, ShardingFactorGroup... groups) throws SQLException; /** * * @param id * @param parameterObject * @param keyProp * @param valueProp * @param groups * @return * @throws SQLException */ Map queryForMapWithSharding(String id, Object parameterObject, String keyProp, String valueProp, ShardingFactorGroup... groups) throws SQLException; 通过SqlMapClientImpl和SqlMapSessionImpl对SqlMapShardingExt的实现是的ibatis具有了DB sharding的功能,iBATIS会自动根据配置的或者是编码的sharding策略将原始的sql语句转变为对应目标表名的sql。 下面看一下如何在iBATIS中配置和使用sharding功能 1.配置sharding策略。在sql-map-config.xml中添加如下配置 <shardingConfig> <!-- 对于app_test使用默认的切分策略,默认的切分策略只是简单按数值取模生成新的表名,如何实现切分策略后面再介绍 --> <sharding tableName="app_test" strategyClass="com.ibatis.sqlmap.engine.sharding.impl.DefaultShardingStrategy"/> <!-- 没有配置切分策略等于使用默认的切分策略 --> <sharding tableName="app_2test" /> </shardingConfig> 这里再通过实例简单介绍一下使用DefaultShardingStrategy时对sql的convert结果 比如sqlmap中定义的原始sql为: SELECT EMPLOYEEIDNO FROM mytable WHERE SALARY >= 50000 那经过convert后的结果将可能是 SELECT EMPLOYEEIDNO FROM mytable_1 WHERE SALARY >= 50000 又例如原始sql为 SELECT a.* FROM ANTIQUES a,ANTIQUEOWNERS b, mytable c where a.id=b.id and b.id=c.id convert的结果可能为 SELECT a.* FROM ANTIQUES_0 AS a, ANTIQUEOWNERS_1 AS b, mytable_1 AS c WHERE a.id = b.id AND b.id = c.id 开始使用sharding api public class SqlMapClientTest { SqlMapClient sqlMapper; @Before public void init() { Reader reader; try { reader = Resources.getResourceAsReader("sql-map-config.xml"); sqlMapper = SqlMapClientBuilder.buildSqlMapClient(reader); reader.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } @Test public void testSharding() throws Exception { Map param = new HashMap(); param.put("cnt", "ttt"); ShardingFactorGroup g = new ShardingFactorGroup();//ShardingFactorGroup为切分策略提供必要参数 g.setTableName("App_Test");//设置为哪张表配置切分策略 g.setParam(new Integer(123));//设置为切分策略提供参数 //这里还可以通过g.setShardingStrategy(...)来设置切分策略 //通过API配置的切分策略可以覆盖xml里配置的App_Test表的切分策略 Integer count = (Integer) sqlMapper.queryForObjectWithSharding( "AppTest.select_paging_count_by_map", param, g); Assert.assertEquals(count.toString(), "0"); } @Test public void testUpdate() throws SQLException { ShardingFactorGroup g = new ShardingFactorGroup(); g.setTableName("App_Test"); g.setParam(new Integer(123)); String cnt = "testUpdate" + System.currentTimeMillis(); AppTest at1 = new AppTest(); at1.setCnt(cnt); Integer id = (Integer) sqlMapper.insertWithSharding( "AppTest.insert_h2", at1, g); AppTest parameterObject = new AppTest(); parameterObject.setCnt(cnt); AppTest ret = (AppTest) sqlMapper.queryForObjectWithSharding( "AppTest.select_by_condition", parameterObject, g); Assert.assertEquals(ret.getId().toString(), id.toString()); ret.setCnt("NEW_CONTENT"); Integer count = sqlMapper.updateWithSharding("AppTest.update", ret, g); Assert.assertEquals(count.toString(), "1"); count = (Integer) sqlMapper.queryForObjectWithSharding( "AppTest.select_paging_count", ret, g); Assert.assertEquals(count.toString(), "1"); } @Test public void testDelete() throws SQLException { ShardingFactorGroup g = new ShardingFactorGroup(); g.setTableName("App_Test"); g.setParam(new Integer(123)); String cnt = "testDelete" + System.currentTimeMillis(); AppTest at1 = new AppTest(); at1.setCnt(cnt); Integer id = (Integer) sqlMapper.insertWithSharding( "AppTest.insert_h2", at1, g); AppTest parameterObject = new AppTest(); parameterObject.setCnt(cnt); AppTest ret = (AppTest) sqlMapper.queryForObjectWithSharding( "AppTest.select_by_condition", parameterObject, g); Assert.assertEquals(ret.getId().toString(), id.toString()); Integer row = sqlMapper.deleteWithSharding("AppTest.delete", ret, g); Assert.assertEquals(row.toString(), "1"); } } 实现自己的sharding策略,只要实现一个简单的接口即可 public interface ShardingStrategy { /** * 计算得到新的表名 * @param baseTableName 逻辑表名 * @param params 为sharding逻辑提供必要参数 * @return */ public String getTargetTableName(String baseTableName,Object params); } Since 0.9.1使用ibatis原生api也可以支持sharding功能 <!-- 在sql-map-config.xml中添加如下配置 --> <shardingConfig> <!-- 对于app_test使用默认的切分策略,默认的切分策略只是简单按数值取模生成新的表名,如何实现切分策略后面再介绍 --> <sharding tableName="app_test" strategyClass="com.ibatis.sqlmap.engine.sharding.impl.DefaultShardingStrategy"/> <!-- 没有配置切分策略等于使用默认的切分策略 --> <sharding tableName="app_2test" /> </shardingConfig> <!-- 在sqlmap.xml中statement节点都可以添加shardingParams属性 shardingParams对应的值使用json数组格式, paramExpr 对应parameterClass对象中的某个属性,这个属性的值会传递给sharding策略用于计算新的表名, 例如paramExpr:testId 等同于appTest.getTestId;paramExpr:model.id 等同于appTest.getModel().getId() tableName 用于表示当前这个json对象配使用于sql中的哪个表 strategyClass strateg接口实现类的全名,这个值如果没有将使用sql-map-config.xml里shardingConfig下配置对应的值。 --> <select id="select_count_native" parameterClass="AppTest" resultClass="java.lang.Integer" shardingParams='[{"paramExpr":"testId","tableName":"app_test"}]'> select count(*) from app_test where cnt=#cnt# </select> 下面开始编码 AppTest param=new AppTest(); param.setTestId(2); param.setCnt("testShardingWithConfig"); Integer count=(Integer)sqlMapper.queryForObject("AppTest.select_count_native",param);//和使用原生的ibatis API没有区别 最终执行的SQL可能是如下样式 select count(*) from app_test_0 where cnt=? 关于shardbatis在spring中的使用方法,以及一些使用注意事项和性能测试结果请大家移步到项目主页http://code.google.com/p/shardbatis/上查看 声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
||||||||||||||||
返回顶楼 | ||||||||||||||||
发表时间:2010-09-01
性能如何?
|
||||||||||||||||
返回顶楼 | ||||||||||||||||
发表时间:2010-09-01
支持下。技术型文章就是牛B
|
||||||||||||||||
返回顶楼 | ||||||||||||||||
发表时间:2010-09-01
D04540214 写道 性能如何?
性能测试结果在项目主页中有写,这里再列一下 50个线程并发,每个线程循环执行100次数据库(h2数据库)查询。 下面的测试结果建议关注两种场景的比较值,而不是关注某个具体值(你懂的) jdk:1.5.0_22 无jvm参数 平均每次查询耗时,单位:毫秒
|
||||||||||||||||
返回顶楼 | ||||||||||||||||
发表时间:2010-09-01
数据库的水平切分 使用局限性还是有的。比如sql中的分组,排序,或者其他一些情况。
另外 兄弟 你的测试方法应该不是很可靠吧。 |
||||||||||||||||
返回顶楼 | ||||||||||||||||
发表时间:2010-09-01
J-catTeam 写道 数据库的水平切分 使用局限性还是有的。比如sql中的分组,排序,或者其他一些情况。
另外 兄弟 你的测试方法应该不是很可靠吧。 数据库水平切分是有特定的场景的,我做这个的目的不是为了解决水平切分的局限性。我做这个的目的是为了通过ibatis实现数据库水平切分的功能。 至于测试方法有不可靠的地方请您不吝赐教,谢谢! |
||||||||||||||||
返回顶楼 | ||||||||||||||||
发表时间:2010-09-01
SeanHe 写道 J-catTeam 写道 数据库的水平切分 使用局限性还是有的。比如sql中的分组,排序,或者其他一些情况。
另外 兄弟 你的测试方法应该不是很可靠吧。 数据库水平切分是有特定的场景的,我做这个的目的不是为了解决水平切分的局限性。我做这个的目的是为了通过ibatis实现数据库水平切分的功能。 至于测试方法有不可靠的地方请您不吝赐教,谢谢! 也没有什么赐教的哈,分库分表的前提是在大数据量的前提下的。 |
||||||||||||||||
返回顶楼 | ||||||||||||||||
发表时间:2010-09-01
J-catTeam 写道 SeanHe 写道 J-catTeam 写道 数据库的水平切分 使用局限性还是有的。比如sql中的分组,排序,或者其他一些情况。
另外 兄弟 你的测试方法应该不是很可靠吧。 数据库水平切分是有特定的场景的,我做这个的目的不是为了解决水平切分的局限性。我做这个的目的是为了通过ibatis实现数据库水平切分的功能。 至于测试方法有不可靠的地方请您不吝赐教,谢谢! 也没有什么赐教的哈,分库分表的前提是在大数据量的前提下的。 呵呵,我这里的对比测试只是为了说明加上sharding功能以后对ibatis本身性能的影响,而不是测试大数据量下sql的性能,大数据量的性能说白了和shardbatis或者和ibatis都没有什么关系 |
||||||||||||||||
返回顶楼 | ||||||||||||||||
发表时间:2010-09-01
最后修改:2010-09-01
我没有用过IBATIS,但最近研究了Hibernate shard这个项目,Hibernate shard没有解决分组、排序,分页用的是应用层面的内存分页,聊胜于无吧,不知道兄弟在这方面解决得如何
|
||||||||||||||||
返回顶楼 | ||||||||||||||||
发表时间:2010-09-01
比较支持这种增强行为,特别是在已经上线的项目里且用上了ibatis后,干净地增加了分表分库的能力,为以后的可维护性大大增强。
不过我觉得这种直接增加方法的方式有点繁琐了,dao最好是直接能用上ibatis原有的sqlMapClient提供的dao方法,这样一来,只用改动配置,不用改动代码,就能直接实现分库分表的逻辑,对于dao开发者来说,他根本无需关心数据库是否分库分表了,展现给他的操作逻辑就是一个库,一个表。 ps:最好是能把读写分离的逻辑也能干净地整合进去,之前的做法大部分人是用在spring事务里面配置readonly=true,不过互联网世界中压根就没有数据库事务的思想(严重影响性能),基本上用app的逻辑控制事务回滚之类的。 |
||||||||||||||||
返回顶楼 | ||||||||||||||||