MySQL —— 簡單聊一聊數據庫設計

在這裏插入圖片描述

原文出自:https://www.pandashen.com

前言

這是關於 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)

11 關係是指對於實體集 A 和 實體集 BA 中的每一個實體最多與 B 中的一個實體有關係,反之在實體集 B 中的每一個實體之多與實體集 A 中的一個實體有關係。

在這裏插入圖片描述

(2) 1 對多(1 : N)

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

在這裏插入圖片描述

(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 圖如下。

在這裏插入圖片描述

事務

1、爲什麼需要事務?

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

2、什麼是事務

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

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

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

3、事務的特性 ACID

事務具有以下特性,被簡稱爲 ACID:

  • 原子性(Atomicity):事務是一個完整的操作,事務各個部分是不可分的,要麼都執行,要麼都不執行;
  • 一致性(Consistency):當事務完成後,數據必須處理完整的狀態;
  • 隔離性(Isolation):併發事務彼此隔離、獨立,它不應該以任何方式依賴於其它事務;
  • 持久性(Durability):事務完成後,它對數據庫的修改被永久保持。

4、如何創建事務

(1) 創建表

創建表 accountid 列爲主鍵列,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 有一些幫助,那這幾篇的文章就達到目的了,也歡迎後端的小夥伴來指出文章中的錯誤和不足。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章