參考地址: https://mp.weixin.qq.com/s/YgKknhoqcUnXOjMs6YGvHg
我們知道,軟件工程是為了解決軟件危機的,它是采用工程的概念、原理、 技術和方法來開發與維護軟件,把經過時間考驗而證明正確的管理技術和當前能夠得到的最好的技術方法結合起來。
在軟件開發的過程中,數據庫設計是非常重要的,它需要根據需求分析設抽象出 E-R 圖,邏輯結構設計,數據庫選型,物理設計,實施及運維。下面就聊聊那些年數據庫設計的那些事。
需求分析階段
要進行數據庫設計首先要了解用戶需求,參與到用戶需求分析中去,需求分析常用 SA(Structured Analysis:結構化分析方法)強調開發方法的結構合理性以及所開發軟件的結構合理性的軟件開發方法,
是生命周期法的繼承與發展,是生命周期法與結構化程序設計思想的結合。
其基本思想是用系統工程的思想和工程化得方法,根據用戶至上的原則,自始自終按照結構化、模塊化,自頂向下地對系統進行分析與設計。建立的主要步驟如下:
- 首先畫系統的輸入輸出,先畫頂層數據流程圖(DFD:Data Flow Diagram),頂層數據流程圖只包含一個加工,用以表示被開發的系統,然后考慮該系統有哪些輸入、輸出數據流。
- 畫系統內部,即畫下層數據流層圖。
下面是一個交易系統的 DFD,需要先畫出頂層數據流圖,主要包括系統子模塊之間的交互,然后再進一步對子模塊進行分解。
概念設計階段
概念設計是整個數據庫設計的關鍵,它是對需求分析階段的成果進行綜合,歸檔以及抽象出一個獨立具體的 DBMS 模型,與具體的 RDBMS 產品無關。
在實際的開發中,常用 E-R(Entity-Relationship:實體關系)圖來表示,常用的工具 PowerDesigner,
可以實現 CDM(概念數據模型)->LDM(邏輯數據模型)->PDM(物理數據模型)->Database 的自動轉換,這個過程稱為正向工程,
如果有 database 建庫腳本,也可以通過 PowerDesigner 工具生成 CDM,即 Database->PDM->LDM->CDM,稱為反向工程。
概念設計通常采用自底向上,首先定義各系統局部的概念模型,然后再將他們集成合並起來,得到全局的概念模型。
舉個例子說明下,現在負責交易系統的開發,主要涉及訂單,價格模塊,分別交給不同的開發去設計開發。
首先每個人要根據需求分析抽象出自己的實體 Entity 及之間的關系 Relationship,設計初步完成之后就要開會討論了,把每個開發的 ER 圖合並起來,就得到全局交易系統的 CDM。
名詞動詞形容詞分析法
開發如何根據需求分析設計 ER 圖,完成模塊的詳細設計,提供接口文檔,最重要的是需求分析抽象 CDM 階段的 ER 圖,一種行之有效的方法就是名稱動詞形容詞分析法,下面就詳細解釋下這種分析方法。
還是舉例說明吧,現在讓我負責交易系統的訂單這塊的開發,在需求分析文檔里看到一句話實現訂單的高效管理,分析的過程如下:
名稱:訂單,訂單就是一個 Entity,也可以拆分成多個 Entity,抽象出每個 Entity 的 Attribute(前期可能由於需求不明確,可以只做確認的內容),
Entity 通過 PowerDesigner 的正向工程轉換成數據庫里的數據表,Attribute 就是表的字段;數據表通過 ORM 映射到 Java 里的就是 Class,字段就是 private 屬性。
動詞:管理,也就是要對訂單要進行增刪改查 CRUD 操作。
形容詞:高效,首先想到在訂單表上創建合適的索引吧,其次根據業務的發展,訂單表太大會影響寫入性能,是否要進行讀寫分離,分庫分表操作。
數據庫設計三范式
第一范式 1NF:確保每個字段保持原子性,不可分割。
對於用戶表 users 來說,有用戶姓名(一般由 first_name 和 last_name 組成),如果使用類似 Oracle 的復合數據類型,就違反了 1NF。
很明顯 users 的字段 user_name 是一個自定類型,是可分解的,這就違反了 1NF。
第二范式 2NF:確保字段完全依賴於主鍵。
一個表中只能保存一種數據,不可以把多種數據保存在一張表里,假如一張表既存儲了用戶信息,又存儲商品信息,
還存儲了訂單信息,這樣就違反了 2NF,而應該將用戶表,商品表,訂單表拆分成三張表,確保字段是該表擁有的。
第三范式 3NF:必須滿足 2NF,實體中每個屬性與主鍵直接相關而不能間接相關。
這個也不難理解,對於訂單表 orders 來講,是要存儲用戶表 users 的 user_id,要明確哪個用戶下的單,有些業務場景是要獲取 users 表的用戶姓名 user_name,
為了減少 orders 和 users 表的關聯查詢,將 user_name 冗余到 orders 表中,這種設計就違反了 3NF,減少數據冗余,可以通過主外鍵進行表之間連接
到底該不該使用外鍵 Foreign Key
外鍵目的是為了保證數據完整性和一致性,避免產生臟數據,設置外鍵有啥缺點呢。
- 影響寫入性能:對於 insert 來說,每次都要判斷從表的外鍵列是否在主表中存在(例如每次插入 orders 表,都要判斷下 user_id 是否在 users 中存在),
會降低數據庫的寫入性能,對於 MySQL 本來就只有 Master 輸出寫能力的數據庫,就不太合適了,MySQL 開發規范規定不允許使用外鍵也是有一定道理的。
- 並發問題:在使用外鍵的情況下,每次修改數據都需要去另外一個表檢查數據,需要獲取額外的鎖。在高並發大場景,使用外鍵造成死鎖或鎖等幾率更大。
實際開發中,更多的是不靠外鍵來保證數據的完整性和一致性,而是通過的業務邏輯,比如用戶要下單,必須先登錄系統,下單只需要將登錄的用戶編號寫入到訂單表,
這個用戶必然是存在於用戶表的,對於一個用戶友好的系統來說,盡量讓用戶選擇,不要人工輸入,這樣可以保證數據一致性,避免臟數據的產生。
邏輯設計階段
邏輯設計階段是將概念數據模型轉換為具體的 DBMS 所支持的數據模型,並將進行優化。雖然 LDM 獨立於 DBMS 的,但可以進行外鍵,索引,視圖等對象的設計工作。
在此階段,各子模塊的 E-R 圖之間的沖突主要有三類:屬性沖突,命名沖突和結構沖突,同時 E-R 圖向關系模型的轉換,
要解決如何將實體性和實體間的聯系轉換為關系模式,確定這些關系模式的屬性和碼,實際開發中,邏輯設計階段不是必須的,有些是從 CDM 直接到 PDM 了。
數據庫選型
數據庫選型是非常重要的環節,一般在需求分析完成之后,通過架構評審會進行確認,數據庫方面主要包括數據存儲,
檢索,安全,讀寫分離,分庫分表,數據歸檔,接入數據倉庫都要進行確認,根據業務的場景對相關的數據庫產品進行調研比對,選擇最適合業務場景的數據庫作為存儲。
舉個例子:對於一個 DAU 1000W TPS 3W 的交易的業務場景,如果使用 MySQL 來存儲,我們知道原生的 MySQL 寫入瓶頸,以及訂單相關表數據量增長過快導致的性能問題,
不太適合這種高並發寫的場景,可以考慮使用分布式 MySQL,例如常見的 DRDS,TiDB,OceanBase。既可以解決原生 MySQL 寫入瓶頸,同時也可以處理單表數據量大導致的分庫分表問題。
同樣對於優酷,愛奇藝這種視頻類系統,使用 MySQL 來存儲就不太合適了,應該采用 MongoDB 集群來存儲;對於京東,淘寶的這種搜索服務采用 ElastSearch 數據庫集群處理會更高效。
物理設計階段
邏輯設計階段和數據庫選型完成之后,就可以通過 LDM 生成 PDM 了,在物理設計階段,需要設計跟 RDBMS 相關的對象,例如設計存儲過程,觸發器,用戶自定義函數,表空間等。
數據庫實施階段
例如選擇的是 MySQL 數據庫,通過 PDM 生成數據庫的建庫腳本之后,需要進行規范性檢查,通過之后就可以創建表結構,規范性檢查可以借助開源的 SQL 審核工具,
如 Yearning,Archery 都可以設置規則,檢查之后會給出整改建議,能夠幫我們自動實現 SQL Review。Yearning 是用 go 開發,目前只支持 MySQL 數據庫,
Archery 可以支持多種數據庫。下面是 Yearning 自動化 SQL 審核平台的一個 DDL 工單的檢測示例。
檢測通過后就可以提交工單了,審核通過后就會自動執行 DDL 腳本建庫。
數據庫維護階段
數據庫維護階段主要包括業務支撐和數據庫運維,簡單總結了下,如下圖所示。
總結
實際開發中,數據庫設計階段是非常重要,通常都是開發自己根據業務模塊的需求去分析,抽取成 CDM 中的 E-R 圖,轉換成 LDM,
經過數據庫選型及生成 PDM,最終生成數據庫表,然后才能開始 coding,測試、發布上線以及版本迭代,為了保證線上業務的安全穩定高效,就需要對數據庫進行精細化管理和維護。
仔細觀察不難發現,數據庫設計的核心就是對需求分析的理解以及抽取沉底出 E-R 圖,這就需要對行業及相關業務有深刻立即及抽象能力,
大家有木有發現,招聘 Java 工程師的前面附加了業務屬性,例如用戶域 Java 工程師,支付域 Java 工程師,主要體現在需求分析抽象以及數據模型設計能力上,開發過程中多參與業務需求討論是非常有必要的。