小視訊原始碼,Bitmap的圓形處理
mysql官方文件 https://dev.mysql.com/doc/refman/
一條查詢語句的執行流程
1. 連線
Mysql服務監聽的埠預設為3306,有專門負責處理連線的模組,連線是需要許可權驗證。
如何檢視mysql的連線數?
show global status 'Thread%';
欄位 | 含義 |
---|---|
Threads_cached | 快取中的執行緒連線數 |
Threads_connected | 當前開啟的連線數 |
Threads_created | 為處理連線建立的執行緒數 |
Threads_running | 非睡眠狀態的連線數,通常指併發連線數 |
為何檢視mysl的連線數是“show Thread”檢視執行緒數呢?
因為客戶端每產生一個連線或一個會話,在伺服器端就會建立一個執行緒來處理。反過來,如果要結束會話,就需要殺死程序。
每一個連線都分配執行緒的話,毋庸置疑是需要消耗服務端資源的,所以在連線時長和連線數(併發量)上mysql就做了些處理。
- 連線時長:mysql會把長時間不活動(sleep)的連線自動斷開。
show variables like 'wait_timeout'; --非互動式超時時間,如JDBC程式
show variables like 'max_connections'; --互動式超時時間,如資料庫工具
互動式和非互動式的預設連線超時時長都是28800秒(8小時)。
- 連線數:mysql服務允許最大的連線數(併發數)是多少?
show variables like 'max_connections';
下圖中的最大連線數為200(這裡是我自己做了修改),在mysql5.7和目前的mysql8.0的版本中,mysql的預設最大連線數為151,最大可支援設定成100000(10w)
mysql8.0官網關於max_connections的描述
2. 查詢快取
mysql中查詢快取預設為關閉狀態(不推薦使用),且mysql8.0中已經將查詢快取移除了。需要快取還是交給ORM(如:mybatis預設開啟一級快取)框架或redis等第三方服務來實現。
show variables like 'query_cache%';
3. 語法解析和預處理
主要是對sql語句基於SQL語法進行詞法分析和語法分析以及語義解析。
3.1 詞法分析:就是把一條sql語句分成一個個單詞。
如
select * from student where student = '1';
會分成select、*、from、student、where、student、=、'1'八個單詞,每個單詞從哪開始從哪結束,是什麼型別。
3.2 語法分析
及對SQL做一些語法檢查,比如單引號是否閉合、識別關鍵字等,然後根據SQL語法規則,生成解析樹(select_lex)。
3.3 前處理器
在語法分析的基礎上(解決語法分析無法解析的語義),對錶名、列名是否存在、別名是否異常等問題進行解析處理,進一步生成一個新的解析樹。
4.查詢優化和查詢執行計劃
4.1 查詢優化器
查詢優化器的目的就是根據解析樹生成不同的執行計劃(Execution Plan),然後選擇一種最優的執行計劃,MySQL裡面使用的是基於開銷(cost)的優化器,哪種計劃開銷最小,就用哪種。
檢視查詢的開銷:
show status like 'Last_query_cost';
4.2 優化器都做哪些優化?
如:
兩表關聯查詢時,以哪個表為基準表;
多個索引可以使用時,使用哪個索引等等。
以下來自《資料庫查詢優化器藝術-原理解析與SQL效能優化》
4.2.1 子查詢優化
4.2.2 等價謂詞重寫
4.2.3 條件簡化
4.2.4 外連線消除
4.2.5 巢狀連線消除
4.2.6 連線的消除
4.2.7 語義優化
4.2.8 非SPJ優化
優化完之後,優化器會把解析樹變成一個查詢執行計劃,查詢執行計劃是一個數據結構。
可以通過在sql語句前加上explain來檢視執行計劃的資訊
如:
EXPLAIN select name from student where id = 1;
獲取詳細資訊:
EXPLAIN FORMAT=JSON select name from student where id = 1;
5.儲存引擎
mysql支援多種儲存引擎,常用的有MyISAM和InnoDB,5.5.5之前mysql預設的儲存引擎為MyISAM,5.5.5之後mysql預設的儲存引擎為InnoDB。
常見的儲存引擎
5.7 https://dev.mysql.com/doc/refman/5.7/en/storage-engines.html
8.0 https://dev.mysql.com/doc/refman/8.0/en/storage-engines.html
MyISAM
- 通常用於只讀或以讀為主的工作,表級鎖定限制了讀寫效能。
特點:- 支援表級別的鎖(插入和更新會鎖表)。不支援事物。
- 擁有較高的插入(insert)和查詢(select)速度。
- 儲存了表的行數(count速度更快)。
tips: 怎麼快速向資料庫插入100w條資料?
- 可以先用MyISAM插入資料,然後修改儲存引擎為InnoDB。
InnoDB
5.7、8.0版本中預設的儲存引擎,適合經常更新的表,存在併發讀寫或者有事務處理的業務系統
- 支援事務,支援外來鍵,因此資料的完整性,一致性更高
- 支援行級別的鎖和表級別的鎖
- 支援讀寫併發,寫不阻塞讀(MVCC)。啥是MVCC?以後再說。
- 特殊的索引存放方式,可以減少IO,提升查詢效率
一條更新語句是如何執行的
執行流程簡述
一個簡化後的過程(因為更新操作涉及到事務,這裡先記一個大概的流程示例)
要將student表中id=1的學生姓名(原為lisi)修改為zhangsan,執行sql語句
update student set name='zhangsan' where id=1;
- 事務開始,從記憶體(buffer pool)或磁碟取到包含這條資料的資料頁,返回給Server的執行器;
- 執行器修改資料頁的一行資料;
- 記錄修改之前的內容到undo log,如update student set name='lisi' where id=1;;
- 記錄要修改的操作到redo log,如update student set name='zhangsan' where id=1;
- 呼叫儲存引擎介面,記錄資料頁到buffer pool
- 事務提交。
緩衝池 Buffer Pool
InnoDB設定了一個儲存引擎從磁碟讀取資料到記憶體的最小單位,叫做頁。
作業系統也有頁的概念。作業系統的頁大小一般是4k(傳聞中的4k對齊),在InnoDB中,這個最小的單位預設是16KB大小。若需要修改這個值的話,修改後需要清空資料重新初始化服務。
https://dev.mysql.com/doc/refman/8.0/en/innodb-parameters.html#sysvar_innodb_page_size
也就是說InnoDB儲存引擎從磁碟讀取資料的時候,每次最少讀16KB的資料,我們所需要的操作的資料就在這樣的頁裡面,也就是常常說的資料頁。
而我們每次拿資料如果都從磁碟中取出來放入記憶體的話,還是避免不了頻繁io消耗資源的問題,這裡就還是需要一個快取的思想,把讀取過的資料頁快取起來。
InnoDB設計了一個記憶體的緩衝區。讀取資料的是會,先判斷緩衝區內是否存在,若存在則直接取用,不存在則從磁碟讀取後將資料放入這個記憶體的緩衝區內。這個緩衝區就叫做Buffer Pool。
修改資料時,也是先寫到buffer pool,而不是直接寫到磁碟。記憶體的資料頁和磁碟資料不一致的時候,我們把記憶體取的這部分資料叫做髒頁。InnoDB中有專門的後臺執行緒把buffer pool的資料寫到磁碟,每隔一段時間就會一次性的把多個修改寫入(同步)的磁碟,這個動作就叫做刷髒。
有次可見,Buffer Pool的作用就是為了提高讀寫的效率。
redo log
因為刷髒不是實時的,如果Buffer Pool裡面的髒頁沒有同步到磁碟時,伺服器或者資料庫宕機或者重啟,這些資料就會丟失。如何避免這部分資料的丟失,實現記憶體內資料的持久化呢?
InnoDB把所有對“頁”的修改操作寫入到一個操作日誌檔案中。如果髒頁中的內容沒有同步到磁碟時,資料庫再啟動的時候,會從這個日誌檔案進行恢復操作(實現crash-safe)。我們說的事務的ACID中的D(永續性),就是用它來實現的。
這個日誌檔案就叫做redo log(重做日誌)。
既然都要寫磁碟,為何不直接寫到DBFile裡面,還要先寫日誌再寫磁碟呢?
- 這個與順序io和隨機io有關
- 如果需要的資料是隨機分散在磁碟的不同頁的不同扇區中的,那麼找到相應的資料需要等磁臂旋轉到指定的頁,然後碟片尋找到對應的扇區(定址的過程),才能找到所需要的的一塊資料,依次進行此過程(不斷地重新定址)直到找完所有資料,這個就是隨機IO。
- 順序IO是指讀寫操作的訪問地址連續。如碟片已經找到了第一塊資料所在的扇區(定址成功)後,並且其他所需的資料就在這一塊資料後邊,那麼就不需要重新定址,可以依次拿到所需的資料,這個就叫順序IO。
- 直接寫資料檔案(寫資料(寫聚簇索引)、寫索引(普通索引))是隨機I/O,而記錄日誌是順序I/O(不斷地追加),因此先把修改寫入日誌檔案,在保證了記憶體資料安全性的情況下,可以延遲刷盤時機,進而提升系統吞吐量。
redo log特點
- 為InnoDB提供了崩潰恢復的特性,實現永續性
- redo log的大小是固定的,前面的內容會被覆蓋,一旦寫滿,就會觸發buffer pool到磁碟的同步,以便騰出空間記錄後面的修改。
- 預設有兩個檔案ib_logfile0和ib_logfile1,每個48m。
可以通過以下命令檢視InnoDB中redo log的相關引數:
show variables like 'innodb_log%';
引數 | 含義 |
---|---|
innodb_log_size | 每個檔案的大小,預設48M |
innodb_log_files_in_group | 檔案的數量,預設為2個 |
innodb_log_group_home_dir | 檔案所在路徑,如果不指定,則為datadir的路徑 |
除了redo log外,還有一個跟修改相關的日誌,叫做undo log。redo log和undo log與實務密切相關,統稱為事務日誌。
undo log
undo log(撤銷日誌或回滾日誌)記錄了實務發生之前的資料狀態,分為insert undo log和update updo log。如果修改資料時出現異常,可以用undo log來實現回滾操作(保持原子性)。
show variables like '%undo%';
引數 | 含義 |
---|---|
innodb_undo_directory | uodo檔案的路徑 |
innodb_undo_log_truncate | 是否開啟線上回收undo log日誌檔案 |
innodb_max_undo_log_size | undo檔案的大小。如果開啟了innodb_undo_log_truncate,超過這個大小的時候就會觸發truncate回收動作,如果page大小是16kb,truncate後空間縮小到10M。預設1073741824位元組=1G。 |
innoidb_undo_tablespaces | 設定undo獨立表空間個數,範圍為0-95,預設為0。0表示不開啟獨立undo表空間,且undo日誌儲存在ibdata檔案中。 |
innodb_undo_log_encrypt |