mysql 存儲結構介紹及執行過程分析

MySQL體系結構介紹

 

1 mysql 的體系結構

 

 

MySQL整體的邏輯結構可以分爲4層,客戶層、服務層、存儲引擎層、數據層

客戶層
  • 客戶層:進行相關的連接處理、權限控制、安全處理等操作
服務層
  • 服務層負責與客戶層進行連接處理、處理以及執行SQL語句等,主要包含連接器、查詢緩存、優化器、執行器、存儲引擎。觸發器、視圖等也在這一層
  • 連接池:存儲和管理客戶端與數據庫的連接,一個線程負責管理一個連接並處理這個連接上的sql請求。

  • 緩存:緩存把執行過的語句和結果集以key-value對的形式存儲。

    如果查詢能夠命中,那麼結果集被直接返回給客戶端。

    如果在緩存未命中,就會繼續後面的執行階段。執行完成後,執行結果會被存入查詢緩存中。

    查詢緩存往往弊大於利。因爲查詢緩存的失效非常頻繁,對一個表的某一條數據更新,這個表上所有的查詢緩存都會被清空。

     

存儲引擎層
  • 存儲引擎層負責對數據的存儲和提取,常見的存儲引擎有InnoDB、MyISAM、Memory等,在MySQL5.5之後,MySQL默認的存儲引擎就是InnoDB,InnoDB默認使用的索引結構就是B+樹,上面的服務層就是通過API接口與存儲引擎層進行交互的
  • 存儲引擎:存儲引擎有多種類型,存儲引擎的作用是負責文件數據的存儲,每種存儲引擎提供了相同名稱但具體實現不同的接口給用戶層。不同類型引擎的存儲機制、索引實現和鎖功能不同,這是爲了讓用戶適應多種應用場景的需求。

  • 需要特別注意,存儲引擎只基於表,而非數據庫。
數據層
  • 數據層系主要包括MySQL中存儲數據的底層文件,與上層的存儲引擎進行交互,是文件的物理存儲層。其存儲的文件主要有:日誌文件、數據文件、配置文件、MySQL的進行pid文件和socket文件等。
    那麼一條SQL語句在MySQL的整個體系結構是如何執行的呢?
  • 文件系統:屬於操作系統層面,mysql應用層可以調用操作系統提供的系統調用操作文件,發起IO請求。

2、SQL語句的執行過程

當向MySQL發送一條SQL語句的時候

1、客戶層
  • 首先連接器與客戶端進行連接、以linux系統爲例,通過在Mysql服務啓動成功之後通過一下命令進行數據庫的登錄
[root@bp18425116f0cojd1vnz ~]# mysql -uroot -p
Enter password: 

如果密碼輸入錯誤的話就會有以下提示

ERROR 1045 (28000): Access denied for user 'root'@'localhost' (using password: YES

如果出現MySQL密碼忘記的情況下,可以通過以下方法進行登錄

1、查詢MySQL服務是否啓動,如若啓動,關閉MySQL服務
[root@bp18425116f0cojd1vnz ~]# ps -ef |grep mysql
root       87531       1  0 Feb09 ?        00:00:00 /bin/sh /www/server/mysql/bin/mysqld_safe --defaults-file=/etc/my.cnf --datadir=/www/server/data --pid-file=/www/server/data/bp18425116f0cojd1vnz.pid --sql-mode=NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION
mysql      88147   87531  0 Feb09 ?        00:43:28 /www/server/mysql/bin/mysqld --defaults-file=/etc/my.cnf --basedir=/www/server/mysql --datadir=/www/server/data --plugin-dir=/www/server/mysql/lib/plugin --user=mysql --sql-mode=NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION --log-error=bp18425116f0cojd1vnz.err --open-files-limit=65535 --pid-file=/www/server/data/bp18425116f0cojd1vnz.pid --socket=/tmp/mysql.sock --port=3306
root     2725702 2724782  0 14:35 pts/0    00:00:00 grep --color=auto mysql
2、關閉MySQL服務
[root@bp18425116f0cojd1vnz ~]# systemctl stop mysql
[root@bp18425116f0cojd1vnz ~]# systemctl status mysql
● mysqld.service - LSB: start and stop MySQL
   Loaded: loaded (/etc/rc.d/init.d/mysqld; generated)
   Active: inactive (dead) since Thu 2022-06-09 14:36:55 CST; 2s ago
     Docs: man:systemd-sysv-generator(8)
  Process: 2725788 ExecStop=/etc/rc.d/init.d/mysqld stop (code=exited, status=0/SUCCESS)

Feb 09 14:30:10 bp18425116f0cojd1vnz systemd[1]: Starting LSB: start and stop MySQL...
Feb 09 14:30:11 bp18425116f0cojd1vnz mysqld[1174]: /etc/rc.d/init.d/mysqld: line 244: my_print_defaults: command not found
Feb 09 14:30:11 bp18425116f0cojd1vnz mysqld[1174]: /etc/rc.d/init.d/mysqld: line 265: cd: /www/server/mysql: No such file or directory
Feb 09 14:30:11 bp18425116f0cojd1vnz mysqld[1174]: Starting MySQLCouldn't find MySQL server (/www/server/mysql/bin/mysqld_safe)[FAILED]
Feb 09 14:30:11 bp18425116f0cojd1vnz systemd[1]: Started LSB: start and stop MySQL.
Jun 09 14:36:52 bp18425116f0cojd1vnz systemd[1]: Stopping LSB: start and stop MySQL...
Jun 09 14:36:55 bp18425116f0cojd1vnz mysqld[2725788]: Shutting down MySQL..[  OK  ]
Jun 09 14:36:55 bp18425116f0cojd1vnz systemd[1]: mysqld.service: Succeeded.
Jun 09 14:36:55 bp18425116f0cojd1vnz systemd[1]: Stopped LSB: start and stop MySQL.
3、修改 vim /etc/my.cnf

在/etc/my.cnf 添加一行 skip-grant-tables

[client]
#password       = your_password
port            = 3306
socket          = /tmp/mysql.sock

[mysqld]
skip-grant-tables
port            = 3306
socket          = /tmp/mysql.sock
datadir = /www/server/data
default_storage_engine = InnoDB
performance_schema_max_table_instances = 400
table_definition_cache = 400
skip-external-locking
key_buffer_size = 32M
max_allowed_packet = 100G
4、重新啓動MySQL數據庫
[root@bp18425116f0cojd1vnz ~]# systemctl start mysql
5、以免密模式登錄數據庫
[root@bp18425116f0cojd1vnz ~]# mysql -uroot -p
Enter password: 
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 1
Server version: 5.6.50-log Source distribution

Copyright (c) 2000, 2020, Oracle and/or its affiliates. All rights reserved.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql> 
6、登錄mysql數據庫
mysql> use mysql;
Database changed
7、修改密碼
mysql> update mysql.user set authentication_string=password('your_password') where user='root';
Query OK, 4 rows affected (0.01 sec)
Rows matched: 4  Changed: 4  Warnings: 0

your_password爲自己想要替換的數據庫密碼

8、修改 /etc/my.cf

修改/etc/my.cf 文件 去除 skip-grant-tables

9、已修改之後的密碼登錄數據庫
[root@bp18425116f0cojd1vnz ~]# mysql -uroot -p
Enter password: 
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 2
Server version: 5.6.50-log Source distribution

Copyright (c) 2000, 2020, Oracle and/or its affiliates. All rights reserved.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql> 

當連接器與客戶端通過TCP進行三次握手連接成功之後,就會要求用戶輸入密碼進行登錄,當輸入密碼無誤時,客戶端與服務器建立連接成功之後,連接器就會去查詢出改用戶的權限然後存儲到查詢緩存中

2、查詢緩存

當客戶端的查詢語句爲select查詢語句的時候,如若再查詢緩存裏面已經查詢到了結果,就會直接把查詢結果返回給客戶端

3、解析器

在查詢緩存並沒有查詢到結果之後,就會走到解析器,在解析器這兒,會做如下工作

1、詞法分析

詞法分析會根據客戶端的SQL語句分析出各個關鍵詞,簡單地說就是把整個SQL拆分爲一個個的單詞,然後生茶一顆詞法分析樹

2、語法分析

在語法分析層面會根據上面生成的詞法分析樹判斷SQL語句是否符合語法規則,如果不符合,就會進行相應的提示信息

mysql> select djglfdjg from user;
ERROR 1054 (42S22): Unknown column 'djglfdjg' in 'field list'

如若在解析器執行正確之後,就會去執行相應的SQL,走到執行器

4、SQL執行器

在執行器這個階段,會進行SQL語句的執行,主要包括以下這幾個部分

1、預處理階段
  • 在開始執行的時候,預處理階段你對這個表有沒有執行查詢的權限,如若沒有,就會返回相應的錯誤
  • 檢查查詢的表或者字段是否存在,如若沒有,也會返回相應的錯誤信息
2、優化器

在優化器階段,優化器會對SQL的執行順序,使用哪個索引進行優化,確定SQL的執行方案,在這裏會生產explain的執行計劃
比如這個語句

mysql> explain SELECT Host  FROM `user` where Host='localhost';
+----+-------------+-------+------+-------------------------+---------+---------+-------+------+--------------------------+
| id | select_type | table | type | possible_keys           | key     | key_len | ref   | rows | Extra                    |
+----+-------------+-------+------+-------------------------+---------+---------+-------+------+--------------------------+
|  1 | SIMPLE      | user  | ref  | PRIMARY,index_user_Host | PRIMARY | 180     | const |    3 | Using where; Using index |
+----+-------------+-------+------+-------------------------+---------+---------+-------+------+--------------------------+
1 row in set (0.00 sec)

通過explan執行語句可以查詢到,在執行語句時,有以下結論

  • id=1 SELECT識別符,查詢序號即爲sql語句執行的順序
  • select_type=SIMPLE 表示SQL查詢語句走的是單表查詢
  • table=user 輸出的行所用的表
  • type=ref 顯示了連接使用了哪種類別,有無使用索引,type掃描方式由快到慢
    system > const > eq_ref > ref > range > index > ALL
    system:系統表,少量數據,往往不需要進行磁盤IO
    const:常量連接
    eq_ref:主鍵索引(primary key)或者非空唯一索引(unique not null)等值掃描 ref:非主鍵非唯一索引等值掃描
    range:範圍掃描
    index:索引樹掃描
    all:全表掃描
  • possible_keys 表示查詢語句可能會用到的索引,在這裏有兩個,PRIMARY表示爲主鍵索引,index_user_Host爲另一個索引
  • key 表示在查詢語句時實際用到的索引,在這裏爲PRIMARY,那爲什麼這裏只用到了PRIMARY這個索引呢,別急,後面會說到
  • key_len 表示使用的索引長度
  • ref 列顯示使用哪個列或常數與key一起從表中選擇行
  • rows 顯示MySQL執行查詢的行數,簡單且重要,數值越大越不好,說明沒有用好索引
  • Extra 該列包含MySQL解決查詢的詳細信息,
    Using index表示相應的select操作中使用了覆蓋索引(covering index),避免訪問了表的數據行,using where,表明索引被用來執行索引鍵值的查找
    Using where表明使用了where過濾
    Using join buffer使用了連接緩存
    Using temporary 使用了臨時表保存中間結果,mysql在對查詢結果排序時使用臨時表。常見於排序order by和分組查詢group by。
    group by一定要遵循所建索引的順序與個數
    using filesort,using temporary,using index最爲常見,出現前兩種表示是需要優化的地方
    通過觀察上面的執行語句,在查詢時,有2個索引,但是隻用到了PRIMARY這個索引,並沒有用到index_user_Host,查詢表所建立的索引
mysql> show  index from mysql.user;
+-------+------------+-----------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table | Non_unique | Key_name        | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+-------+------------+-----------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| user  |          0 | PRIMARY         |            1 | Host        | A         |        NULL |     NULL | NULL   |      | BTREE      |         |               |
| user  |          0 | PRIMARY         |            2 | User        | A         |           8 |     NULL | NULL   |      | BTREE      |         |               |
| user  |          1 | index_user_Host |            1 | Host        | A         |        NULL |     NULL | NULL   |      | BTREE      |         |               |
+-------+------------+-----------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+

該表有建立3個索引,查詢語句SELECT Host FROM user where Host='localhost';中查詢字段爲Host ,Extra爲Using where; Using index表明用到了覆蓋索引,也就是二級索引的 B+ 樹的葉子節點的數據存儲的是主鍵值,沒有必要再索引檢索磁盤IO來查詢數據,也就是覆蓋索引優化,所以並沒有通過index_user_Host這個索引去檢索數據

3、執行器

在執行器執行SQL語句會對權限進行校驗,如果有權限,就打開表繼續執行。打開表的時候,執行器就會根據表的引擎定義,去使用這個引擎提供的接口與存儲引擎層進行交互,執行SQL語句,並將結果返回個客戶端。這裏只是簡單介紹,後面章節詳細講解。

 

 

3 MySQL存儲引擎

 

InnoDB:支持事務,行鎖、外鍵、非鎖定讀(讀數據不上鎖)、使用多版本併發控制(MVCC)提高併發性、隔離級別爲repeatable並使用next-key locking 策略避免幻讀、採用聚集索引。

需要注意:聚集索引和非聚集索引關鍵不在於索引和數據記錄是否放在一個文件,而在於數據記錄是否按照主鍵的順序存放。

MyISAM:不支持事務、使用表鎖、採用非聚集索引、一個表由MYD(存放數據記錄)和MYI(存放索引)兩個文件組成(frm表結構文件除外)。

NDB:是一個集羣存儲引擎,數據全部放在內存中。

Memory:表數據全部存放於內存,適合用於存儲臨時數據的臨時表、使用哈希索引、只支持表鎖,併發性差、不支持TEXT 和 BLOB類型,存儲varchar時按照char方式存儲,比較浪費內存。

mysql使用memory存儲引擎作爲臨時表存放查詢的中間結果(如分組、排序等),如果中間結果集超過Memory表的容量設置或者中間結果含有TEXT或BLOB類型字段,則會將中間結果轉爲MyISAM表存在磁盤中導致查詢性能更慢。

Archive:其目標是提供高速插入和壓縮存儲功能、只支持insert和select操作、使用zlib對數據行壓縮後存儲、適用於存儲歸檔數據如日誌、使用行鎖,但本身不是事務安全的。

Federated:本身不存放數據,而是指向遠程mysql服務的一個表,實現對該表的操作。

Maria:開發時目的是用於替代MyISAM成爲默認引擎的,提供和InnoDB基本一樣的功能。

 

比對InnoDB和MyISAM

 

MyISAM 和 InnoDB 比較
1、MyISAM:是MySQL的默認數據庫引擎(5.5版之前),由早期的ISAM所改良。雖然性能極佳,但卻有一個缺點:不支持事務處理(transaction)。它是存儲記錄和文件的標準方法。不是事務安全的,而且不支持外鍵,如果執行大量的select,insert MyISAM比較適合
2、InnoDB:支持事物安全的引擎,支持外鍵,行鎖,事務控制是它最大的特點,在有大量的insert,update語句時,使用InnoDB比較合適,特別是針對多個併發和QPS較高的時候。

 

區別
表鎖差異
MyISAM:
myisam只支持表級鎖,用戶在操作myisam表時,select,update,delete,insert語句都會給表自動加鎖,如果加鎖以後的表滿足insert併發的情況下,可以在表的尾部插入新的數據。也可以通過lock table命令來鎖表,這樣操作主要是可以模仿事務,但是消耗非常大,一般只在實驗演示中使用。

InnoDB :
Innodb支持事務和行級鎖,是innodb的最大特色。
Innodb的行鎖模式有以下幾種:共享鎖,排他鎖,意向共享鎖(表鎖),意向排他鎖(表鎖),間隙鎖。
注意:當語句沒有使用索引,innodb不能確定操作的行,這個時候就使用的意向鎖,也就是表鎖

數據庫文件差異
MyISAM :
myisam屬於堆表
myisam在磁盤存儲上有三個文件,每個文件名以表名開頭,擴展名指出文件類型。
.frm 用於存儲表的定義
.MYD 用於存放數據
.MYI 用於存放表索引
myisam表還支持三種不同的存儲格式:
靜態表(默認,但是注意數據末尾不能有空格,會被去掉)
動態表
壓縮表

InnoDB :
innodb屬於索引組織表
innodb有兩種存儲方式,共享表空間存儲和多表空間存儲
兩種存儲方式的表結構和myisam一樣,以表名開頭,擴展名是.frm。
如果使用共享表空間,那麼所有表的數據文件和索引文件都保存在一個表空間裏,一個表空間可以有多個文件,通過innodb_data_file_path和innodb_data_home_dir參數設置共享表空間的位置和名字,一般共享表空間的名字叫ibdata1-n。
如果使用多表空間,那麼每個表都有一個表空間文件用於存儲每個表的數據和索引,文件名以表名開頭,以.ibd爲擴展名。

索引差異
1、關於自動增長
myisam引擎的自動增長列必須是索引,如果是組合索引,自動增長可以不是第一列,他可以根據前面幾列進行排序後遞增。
innodb引擎的自動增長列必須是索引,如果是組合索引也必須是組合索引的第一列。

2、關於主鍵
myisam允許沒有任何索引和主鍵的表存在,
myisam的索引都是保存行的地址。
innodb引擎如果沒有設定主鍵或者非空唯一索引,就會自動生成一個6字節的主鍵(用戶不可見)
innodb的數據是主索引的一部分,附加索引保存的是主索引的值。

3、關於count()函數
myisam保存有表的總行數,如果select count() from table;會直接取出出該值
innodb沒有保存表的總行數,如果使用select count() from table;就會遍歷整個表,消耗相當大,但是在加了wehre 條件後,myisam和innodb處理的方式都一樣。

4、全文索引
myisam支持 FULLTEXT類型的全文索引
innodb不支持FULLTEXT類型的全文索引,但是innodb可以使用sphinx插件支持全文索引,並且效果更好。(sphinx 是一個開源軟件,提供多種語言的API接口,可以優化mysql的各種查詢)

5、delete from table
使用這條命令時,innodb不會從新建立表,而是一條一條的刪除數據,在innodb上如果要清空保存有大量數據的表,最 好不要使用這個命令。(推薦使用truncate table,不過需要用戶有drop此表的權限)

6、索引保存位置
myisam的索引以表名+.MYI文件分別保存。
innodb的索引和數據一起保存在表空間裏。

 

 

關於死鎖:

什麼是死鎖?當兩個事務都需要獲得對方持有的排他鎖才能完成事務,這樣就導致了循環鎖等待,也就是常見的死鎖類型。

解決死鎖的方法:
(1)按同一順序訪問對象:
如果所有併發事務按同一順序訪問對象,則發生死鎖的可能性會降低。例如,如果兩個併發事務獲得 Supplier 表上的鎖,然後獲得 Part 表上的鎖,則在其中一個事務完成之前,另一個事務被阻塞在 Supplier 表上。第一個事務提交或回滾後,第二個事務繼續進行。不發生死鎖。將存儲過程用於所有的數據修改可以標準化訪問對象的順序。
(2)避免事務中的用戶交互:
避免編寫包含用戶交互的事務,因爲運行沒有用戶交互的批處理的速度要遠遠快於用戶手動響應查詢的速度,例如答覆應用程序請求參數的提示。例如,如果事務正在等待用戶輸入,而用戶去喫午餐了或者甚至回家過週末了,則用戶將此事務掛起使之不能完成。這樣將降低系統的吞吐量,因爲事務持有的任何鎖只有在事務提交或回滾時纔會釋放。即使不出現死鎖的情況,訪問同一資源的其它事務也會被阻塞,等待該事務完成。
(3)保持事務簡短並在一個批處理中:
在同一數據庫中併發執行多個需要長時間運行的事務時通常發生死鎖。事務運行時間越長,其持有排它鎖或更新鎖的時間也就越長,從而堵塞了其它活動並可能導致死鎖。
保持事務在一個批處理中,可以最小化事務的網絡通信往返量,減少完成事務可能的延遲並釋放鎖。
(4)使用低隔離級別:
確定事務是否能在更低的隔離級別上運行。執行提交讀允許事務讀取另一個事務已讀取(未修改)的數據,而不必等待第一個事務完成。使用較低的隔離級別(例如提交讀)而不使用較高的隔離級別(例如可串行讀)可以縮短持有共享鎖的時間,從而降低了鎖定爭奪。
(5)使用綁定連接:
使用綁定連接使同一應用程序所打開的兩個或多個連接可以相互合作。次級連接所獲得的任何鎖可以象由主連接獲得的鎖那樣持有,反之亦然,因此不會相互阻塞。

 

 

參考:沛沛 http://www.zbpblog.com/blog-368.html

 

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