1. 程式人生 > >資料庫設計(1)轉載

資料庫設計(1)轉載

轉自:https://www.pandashen.com/2018/08/14/20180814131241/
前言
這是關於 MySQL 系列文章的第三篇,在前兩篇文章 《MySQL —— 資料庫基礎》 和 《MySQL —— SQL 語句總結》 中,主要介紹了一些資料庫的基礎概念、建立表的方式以及 SQL 語句的使用,本篇在使用的基礎上做一個小小的昇華,來簡單聊一聊資料庫的設計,還有一句話不得不再次贅述,資料庫博大精深,本系列文章內容較淺,適合於前端的同學們對 MySQL 的入門,這也是我的學習筆記,希望可以幫助大家。

為什麼設計資料庫
說到為什麼要設計資料庫,就要說到資料的完整性,我們要在設計資料庫時保證域的完整性和實體的完整性,同時從效能出發,我們要保證最大限度的節省儲存空間,比如一張成績表,上面沒必要儲存學生的姓名、年齡等資訊,只需要儲存成績,如果一個數據庫設計的合理,最後的結果就是方便我們對資料庫的開發和擴充套件。

如果是一個 “糟糕” 的資料庫設計會造成一系列的不良反應,比如資料冗餘,儲存空間浪費,記憶體浪費,有時甚至會造成資料插入和更新的異常,比如學生表存了學生資訊,而成績表也存了,這樣在修改時沒有全部修改就會出現錯誤。

軟體專案開發中資料庫設計的生命週期
軟體專案開發中資料庫設計的生命週期可大概分為以下幾個階段:

需求分析階段,分析客戶的業務和資料處理需求;
概要設計階段,設計資料庫 E-R 模型圖,確認需求的正確和完整性;
詳細設計階段,應用三大正規化稽核資料庫;
程式碼編寫階段,物理實現資料庫,編碼實現應用;
軟體測試階段;
安裝部署階段。
上面資料庫的設計經歷了從 “現實世界” 到 “資訊世界” 到 “資料庫模型” 再到 “資料庫” 產生的一個完整過程。

設計資料庫的步驟
收集資訊:收集資訊其實就是與相關人員進行交流、訪談、調研,充分了解使用者需求,理解整個專案的完整流程,並理解資料庫需要完成的任務,這部分工作大部分由需求人員完成,並根技術人員進行對接。

標識實體和實體屬性:開發人員在明確需求和流程之後,標識資料庫的實體,比如學生資訊表,每一條實體中應該由哪些欄位組成,成績表中實體由哪些欄位組成等等。

標識實體之間的關係:其實就是通過表之間的某欄位對錶進行關聯,對錶的實體之間建立對應關係,如學生表的 id 欄位會關聯成績表的 student_id 欄位,用來查詢某個學生的成績。

資料庫 E-R 圖
1、E-R 圖基本概念
E-R 圖也叫做實體關係圖,是指用實體、關係、屬性三個基本概念概括資料的基本結構,從而描述靜態資料的概念模型。

E-R 圖的實體:即資料模型中的資料物件,每一張表就是一個 E-R 圖的實體。

E-R 圖的屬性:即資料物件中所具有的屬性,例如學生表的學生、姓名、年齡等,屬性又分為唯一屬性和非唯一屬性,唯一屬性如經過唯一約束和主鍵約束的屬性,不可重複,其他的都是非唯一屬性。

E-R 圖的關係:用來表示每一個數據物件與資料物件之間的聯絡,即每一個實體之間的聯絡,例如學生表和成績表之間的聯絡,因為每個學生都有自己的成績。

2、E-R 圖的關聯關係
(1) 1 對 1 (1 : 1)
1 對 1 關係是指對於實體集 A 和 實體集 B,A 中的每一個實體最多與 B 中的一個實體有關係,反之在實體集 B 中的每一個實體之多與實體集 A 中的一個實體有關係。

1 對 1 關係

(2) 1 對多(1 : N)
1 對多關係是指實體集 A 與實體集 B 中至少有 N (N > 0) 個實體有關係,並且實體集 B 中最多與實體集 A 中的一個實體有關係。

1 對多關係

(3) 多對多(M : N)
多對多關係是指實體集 A 中的每一個實體與實體集 B 中至少有 M (M > 0) 個實體有關係,並且實體集 B 中的每一個實體與實體集 A 中至少有 N (N > 0) 個實體有關係。

多對多關係

資料庫設計的三大正規化
1、確保每列的原子性
如果每列都是不可再分的最小單元資訊,則滿足第一正規化,比如下圖中,地址是由國家和城市組成的,顯然可以繼續在拆分成兩個列,國家和城市,是不滿足第一正規化的,需要將地址列差分成國家和城市兩個列。

確保每列的原子性

舉一個簡單的例子,我們平時在淘寶購物的時候需要新增地址,在填寫新地址時,都是讓我們選擇國家、省、城市、區、街道、小區這樣的方式,而不是讓我們自己將這些地址寫在一起,其原因就是因為淘寶的資料庫設計嚴格遵循每列的原子性,這樣的提交可以方便後端獲取每一個列的資訊在資料庫中進行儲存。

2、每個表只能描述一件事情
如下圖中所示,在左側的表中,描述了學生資訊和課程資訊,這明顯是兩件事情,假設再有一張成績表,也要描述學生資訊,課程資訊和成績等多件事情,就會造成資料的重複、冗餘,也可能會導致更新、插入、刪除資料異常的現象。

每個表只能描述一件事情

所以正確的做法是應該將左側表差分成兩張表分別為學生表和課程表,並使用學生編號與課程編號進行關聯。

3、其他列都不傳遞依賴於主鍵列
其他列都不傳遞依賴於主鍵列的意思是表中各列必須都與主鍵直接相關,不能間接相關,從下圖左表可以看出,學生編號為主鍵,年級 ID 也應該為主鍵,正常應該通過學生編號找到年級 ID,再找到年級名稱,這樣年級名稱與學生編號之間就形成了一個傳遞並且依賴於主鍵年級 ID,即年級 ID 做為主鍵在中間隔了一層,這樣就使年級名稱與主鍵學生編號間接相關,如果在同一張表中,所有的欄位都是應該直接依賴於主鍵,而不是再通過其他的主鍵傳遞。

其他列都不傳遞依賴於主鍵列

如果一個表中表述了多件事情並有多個作為主鍵的列,與上一條的處理方式相同,應該拆成多張表,並且每張表只有一個主鍵列。

RBAC 基於角色的訪問控制
1、RBAC 的含義
RBAC(Role-Based Access Control)基於角色的訪問控制,就是使用者通過角色與許可權進行關聯,簡單的說,一個使用者擁若干個角色,每個角色擁有若干個許可權,這樣就構造成了 “使用者 → 角色 → 許可權 → 資源” 的授權模型,在這個模型中,使用者與角色之間,角色與許可權之間,許可權與資源之間,一般都是多對多的關係,在 RBAC 中最重要的概念主要有四部分,就是使用者(User)、角色(Role)、許可權(Permission)和資源(Resource)。

2、RBAC 的安全原則
最小許可權原則:最小許可權原則之所以被 RBAC 所支援,是因為 RBAC 可以將其角色配置成完成任務所需要的最小的許可權集;
責任分離原則:可以通過呼叫相互獨立互斥的角色來共同完成敏感的任務而體現,比如要求一個計帳員和財務管理員共參與同一個帳目;
資料抽象原則:資料抽象可以通過許可權的抽象來體現,如財務操作用借款、存款等抽象許可權,而不用作業系統提供的典型的讀、寫、執行許可權。
3、RBAC 的 E-R 圖
之前說 RBAC 最重要的概念由四部分,其實體現在資料庫的表中有主要三部分,因為角色和使用者是重疊的,那麼主要有三張表分別為使用者表、許可權表和資源表,其中使用者表與許可權表之間有一張關聯表,許可權表與資源表之間有一張關聯表,E-R 圖如下。

RBAC 的 E-R 圖

事務
1、為什麼需要事務?
在生活中我們經常使用銀行轉賬或者支付寶和微信支付,這種操作每一次至少影響兩個使用者的資料資訊,比如一方給另一方轉錢,如果成功則轉錢方餘額減去轉出金額,而收錢方餘額增加收到的金額,這應該是一個請求操作了資料表中的倆個實體,如果在兩個操作資料的環節任意一個失敗了,都會影響兩個人資料的正確性,這種時候需要兩個操作同時失敗或同時成功,就是說有一個操作出現失敗的情況,即使另一個成功了也需要進行回滾操作,這就是事務的由來。

2、什麼是事務
事務是作為單個邏輯工作單元執行的一系列操作,多個操作作為一個整體向系統提交,要麼都執行,要麼都不執行,是一個不可分割的工作邏輯單元。

轉賬過程就是一個整體,它需要兩條 UPDATE 語句,如果任何一個出錯,則整個轉賬業務取消,兩個賬戶的餘額都恢復到原來的資料(回滾),確保總餘額不變。

這裡再舉一個例子,有一個上傳檔案的功能,後端接收到檔案流時是需要先寫入的,當寫入成功後,會將上傳成功的結果返回給客戶端,如果檔案很大,寫入的時間就會長,如果在此期間突然寫入失敗,則會刪除之前寫入的內容,將整個操作回滾到寫入之前,這裡面主要兩步操作,建立一個新檔案並寫入,寫入成功刪除舊檔案,如果寫入失敗,兩個操作將會同時失敗,即不會刪除舊檔案,這也是一個事務的例子,只是沒有轉賬那麼明顯。

3、事務的特性 ACID
事務具有以下特性,被簡稱為 ACID:

原子性(Atomicity):事務是一個完整的操作,事務各個部分是不可分的,要麼都執行,要麼都不執行;
一致性(Consistency):當事務完成後,資料必須處理完整的狀態;
隔離性(Isolation):併發事務彼此隔離、獨立,它不應該以任何方式依賴於其它事務;
永續性(Durability):事務完成後,它對資料庫的修改被永久保持。
4、如何建立事務
(1) 建立表
建立表 account,id 列為主鍵列,name 列為姓名,balance 為餘額。

CREATE TABLE account (
id INT(11) NOT NULL AUTO_INCREMENT,
name VARCHAR(64) NOT NULL,
balance INT(11) DEFAULT 0
PRIMARY KEY (id)
);
(2) 新增資料
將表 account 新增兩條資料,分別為 “張三” 和 “李四”,餘額都為 100。

INSERT INTO student (name, balance) VALUES (“張三”, 100);
INSERT INTO student (name, balance) VALUES (“李四”, 100);
(3) 使用 NodeJS 實現事務
一個非常簡單的事務
const mysql = require(“mysql”);

// 建立資料庫連線
const connection = mysql.createConnection({
host: “localhost”, // 主機名
port: “3306”, // 資料庫服務埠號
username: “root”, // 資料庫名稱
pwd: “123456”, // 資料庫密碼
database: “school” // 連線的資料庫名稱
});

connection.connect();

// 開啟事務
connection.beginTransaction(err => {
// 回撥引數為錯誤物件,返回結果,返回欄位描述
connection.query(“UPDATE account SET balance - 50 WHERE id = 1”, (err, result, fields) => {
if (err) {
connection.rollback(); // 如果失敗直接回歸
} else {
connection.query(“UPDATE account SET balance - 50 WHERE id = 1”, (err, result, fields) => {
if (err) {
connection.rollback(); // 如果失敗直接回歸
} else {
connection.commit(); // 如果兩個都成功了則提交事務
}
});
}
});
});
總結
到此關於 MySQL 的系列文章就告一段落了,希望前端的同學們在看了這幾篇文章後對你們入門 MySQL 有一些幫助,那這幾篇的文章就達到目的了,也歡迎後端的小夥伴來指出文章中的錯誤和不足。