本文件記錄 /codes 後台介面的效能優化措施。
問題:
- 每次訪問
/codes/*頁面時,CodesController構造函數都會調用CodesRepository::allowedTableMap() - 該方法執行
SHOW TABLES查詢獲取資料庫中所有表的列表 - 即使只訪問單一表格(如
/codes/CBDB__NAME_FTS),也會查詢整個資料庫的表結構
影響:
- 每個請求都執行
SHOW TABLES,增加不必要的資料庫負載 - 在表數量較多的資料庫中,查詢時間會增加
- 浪費網路和 CPU 資源
解決方案:
- 直接從配置文件(
config/codes.php)讀取白名單表名 - 在記憶體中構建大小寫映射,不需要查詢資料庫
- 如果配置的表名不存在,在實際查詢時自然會報錯
代碼變更:
// 優化前(每次都執行 SHOW TABLES)
public function __construct(CodesRepository $codesRepository, OperationRepository $operationRepository)
{
$this->allowedTables = $this->codesrepostory->allowedTables();
$map = $this->codesrepostory->allowedTableMap(); // ← 執行 SHOW TABLES
$this->allowedTablesMap = $map;
}
// 優化後(不查詢資料庫)
public function __construct(CodesRepository $codesRepository, OperationRepository $operationRepository)
{
$this->allowedTables = $this->codesrepostory->allowedTables();
// 直接從配置構建映射
$this->allowedTablesMap = [];
foreach ($this->allowedTables as $table) {
$this->allowedTablesMap[strtoupper($table)] = $table;
}
}效能提升:
- 消除每個請求的
SHOW TABLES查詢(~5-20ms) - 減少資料庫連接開銷
- 降低資料庫伺服器負載
注意事項:
- 表名必須在
config/codes.php中正確配置(包括大小寫) - 如果手動修改資料庫表名,需同步更新配置文件
- 如果配置的表不存在,會在實際查詢時報錯(而非在控制器初始化時)
問題:
buildTableHead()方法需要樣本行來智能推斷要顯示哪些列- 原代碼對所有表都執行
SELECT * FROM table LIMIT 1查詢樣本行 - 但對於有
tableColumnOverrides配置的表,根本不需要樣本行
解決方案:
- 先檢查是否有列配置(
tableColumnOverrides) - 只有沒有配置的表才查詢樣本行
- 有配置的表(如
CBDB__NAME_FTS)直接使用配置,不查詢數據庫
代碼變更:
// 優化前(總是查詢樣本行)
$sampleRow = (clone $query)->first();
$thead = $this->buildTableHead($table, $sampleRow);
// 優化後(有配置時不查詢)
$upperTable = strtoupper($table);
$hasColumnConfig = isset($this->tableColumnOverrides[$upperTable]);
$sampleRow = $hasColumnConfig ? null : (clone $query)->first();
$thead = $this->buildTableHead($table, $sampleRow);影響的表(有 tableColumnOverrides 配置):
CBDB__NAME_FTS✅CBDB__TRAD_SIMP_MAP✅ADDR_BELONGS_DATA✅ADDR_CODES✅ADDRESSES✅DYNASTIES✅TEXT_INSTANCE_DATA✅MERGED_PERSON_DATA✅OFFICE_CODES✅ALTNAME_CODES✅APPOINTMENT_CODES✅TEXT_CODES✅SOCIAL_INSTITUTION_CODES✅
效能提升:
- 對於有配置的表,消除
LIMIT 1查詢(~2-5ms) - 減少約 14 個已配置表的不必要查詢
問題:
getKeyColumns()方法在開頭就查詢所有列(Schema::getColumnListing())- 但對於有
tablePrimaryKeyOverrides配置的表,會直接返回配置 - 查詢結果根本沒用到,造成不必要的
information_schema查詢
解決方案:
- 先檢查
tablePrimaryKeyOverrides配置,有配置則直接返回 - 只有在真正需要時(作為 fallback)才查詢列
代碼變更:
// 優化前(總是查詢列)
$columns = Schema::getColumnListing($table);
if (isset($this->tablePrimaryKeyOverrides[$upperTable])) {
return $overrideKeys; // $columns 根本沒用到!
}
// ... 後面才用到 $columns
// 優化後(只在需要時查詢)
if (isset($this->tablePrimaryKeyOverrides[$upperTable])) {
return $overrideKeys; // 直接返回,不查詢
}
// ... 其他邏輯
if (empty($keys)) {
$columns = Schema::getColumnListing($table); // 只在這裡才查詢
// ...
}影響的表(有 tablePrimaryKeyOverrides 配置):
CBDB__NAME_FTS✅CBDB__TRAD_SIMP_MAP✅TEXT_CODES✅
效能提升:
- 對於有主鍵配置的表,消除
information_schema.columns查詢(~3-5ms) - 從兩次
Schema::getColumnListing()減少到一次(對於CBDB__NAME_FTS)
詳見 CODES_TABLES.md。
效能提升:
- 分頁查詢:從 5000ms(第 40000 頁)降至 3ms(恆定)
- 前綴搜尋:可利用索引,~5ms
優化前(訪問 /codes/CBDB__NAME_FTS):
SHOW TABLES; -- ~10ms
SELECT column_name FROM information_schema.columns WHERE table_name = ?; -- ~3ms (第1次)
SELECT * FROM CBDB__NAME_FTS LIMIT 1; -- ~3ms
SELECT column_name FROM information_schema.columns WHERE table_name = ?; -- ~3ms (第2次)
SELECT * FROM CBDB__NAME_FTS LIMIT 20 OFFSET 0; -- ~5ms
-- 總計:~24ms優化後:
SELECT column_name FROM information_schema.columns WHERE table_name = ?; -- ~3ms (僅1次)
SELECT * FROM CBDB__NAME_FTS WHERE id > 0 LIMIT 20; -- ~3ms
-- 總計:~6ms(減少 75%)優化措施:
- ✅ 移除
SHOW TABLES查詢(-10ms) - ✅ 移除樣本行
LIMIT 1查詢(-3ms) - ✅ 優化
getKeyColumns()減少一次information_schema查詢(-3ms) - ✅ 使用游標分頁替代 OFFSET(-2ms)
- 頁面響應時間:應 < 100ms
- 資料庫查詢數:每個請求應 ≤ 3 次查詢
- 慢查詢:標記 > 100ms 的查詢
- CODES_TABLES.md - 代碼表白名單與游標分頁
- NAME_SEARCH_COMMANDS.md - 姓名搜尋索引說明
- DATABASE.md - 資料庫架構說明