零、什麼是數據庫設計?
簡單來說,數據庫設計就是根據業務系統的具體需求,結合我們所選的DBMS(數據庫管理系統),爲這個業務系統構造最優的數據庫存儲模型。並建立好數據庫中的表結構及表與表之間的關聯關係的過程。使之能有效的對應用系統中的數據進行存儲,並可以高效的對已經存儲的數據進行訪問。
- 關係型數據庫管理系統:MySQL、Oracle、SQLServer、PgSql
- 非關係型數據庫管理系統:Redis、MongoDB、Memcache
- 需求分析
1、數據是什麼
2、數據有哪些屬性
3、數據和屬性各自的特點有哪些
特點舉例:時效性和非時效性數據
(1)具有時效性的數據可以採取過期、清理或者歸檔方式處理(例如驗證碼等)
(2)增長很快數據量大但非核心數據
-
邏輯設計
使用ER圖對數據庫進行邏輯建模 -
物理設計
這一步開始需要考慮數據庫管理系統,根據數據庫自身的特點把邏輯設計轉換爲物理設計 -
維護優化
1、新的需求信息建表
2、索引優化
3、大表拆分
二、需求分析
實體關係
實體及實體之間的關係(1對1、1對多、多對多)
需求分析例子
以一個小型的電子商務網站爲例,在這個電子商務網站的系統中包含了以下幾個核心模塊:用戶模塊、商品模塊、訂單模塊、購物車模塊、供應商模塊。
- 用戶模塊
- 商品模塊
- 訂單模塊
- 購物車模塊
- 供應商模塊
最終實體關係圖(通過聯繫集可轉換爲實際應用關係集,下面ER圖例子)
商品——供應商:一種商品可由多個供應商提供,一個供應商可提供多種商品
商品——訂單:一種商品於存在多個訂單,一條訂單存在多個商品
商品——購物車:一個購物車存在多個商品,一種商品可存放在多個購物車中
訂單——用戶:一個用戶可存在多個訂單,一條訂單隻屬於一個用戶
購物車——用戶:一個用戶可擁有多個購物車,一個購物車只屬於一個用戶
三、邏輯設計
- 將需求轉換爲數據庫的邏輯模型
- 通過ER圖的形式對邏輯模型進行展示
- 和所選用的具體的DBMS系統無關
名詞解釋:
實體:顯示世界中客觀存在並可以被區別的事物。例如“一個學生”“一本書”
關係:一個關係對應通常所說的一張表
元組:表中的一行即爲一個元組
屬性:表中的一列即爲一個屬性;每一個屬性都有一個名詞,稱爲屬性名
候選碼:表中的某個屬性組或屬性,它可以唯一確定一個元祖(唯一值,篩選某行數據)
主碼:一個關係有多個候選碼,選定其中一個爲主碼(例如用戶ID和身份證號都爲唯一侯,選擇ID爲主碼,即主鍵)
外碼:一個屬性(或屬性組),它不是本表的候選碼,但它是另一張表的候選碼,則爲外碼(外鍵)
域:屬性的取值範圍(例如性別只有男和女)
分量:元組的一個屬性值
表的設計範式:
注:後一範式的規則都是基於前一範式
第一範式(1NF):要求數據庫中的表都是二維表。(參考二維數組和三維數組的區別)
(確保每列保持原子性)
第二範式(2NF):在第一範式基礎上,表中不存在非關鍵字段(不唯一),對任一候選字段(唯一)的部分函數依賴,必須全部依賴於全部主鍵。
(確保表中的每列都和主鍵相關)
一張圖解釋範式定義:
圖片來源: https://blog.csdn.net/weixin_43971764/article/details/88677688
舉個例子:
上圖主鍵爲(商品名稱,供應商名稱)
問題,部分非關鍵字字段依賴於部分主鍵:
(商品名稱)->(價格,描述,重量,商品有效期)
(供應商名稱)->(供應商電話)
不符合第二範式要求的表存在下列問題:
- 數據冗餘(例如上圖部分非主鍵依賴於部分主鍵屬部分函數依賴,造成另一部分主鍵和非關鍵字段重複出現)
- 插入異常(例如上圖,當沒有插入一廠的信息時,一廠的相關分類信息也就不存在)
- 更新異常(例如上圖,一插入新廠可樂時,對應的可樂屬性信息又插入一次,造成冗餘)
- 刪除異常(例如上圖,當刪除可樂信息時,一廠的相關分類信息也被刪除)
第三範式(3NF):於第二範式基礎上,表中不存在非關鍵字段(不唯一),對任意候選字段(唯一)存在傳遞函數。
(確保每列都和主鍵列直接相關,而不是間接相關)
舉個例子:
上圖的關鍵字段爲商品名稱,而後面兩個字段存在以下傳遞函數依賴關係:
(商品名稱)->(分類)->(分類描述)
不符合第三範式要求的表存在下列問題:
- 數據冗餘(例如上圖中,分類和分類描冗餘字段,關於描述字段往往比較長,一般單獨分一張表則只需一條描述記錄)
- 數據的插入異常(例如上圖,每次都需要插入一次分類和分類描述,沒有插入某類商品時,對應分類也就不存在)
- 數據的更新異常(例如上圖,單獨更新商品描述信息同時則需要更新所有商品對應的描述)
- 數據的刪除異常(例如上圖,當刪除所有商品時,對應的分類和描述也隨之刪除。此分類也就不存在)
BC範式(BCNF):在第三範式的基礎上,如果是複合關鍵字,則複合關鍵字之間也不能存在函數依賴關係
(聯合主鍵之間互相依賴)
舉個例子:
存在下列關係因不符合BCNF要求:
供應商——>供應商聯繫人
供應商聯繫人——>供應商
-
當需求:需商品ID綁定供應商聯繫人,而供應商聯繫人綁定供應商時。則分表如下:
表1:應商聯繫人、商品ID、商品數量
表2:供應商聯繫人、供應商 -
當需求:需需商品ID直接綁定供應商。則分表如下:
- 插入和刪除異常(如上圖,如果飲料廠沒有提供商品信息,也就找不到廠商相關信息)
- 更新異常和數據冗餘(如上圖,如果飲料廠新增商品,則會一直更新聯繫人信息,且如果廠商聯繫表多了聯繫方式、地址等字段就比較浪費空間)
四、物理設計
- 選擇合適的數據庫管理系統
Oracle、SQLServer、MySQL及PgSQL等。 - 定義數據庫、表及字段的命名規範
- 根據所選的DBMS系統選擇合適的字段類型
- 反範式化設計
爲提高查詢速度,可以在表中適當增加冗餘。以空間換取時間
選擇合適的數據庫管理系統
MySQL常用的存儲引擎
表及字段的命名規則
所有對象命名應該遵循下述原則:
- 可讀性原則
使用大寫和小寫來格式化庫對象名字以獲得良好的可讀性。(大小駝峯)
例如:使用CustAddress而不是custaddress - 表意性原則
對象的名字應該能夠描述它所標識的對象。
例如:對於表,表名應體現所存儲的內容,對於存儲過程,應體現存儲過程的功能 - 場面原則
儘可能少使用或不使用縮寫
適用於數據庫名之外的任一對象
字段類型的選擇原則
列的數據類型一方面影響數據存儲空間的開銷,另一方面也會影響查詢性能。
當一個列可以選擇多種數據時,優先順序如下:
數字類型——>日期或二進制類型——>字符類型
對於相同級別的數據類型,應優先選擇佔用空間小的數據類型
例如下面Birthday,最佳的選擇類型是int
以上選擇原則主要是從下面兩個角度考慮:
- 在對數據進行比較(查詢條件、join條件及排序)操作時:
同樣的數據,字符處理往往比數字處理慢 - 在數據庫中,數據處理以頁(16k)爲單位,列的長度越小,利於性能的提升。
char、varchar與nvarchar的選擇
- 如果列中要存儲的數據長度差不多是一致的,則應該考略用char(例如性別等);
否則應該考慮用varchar - 如果列中的最大數據長度小於50Byte,則一般也考慮用char。
- 一般不宜定義大於50Byte的char類型列。
char:固定長度,固定存儲空間
varchar:除了存儲數據的長度外,還需要額外的字節儲存變長數據的字典,而在檢索數據時還需要確定數據存儲的起始位置
nvarcahr:無論中英文都是2個字節,所以當不小於50字節又爲中文時選擇nvarchar
utf-8字符類型:每1個字符佔用3個字節,所以如果大於15個字符則一般考慮使用varchar存儲
decimal、float與double如何選擇
- decimal用於存儲精確數據,而float只能用於存儲非精確數據。
所以精確數據只能選擇decimal類型,例如金額等 - 由於float的存儲空間開銷一般比decimal小
所以非精確數據優先選擇float類型或double。
float:浮點型,含字節數爲4,32bit,數值範圍爲-3.4E38~3.4E38(7個有效位),對最後一位四捨五入。
double:雙精度實型,含字節數爲8,64bit數值範圍-1.7E308~1.7E308(15個有效位),對最後一位四捨五入。
decimal:數字型,128bit,不存在精度損失,常用於銀行帳目計算。(28個有效位),對最後一位四捨五入。
注:float和double的相乘操作,數字溢出不會報錯,會有精度的損失。
注:當對decimal類型進行操作時,數值會因溢出而報錯。
時間類型如何存儲
- 使用int來存儲時間字段的優缺點
優點:字段長度比datetime小
缺點:使用不方便,要進行函數轉換
限制:智能存儲到2038-1-19 11:14:07,即2^32 - 需要存儲的時間粒度
年 月 日 小時 分 秒 周
如果需要存儲的時間爲年,則選擇year字段,只佔一個字節
如果需要存儲到秒,則使用timestamp字段
如何選擇主鍵
- 區分業務主鍵和數據庫主鍵
業務主鍵用於標誌業務數據,進行表與表之間的關聯;
數據庫主鍵爲了優化數據存儲(當使用InnoDB引擎創建表,而表沒有主鍵時,會生成6個字節的隱含主鍵(不可見不可讀),這和它的排列特性有關,所以最好手動創一個主鍵(例如自增ID)) - 根據數據庫的類型,考慮主鍵是否順序增長
有些數據庫是按主鍵的順序邏輯存儲的,同時自增主鍵所佔用的長度也比較小 - 主鍵的字段類型所佔空間要儘可能小
對於使用聚集索引方式存儲的表,每個索引後都會附加主鍵信息
區別:聚集索引和非聚集索引的根本區別是表記錄的排列順序和與索引的排列順序是否一致。
(聚集插入數據時會自動根據索引排序插入到任意位置,非聚集索引直接追加到最後位置)
聚集索引和非聚集索引: https://blog.csdn.net/jiadajing267/article/details/54581262
避免使用外鍵約束
- 避免數據導入或寫入的效率。每寫入一條約束都需要查詢是否符合外鍵約束
- 增加維護成本
- 雖然不建議使用外鍵約束,但是相關聯的列上一定要建立索引
避免使用觸發器
- 降低數據導入的效率
- 可能會出現意想不到的數據異常
- 使業務邏輯變得複雜(人員交流不暢等)
觸發器使用(類似TP5的事件,TP3.2的鉤子): https://blog.csdn.net/Eastmount/article/details/52344036
關於預留字段
- 無法準確的知道預留字段的類型
- 無法準確的知道預留字段中所存儲的內容
- 後期維護預留字段所要的成本,同增加一個字段所需要的成本是相同的
- 嚴禁使用預留字段
反範式化設計
爲符合第三範式一般採取分表的方式。但在現代網站開發中,連表查詢往往效率低下。甚至當出現分庫情況時還可能需要跨庫查詢。所以採用反範式化適當的增加冗餘,採取以空間(表的數據冗餘)換取時間(讀取效率)的方式優化查詢效率。
修改前
上圖雖然符合範式化,但是查詢性能比較慢,查詢訂單信息需要採用多表查詢
適當增加冗餘字段
上圖採用反範式化設計,查詢訂單信息不再需要連表查詢,單表操作即可完成。
- 減少表的關聯數量
- 增加數據的讀取效率
- 反範式化一定要適度
維護設計
- 維護數據字典(對字段進行說明或註釋等實現理解的方式)
數據字典: https://blog.csdn.net/qq_37023388/article/details/79061881 - 維護索引
隨着數據量增長和需求查詢變化,需要對老的索引進行刪除建立新索引 - 維護表結構
隨着需求變化,需對列的增加更改。 - 在適當的時候對錶進行水平或垂直拆分
隨着數據量的大量增加,可以對錶進行拆分優化
如何維護數據字典
- 使用第三方工具對數據字典進行維護
- 利用數據庫本身的備註字段來維護數據字典。例MySQL
- 導出數據字典
以下用的是Navicat Premium,可以換成任意圖形化客戶端
SELECT
COLUMN_NAME 列名,
COLUMN_COMMENT 名稱 ,
COLUMN_TYPE 數據類型,
DATA_TYPE 字段類型,
CHARACTER_MAXIMUM_LENGTH 長度,
IS_NULLABLE 是否必填,
COLUMN_DEFAULT 描述
FROM
INFORMATION_SCHEMA.COLUMNS
where
-- developerclub爲數據庫名稱,到時候只需要修改成你要導出表結構的數據庫即可
table_schema ='litchi'
AND
-- article爲表名,到時候換成你要導出的表的名稱
-- 如果不寫的話,默認會查詢出所有表中的數據,這樣可能就分不清到底哪些字段是哪張表中的了,所以還是建議寫上要導出的名名稱
table_name = 'tb_item'\
如何選擇合適的列建立索引
- 出現在where從句,group by 從句,order by從句中的列
- 可選擇性大的列要放到索引的前面
目前的SQL執行前都會經過SQL優化器進行重新編譯,在編譯過程中,SQL優化器會按照數據庫中的索引和統計信息對查詢列進行重新排序和優化。會自動選擇適合的索引。索引目前對於where條件中的列和索引中的列就順序可以不用過度苛刻 - 索引中不要包括太長的數據類型
數據庫的數據是採用頁的方式進行存儲的,一頁16K,在這限定的大小中,每頁能夠存儲的數據行數越多,索引的查找速度相對也就越快。
對於長字符串數據需建立數據可採用前綴索引:
ALTER table 表名 add index title_pre(列名(16))
SQL優化器: https://my.oschina.net/u/1859679/blog/1586098
如何維護索引
注意事項
- 索引過多不僅會降低寫效率,而且會降低讀的效率
SQL優化器會根據索引信息和統計信息來選擇適合SQL所使用的索引,而如果有太多可以使用的索引的話,SQL優化選擇的過程會降低查詢效率。 - 定期維護索引碎片
索引碎片整理: https://blog.csdn.net/weixin_33779515/article/details/85977181 - 在SQL語句中不要使用強制索引關鍵字(在select 後面的列名被/…/ 包含)
在MySQL中,由於一些老的索引已經不適用或者更名刪除等,如果不知道這種情況就會出錯。
如何維護表結構
- 使用workbench等軟件在線變更表結構
- 對數據字段的維護
- 控制表的寬度和大小(垂直拆分)
數據庫中適合的操作
- 批量操作 VS 逐條操作
適合的是批量操作,逐條操作適合在從句中完成。
批量操作: https://www.cnblogs.com/ryanzheng/p/8317978.html - 禁止使用Select *這樣的查詢
- 控制使用用戶自定義函數
大量使用自定義函數會對索引的使用造成影響
自定義函數: https://blog.csdn.net/peng_666666/article/details/57497242 - 不要使用數據庫中的全文索引(alter table 表名 add fulltext 索引名(列名)
全文索引需要另外建立索引文件,對索引進行維護。對中文的支持不是很好。可以使用專門的搜索引擎工具來完成。