1. 程式人生 > >select * from user 這條 SQL 語句,背後藏著哪些不可告人的祕密?

select * from user 這條 SQL 語句,背後藏著哪些不可告人的祕密?

作為一名 Java開發人員,寫 SQL 語句是常有的事,但是你知道 SQL 語句背後的處理邏輯嗎?比如下面這條 SQL 語句:

select * from user where id=1

執行完這條語句後,我們就會得到 id 為 1 的使用者資訊。那麼對於這一條 SQL 語句,MySQL伺服器做了哪些處理呢?這篇文章我們就一起打卡 MySQL 資料庫中對 SQL 語句的處理邏輯。

瞭解 MySQL 資料庫的 SQL 語句內部處理邏輯有什麼好處?當我們碰到 MySQL 的一些異常或者問題時,就能夠直戳本質,更為快速地定位並解決問題。

想要更好的瞭解 SQL 語句的內部處理邏輯,我們可以先看 MySQL 的基本架構圖,這樣我們可以站在更高的角度去俯瞰 MySQL 資料庫,MySQL 的基本架構示意圖如下:

從圖中,我們可以清晰的看出 MySQL 的架構和各個模組以及 SQL 語句的執行過程,MySQL 資料庫整體可以分為 Server 層和儲存引擎層兩部分,其中 Server 層是共有的,而儲存引擎層則是可以以外掛的形式進行擴充套件。一條 SQL 語句大概會經歷連結管理、解析與優化、最後到儲存引擎,這三個模組。接下來我們就來聊一聊這三個模組。

連線管理

連線管理是 SQL 語句執行過程中碰到的第一關,連結管理就像一扇大門一樣,控制著客戶端與 Server 服務端的互動,連線管理主要工作是客戶端的身份認證和連線執行緒的管理。

每個客戶端與 Server 建立連線時,服務端都會建立一個執行緒來與客戶端進行互動,互動的第一項內容就是驗證客戶端的身份,認證憑據是基於客戶端發起連線請求時攜帶的主機資訊、使用者名稱、密碼。如果認證失敗,則結束連線任務,並且返回的 Access denied for user

錯誤。

如果認證成功,連線管理還會做一件事情,到許可權表中查詢出該使用者的許可權,在這次連線下,後續的許可權判斷都是基於此時讀取的許可權為依據,也就是說連線成功後,即使管理員對這個使用者做了許可權修改,也不會影響這次連線的許可權驗證。

連線管理需要做的事情就比較簡單,主要是負責客戶端與服務端進行連線,當然在連線執行緒上,連線管理也做了優化,並不是每個客戶端執行完任務之後,就把該執行緒銷燬,連線管理會把這些執行緒快取起來,等待新的連線,這也就不會頻繁的建立和銷燬執行緒,從而節約了開銷。

解析與優化

完成連線管理之後,SQL 語句執行的第二步就是解析和優化,這一步就非常的複雜,SQL 語句查詢的所有操作都在這裡了。我們可以將這一步細分為 4 小步。

查詢快取

在 MySQL 服務端也有快取,這是一個非常雞肋的功能,為什麼呢?看完了你就知道了。

MySQL 伺服器拿到查詢請求後,會先到查詢快取看看,之前是不是執行過這條語句。之前執行過的語句及其結果可能會以 key-value 對的形式,被直接快取在記憶體中。key 是查詢的語句,value 是查詢的結果。如果你的查詢能夠直接在這個快取中找到 key,那麼這個 value 就會被直接返回給客戶端。如果語句不在查詢快取中,就會繼續後面的執行階段。執行完成後,執行結果會被存入查詢快取中。

看上去沒毛病,這樣做會大大提升 MySQL 的效能,然而,你想多了,MySQL 的查詢快取命中率非常的低,主要原因是如果兩個查詢請求在任何字元上的不同(例如:空格、註釋、大小寫),都會導致快取不會命中。

還有就是快取有可能獲取到錯誤的資料,以某些系統函式舉例,可能同樣的函式的兩次呼叫會產生不一樣的結果,比如函式NOW,每次呼叫都會產生最新的當前時間,如果在一個查詢請求中呼叫了這個函式,那即使查詢請求的文字資訊都一樣,那不同時間的兩次查詢也應該得到不同的結果,如果在第一次查詢時就快取了,那第二次查詢的時候直接使用第一次查詢的結果就是錯誤的!

除了這些之外,MySQL 快取的失效也非常的頻繁,MySQL的快取系統會監測涉及到的每張表,只要該表的結構或者資料被修改,如對該表使用了 INSERT、 UPDATE、DELETE、TRUNCATE TABLE、ALTER TABLE、DROP TABLE 或 DROP DATABASE 語句,那使用該表的所有快取記憶體查詢都將變為無效並從快取記憶體中刪除!

看到這裡你知道查詢快取很雞肋了吧,快取對 MySQL 資料庫來說弊大於利,所以在 MySQL 8.0 版本直接將查詢快取的整塊功能刪掉了

語法解析和預處理

如果查詢快取沒有命中,接下來就需要進入正式的查詢階段了。因為客戶端程式傳送過來的請求只是一段文字而已,所以 MySQL 伺服器程式首先要對這段文字做語法解析。

首先通過關鍵字將 SQL 語句進行解析,並且生成一個“解析樹”。MySQL 解析器將使用 MySQL 語法規則驗證和解析查詢,例如,關鍵字是否使用正確、關鍵字的順序是否正確或者引號是否前後匹配等。

預處理是根據一些 MySQL 規則進一步檢查解析樹是否合法,例如資料表和資料列是否存在,還會解析名字和別名,看看他們是否有歧義等。

查詢優化

語法解析和預處理之後,你的需求就明白了,需要查詢哪張表,查詢的資料列是哪些、條件是什麼等等。但是使用怎麼樣的方式是最優查詢方式呢?查詢優化就是來幹這個事的,MySQL 的優化程式會對我們的語句做一些優化,如外連線轉換為內連線、表示式簡化、子查詢轉為連線等等。優化的結果就是生成一個執行計劃,這個執行計劃表明了應該使用哪些索引進行查詢,表之間的連線順序是啥樣的。

執行器

執行器會執行查詢優化後的執行計劃,通過與儲存引擎互動,完成資料的查詢操作,返回最終的資料結果。

開始執行的時候,要先判斷一下你對這個表 T 有沒有執行查詢的許可權,如果沒有,就會返回沒有許可權的錯誤,如下所示 (在工程實現上,如果命中查詢快取,會在查詢快取返回結果的時候,做許可權驗證。查詢也會在優化器之前呼叫 precheck 驗證許可權)。


mysql> select * from user where ID=1;

ERROR 1142 (42000): SELECT command denied to user 'b'@'localhost' for table 'T'

如果有許可權,就開啟表繼續執行。開啟表的時候,執行器就會根據表的引擎定義,去使用這個引擎提供的介面。

比如我們這個例子中的表 user 中,假設 ID 欄位沒有索引,那麼執行器的執行流程是這樣的:

1、呼叫 InnoDB 引擎介面取這個表的第一行,判斷 ID 值是不是 10,如果不是則跳過,如果是則將這行存在結果集中;

2、呼叫引擎介面取“下一行”,重複相同的判斷邏輯,直到取到這個表的最後一行。

3、執行器將上述遍歷過程中所有滿足條件的行組成的記錄集作為結果集返回給客戶端。

到這裡,執行 SQL 語句就執行完了,其實這內部還是非常複雜的。

儲存引擎

到上面為止,SQL 語句就執行完了,但是與真實資料打交道的是儲存引擎,儲存引擎是 MySQL伺服器對資料的儲存和提取操作的封裝模組。我們知道表是由一行一行的記錄組成的,但這只是一個邏輯上的概念,物理上如何表示記錄,怎麼從表中讀取資料,怎麼把資料寫入具體的物理儲存器上,這都是儲存引擎負責的事情。

為了實現不同的功能,MySQL提供了各式各樣的儲存引擎,不同儲存引擎管理的表具體的儲存結構可能不同,採用的存取演算法也可能不同。比如,MySQL5.7 之後預設的 InnoDB 儲存引擎。

可以看出一條 SQL 語句的執行還是非常複雜的,涉及到了很多的模組,文章到這裡就結束了,感謝您的閱讀,希望這篇文章對你的學習和工作有所幫助,如果您覺得文章有用,歡迎點贊+轉發。

最後

目前網際網路上很多大佬都有 MySQL 內部架構相關文章,如有雷同,請多多包涵了。原創不易,碼字不易,還希望大家多多支援。若文中有所錯誤之處,還望提出,謝謝。

歡迎掃碼關注微信公眾號:「平頭哥的技術博文」,和平頭哥一起學習,一起進步。

相關推薦

select * from user SQL 語句後藏著哪不可告人祕密

作為一名 Java開發人員,寫 SQL 語句是常有的事,但是你知道 SQL 語句背後的處理邏輯嗎?比如下面這條 SQL 語句: select * from user where id=1 執行完這條語句後,我們就會得到 id 為 1 的使用者資訊。那麼對於這一條 SQL 語句,MySQL伺服器做了哪些處理呢?

只使用一sql語句刪除表中的重複記錄

一個表,表1,ref(唯一值),type(有重複)如何去掉重複記錄。 建表sql語句: -- ---------------------------- -- Table structure for `deletechongfu` -- -------------------

執行多SQL語句實現資料庫事務(不可傳入Sql引數)

          《圖書館維護系統》的時候我負責任務管理模組,由於一些業務有些複雜,所以想用儲存過程,但是賈琳師哥說了一堆儲存過程的不好,讓我用sql語句。好吧,用就用吧,但是吧,大家都知道執行多條sql語句,是需要用到事務的(保持資料一致性),儲存過程中加事務我還是會滴

SQL中DQL語句select * from tb_name為什麼不介意使用而建議使用select 欄位1欄位2...欄位n from tb_name

  首先,其實select 欄位1,欄位2...欄位N from tb_name語句是select * from tb_name的優化。  1、執行效率。做一個實驗 select top 10000 gid,fariqi,reader,title from tgo

前端之路:sql語句表中隨機獲取一記錄(資料)。(或者獲取隨機獲取多(記錄)資料)

<!--表中獲取隨機一條title 耗時0.01s id==隨機欄位,最好為表id--> SELECT * FROM `tableName` AS t1 JOIN (SELECT ROUND(RAND() * ((SELECT MAX(id) FROM `ta

MyBatis中的多表操作情形一:一對一(方式1:一sql語句查詢MyBatis3.0可以用association和collection標籤)

MyBatis支援多表操作,即可以將資料庫中多表的關係對映到物件之間的關係中 表與表之間的關係可以有:一對一,一對多,多對多 關係一演示案例:人和身份證是一對一的,分別建兩個表person和card,其中person的cardid欄位外來鍵關聯card的id,

SQL語句提交後db2都做了什麼?

一直在做 db2 資料倉庫的運維工作,對一些常用操作已經非常熟悉,但是總感覺自己學到是仍然是操作的細節,而不是真正的知識。如果你問我,一條 SQL 語句提交後,db2 都做了哪些工作,我可能會有點慌,因為我不能肯定的回答出來。於是,我就搜尋一些資料,結合自己的理解,總結一下關於 db2 體系

如何在mybatis中一個insert,deleteupdate中執行多SQL語句

一般我們會在applicationContect中這麼配置: <!-- 1. 資料來源 : DriverManagerDataSource --> <bean id="dataSource" class="org.apache.commons.dbcp2.BasicDataSo

資料庫表3個欄位姓名薪水部門用一sql語句求每個部門薪水最高的人姓名

select t1.姓名 from 表 t1, (select max(薪水) 薪水,部門 from 表 group by 部門) t2 where t1.部門=t2.部門 and t1.薪水 = t2.薪水 create table t1 (   NAME VARCH

Mysql 一SQL語句實現批量更新資料update結合case、when和then的使用案例

如何用一條sql語句實現批量更新?mysql並沒有提供直接的方法來實現批量更新,但是可以用點小技巧來實現。 複製程式碼 程式碼如下: UPDATE mytable SET myfield = CASE id WHEN 1 THEN

關於postgres一sql語句中子查詢的返回值不止一個且拼接成相關規則的字串

這個問題之前好像碰到過,後來分成兩次查詢,最後把相關資料有拼接了一下,當時算是矇混過關了。 但是古語有云:自己挖的坑,含著淚也要給填了。 這次有碰到這個問題,但是不想還查兩遍了,就查閱相關資料把它解決了~! 資料庫postgreSQL select array_to_str

php中mysql一sql語句查詢出所有符合條件的資料該怎麼寫?

假如一個表裡有個classid欄位是類別的id,我想用一條sql語句查出classid=5的所有資料的id該怎麼查呢?正常是要迴圈,放到數組裡的吧 如圖,我想查詢classid=2的對應所有id,用

ORACLE觸發器:在insert之前判斷插入語句是否可插入不可插入時拋異常........

CREATE OR REPLACE TRIGGER "RQC_RECORD_TRI_INS"  BEFORE insert ON RQC_RECORD FOR EACH ROW DECLARE     NEW_TIME VARCHAR2(8);     START_TIME

師兄大廠面試遇到 SQL 資料分析題差點含淚而歸!

> **寫在前面**:我是「雲祁」,一枚熱愛技術、會寫詩的大資料開發猿。暱稱來源於王安石詩中一句 `[ 雲之祁祁,或雨於淵 ]` ,甚是喜歡。 > > 寫部落格一方面是對自己學習的一點點總結及記錄,另一方面則是希望能夠幫助更多對大資料感興趣的朋友。如果你也對 `資料中臺、資料建模、資料分析

用一SQL語句查出每門課都大於80分的學生的姓名

src exist spa weight utf8 exists null ont http 用一條SQL語句查出每門課都大於80分的學生的姓名,數據表結構如下: 建表SQL如下: SET FOREIGN_KEY_CHECKS=0; -- --------------

MyBatis插件及示例----打印每SQL語句及其執行時間

應該 log bject 兩個 ack ctrl+ 給定 打破 insert Plugins 摘一段來自MyBatis官方文檔的文字。 MyBatis允許你在某一點攔截已映射語句執行的調用。默認情況下,MyBatis允許使用插件來攔截方法調用 Execut

如何一sql語句查找表中第二大值

roman splay 排名 數據庫 依然 sel col eight over 例1: 一個Customer表,一個字段Value,現請問如何查到Value中第二大的值 select max(value) from Customer

使用jmeter執行多sql語句

多個 true targe csdn allow 知識 word sta lib 註意2個地方 1、 【JDBC Connection Configuration】 在配置DataBase URL的時候,加上allowMultiQueries=true參數如: jdbc:

sql語句無法綁定由多個部分組成的標識符 "xxx"

什麽 lec title id字段 數據庫 pan 代碼 今天 esc String sql = "select TOP 7 news_id,news_title,news_addtime,news_url from web_news_info a"

sql語句update 多記錄

key mysq ech tle date class spl imp set UPDATE categories SET display_order = CASE id WHEN 1 THEN 3 WHEN 2 THEN 4 WHEN 3 THEN 5 END