Hibernate 中的 PartitionKey:Spring Boot 實用指南
1.概述
在關聯式資料庫中處理大型資料集可能會對查詢效能造成挑戰。雖然 Hibernate 提供了實體映射優化,但隨著資料集的擴展,分區變得至關重要。
在本教學中,我們將深入探討如何使用 Spring Boot 處理分割區表。我們將使用 Hibernate 和 Spring Data JPA 來處理交互,但實際的分區操作是直接在資料庫內部配置的,例如 PostgreSQL。
2. @PartitionKey
的作用
當我們將一個非常大的表拆分成更小、更易於管理的表時,這稱為分區。每個分區包含一個資料子集,通常由分區鍵決定。在 Hibernate 中,我們可以將特定列對應為分區鍵,以便查詢僅針對相關分區,從而顯著提高查詢速度。
分區會為每個資料段建立單獨的實體表。例如,如果Sales
表按saleDate
分區,則可能會有sales_2024_q1
、 sales_2024_q2
等等。這種實體隔離正是效能提升的關鍵所在。
我們不能簡單地對現有的大型表進行分區。這個過程需要遷移策略。這通常涉及建立一個新的分區表結構,然後將資料從舊表遷移到新表。這確保了資料的完整性和一致性。
當我們的查詢在其 WHERE 子句中包含分區鍵時,分區的全部優勢才能充分發揮。如果查詢未指定分區鍵,資料庫的查詢規劃器可能需要搜尋所有分區,這會抵銷效能優勢。
3.設定PostgreSQL資料庫
我們將使用 PostgreSQL 作為資料庫。讓我們建立一個帶有PARTITION BY
子句的表,在 PostgreSQL 中,我們可以將其定義為:
CREATE TABLE sales (
id BIGINT PRIMARY KEY,
sale_date DATE NOT NULL,
amount DECIMAL(10, 2)
) PARTITION BY RANGE (sale_date);
CREATE TABLE sales_2024_q1 PARTITION OF sales
FOR VALUES FROM ('2024-01-01') TO ('2024-04-01');
CREATE TABLE sales_2024_q2 PARTITION OF sales
FOR VALUES FROM ('2024-04-01') TO ('2024-07-01');
-- And so on for other partitions
4. 所需依賴項
我們需要[spring-boot-starter-web](https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-web)
來建立我們的 REST 端點:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
我們的主要依賴項是spring-boot-starter-data-jpa
用於資料庫互動。它捆綁了 Spring Data JPA 和預設的 JPA 提供者 Hibernate。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
這是 PostgreSQL 的JDBC(Java 資料庫連線)驅動程式。 JDBC 驅動程式是一個關鍵的軟體元件,它使我們的 Java 應用程式能夠連接到 PostgreSQL 資料庫並進行互動:
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<scope>runtime</scope>
</dependency>
5.設定 Spring Boot 項目
這是一個在 Spring Boot 和 Hibernate 設定中表示分區鍵的清晰範例。
5.1. 資料庫實體設置
首先,我們來看看實體。我們將建立一個Sales
實體,並使用@PartitionKey
註解saleDate
字段,以啟用 Hibernate 的最佳化功能:
@Entity
@Table(name = "sales")
public class Sales {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@PartitionKey
private LocalDate saleDate;
private BigDecimal amount;
public Sales(Long id, LocalDate saleDate, BigDecimal amount) {
this.id = id;
this.saleDate = saleDate;
this.amount = amount;
}
public Sales() {
}
}
@PartitionKey
是一個自訂註解,它向 Hibernate 發出訊號,告知該欄位將作為資料庫中表格分割的基礎。此註解僅在 Hibernate 6.2 及更高版本中存在。
接下來,我們定義我們的儲存庫,它擴展了 JpaRepository 以利用 Spring Data JPA 的資料存取功能:
@Repository
public interface SalesRepository extends JpaRepository<Sales, Long> {
}
5.2. 設計控制器來測試分區
現在,讓我們建立一個控制器來與銷售數據互動。 testPartition()
和getAllPartition()
方法示範如何儲存一筆新的Sales
記錄。我們需要考慮的是,對於分區表,資料庫會根據saleDate
自動將此記錄放入正確的分區中:
@RestController
public class Controller {
@Autowired
SalesRepository salesRepository;
@GetMapping
public ResponseEntity<List<Sales>> getAllPartition() {
return ResponseEntity.ok()
.body(salesRepository.findAll());
}
@GetMapping("add")
public ResponseEntity testPartition() {
return ResponseEntity.ok()
.body(salesRepository.save(new Sales(104L, LocalDate.of(2025, 02, 01),
BigDecimal.valueOf(Double.parseDouble("8476.34d")))));
}
}
最後,我們有application.properties
文件,用於設定資料庫連線。我們還必須根據資料庫更新使用者名稱和密碼:
spring.application.name=partitionKeyDemo
# PostgreSQL connection properties
spring.datasource.url=jdbc:postgresql://localhost:5432/salesTest
spring.datasource.username=username
spring.datasource.password=password
5.3. 輸出
現在,當我們執行應用程式並點擊http://localhost:8080/add
,我們可以看到該行進入 PostgreSQL DB 中的正確分割區:
這裡我們可以看到左側窗格中建立了兩個分割區。然後,我們可以執行查詢來自動取得插入到正確分區中的行:
SELECT * FROM public.sales_2025_q1;
6. 使用分區最佳化查詢
為了真正從分區中獲益,我們必須精心設計查詢。假設我們想要找出特定日期的所有銷售記錄。一個設計良好的查詢應該如下所示:
SELECT * FROM sales WHERE sale_date = '2025-02-01';
在這種情況下,資料庫可以透過分區鍵(sale_date)立即識別並僅搜尋與「2025-02-01」對應的分區,而完全忽略所有其他分區。這是一個非常有效率的操作。
但是,如果我們執行不包含分區鍵的查詢,例如:
SELECT * FROM sales WHERE amount > 5000;
資料庫將被迫掃描每個分區以查找匹配的記錄。這種查詢稱為全域掃描,其效能可能比在非分區且正確索引的表上執行的查詢差得多。
7. 結論
在本教程中,我們研究如何將 Hibernate 的實體映射與資料庫層級分區結合,以便在資料成長時保持效能一致。分區是一種強大的技術,可以擴展關聯式資料庫以處理大量資料集。
雖然實現位於資料庫級別,但 Hibernate 和 Spring Boot 提供了必要的工具來與這些結構無縫互動。同時,需要建構涉及分區列的查詢才能利用分區鍵的優勢。
與往常一樣,本文的程式碼可在 GitHub 上找到。