原文以 Borland C#Builder + DB2 UDB 8.1 + ASP.NET 為例,但五大鐵律**普適於所有關係型數據庫與應用框架組合**。核心思想:將數據庫視為稀缺資源,以最小代價獲取業務所需數據。
| 查詢模式 | 耗時 | 網絡傳輸 | 備註 |
| SELECT * FROM LOTSOFRECORDS | 1.622s | ~10MB | 全欄位、全行傳輸 |
| SELECT KEYCOL, COL1, COL2, COL7 FROM LOTSOFRECORDS | 1.311s (81%) | ~4MB (40%) | 僅需欄位、索引覆蓋可能 |
延伸:WHERE 子句限制行數
-- 差:應用層過濾
DataTable dt = GetAllUsers(); // 100萬行
var vip = dt.AsEnumerable().Where(r => r.Field("Level") > 5);
// 優:資料庫層過濾
SELECT UserID, Name, Email FROM Users WHERE Level > 5; -- 命中索引,返回 500 行
原則:越早過濾 (資料庫 > 網絡 > 應用層),總體代價越低。
鐵律二:優化數據庫 — 索引是銀彈,統計是彈藥
索引建立決策樹
WHERE/JOIN/ORDER BY/GROUP BY 涉及欄位?
├─ 基數高 (唯一值多) → B-Tree 索引 (單欄/組合)
├─ 基數低 (性別/狀態) → 不建索引,或組合索引前綴
├─ 全文搜尋 → Fulltext Index / ngram
├─ 空間查詢 → Spatial Index
└─ JSON 欄位 → Functional Index / Generated Column + Index
DB2 專用工具:Index Advisor (索引建議器)
-- 提交工作負載 SQL 集合
db2advis -d sample -s "SELECT * FROM orders WHERE cust_id=100 AND order_date>'2024-01-01'" -o advice.txt
-- 輸出建議
CREATE INDEX IDX_ORDERS_CUST_DATE ON ORDERS (CUST_ID, ORDER_DATE);
-- Estimated improvement: 95%
通用做法:定期收集慢查詢、EXPLAIN 輸出,餵給索引建議工具 (MySQL `pt-index-usage`、PG `pg_stat_statements` + `hypopg`、SQL Server DTA)。
統計信息維護 (所有 RDBMS 通用)
-- DB2
RUNSTATS ON TABLE schema.table WITH DISTRIBUTION AND DETAILED INDEXES ALL;
-- MySQL
ANALYZE TABLE tbl_name; -- 輕量
ANALYZE TABLE tbl_name UPDATE HISTOGRAM; -- 8.0+ 直方圖
-- PostgreSQL
ANALYZE [VERBOSE] [table];
-- SQL Server
UPDATE STATISTICS schema.table WITH FULLSCAN;
統計過期 = 優化器盲目 = 執行計畫災難。納入定時任務 (每日/每周)。
鐵律三:利用 OLAP 函數優化分頁 — 徹底消除「全表掃描 + 應用層分頁」
傳統 DataGrid 預設分頁災難
- 查詢 10,000 行 → 傳輸 10,000 行 → 應用層只顯示第 3 頁 (20 行) → **99.8% 資源浪費**。
- 百萬行表 → 記憶體溢出 / 超時 / 鎖爭用。
DB2 ROW_NUMBER() OVER() 窗口函數方案
SELECT * FROM (
SELECT KEYCOL, COL1, COL2, COL7,
ROW_NUMBER() OVER (ORDER BY KEYCOL ASC) AS rn
FROM LOTSOFRECORDS
) AS t
WHERE t.rn BETWEEN 21 AND 40; -- 第 2 頁,每頁 20 行
通用分頁模板 (參數化)
-- 參數: @PageNo (1-based), @PageSize
DECLARE @StartRow INT = (@PageNo - 1) * @PageSize + 1;
DECLARE @EndRow INT = @StartRow + @PageSize - 1;
SELECT * FROM (
SELECT KEYCOL, COL1, COL2, COL7,
ROW_NUMBER() OVER (ORDER BY KEYCOL ASC) AS rn
FROM LOTSOFRECORDS
) AS t
WHERE t.rn BETWEEN @StartRow AND @EndRow;
各數據庫分頁語法對照
| 數據庫 | 語法 | 版本要求 |
| DB2 / Oracle / PostgreSQL / SQL Server 2012+ | OFFSET ... FETCH NEXT ... / ROW_NUMBER() 窗口函數 | 標準 SQL:2008 |
| MySQL 8.0+ | LIMIT ... OFFSET ... / 窗口函數 | 8.0+ |
| MySQL 5.7 | LIMIT ... OFFSET ... (大偏移慢) | 游標分頁優化 |
實測效能提升
- 預設分頁:0.9-1.5s (全表掃描 + 傳輸)
- 窗口函數分頁:0.5-0.7s (索引掃描 + 僅傳輸目標頁)
- **資源佔用降至 0.2%**,線性擴展支撐百萬/億級表。
鐵律四:存儲過程 — 將邏輯下沉至數據庫內核
執行路徑對比
| 階段 | 動態 SQL | 存儲過程 |
| 語法解析 | 每次執行 | 創建時一次 |
| 計畫生成 | 每次執行 (可緩存) | 創建時編譯優化 |
| 網絡傳輸 | 完整 SQL 文本 | 過程名 + 參數 (極小) |
| 權限檢查 | 每次表級檢查 | 過程級 EXECUTE 權限 |
DB2 存儲過程範例 (帶參數、返回游標)
CREATE PROCEDURE GET_PRODUCTS_IN_CATEGORY (IN CATCODE INTEGER)
DYNAMIC RESULT SETS 1
LANGUAGE SQL
P1: BEGIN
DECLARE C1 CURSOR WITH RETURN FOR
SELECT PRODUCTCODE, PRODUCTNAME, DESCRIPTION, UNITPRICE, IMAGEURL
FROM PRODUCTLIST
WHERE CATEGORYCODE = CATCODE;
OPEN C1;
END P1
.NET 調用模式 (Borland Data Provider / ADO.NET 通用)
BdpCommand cmd = new BdpCommand("GET_PRODUCTS_IN_CATEGORY", conn);
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add("CATCODE", BdpType.Integer).Value = categoryCode;
BdpDataAdapter da = new BdpDataAdapter(cmd);
DataSet ds = new DataSet();
da.Fill(ds, "Products");
dataGrid.DataSource = ds;
適用性邊界
- ✅ 複雜業務邏輯、多步事務、高頻調用、安全敏感 (隱藏表結構)。
- ❌ 簡單 CRUD、動態構建查詢、結果集極小 (開銷不抵收益)、團隊缺乏 DBA 技能。
鐵律五:緩存為王 — 記憶體速度碾壓磁盤/網絡
ASP.NET Cache 物件三大模式
// 1. 頁面級輸出緩存 (最簡單)
<%@ OutputCache Duration="60" VaryByParam="none" %>
// 2. 數據緩存 (程式化控制)
if (Cache["Categories"] == null) {
// 從 DB 讀取
DataSet ds = GetCategoriesFromDB();
// 插入緩存,6 小時絕對過期
Cache.Insert("Categories", ds, null,
DateTime.Now.AddHours(6), Cache.NoSlidingExpiration);
} else {
ds = (DataSet)Cache["Categories"];
}
// 3. 依賴緩存 (文件/鍵/聚合依賴)
Cache.Insert("Products", ds,
new SqlCacheDependency("connStr", "ProductsTable")); // SQL Server 專用
// 或文件依賴
Cache.Insert("Config", configObj, new CacheDependency("config.xml"));
緩存策略決策矩陣
| 數據特性 | 緩存策略 | 過期/淘汰 | 一致性保障 |
| 極少變更 (字典/配置) | 應用啟動載入、長期駐留 | 文件依賴/手動刷新 | 強一致 |
| 低頻變更 (商品分類) | 定時過期 (小時/天) | AbsoluteExpiration | 最終一致 (業務可接受) |
| 高頻讀、低頻寫 (用戶資料) | Write-Through / Read-Through | SlidingExpiration (20min) | 緩存旁路更新 DB 同步刪除 |
| 高頻讀寫 (庫存/計數器) | **不緩存** 或 Redis 原子操作 | N/A | 強一致 (DB 為準) |
分散式緩存升級 (Redis / Memcached)
- 解決單機記憶體上限、多實例緩存不一致、重啟預熱問題。
- StackExchange.Redis / EnyimMemcached 客戶端支援連接池、管道、Lua 腳本原子操作。
- 緩存穿透/擊穿/雪崩防護:布隆過濾器、互斥鎖重建、隨機過期時間。
總結:從五大鐵律到工程體系
- 按需檢索 → 代碼審查強制 `SELECT` 列清單,禁止 `*`。
- 優化數據庫 → 索引變更走變更流程,統計信息納入定時任務,慢查詢日誌每日分析。
- OLAP 分頁 → 框架封裝通用分頁組件,禁止手寫 `OFFSET` 大偏移。
- 存儲過程 → 核心交易邏輯下沉,應用層僅調用,版本管理納入 Git (SQL 腳本)。
- 緩存分層 → 本地緩存 (熱點小數據) + 分散式緩存 (共享大數據) + CDN (靜態資源),統一緩存鍵命名規範。
這五大鐵律不是孤立技巧,而是構建**高效能、可擴展、可維護**數據訪問層的基因密碼。將其內化為團隊開發規範、CI/CD 檢查門禁、架構評審核心指標,才能真正實現「優秀的代碼行行相似」。
- 上一篇文章: MySQL 單向主從同步部署實戰:FreeBSD 環境下雙機資料實時同步
- 下一篇文章: MySQL 入門完全指南:從連線、建庫建表、增刪改查到備份還原的零基礎實戰
Copyright ©
程式碼之家 All Rights Reserved