當關係型資料庫單表達 TB 級資料時,索引維護與讀寫效能急劇下降。NoSQL (如 Bigtable) 雖適合巨量資料,但犧牲 ACID 與結構化查詢。資料庫分片 在應用層將邏輯資料庫水平切分為多個物理分片,成為關係型資料庫擴展的實用方案。Hibernate Shards 是 Java 生態最成熟的分片框架,讓 POJO 映射邏輯資料庫,透明處理底層分片路由。
ShardedUUIDGenerator 嵌入分片 ID)。領域模型:Race (比賽) 為根聚合,Runner (選手) 關聯比賽。分片策略:
妥協:選手可能參加多類比賽,導致跨分片重複儲存 Runner (不支援跨分片 Join 的代價)。
public class RacerShardSelectionStrategy implements ShardSelectionStrategy {
public ShardId selectShardIdForNewObject(Object obj) {
if (obj instanceof Race) {
return determineShardId(((Race) obj).getDistance());
} else if (obj instanceof Runner) {
Runner r = (Runner) obj;
if (r.getRaces().isEmpty()) throw new IllegalArgumentException("Runner must have race");
// 取第一場比賽距離決定分片 (簡化邏輯)
return determineShardId(r.getRaces().iterator().next().getDistance());
}
throw new IllegalArgumentException("Non-shardable object");
}
private ShardId determineShardId(double distance) {
return new ShardId(distance > 10.0 ? 1 : 0);
}
}
使用 ShardedUUIDGenerator 產生含分片 ID 的 UUID,搭配內建 AllShardsShardResolutionStrategy 自動從 ID 解析分片,無需自行實作。
採 SequentialShardAccessStrategy 循序查詢;可改 ParallelShardAccessStrategy 並行查詢提升效能。
shard0.hibernate.cfg.xml / shard1.hibernate.cfg.xml)<property name="hibernate.connection.shard_id">0</property>
<property name="hibernate.shard.enable_cross_shard_relationship_checks">true</property> <!-- 僅開發測試開啟 -->
兩分片僅連線 URL 與 shard_id 不同,其餘方言、驅動、映射檔共用。
racer.hbm.xml)<id name="id" column="RACE_ID" unsaved-value="-1">
<generator class="org.hibernate.shards.id.ShardedUUIDGenerator"/> <!-- 嵌入分片 ID -->
</id>
public SessionFactory createSessionFactory() {
Configuration prototype = getPrototypeConfig(hibernateConfigs.get(0), resourceFiles);
List<ShardConfiguration> shardConfigs = buildShardConfigs(hibernateConfigs);
ShardStrategyFactory factory = buildShardStrategyFactory(); // 註入三大策略
ShardedConfiguration config = new ShardedConfiguration(prototype, shardConfigs, factory);
return config.buildShardedSessionFactory();
}
private ShardStrategyFactory buildShardStrategyFactory() {
return new ShardStrategyFactory() {
public ShardStrategy newShardStrategy(List<ShardId> shardIds) {
return new ShardStrategyImpl(
new RacerShardSelectionStrategy(),
new AllShardsShardResolutionStrategy(shardIds),
new SequentialShardAccessStrategy()
);
}
};
}
<bean id="shardedSessionFactoryBuilder" class="...ShardedSessionFactoryBuilder">
<property name="resourceConfigurations"><list><value>racer.hbm.xml</value></list></property>
<property name="hibernateConfigurations">
<list><value>shard0.hibernate.cfg.xml</value><value>shard1.hibernate.cfg.xml</value></list>
</property>
</bean>
<bean id="mySessionFactory" factory-bean="shardedSessionFactoryBuilder" factory-method="createSessionFactory"/>
<bean id="race_dao" class="...RaceDAOImpl">
<property name="sessionFactory" ref="mySessionFactory"/>
</bean>
DAO 完全無感知分片,僅依賴標準 SessionFactory,分片邏輯全在配置層封裝。
scenario "races > 10 miles in shard 1", {
given "a marathon", {
new Race("Leesburg Marathon", new Date(), 26.2, "...").create()
}
then "stored in shard 1 (db02)", {
sql = Sql.newInstance(db02url, user, pass, driver)
sql.eachRow("select * from race where name=?", ["Leesburg Marathon"]) { row ->
row.distance.shouldBe 26.2
}
sql.close()
}
and "NOT in shard 0 (db01)", {
sql = Sql.newInstance(db01url, user, pass, driver)
sql.eachRow("select * from race where name=?", ["Leesburg Marathon"]) { row ->
fail "shard 0 contains a marathon!"
}
sql.close()
}
}
同理驗證短跑落入分片 0。測試碼即文檔,確保分片邏輯正確。
| 優勢 | 代價 |
|---|---|
| 讀寫效能線性擴展 | 應用層編碼分片路由邏輯 |
| 跨機高可用,單分片故障不影響整體 | 鎖定分片模型,重分片極難 |
| 低成本 (商用硬體叢集) | 無跨分片 Join,領域建模受限 |
| 相容現有 RDBMS 技術棧 | 主鍵需應用層管理 |
適用場景:TB 級資料、高併發、受限於關係型架構無法遷移 NoSQL 的系統。Hibernate Shards 以最小侵入性實現分片透明化,是關係型資料庫擴展的利器。




