Richie

Sometimes at night when I look up at the stars, and see the whole sky just laid out there, don't you think I ain't remembering it all. I still got dreams like anybody else, and ever so often, I am thinking about how things might of been. And then, all of a sudden, I'm forty, fifty, sixty years old, you know?

Hibernate Shards 数据的水平、垂直切割(三)- Hibernate Shards结构

主要处理方式
hibernate shards的主要工作方式如下图:
    
他在hibernate的基础上实现了一层数据切分的处理逻辑。不需要切分的数据直接使用hibernate的SessionFactory和Session进行操作;需要切分的数据,则使用hibernate shards的ShardedSessionFactory和ShardedSession进行操作
hibernate shards的主要任务:
1. 对shards配置的处理
2. 提供一个可扩展的sharding机制,能方便的运用各种sharding策略以及resharding支持等要求
3. 根据配置和sharding机制正确的存取数据

    shards的配置主要包含每个shard对应的数据库连接信息,以及定义一个shard id
    为了充分实现伸缩性以及简化业务逻辑的开发,sharding策略应当以实体id为基础,这样在类似hibernate Session这样的数据操作接口上实现 sharding处理会比较容易,对业务逻辑代码的开发也能比较透明
    上图中get, update, delete等操作都可以取到实体id,然后根据sharding策略的算法计算出shard id,从对应的数据库中执行这些操作。hibernate shards中与这一操作相关的接口是ShardResolutionStrategy
    sharding环境下的save操作涉及到id的生成机制,以及将数据按照sharding策略insert到正确的shard中。hibernate shards中与这一操作相关的接口是ShardSelectionStrategy
    query操作通过hql、Criteria等方式查询结果集,这些查询有可能需要在某个shard或某几shards中进行,也可能需要在全部的shards中进行,与具体业务相关。hibernate shards中与这一操作相关的接口是ShardAccessStrategy
    在resharding的支持方面,hibernate shards实现了virtual shards
    在resharding时主要任务有2个,其一是对sharding算法、配置的修改,其二是对已有数据的重分区处理。 如果最开始进行sharding设计时考虑不够完整,resharding时可能会很麻烦。 virtual shards方式在于在一开始就考虑到后续的resharding需求,估算出最大需要的shards数量,以估算出的这个数量配置虚拟shard,然后将虚拟shard映射到当前的实际shard中
    比如对某个数据,目前使用2个shard就足够了,但考虑到后续的业务增长,设置100个虚拟shard,sharding算法均以这100个虚拟shard为基础,然后将虚拟shard 50个一组映射到当前使用的实际shard上。 这样在后续需要resharding时,只需要对数据重分区以及修改虚拟shard和实际shard的映射配置就可以,风险比较小

Configuration的处理
configuration部分类图如下:
    
从前面一篇Hibernate Shards 数据的水平、垂直切割(二)- Hibernate Shards基本演示已经看到了 加载配置文件以及创建ShardedSessionFactory的过程

ShardConfiguration用来表示每个shard特有的配置信息,包括:
hibernate.connection.url
hibernate.connection.username
hibernate.connection.password
hibernate.session_factory_name
hibernate.connection.datasource
hibernate.cache.region_prefix
hibernate.connection.shard_id

ShardedConfiguration则持有所有shards对应的ShardConfiguration列表、一个作为原型的Configuration对象以及一个sharding策略的工厂对象ShardStrategyFactory
构造ShardedConfiguration时,他从各个配置文件读取shard配置信息,得到virtual shards与实际shards列表以及之间的映射关系
ShardedConfiguration还具有创建ShardedSessionFactory的职责。在创建ShardedSessionFactory时,hibernate shards循环为每个shard创建一个hibernate的SessionFactory对象,丢给ShardedSessionFactory构造函数
每一个hibernate的SessionFactory对象都是通过原型Configuration来创建的,只是在循环中会将每个shard特有的配置信息(ShardConfiguration的属性)设置到原型Configuration中,再用它来创建SessionFactory对象,这是一种简单的处理方式,避免再去建立额外的配置文件

关键实现
hibernate shards的关键实现部分类图如下:
    
处理上非常简单,hibernate shards重新实现了hibernate的Session、SessionFactory接口。在前面的configuration处理中,ShardedConfiguration在创建 ShardedSessionFactory时将每个shard对应的hibernate SessionFactory列表传给了ShardedSessionFactory。ShardedSession的主要工作就是调用相关的sharding策略类确定目标shard, 使用与他对应的SessionFactory创建hibernate的Session对象,执行操作。对于使用了sharding之后不支持的方法则抛出异常。当然其中会有像query这样特殊的操作(涉及到合并结果集、排序、distinct等一些较麻烦的处理),还要处理一些hibernate lazy loading、detached object等相关的问题,以及其他一些sharding中必须处理的问题,例如cross-shard等
对于hibernate的Criteria、Query等对象,hibernate shards也都实现了Sharded***的版本,用于支持针对shard的query操作

Shards Strategy
shards策略的类图:
    
ShardSelectionStrategy: 为新增的实体选择shard id,用于save操作的场景,接口方法中传入要执行操作的实体对象
ShardResolutionStrategy: 根据实体类名和id计算出与其对应的shard id,一般用于get、load、update、delete等操的场景,接口方法中会传入实体类名和实体id
ShardAccessStrategy: 主要用于hql、Criteria等查询情况,因其设计机制以及需要处理的事情方面存在些共同性,因此所有的get方法最终也通过ShardAccessStrategy来加载数据。 他只是循环为每个目标shard调用ShardOperation<T>的execute方法,把结果丢给ExitStrategy<T>处理。get操作时目标shard是一个明确的shard(通过ShardResolutionStrategy得到),而query操作时则是全部的shard列表(可以通过自定义的ShardAccessStrategy改变)
    hibernate shards提供了2个ShardAccessStrategy的实现:
    SequentialShardAccessStrategy: 逐个shard顺序执行,由ExitStrategy<T>决定是否结束执行过程以及如何处理结果集
    ParallelShardAccessStrategy: 并行的在各个shard执行查询操作,由ExitStrategy<T>决定如何处理结果集
    ShardAccessStrategy加载数据时用到了下面一些接口:
    ShardOperation<T>: 这个是在每个shard上真正执行get或者query动作的接口,get和query操作时通过匿名类实现这个接口
    ExitStrategy<T>: 这个接口的职责是解决如何处理结果集的问题,以及在SequentialShardAccessStrategy这样的场景下是否该结束对后续shard的执行
        对每个shard执行完读取操作后调用该接口的方法addResult,把执行结果丢给他;整个执行结束后调用该接口的compileResults方法处理结果集,并返回给调用者
        FirstNonNullResultExitStrategy: 查询到对象即返回,用于get的情况
        ConcatenateListsExitStrategy: 语义上是合并结果集,返回给调用者,用于hql和Criteria查询。他使用ExitOperationsCollector来完成这一任务
    ExitOperationsCollector: 处理如何合并结果集的逻辑。目前对于hql查询均使用ExitOperationsQueryCollector,他把从各个shard执行的结果集合并返回给调用者;对于criteria查询使用ExitOperationsCriteriaCollector,他在处理结果集时使用另一接口ExitOperation的实现,来处理count、sum、distinct等操作。目前hibernate shards没有对hql进行解析,因此hql中的count、sum、distinct等这样的操作还无法支持

限制
1. 还有很多hibernate的API没有实现
2. 不支持cross-shard的对象关系,比如A、B之间存在关联关系,而A、B位于不同的shard中。hibernate shards提供了CrossShardRelationshipDetectingInterceptor,以hibernate Interceptor的方式来检测cross-shard的对象关系问题,但这个拦截器有一定的开销(比如需要查映射的元数据、有可能过早的触发延迟加载行为等),可以用于测试环境来检测cross-shard关系的问题
3. hibernate shards本身不支持分布式事务,若要使用分布式事务需要采用其他解决方案
4. hql、criteria存在不少限制,相比于hql,criteria支持的特性更多一些
5. Session或者SessionFactory上面有状态的拦截器,在shard环境下面会存在一些问题。拿session来说,在hibernate中拦截器是对单个session上的多次sql执行事件进行拦截,而在shard情况下hibernate shards的ShardedSession会对应每个shard建立一个session,这时拦截器就是跨多个session了,因此hibernate shards要求有状态的拦截器必须通过实现StatefulInterceptorFactory来提供新的实例。如果拦截器需要使用到目标shard的session,则必须实现hibernate shards的RequiresSession接口

posted on 2010-04-14 10:41 riccc 阅读(...) 评论(...) 编辑 收藏

导航

News