常見 Anti-pattern
使用方面
1. 把 RDBMS 用在 Inter-process communication
- RDBMS 內有 REDO LOG,UNDO LOG,Lock Manager,都是為了維護資料的完整性和正確性。
- 單純用來暫存 IPC 數據,會浪費 CPU 和 Storage IO
- 最簡單的 IPC,可以使用 Redis 中的 List,進階的請用 rabbitMQ
2. 把 RDBMS 當作 logging server
- log 是建立了便永不改動的數據,而且這些數據極少被單獨使用,一般都會經過 aggregation 用來產生報告
- txt 檔案是 log 的理想存放位置
3. 在 Master 上產生報告
- 應該在 read-only slave 上進行
- 產生報告時會吃掉 Master 寶貴的 Storage IO,而且佔用寶貴的 Memory
- 更大型系統,應該有專門用來做 Reporting 的資料庫(像是 Data Warehouse)
編程方面
1. SQL statement 超過 100 行
- 會有報應的,請相信我
- 太長的 statement,會讓 Query optimizer 無法有效工作
- 應該把太長的 statement 改成多個步驟,每個步驟的結果用 temporary table 去暫存
2. 把系統邏輯放到 RDBMS 的 trigger
- 除非正在 database refactoring,利用 trigger 作為一種暫時性手段
- 應該把系統邏輯放到 application tier
- 當出現了 bug 而引起資料錯誤,而你要在十分鐘內作緊急的資料更正,這些 trigger 會妨礙你的
3. 把所有的東西都寫在 application tier
- data intensive / multiple steps 工作可以考慮用 stored procedure 來做
4. 在 stored procedure 中使用 loop
- SQL 本身是 set-based 語言,請盡量使用 SQL 作 set operation
5. 在 stored procedure 中使用 recursion
- 請考慮把工作放在 application tier
Schema 設計方面
1. 使用 auto-increment 作為 PK
- 別害怕 ORM 層的麻煩,系統發生 blocking 時要花的氣力遠比 ORM 麻煩要多
- 如果有 natural key,請優先使用,natural PK 有機會能用作 index skip scan
- 真的不能用 natural key 時,請用 UUID
2. 為了報表而建立 index
- index 會大幅影響 master 的 WRITE 效能
- 報表能在 slave node 慢慢產生
- 報表很多時候需要 range scan,secondary index 幫助不大
3. smart column
- 1NF 的定義:所有 column 的 value 都必須是 atomic value
- xml, json, array 這些東西全都違反 1NF
- xml, json 內有過份高的自由性,未把 column value 內容拿出來前,永遠猜不到 column 內具體存了什麼東西
- smart column 內部資料不能再建立 index,當報表要用上 smart column 內部資料作 filtering/joining 時,不但 Query 難寫,其效能也會低下
- 空運貨物管制系統中,flight 這個 table 的 PK 不是 flight_no ,而是
- 不是以<”BR892”> 作為 PK,而是<”BR”, “892”> 作為 PK
- 系統想要找到長榮航空的所有航次時
- 只需要 where airline_code = 'BR'
- 而不是 where flight_no like 'BR%'
4. denormalization
- 為了迴避 subquery 和 join,有人想進行 denormalization,故意地在一個 table 放進 multiple data model 的數據
- 例子:因為用戶在檢查自己所買的機票時,頁面也會顯示航次出發時間/航次到達時間的資訊,因此做了 denormalization,在
ticket
這一個 table 中也存放了航次資訊 - OLTP 的 denormalization 一般無助提升系統效能,反而大幅增加改動資料時的麻煩/出錯可能性
- denormalization 讓 table schema 變得不再直覺
Documentation?你認真的嗎?三個月後,有多少人還記得自己以前做過的東西(特別是長期爆肝下)
5. 多用途 column
你的系統是用來生產瑞士軍刀的嗎?- 分辦方法:系統需要知道這個 column /其他 column 的 value ,才知道怎麼去使用這個 column 的內容
Example
能讓用戶以 username/password 或是 facebook 身份登入的系統
有人建議反正用戶只會以單一的方式登入,所以 user 這個 table 只有一個 auth 的 column - 以 username 登入的用戶,auth 的內容是:username:<username>/<hashed_password>
- 以 facebook 身份登入的用戶,auth 的內容是:facebook_id:<facebook_id>
- 理由是這樣的做法可以只使用一個 column,也只有一個 secondary index
致命缺點
- 這設計只能讓用戶以單一方法登入
- 因為 hashed_password 是以 salt password 來計算的,這設計讓 index 失效
- 如果要知道系統有多少 facebook 用戶,便需要
where auth like 'facebook_id%'
6. 多用途 table
- 多用途 column 的進化版
此 anti-pattern 一旦發生,系統必亡- 分辦方法:
- 某一 table 內,大量 column 的 value 是 null
- 這個 table 內有一個叫「type 」或是「kind 」的 column,application tier 需要先知道這個 column 的值才能決定這筆 record 怎麼使用
Example
某空運系統(如有雷同,實屬不幸)
- 出境貨物和入境貨物都放到
freight
這個 table,然後 freight_type 這個 column,I 代表入境貨物,E 代表出境貨物 - 結果某天某個 procedure 出錯,type 由 I 改成 E,引起全面性數據錯誤
- 對於出境的系統流程,freight 當然只跟出境相關的 table join;入境時只跟入境相關的 table join
- 結果,optimizer 對於這種擁有雙重性格的 table,常常使用了非最佳化的 execution plan