HibernateShards-数据库水平分区解决方案

来源:互联网 发布:纽约时报淘宝 编辑:程序博客网 时间:2024/06/04 18:20
HibernateShard 
    多数据库水平分区解决方案。 

1. 简介 
     Hibernate 的一个扩展,用于处理多数据库水平分区架构。 
     由google工程师 2007年 捐献给 Hibernate社区。  
     http://www.hibernate.org/414.html 
     目前版本:   3.0.0 beta2, 未发GA版。 
     条件:Hibernate Core 3.2, JDK 5.0 

2. 水平分区原理 
     一个库表如 Order 存在于多个数据库实例上。按特定的分区逻辑,将该库表的数据存储在这些实例中,一条记录的主键 PK,在所有实例中不得重复。 
    
    水平分区在大型网站,大型企业应用中经常采用。 
    目的出于海量数据分散存储,分散操作,分散查询以便提高数据处理量和整体数据处理性能。 
   
    使用: 
      google工程师的设计还是非常好的,完全兼容 Hibernate本身的主要接口。 
Java代码  收藏代码
  1. org.hibernate.Session  
  2. org.hibernate.SessionFactory   
  3.  org.hibernate.Criteria   
  4.  org.hibernate.Query   

     因此程序员开发变化不大,甚至不需要关心后台使用了分区数据库。程序迁移问题不大。而且配置上比较简明。 

3. 三种策略: 
   1) ShardAccessStrategy, 查询操作时,到那个分区执行。 
      默认提供两个实现: 
      顺序策略:SequentialShardAccessStrategy, 每个query按顺序在所有分区上执行。 
      平行策略:ParallelShardAccessStrategy, 每个query以多线程方式并发平行的在所有分区上执行。 此策略下,需要使用线程池机制满足特定的性能需要,java.util.concurrent.ThreadPoolExecutor。 

   2) ShardSelectionStrategy, 新增对象时,存储到哪个分区。 
         框架默认提供了一个轮询选择策略 RoundRobinShardSelectionStrategy, 但一般不这样使用。 
        通常采用“attribute-based sharding”机制,基于属性分区。一般是用户根据表自己实现一个基于属性分区的策略类ShardSelectionStrategy ,例如,以下WeatherReport基于continent属性选择分区: 
Java代码  收藏代码
  1.   public class WeatherReportShardSelectionStrategy implements ShardSelectionStrategy {  
  2. public ShardId selectShardIdForNewObject(Object obj) {  
  3.     if(obj instanceof WeatherReport) {  
  4.         return ((WeatherReport)obj).getContinent().getShardId();  
  5.     }  
  6.     throw new IllegalArgumentException();  
  7. }  

  

   3) ShardResolutionStrategy, 该策略用于查找单个对象时,判断它在哪个或哪几个分区上。 
      默认使用 AllShardsShardResolutionStrategy ,可以自定义例如: 
Java代码  收藏代码
  1. public class WeatherReportShardResolutionStrategy extends AllShardsShardResolutionStrategy {  
  2.     public WeatherReportShardResolutionStrategy(List<ShardId> shardIds) {  
  3.         super(shardIds);  
  4.     }  
  5.   
  6.     public List<ShardId> selectShardIdsFromShardResolutionStrategyData(  
  7.             ShardResolutionStrategyData srsd) {  
  8.         if(srsd.getEntityName().equals(WeatherReport.class.getName())) {  
  9.             return Continent.getContinentByReportId(srsd.getId()).getShardId();  
  10.         }  
  11.         return super.selectShardIdsFromShardResolutionStrategyData(srsd);  
  12.     }  
  13. }  



4. 水平分区下的查询 

   对于简单查询 HibernateShard 可以满足。 

   水平分区下多库查询是一个挑战。主要存在于以下三种操作: 
   1) distinct 
         因为需要遍历所有shard分区,并进行合并判断重复记录。 
   2) order by 
         类似 1) 
   3) aggregation 
         count,sim,avg等聚合操作先分散到分区执行,再进行汇总。 
         是不是有点类似于 MapReduce ? 呵呵。 
   
   目前 HibernateShard 不支持 1), 2), 对 3) 部分支持 

    HibernateShard 目前通过 Criteria 接口的实现对 聚合提供了较好的支持, 因为 Criteria 以API接口指定了 Projection 操作,逻辑相对简单。 

    而HQL,原生 SQL 还不支持此类操作。 

     
5. 再分区和虚拟分区 
      当数据库规模增大,需要调整分区逻辑和数据存储时, 需要再分区。 
      两种方式: 1)数据库数据迁移其他分区; 2) 改变记录和分区映射关系。这两种方式都比较麻烦。尤其“改变记录和分区映射关系”,需要调整 ShardResolutionStrategy。 

     HibernateShard 提供了一种虚拟分区层。当需要调整分区策略时,只需要调整虚拟分区和物理分区映射关系即可。以下是使用虚拟分区时的配置创建过程: 

Java代码  收藏代码
  1.    
  2.   Map<Integer, Integer> virtualShardMap = new HashMap<Integer, Integer>();  
  3. virtualShardMap.put(00);  
  4. virtualShardMap.put(10);  
  5. virtualShardMap.put(21);  
  6. virtualShardMap.put(31);  
  7. ShardedConfiguration shardedConfig =  
  8.     new ShardedConfiguration(  
  9.         prototypeConfiguration,  
  10.         configurations,  
  11.         strategyFactory,  
  12.         virtualShardMap);  
  13. return shardedConfig.buildShardedSessionFactory();  


6.  局限: 
    1)HibernateShard 不支持垂直分区, 垂直+水平混合分区。 

    2) 水平分区下 查询功能受到一定限制,有些功能不支持。实践中,需要在应用层面对水平分区算法进行更多的考虑。 
    3) 不支持跨分区的 关系 操作。例如:删除A分区上的 s 表,B分区上的关联子表 t的记录无法进行参照完整性约束检查。 (其实这个相对 跨分区查询的挑战应该说小的多,也许google工程师下个版本会支持,呵呵) 

    4) 解析策略接口似乎和对象ID全局唯一性有些自相矛盾, 
AllShardsShardResolutionStrategy 的接口返回的是给定对象ID所在的 shard ID集合,按理应该是明确的一个 shard ID. 

参考资料:HibernateShard 参考指南。