#  讓歷史人物搜索快 500 倍 

 



*從 1500 毫秒到 3 毫秒：倒排索引在 CBDB 的落地*

[English Version](https://cbdb.hsites.harvard.edu/making-historical-figure-search-500x-faster)

---

CBDB 目前收錄約 **65 萬名**歷史人物、**100 萬條**姓名記錄。過去搜尋如「蘇軾」「安石」等名稱，常需要 1～2 秒；在大量查詢中，延遲會累積成顯著的研究成本。

CBDB 技術團隊重新設計了姓名資料的索引方式,將查詢速度由 1500 毫秒縮減至 3 毫秒，**提升約 500 倍**。核心方法是依中文姓名特性構建**倒排索引**(Inverted Index)，並支援**繁簡互查**。

---

## 為什麼原本的搜尋會慢？

傳統查詢使用：

`WHERE name LIKE '%蘇軾%'`

這種「包含式」查詢無法使用資料庫的 B-Tree 索引，只能逐條掃描 100 萬條記錄，因此需要 1～1.5 秒。

實測數據：

Sort查詢耗時「安石」

1374 ms

「蘇軾」

1540 ms





**瓶頸並非計算能力，而是資料組織方式不適合中文姓名搜尋。**

---

## 為何不直接用全文檢索？

雖然 Elasticsearch、PostgreSQL FTS、MySQL ngram 都能加速中文搜尋，但面臨 CBDB 的限制：

- **MariaDB 10.3** 無 ngram（相容性問題）
- **PostgreSQL FTS** 會綁定單一資料庫
- **Elasticsearch** 額外維護成本高

**CBDB 的原則**：必須跨資料庫可用、長期可維護、使用標準 SQL。

因此選擇**自行構建倒排索引**。

---

## 什麼是倒排索引？

**傳統方式**（正排）：

`人物 ID → 姓名`

**倒排方式**：

`姓名的部分 → 多個人物 ID`

例如根據 CBDB 真實資料：

`安石 → [陳安石, 裴安石, 李安石, 盧安石, 楊安石, 吳安石, 邵安石, ...]       （共 165 人） 軾   → [蘇軾, 蔣繼軾, 張軾, 林軾, 周軾, ...]`

搜尋時，資料庫可直接查詢這份「反向關係表」，完全不需掃描全部姓名記錄。

---

## 中文姓名的拆分方式

採用**後綴拆分**（suffix splitting）：

`王安石 → [王安石, 安石, 石] 介甫   → [介甫, 甫] 半山   → [半山, 山]`

因此原本的：

`LIKE '%石%'  -- 無法使用索引`

轉換為可利用索引的：

`LIKE '石%'   -- 完整使用 B-Tree 索引`

**這是效能提升的關鍵。**

---

## 支援繁簡互查

以 OpenCC 單字映射建立簡體後綴：

`蘇軾 → [蘇軾, 軾]  （繁體） 苏轼 → [苏轼, 轼]  （簡體）`

**查詢效果**：

- ✅ 輸入「蘇軾」→ 命中
- ✅ 輸入「苏轼」→ 也能命中
- ✅ 輸入「軾」 / 「轼」→ 同樣命中

**不需切換輸入法，繁簡一致處理。**

---

## 實作與資料規模

倒排索引表 `CBDB__NAME_FTS` 以標準 SQL 建構：

`CREATE TABLE CBDB__NAME_FTS (    search_term VARCHAR(100),  -- 後綴    c_personid INT,            -- 人物 ID    full_name VARCHAR(100),    is_simplified TINYINT(1),    INDEX (search_term, c_personid) );`

**資料量**：

Sort項目數值人物

65 萬

姓名

100 萬

倒排索引

約 300 萬

空間需求

250–350 MB

重建時間

15–30 分鐘





**相容** MySQL、MariaDB、PostgreSQL、SQLite。

---

## 性能效果

Sort查詢類型優化前優化後提升「石」

~1500 ms

5 ms

**300×**

「安石」

~1374 ms

3 ms

**458×**

「王安石」

~1540 ms

3 ms

**513×**

人物 ID

~1500 ms

2 ms

**750×**





**平均約 500× 提升。**

對研究者而言，搜尋結果幾乎是「瞬間出現」。

---

## 使用者無需改變任何搜尋習慣

現在皆可毫秒級完成：

- ✅ 「王安石」
- ✅ 「安石」
- ✅ 「石」
- ✅ 「蘇軾」 / 「苏轼」
- ✅ 「介甫」
- ✅ 「歐陽」等複姓

搜尋邏輯與過去完全一致，但速度提升數百倍。

---

## 技術選擇的原則

1. **資料組織決定性能上限**：倒排索引以空間換時間
2. **可移植性**：不依賴特定資料庫功能
3. **長期可維護**：十年後仍能重建與更新
4. **繁簡互查在資料層解決**，而非外掛補丁

---

## 結語：毫秒級查詢的 CBDB

倒排索引讓 CBDB 姓名搜尋從秒級進入毫秒級，極大提升研究體驗。

**無論研究者輸入繁體、簡體、全名或片段，系統都能在瞬間返回結果。**

在更快速的系統基礎上，未來也可探索拼音搜尋、異體字處理與語義查詢等功能。

---

## 附錄：深入閱讀

### 技術文檔

詳細的設計原理、實作細節與代碼，請參閱：

- [**完整技術文檔**](https://github.com/cbdb-project/cbdb-online-main-server/blob/develop/NAME_SEARCH_PERFORMANCE_IMPROVEMENT.md) - 包含拆分算法、內存優化、事務管理等細節
- [**命令行工具說明**](https://github.com/cbdb-project/cbdb-online-main-server/blob/develop/NAME_SEARCH_COMMANDS.md) - 索引重建、繁簡映射表導入等操作指南
- [**其他性能優化**](https://github.com/cbdb-project/cbdb-online-main-server/blob/develop/CODES_PERFORMANCE.md) - CBDB 系統的其他優化措施

### 核心代碼

- `app/Console/Commands/RebuildNameSearchIndex.php` - 索引重建命令
- `app/Repositories/BiogMainRepository.php` - 搜尋查詢邏輯
- `database/migrations/*_create_internal_name_search_tables.php` - 表結構定義

### 關鍵指標

Sort指標數值查詢時間（優化前）

1500ms

查詢時間（優化後）

3ms

性能提升

500×

倒排記錄數

300萬條

索引空間

250-350 MB

覆蓋人物數

65萬

覆蓋名字數

100萬

繁簡映射

7000+ 字符





### 邊界條件說明

- **複姓處理**：對於歐陽、司馬等複姓，索引不做姓氏識別，視為一般字串拆分（詳見完整文檔）
- **多義字**：採用 OpenCC 逐字映射，不做語境消歧（如「干/幹/乾」使用默認對應）
- **外文名**：暫不拆分，整詞存儲
- **單字後綴**：允許產生單字後綴（如「甫」），不特別排除

---

*本文由 Frank Lin 撰寫，2025年11月*

*技術問題或建議歡迎通過* [*GitHub Issues*](https://github.com/cbdb-project/cbdb-online-main-server/issues) *聯繫我們*