「這是我參與8月更文挑戰的第2天,活動詳情查看:8月更文挑戰」mysql
MySQL 是一種典型的 C/S 結構,C/S 結構即 客戶端/服務端 模型。git
服務端程序:mysqld MySQL 自帶的客戶端:mysql、mysqladmin、mysqldump 等程序員
經過網絡鏈接串:TCP/IP
# mysql -uroot -p -h 127.0.0.1 -P3306
經過套接字文件,socket
# mysql -uroot -p -S /tmp/mysql.sock
複製代碼
首先來講說,數據庫是作什麼的。github
數據庫是用來長久存儲數據的,而咱們你們都知道內存只能臨時存儲,磁盤等才能真正存儲數據。那麼數據庫會放在哪裏呢?確定是存放在磁盤上,因此數據庫其實就是磁盤上的一個文件。算法
簡單的理解就是:數據庫 = 磁盤上的文件。sql
既然數據庫能夠當作是磁盤上文件,那麼咱們怎麼使用數據庫呢?shell
若是說咱們能夠直接使用數據庫,那就等價於直接使用磁盤上的文件。咱們還知道,咱們只有把磁盤上的文件讀入內存中才可使用。這即是正確的數據庫的使用流程。數據庫
那麼數據庫如何把數據讀入內存中呢?緩存
這個時候就須要咱們將要介紹的實例(instance)了,實例能夠理解爲內存結構和一組後臺進程。bash
實例是用來將磁盤中的數據讀入內存中,並使用數據。
MySQL 在啓動過程當中,會:
因此實例是:MySQL 的後臺進程 + 線程 + 預分配的內存結構
而 MySQL 是單進程多線程,也就是說 MySQL 實際在系統中表現的就是一個服務進程,即進程(經過多種方法能夠建立多實例,再安裝一個端口號不一樣的 MySQL,或者經過 workbench 來新建一個端口號不一樣的服務實例等)
咱們平時在 Linux 常常會使用一些專業的命令來管理咱們操做系統中的對象,好比 touch
,mkidr
。這些命令是 Linux 操做系統 bash shell 支持的一些功能。
對於 MySQL 也同樣,MySQL 可能不會用 ls
這些命令,MySQL 也有一些專用的內置命令,用來管理數據庫中的數據,咱們把這種命令稱爲 SQL(Structured Query Language,結構化查詢語言)。
爲了更方便的學習記憶,咱們將 SQL 語句分爲以下經常使用的幾類:
select
、insert
、update
、delete
、merge
、explain plan
、call
、lock table
等語句。create
、alter
、drop
、truncate
、comment
、replace(rename)
等語句,通常不須要 commit 等事務操做。grant
、revoke
等語句。savepoint
、rollback
,commit
,set transaction
等語句。好比咱們熟悉的這條語句,在 Linux 中沒法執行。
# select user,host from mysql.user;
-bash: 未預期的符號 `from' 附近有語法錯誤
複製代碼
咱們得進入到 mysql 中才能運行。
mysql> select user,host from mysql.user;
+---------------+-----------+
| user | host |
+---------------+-----------+
| mysql.session | localhost |
| mysql.sys | localhost |
| root | localhost |
+---------------+-----------+
3 rows in set (0.00 sec)
複製代碼
整個過程看起來很是簡單,輸入語句,點擊回車,而後就顯示結果了。
可是咱們有沒有想過,爲何咱們在平時的業務開發中,好比要實現一個功能,有的人寫的語句執行就快,有的人寫的語句執行就慢,結果都是同樣的,那麼爲何一個快一個慢呢?
咱們可能會想到有可能執行的慢的語句很是長、寫的很複雜,是的,有多是這個緣由。
那麼咱們再深刻想想,有沒有多是語句在內部執行過程當中,發生了什麼不可預料的操做,或者是一些操做是代價較高的操做。咱們可能不能從表面上看出問題,另外呢,也不必定 SQL 長就慢,反而有多是越長的 SQL 執行越快,越簡易的語句執行的越慢。
所以,咱們不該該僅僅關注結果,而不去關注過程,就沒法找到根本的緣由,接下來,咱們來學習一下,從鏈接數據庫開始,到輸入 SQL 語句,而後點擊回車,而後顯示結果,這個過程當中間到底發生了什麼。
接下來,咱們就經過這一條查詢語句,來學習一下 MySQL 詳細的工做流程。
mysqld 結構,咱們分紅了三層:鏈接層、SQL 層、存儲引擎層。
首先一條語句多是一個用戶發起的,好比 Navicat,Navicat 執行以下一條語句:
select user,host from mysql.user;
複製代碼
那麼首先 Navicat 得鏈接上 MySQL,那麼它怎麼鏈接呢?咱們以前有說過,經過 tcp/ip 或者 socket 方式進行鏈接。這說明鏈接層得支持這兩種協議,並支持使用這兩種協議進行鏈接。
而後,好比咱們使用 tcp/ip 方式鏈接,咱們還要輸入用戶名、密碼、IP、端口號。假設我輸入的端口號是 3307,那麼可以登錄嗎,確定是不能夠的,由於咱們配置的端口號是 3306,那說明鏈接層的做用還有對用戶名、密碼、IP、端口號等進行校驗,驗證合法性。
連上了以後,咱們也說了,在 MySQL 裏面全部要實現的功能都須要工做的線程來提供,好比說,接收請求語句,返回結果。因此,在鏈接層會自動開啓一個鏈接線程,接收語句、查看結果。咱們能夠經過 show processlist
來查看鏈接線程狀況。
mysql> show processlist;
+-----+------+-----------+------+---------+------+----------+------------------+
| Id | User | Host | db | Command | Time | State | Info |
+-----+------+-----------+------+---------+------+----------+------------------+
| 282 | root | localhost | NULL | Query | 0 | starting | show processlist |
+-----+------+-----------+------+---------+------+----------+------------------+
1 row in set (0.01 sec)
複製代碼
而後我在另外一個終端上再啓動一個 MySQL,再執行一下,發現鏈接線程變成了兩個。
mysql> show processlist;
+-----+------+-----------+------+---------+------+----------+------------------+
| Id | User | Host | db | Command | Time | State | Info |
+-----+------+-----------+------+---------+------+----------+------------------+
| 282 | root | localhost | NULL | Sleep | 20 | | NULL |
| 286 | root | localhost | NULL | Query | 0 | starting | show processlist |
+-----+------+-----------+------+---------+------+----------+------------------+
2 rows in set (0.00 sec)
複製代碼
默認鏈接線程最多有 151 個,固然這個數字能夠調整。沒有用戶請求進來都會開闢一個會話,若是這個會話 8 個小時沒有動做的話,就會斷開這個鏈接。
到這裏,鏈接層的工做就作完了,接着把請求傳遞給下一層。
總結一下,鏈接層做用:
show processlist
命令能夠查看用戶鏈接的線程狀況)接收上層傳送的 SQL 語句。
SQL 層確定首先將 SQL 接收到,而後才能執行後面的操做。
語法檢查:驗證語句語法,判斷是否知足 SQL_MODE。
語法檢查確定是必要的,若是語法檢查都沒用過,那確定也無法執行了。
SQL_MODE 經常使用來解決下面幾類問題:
語義檢查。判斷 SQL 語句的類型。
在 Linux 上,咱們以某個用戶登陸,好比 work 用戶,有些文件是不能刪除的,必須切換到 root 用戶才能夠操做。對於 MySQL 也是如此。可是在進行權限以前,咱們能夠先對 SQL 語句的類型作一下判斷,而後再來判斷相應的權限。
權限檢查,檢查用戶對庫、表是否有相應權限。
解析器:進行 SQL 語句的預處理,生成解析器(explain desc),生成多種執行方案。
MySQL 執行語句是否能夠直接執行?執行完以後是否會產生很差的效果?咱們是否須要評估一下語句的代價有多高?找到一個最快的、最合適的執行方式。
優化器:根據解析器得出的多種執行方案,進行判斷,選出最優的執行計劃。
代價模型:之前 MySQL 是按照時間去衡量 SQL 語句的優劣,如今按(CPU/IO/MEM)的損耗評估性能的好壞(基於代價)。
各個版本的優化器算法是不一樣的。
執行器,選擇最優的執行計劃去執行 SQL 語句,產生執行的結果。
真正運行 SQL。
執行結果:會提供給存儲引擎層一個結果說明這個查詢的結果在磁盤的哪一個位置。
提供查詢緩存(默認不開)。
若是某些 SQL 一直執行,好比執行 1000 萬次,那麼咱們就沒有必要一直執行了,咱們能夠提供一個查詢緩存,將請求結果放到緩存中。
提供日誌記錄(binlog),記錄二進制日誌,默認不開啓。
包括審計日誌、通用日誌、binlog。
在 SQL 層中,執行器執行完,會得出一個結果,來展現給咱們,可是咱們的數據在哪呢,其實還在磁盤上。
Linux 上對於磁盤使用是文件系統,不能直接對磁盤進行讀寫。MySQL 也是如此。MySQL 將這個專門負責特殊數據讀寫的文件系統叫作存儲引擎。(相似於 FS)。
存儲引擎層的做用:
根據 SQL 的執行結果,去磁盤上找到相應數據。
找到磁盤上的 16 進制數據,再次返回 SQL 層,結構化成二維表的方式,再由鏈接層的專用線程返回用戶,最終展示出來。
咱們能夠把 MySQL 理解成一個文件系統,所以不少概念、命令能夠對比 Linux 學習。下標中總結了 MySQL 和 Linux 中相似的概念。
MySQL 中概念 | Linux 中概念 |
---|---|
庫 | 目錄 |
create database account charset utf8mb4; | mkdir /account |
show databases; | ls / |
use account; | cd /account |
表 | 文件 |
列(字段) | |
數據行(記錄) | 數據行 |
表屬性 | 文件屬性 |
列屬性 |
MySQL 中的庫在文件系統上是使用目錄來表示。咱們 MySQL 的數據存儲在 /data/mysql/data
下。
首先咱們查看一下咱們的數據庫。
mysql> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| mysql |
| performance_schema |
| sys |
+--------------------+
4 rows in set (0.00 sec)
複製代碼
而後我在 /data/mysql/data
下建立個 account
目錄。
# mkdir account
複製代碼
咱們再來看一下數據庫。
mysql> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| account |
| mysql |
| performance_schema |
| sys |
+--------------------+
5 rows in set (0.00 sec)
複製代碼
能夠看到多了一個 account
數據庫。
咱們如今來看一下常見的 MyISAM 存儲引擎和 InnoDB 存儲引擎中數據是如何存儲的。以 mysql.user
表爲例。
# cd /data/mysql/data/mysql
# ls
......
-rw-r----- 1 mysql mysql 10816 1月 13 22:25 user.frm
-rw-r----- 1 mysql mysql 396 1月 14 17:21 user.MYD
-rw-r----- 1 mysql mysql 4096 1月 14 17:21 user.MYI
複製代碼
對於 MyISAM 存儲引擎,MySQL 用 user.frm
、user.MYD
、user.MYI
三張表來存儲數據。
user.frm
:存儲表結構(列、屬性)user.MYD
:存儲的數據記錄user.MYI
:存儲索引-rw-r----- 1 mysql mysql 8636 1月 13 22:25 time_zone.frm
-rw-r----- 1 mysql mysql 98304 1月 13 22:25 time_zone.ibd
複製代碼
對於 InnoDB 存儲引擎,MySQL 用 time_zone.frm
、time_zone.ibd
兩張表來存儲數據。
time_zone.frm
:存儲表結構(列、屬性)time_zone.ibd
:存儲的數據記錄和索引其實對於 InnoDB 存儲引擎,MySQL 還有一個文件用來存儲元數據,也就是數據字典。該文件名叫 ibdata1
。咱們在 /data/mysql/data
目錄下。
通常狀況下(非分區表):
用戶名@'白名單'
,例如:root@'localhost'
mysql> create user user2@'localhost';
mysql> exit
複製代碼
# mysql -uuser2
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 307
......
複製代碼
mysql> create user user3@'localhost' identified by '123';
Query OK, 0 rows affected (0.00 sec)
複製代碼
mysql> select user,host from mysql.user;
+---------------+-----------+
| user | host |
+---------------+-----------+
| user1 | 10.0.0.% |
| mysql.session | localhost |
| mysql.sys | localhost |
| root | localhost |
| user2 | localhost |
| user3 | localhost |
+---------------+-----------+
6 rows in set (0.00 sec)
複製代碼
mysql> alter user user3@'localhost' identified by '123456';
Query OK, 0 rows affected (0.00 sec)
複製代碼
mysql> drop user user3@'localhost';
Query OK, 0 rows affected (0.01 sec)
mysql> select user,host from mysql.user;
+---------------+-----------+
| user | host |
+---------------+-----------+
| user1 | 10.0.0.% |
| mysql.session | localhost |
| mysql.sys | localhost |
| root | localhost |
| user2 | localhost |
+---------------+-----------+
5 rows in set (0.00 sec)
複製代碼
ALL:全部權限
select、insert、update、delete ......
with grant option:受權權限(能夠給其餘用戶受權)
grant 權限 on 做用目標 to 用戶 identified 密碼 with grant option;
做用目標:
*.*
:MySQL 下全部數據庫全部表。account.*
:account 數據庫下面的全部表account.t1
:account 數據庫下面的 t1 表mysql> grant all on *.* to root@'10.0.0.*' identified by '123' with grant option;
複製代碼
mysql> grant select,insert,delete,update on account.* to user4@'10.0.0.*' identified by '123';
複製代碼
mysql> show grants for user4@'10.0.0.*';
+---------------------------------------------------------------------------+
| Grants for user4@10.0.0.* |
+---------------------------------------------------------------------------+
| GRANT USAGE ON *.* TO 'user4'@'10.0.0.*' |
| GRANT SELECT, INSERT, UPDATE, DELETE ON `account`.* TO 'user4'@'10.0.0.*' |
+---------------------------------------------------------------------------+
2 rows in set (0.00 sec)
複製代碼
mysql> revoke delete on account.* from user4@'10.0.0.*';
Query OK, 0 rows affected (0.00 sec)
mysql> show grants for user4@'10.0.0.*';
+-------------------------------------------------------------------+
| Grants for user4@10.0.0.* |
+-------------------------------------------------------------------+
| GRANT USAGE ON *.* TO 'user4'@'10.0.0.*' |
| GRANT SELECT, INSERT, UPDATE ON `account`.* TO 'user4'@'10.0.0.*' |
+-------------------------------------------------------------------+
2 rows in set (0.00 sec)
複製代碼