天天看點

Python資料庫操作 Mysql資料庫表引擎與字元集#學習猿地

# Mysql資料庫表引擎與字元集

![](./imgs/752951346A5F4E7EBDE362FA97107707.png)

### 1.伺服器處理用戶端請求

其實不論用戶端程序和伺服器程序是采用哪種方式進行通信,最後實作的效果都是:**用戶端程序向伺服器程序發送一段文本(MySQL語句),伺服器程序處理後再向用戶端程序發送一段文本(處理結果)。**那伺服器程序對用戶端程序發送的請求做了什麼處理,才能産生最後的處理結果呢?用戶端可以向伺服器發送增删改查各類請求,我們這裡以比較複雜的查詢請求為例來畫個圖展示一下大緻的過程:

![image](./imgs/167f4c7b99f87e1c.png)

> 雖然查詢緩存有時可以提升系統性能,但也不得不因維護這塊緩存而造成一些開銷,比如每次都要去查詢緩存中檢索,查詢請求處理完需要更新查詢緩存,維護該查詢緩存對應的記憶體區域。從MySQL 5.7.20開始,不推薦使用查詢緩存,并在MySQL 8.0中删除。

### 2.存儲引擎

`MySQL`伺服器把資料的存儲和提取操作都封裝到了一個叫`存儲引擎`的子產品裡。我們知道`表`是由一行一行的記錄組成的,但這隻是一個邏輯上的概念,實體上如何表示記錄,怎麼從表中讀取資料,怎麼把資料寫入具體的實體存儲器上,這都是`存儲引擎`負責的事情。為了實作不同的功能,`MySQL`提供了各式各樣的`存儲引擎`,不同`存儲引擎`管理的表具體的存儲結構可能不同,采用的存取算法也可能不同。

> 存儲引擎以前叫做`表處理器`,它的功能就是接收上層傳下來的指令,然後對表中的資料進行提取或寫入操作。

為了管理友善,人們把`連接配接管理`、`查詢緩存`、`文法解析`、`查詢優化`這些并不涉及真實資料存儲的功能劃分為`MySQL server`的功能,把真實存取資料的功能劃分為`存儲引擎`的功能。各種不同的存儲引擎向上邊的`MySQL server`層提供統一的調用接口(也就是存儲引擎API),包含了幾十個底層函數,像"讀取索引第一條内容"、"讀取索引下一條内容"、"插入記錄"等等。

是以在`MySQL server`完成了查詢優化後,隻需按照生成的執行計劃調用底層存儲引擎提供的API,擷取到資料後傳回給用戶端就好了。

`MySQL`支援非常多種存儲引擎:

|  存儲引擎  |                描述                |

| :---------: | :----------------------------------: |

|  `ARCHIVE`  | 用于資料存檔(行被插入後不能再修改) |

| `BLACKHOLE` |    丢棄寫操作,讀操作會傳回空内容    |

|    `CSV`    |  在存儲資料時,以逗号分隔各個資料項  |

| `FEDERATED` |            用來通路遠端表            |

|  `InnoDB`  |    具備外鍵支援功能的事務存儲引擎    |

|  `MEMORY`  |            置于記憶體的表            |

|  `MERGE`  |  用來管理多個MyISAM表構成的表集合  |

|  `MyISAM`  |      主要的非事務處理存儲引擎      |

|    `NDB`    |        MySQL叢集專用存儲引擎        |

### 3,MyISAM和InnoDB表引擎的差別

#### 1) 事務支援

MyISAM不支援事務,而InnoDB支援。

> 事物:通路并更新資料庫中資料的執行單元。事物操作中,要麼都執行要麼都不執行

#### 2) 存儲結構

MyISAM:每個MyISAM在磁盤上存儲成三個檔案。

+  .frm檔案存儲表結構。

+  .MYD檔案存儲資料。

+  .MYI檔案存儲索引。

InnoDB:主要分為兩種檔案進行存儲

+ .frm 存儲表結構

+ .ibd 存儲資料和索引 (也可能是多個.ibd檔案,或者是獨立的表空間檔案)

#### 3) 表鎖差異

**MyISAM:隻支援表級鎖**,使用者在操作myisam表時,select,update,delete,insert語句都會給表自動加鎖,如果加鎖以後的表滿足insert并發的情況下,可以在表的尾部插入新的資料。

**InnoDB:支援事務和行級鎖,是innodb的最大特色**。行鎖大幅度提高了多使用者并發操作的新能。但是InnoDB的行鎖,隻是在WHERE的主鍵是有效的,非主鍵的WHERE都會鎖全表的。

#### 4) 表主鍵

MyISAM:允許沒有任何索引和主鍵的表存在,索引都是儲存行的位址。

InnoDB:如果沒有設定主鍵或者非空唯一索引,就會自動生成一個6位元組的主鍵(使用者不可見),資料是主索引的一部分,附加索引儲存的是主索引的值。InnoDB的主鍵範圍更大,最大是MyISAM的2倍。

#### 5) 表的具體行數

MyISAM:儲存有表的總行數,如果select count() from table;會直接取出出該值。

InnoDB:沒有儲存表的總行數(隻能周遊),如果使用select count() from table;就會周遊整個表,消耗相當大,但是在加了wehre條件後,myisam和innodb處理的方式都一樣。

#### 6) CURD操作

MyISAM:如果執行大量的SELECT,MyISAM是更好的選擇。

InnoDB:如果你的資料執行大量的INSERT或UPDATE,出于性能方面的考慮,應該使用InnoDB表。DELETE 從性能上InnoDB更優,但DELETE FROM table時,InnoDB不會重建立立表,而是一行一行的删除,在innodb上如果要清空儲存有大量資料的表,最好使用truncate table這個指令。

#### 7) 外鍵

MyISAM:不支援

InnoDB:支援

#### 8) 查詢效率

MyISAM相對簡單,是以在效率上要優于InnoDB,小型應用可以考慮使用MyISAM。

推薦考慮使用InnoDB來替代MyISAM引擎,原因是InnoDB自身很多良好的特點,比如事務支援、存儲 過程、視圖、行級鎖定等等,在并發很多的情況下,相信InnoDB的表現肯定要比MyISAM強很多。

另外,任何一種表都不是萬能的,隻用恰當的針對業務類型來選擇合适的表類型,才能最大的發揮MySQL的性能優勢。如果不是很複雜的Web應用,非關鍵應用,還是可以繼續考慮MyISAM的,這個具體情況可以自己斟酌。

#### 9)MyISAM和InnoDB兩者的應用場景:

MyISAM管理非事務表。它提供高速存儲和檢索,以及全文搜尋能力。如果應用中需要執行大量的SELECT查詢,那麼MyISAM是更好的選擇。

InnoDB用于事務處理應用程式,具有衆多特性,包括ACID事務支援。如果應用中需要執行大量的INSERT或UPDATE操作,則應該使用InnoDB,這樣可以提高多使用者并發操作的性能。現在預設使用InnoDB。

### 4.了解一下字元集和亂碼

#### 字元集簡介

我們知道在計算機中隻能存儲二進制資料,那該怎麼存儲字元串呢?當然是建立字元與二進制資料的映射關系了,建立這個關系最起碼要搞清楚兩件事兒:

1. 你要把哪些字元映射成二進制資料?

  也就是界定清楚字元範圍。

2. 怎麼映射?

  将一個字元映射成一個二進制資料的過程也叫做`編碼`,将一個二進制資料映射到一個字元的過程叫做`解碼`。

人們抽象出一個`字元集`的概念來描述某個字元範圍的編碼規則

我們看一下一些常用字元集的情況:

- `ASCII`字元集

  共收錄128個字元,包括空格、标點符号、數字、大小寫字母和一些不可見字元。由于總共才128個字元,是以可以使用1個位元組來進行編碼,我們看一些字元的編碼方式:

  ```

  'L' ->  01001100(十六進制:0x4C,十進制:76)

  'M' ->  01001101(十六進制:0x4D,十進制:77)

  ```

- `ISO 8859-1`字元集

  共收錄256個字元,是在`ASCII`字元集的基礎上又擴充了128個西歐常用字元(包括德法兩國的字母),也可以使用1個位元組來進行編碼。這個字元集也有一個别名`latin1`。

- `GB2312`字元集

  收錄了漢字以及拉丁字母、希臘字母、日文平假名及片假名字母、俄語西裡爾字母。其中收錄漢字6763個,其他文字元号682個。同時這種字元集又相容`ASCII`字元集,是以在編碼方式上顯得有些奇怪:

  - 如果該字元在`ASCII`字元集中,則采用1位元組編碼。

  - 否則采用2位元組編碼。

  這種表示一個字元需要的位元組數可能不同的編碼方式稱為`變長編碼方式`。比方說字元串`'愛u'`,其中`'愛'`需要用2個位元組進行編碼,編碼後的十六進制表示為`0xCED2`,`'u'`需要用1個位元組進行編碼,編碼後的十六進制表示為`0x75`,是以拼合起來就是`0xCED275`。

  > 小貼士: 我們怎麼區分某個位元組代表一個單獨的字元還是代表某個字元的一部分呢?别忘了`ASCII`字元集隻收錄128個字元,使用0~127就可以表示全部字元,是以如果某個位元組是在0~127之内的,就意味着一個位元組代表一個單獨的字元,否則就是兩個位元組代表一個單獨的字元。

- `GBK`字元集

  `GBK`字元集隻是在收錄字元範圍上對`GB2312`字元集作了擴充,編碼方式上相容`GB2312`。

- `utf8`字元集

  收錄地球上能想到的所有字元,而且還在不斷擴充。這種字元集相容`ASCII`字元集,采用變長編碼方式,編碼一個字元需要使用1~4個位元組,比方說這樣:

  ```

  'L' ->  01001100(十六進制:0x4C)

  '啊' ->  111001011001010110001010(十六進制:0xE5958A)

  ```

  > 小貼士: 其實準确的說,utf8隻是Unicode字元集的一種編碼方案,Unicode字元集可以采用utf8、utf16、utf32這幾種編碼方案,utf8使用1~4個位元組編碼一個字元,utf16使用2個或4個位元組編碼一個字元,utf32使用4個位元組編碼一個字元。更詳細的Unicode和其編碼方案的知識不是本書的重點,大家上網查查哈~ MySQL中并不區分字元集和編碼方案的概念,是以後邊唠叨的時候把utf8、utf16、utf32都當作一種字元集對待。

對于同一個字元,不同字元集也可能有不同的編碼方式。比如對于漢字`'我'`來說,`ASCII`字元集中根本沒有收錄這個字元,`utf8`和`gb2312`字元集對漢字`我`的編碼方式如下:

```

utf8編碼:111001101000100010010001 (3個位元組,十六進制表示是:0xE68891)

gb2312編碼:1100111011010010 (2個位元組,十六進制表示是:0xCED2)

```

### 5.MySQL中的utf8和utf8mb4

我們上邊說`utf8`字元集表示一個字元需要使用1~4個位元組,但是我們常用的一些字元使用1~3個位元組就可以表示了。而在`MySQL`中字元集表示一個字元所用最大位元組長度在某些方面會影響系統的存儲和性能,是以設計`MySQL`的大叔偷偷的定義了兩個概念:

- `utf8mb3`:閹割過的`utf8`字元集,隻使用1~3個位元組表示字元。

- `utf8mb4`:正宗的`utf8`字元集,使用1~4個位元組表示字元。

有一點需要大家十分的注意,在`MySQL`中`utf8`是`utf8mb3`的别名,是以之後在`MySQL`中提到`utf8`就意味着使用1~3個位元組來表示一個字元,如果大家有使用4位元組編碼一個字元的情況,比如存儲一些emoji表情啥的,那請使用`utf8mb4`。

#### 字元集的檢視

`MySQL`支援好多好多種字元集,檢視目前`MySQL`中支援的字元集可以用下邊這個語句:

```mysql

show charset;

```