WHERE 子句決定索引選擇。常見錯誤:每新增 WHERE 就建新索引。索引帶來寫入開銷,應用最少索引覆蓋最多查詢。
策略:收集所有搜索條件 → 後端剖析工具分析 SQL → 分類構建最小索引集。嘗試向 WHERE 添加謂語以復用現有復合索引。
構建索引 1:(unexpectedFlag, dealStatus, tradeDate, isold) — 兩 UI 共用 (順序關鍵)
構建索引 2:(isold, participantCode, transactionType) — 後台專用
| 場景 | 推薦 | 理由 |
|---|---|---|
| OLTP (高頻增刪改、小結果集) | 綁定參數 | 解析一次、復用執行計劃、省 CPU/內存 |
| 數據倉庫 (低頻查詢、大結果集) | 字符串拼接 | 確定最優執行計劃 (如全表掃描 vs 索引) 比省解析更重要 |
Oracle 9i+ 首次綁定時探測值生成計劃,後續復用。若 OLTP 確知不同值需同計劃,綁定參數最優。
推薦在數據庫層做 GROUP BY/ORDER BY:數據庫優化器更擅長、節省網絡帶寬、跨數據庫可移植。除非業務規則極其特殊 HQL 不支持。
詳見 4.7.1。
HQL 允許傳遞 Native SQL,但跨數據庫不可移植,非必要勿用。
兩處定義:映射元數據 (默認) + HQL/Criteria (覆蓋)。
lazy/proxy,覆蓋為 join fetch (HQL/Criteria)eager/join,覆蓋為 lazy (Criteria 支持,HQL 不支持)select fetching 導致 N+1。兩種場景可視為模式:
join fetch 導致父對象重複、數據量暴增、會話緩存/GC 壓力大DISTINCT/Set 去重在 Hibernate 層而非數據庫層。若測試證明 N+1 更優,可用:
<set batch-size="10"> → WHERE id IN (?,?,...) 10 個一批,N 次降為 N/10fetch="subselect" → 子查詢 1 次搞定,前提是父查詢開銷低<set name="pojoBs" fetch="select" batch-size="10"> -- 批量
<set name="pojoBs" fetch="subselect"> -- Subselect
-- 批量 SQL: SELECT ... FROM pojoB WHERE pojoa_id IN (?,?,?,?,?,?,?,?,?,?)
-- Subselect SQL: SELECT ... FROM pojoB WHERE pojoa_id IN (SELECT id FROM pojoA WHERE ...)
除非遺留表包含大量無用列,否則不推薦 (分組帶來額外 SQL)。優先重設計領域對象分離關注點,或用 HQL/Criteria 投影 (select new DTO(...)) 獲取所需列。
3.3+ 推薦 RegionFactory,但多數緩存提供商 (OSCache、SwarmCache、Coherence、GigaSpaces) 僅支持舊 API。舊 API 核心:
CacheConcurrencyStrategy 四種實現對應 usage 屬性EntityUpdate/Delete/InsertAction 觸發緩存同步| Usage | 模式 | 特點 | 集群更新策略 |
|---|---|---|---|
| read-only | 只讀 | 最簡單、最高效、集群安全 | 無更新 |
| read-write | 非事務感知讀寫 | 需緩存鎖、推(push)更新、性能倒數第二 | 更新後推送新值到所有節點 |
| nonstrict-read-write | 非事務感知讀寫 (寬鬆) | 無鎖、拉(pull)失效、性能第二好 | 失效所有副本,下次讀取重載 |
| transactional | 事務感知 (JTA) | 數據庫+緩存同一 JTA 事務、兩階段提交、性能最差 | 事務內更新緩存 |
後兩種異步序列仍能保證 read committed 級一致性 (歸功軟鎖/悲觀清除)。
<class name="IsoChargeType">
<many-to-one name="estimateMethod" fetch="join" lazy="false"/>
<many-to-one name="allocationMethod" fetch="join" lazy="false"/>
<many-to-one name="chargeTypeCategory" fetch="join" lazy="false"/>
</class>
問題:所有用戶強制加載三關聯。優化:改 lazy="true" + 開啟三關聯 read-only 二級緩存,或保持 join fetch 但受益於引用數據緩存。兩法均能提升全局性能。
| 新 (RegionFactory) | 舊 (CacheProvider) |
|---|---|
| RegionFactory | CacheProvider |
| Region | Cache |
| EntityRegionAccessStrategy | CacheConcurrencyStrategy |
| CollectionRegionAccessStrategy | CacheConcurrencyStrategy |
理解舊模型助快速掌握新模型,<cache usage="..."> 配置共用。
hibernate.jdbc.fetch_size = 10 -- Oracle 建議經驗值,慢網絡更關鍵
hibernate.jdbc.batch_size = 20 -- 批量插入/更新
必須關閉緩存:批量加載通常一次性,大量數據入緩存會觸發頻繁 GC。
query.list() + 循環 setStatus() + Spring 聲明式事務 → 10 分鐘、頻繁 OOM、GC 暴增UPDATE ... SET status='Inprocess' WHERE ...) → 6 分鐘session.clear() 清空會話緩存 → 4 分鐘 (純加載耗時)Spring 2.5 不支持無狀態會話,故採用上述變通方案。
優先 join fetch,不適用時用 subselect/batch/lazy 減少 SQL。
單向 one-to-many (ElectricityDeal → DealCharge) 默認 not-null="false" update="true":
修復:<key column="DEAL_KEY" not-null="true" update="false"/> → INSERT 直接含外鍵,消除 10 條 UPDATE。或改雙向 many-to-one 由多端維護關聯。
select-before-update="true":對瞬時/分離對象產生額外 SELECT,但避免無異動 UPDATE。權衡:屬性少不值得,大表/遺留表配合 dynamic-update 開啟。
<key on-delete="cascade"/> + DBA 開啟 ON CASCADE DELETE 約束 → 刪父時數據庫自動刪子,省 100 條 DELETE。風險:繞過 Hibernate 版本樂觀鎖,誤刪父級連帶災難,多數 DBA 拒絕開啟。
Oracle 序列每次 SELECT NEXTVAL FROM DUAL,網絡慢時成瓶頸。3.2.3+ SequenceStyleGenerator 提供兩優化器:
| 優化器 | HiLo 算法 | 序列增量 | 安全性 |
|---|---|---|---|
| hilo | Hi = 組號 × increment_size | 1 | 序列專用,其他應用需同邏輯 |
| pooled | Hi = 序列值 (直接) | increment_size | 其他應用同用序列也安全 |
組內值用完才訪問序列。increment_size=100 時每 100 次訪問一次 DB。崩潰丟失組內值通常可接受 (Oracle 自帶序列緩存崩潰也丟)。
<id name="key" column="ID">
<generator class="org.hibernate.id.enhance.SequenceStyleGenerator">
<param name="sequence_name">SEQ_ELECTRICITY_DEALS</param>
<param name="initial_value">0</param>
<param name="increment_size">100</param>
<param name="optimizer">pooled</param>
</generator>
</id>
本文覆蓋 Hibernate 調優核心戰場:HQL/索引/綁定參數、抓取策略覆蓋/N+1 多種解法、二級緩存併發模式選擇/只讀引用數據緩存、批量加載 fetch_size/會話清理/DML、SQL 生成優化 (Insert+Update/Select-before-update/級聯刪除/增強序列生成器)。文中案例均來自實戰,可直接參考應用。
補充:Hibernate 可整合 In-Memory Data Grid (Oracle Coherence、GigaSpaces IMDG) 達到毫秒級響應。