1. 程式人生 > 資料庫 >資料庫(MySQL) 入門實踐

資料庫(MySQL) 入門實踐

1 資料庫

存放資料的倉庫。例如你的賬號資訊,訂單記錄等。

2 SQL

Structured Query Language,用於訪問和處理關係資料庫的標準的計算機語言。

按照功能又可分為四大類;

  1. DQL

    查詢語言,基本語句 SELECT;

  2. DML

    操縱語言,主要有三種形式,INSERT、UPDATE 和 DELETE;

  3. DDL

    定義語言,建立表、檢視、索引等,CREATE TABLE;

  4. DCL

    控制語言,用來授權或回收某種特權,基本形式有 GRANT、 COMMIT 和 ROLLBACK;

3 NoSQL

Not Only SQL,泛指非關係型的資料庫,通常以鍵值對或者文件形式儲存。例如 Redis、MongoDB。

關係型資料庫(MySQL)能通過外來鍵建立表之間的聯絡,且相比 NoSQL 而言,還具備 ACID 特性。

但 NoSQL 操作無須 SQL 解析,讀寫效能較高,相比關係型資料庫來說,不用預設儲存結構,且天然支援分散式儲存。

4 正規化

資料庫滿足一定要求的條件稱為資料庫正規化。又能根據程度的不同,簡稱為第 N 正規化。

  1. 第一正規化 1NF

    所有屬性不可再分,例如屬性 product 就不能分為 title 和 price,可以單獨設定兩個屬性 productTitle、productPrice;

  2. 第二正規化 2NF

    每張表都有一個屬性作為唯一標識,其他屬性完全依賴該標識,例如自增主鍵ID;

  3. 第三正規化 3NF

    所有的非主屬性不依賴於其他的非主屬性。例如訂單表中可以關聯商品ID,但不應該關聯商品非主屬性 title 和 price 等;

為了提高查詢效率,通常會新增冗餘欄位,這也就違背了 3NF,也稱之為反三正規化。

5 MySQL

MySQL 是一個 Oracle 旗下的關係型資料庫,使用 SQL 語言進行增刪改查操作。

開源免費,效能也比較好,和 PHP、Java 等 Web 開發語言完美配合,在中小型企業應用非常廣泛。

後續內容都是基於 MySQL 資料庫的前提下。

6 儲存引擎

常見的有 MyISAM 和 InnoDB 引擎;

引擎預設版本外來鍵鎖粒度count(*)事務
MyISAM< 5.5不支援表鎖變數儲存不支援
InnoDB>= 5.5支援行鎖全表掃描支援

7 事務

一條或多條 SQL 組成一個事務(transaction),具備 ACID 四個特性;

  1. Atomicity 原子性

    一個事務內的所有操作,要麼全部完成,要麼全部失敗;

  2. Consistency 一致性

    事務開始前後結束後不會破壞資料庫的完整性,也就是說寫入或修改的結構需要符合預設的規則;

  3. Isolation 隔離性

    防止事務交叉執行時導致資料的不一致。根據隔離程度分為 read uncommitted、read committed、repeatable read 和 serializable;

  4. Durability 永續性

    事務結束後,對資料的修改是永久的;

事務交叉執行可能會造成“髒讀”、“幻讀” 和 “不可重複讀”;

  • 髒讀

    一個事務讀取到另外一個事務還未提交的資料;

  • 不可重複讀

    一個事務內,多次讀取同一資料返回結果不同;由於在此期間在資料被其他事務修改並已提交;

  • 幻讀

    一個事務內,多次讀取,返回不存在的記錄;由於在此期間有其他事務寫入資料;

read uncommittedread committedrepeatable readserializable
髒讀×××
不可重複讀××
幻讀×

8 索引

資料庫的“目錄”,在資料量較大的情況下,可以極大地提高查詢效率。

常見的索引資料結構有 B+ 樹、Hash。以最常用的 B+ 樹為例;

按照 B+ 樹儲存方式可以把索引分為兩大類;

  1. 聚簇索引;

    葉子節點存放了一整行的資訊;

  2. 非聚簇索引;

    葉子節點存放的是對應那行資料的主鍵,和該索引的值;

為什麼是 B+ 樹?

  1. 磁碟代價低;
  2. 查詢更加穩定;
  3. 便於遍歷;
  4. 支援範圍查詢;

一張結構為 id,groupId,name 的 t_user 表,id 為主鍵(聚簇索引),groupId 為普通索引(非聚簇索引)。

select name from t_user where groupId = 123;

先在葉子節點上得到對應的主鍵 id,然後再根據主鍵 id 得到 name 的值,這種行為稱之為回表

select groupId from t_user where groupId = 123;

直接在葉子節點上就能得到 groupId 的值,不用回表操作,這種索引也被稱為覆蓋索引

按照功能型別又可以把索引分為三大類;

  1. 普通索引;

    最基本的索引型別,沒有限制條件;

  2. 唯一索引;

    保證索引欄位的值唯一,允許有 NULL;主鍵是一種特殊的唯一索引,不允許有 NULL;

  3. 聯合索引;

    多個欄位組成一個索引,具有“最左字首”的原則;

什麼是最左字首?

a、b、c 三個欄位組成聯合索引,那麼生效的列為 a、ab、abc、ac。(等值判斷時順序可交換,範圍查詢時會停止匹配)

9 鎖

巨集觀來看,鎖分為兩種;行鎖可歸納為兩類;

  1. 共享鎖(S)

    share,又稱為讀鎖,已有 S 鎖,可以加其他 S 鎖,但不能加 X 鎖;

  2. 排他鎖(X)

    exclusive,又稱為寫鎖,X 與其他任何鎖互斥;

InnoDB 是通過給索引項加鎖實現的行鎖,可分為三種類型;

  1. record lock

    行級鎖,鎖定對應索引項;

  2. gap lock

    間隙鎖,鎖定索引項之間的間隙,左開右閉;

  3. next-key lock

    前兩種的結合;

如果不通過索引項檢索資料,會鎖住整個表。

InnoDB 加鎖方法:

  • 對於 UPDATE、DELETE、INSERT 自動加 X 鎖;
  • 對於普通 SELECT 不會加任何鎖;
  • SELECT … LOCK IN SHARE MODE 顯示加 S 鎖;
  • SELECT … FOR UPDATE 顯示加 X 鎖;

查詢當前資料庫鎖狀態;

select * from information_schema.innodb_locks;

對於不同型別的索引,加鎖的方式也不一樣;

  1. 普通索引

    加 next-key lock;

  2. 唯一索引

    加 record lock;

由於普通索引葉子節點儲存了主鍵,所以加鎖的欄位是:普通索引 + 主鍵索引;

假設有如下資料表 t_ lock,其中 id 為主鍵,xid 為 普通索引;

+-----+----+
| xid | id |
+-----+----+
|   1 | 10 |
|   3 | 20 |
|   5 | 30 |
|   8 | 40 |
|  11 | 50 |
+-----+----+

給 (8, 40) 這條記錄加 X 鎖;

select * from t_lock where xid = 8 for update;

那麼根據 next-key lock 的定義,鎖住區間為 (5, 30) 到 (8, 40),(8, 40) 到 (11, 50) 這兩個區間;

便於理解我會合併為一個區間 (5, 30) 到 (11, 50)。

按照所以排序規則,假設另插入 (xid, id) 記錄,那麼總是滿足以下條件;

  1. xid < 5;id 無限制;(正常)
  2. xid = 5;id < 30;(正常)
  3. xid = 5;id > 30;(阻塞)
  4. xid > 5 && xid < 11;id 無限制;(阻塞)
  5. xid = 11;id < 50;(阻塞)
  6. xid = 11;id > 50;(正常)
  7. xid > 11;id 無限制;(正常)

簡單圖示,當插入的資料落在這個區間則會阻塞,反之亦然;

10 RR 幻讀

上面事務章節描述 RR 會導致幻讀,MySQL 在 RR 下通過如下兩點規避掉了;

  1. MVCC

    Multi-Version Concurrency Control,多版本併發控制。在普通 SELECT (快照度)時引入版本,同一個事務中只能讀取不大於當前版本的資料快照;

  2. next-key lock

    需要加 X 鎖的操作(當前讀),加 next-key lock 可以有效避免產生幻讀;

11 SQL 執行順序

根據建立時間升序,查詢支付成功超過 3 單的使用者,需要去重;

select distinct t1.nickname
from t1 inner join t2
on t1.uid = t2.uid
where t2.pay_time > 0
group by t1.uid, t1.nickname
having count(*) > 3
order by t2.create_time
limit 10
  1. from
  2. on
  3. join
  4. where
  5. group
  6. having
  7. order
  8. select
  9. distinct
  10. limit

12 binlog

binlog 是 MySQL 最重要的日誌,記錄了所有的 DDL 和 DML 語句,主要目的是;

  1. 主從複製;

    在 Master 開啟 binlog,並傳遞到 Slave 節點來達到 Master-Slave 資料一致性;

  2. 資料恢復;

    通過 mysqlbinlog 恢復資料;

檢查 binlog 是否開啟;

show variables like 'log_bin';

編輯 mysql 配置檔案/etc/mysql/mysql.conf.d/mysqld.cnf,開啟 binlog 功能;

[mysqld]
server-id=1
log-bin=/var/lib/mysql/mysql-bin

常用幾個命令;

  • show master status;
  • show binary logs;
  • mysqlbinlog -v --start-position 2755 --stop-position 3076 mysql-bin.000003;

例如誤刪除了某條記錄;

  1. 通過 mysqlbinlog 定位到誤操作的 position;
  2. 通過 mysqlbinlog 定位到誤刪之前最早入庫的 position;
  3. 擷取中間 binlog 日誌, echo > db.sql 輸出到可執行 SQL 檔案中;
  4. 執行恢復資料即可;

可直接流式執行:mysqlbinlog -v --start-position 2432 --stop-position 2533 mysql-bin.000003 | mysql -uroot -p

mysqldump 是用來備份資料庫的,例如備份 db_test 資料庫;

mysqldump -h127.0.0.1 -uroot -p123456 db_test > db.sql

13 效能優化

  1. 索引;

    給經常用作查詢條件,且區分度較高的欄位建立索引;

  2. 分頁查詢;

    where id > ${lastId} order by id limit ${size},提高大表分頁效率;

  3. 批量操作;

    批量插入用 insert into xxx values (xxx…), (xxx…),批量更新用 case when id;

  4. not null;

    null 會額外佔用空間,且 count(xxx) 不會參與統計,若是索引列 is not null 也會失效;