就像你調用方法一樣嘛
通過調用
exec 存儲過程名 參數名1,參數名2,.....
進入存儲過程,根據存儲過程編寫語句順序執行
2. 資料庫 SQL語句 子查詢執行過程
子查詢執行過程,可以用以下例子來說明:
語句如下:
select*fromscorewheresidin(selectsidfromstudentwhere班級='一班')
在sql語句中,資料庫先執行的是括弧中的部分,得出student表中一班學生的sid,然後再在score表中選出sid為一班id的哪些學生的詳細內容。
3. sql語句分析
SQL 語句摘要可以用在MySQL的各個方面,比如 性能字典里對語句的分析,查詢重寫插件規則改寫等等。
接下來依次看下語句摘要在這兩方面的使用。
1. 性能字典
mysql> call sys.ps_setup_enable_consumer('statements');
+---------------------+
| summary |
+---------------------+
| Enabled 4 consumers |
+---------------------+
1 row in set (0.00 sec)
Query OK, 0 rows affected (0.00 sec)
開啟後,執行幾次之前的幾條 SQL。
完後可以很方便的從 sys 庫里分析這類語句的執行情況,包括執行次數,執行時間,掃描的記錄數,鎖定的時間,是否用到排序等等。
2. 查詢重寫插件
比如要阻止對表 p1 通過欄位 r1 的刪除動作,可以用查詢重寫插件在 MySQL 語句分析層直接轉換,這時候就得用到摘要函數 statement_digest_text。
假設:表 p1 欄位 id 值全部為正。
delete from p1 where id = 1000;
要改寫為,
delete from p1 where id = -1;
利用函數 statement_digest_text 來定製這條 SQL 的重寫規則。
mysql> INSERT INTO query_rewrite.rewrite_rules (pattern, replacement,pattern_database) -> VALUES( -> statement_digest_text('delete from p1 where id = 1000') , -> statement_digest_text('delete from p1 where id = -1'), -> 'ytt' -> );Query OK, 1 row affected (0.01 sec)
語句被查詢重寫後的效果:
mysql> delete from p1 where id = 20000;Query OK, 0 rows affected, 1 warning (0.00 sec)
mysql> show warnings\G*************************** 1. row *************************** Level: Note Code: 1105Message: Query 'delete from p1 where id = 20000' rewritten to 'DELETE FROM `p1` WHERE `id` = - 20000' by a query rewrite plugin1 row in set (0.00 sec)
mysql> select count(*) from p1;+----------+| count(*) |+----------+| 9000001 |+----------+1 row in set (1.59 sec)
總結
MySQL 8.0 新增的語句摘要函數可以很方便的分析 SQL 語句執行的各個方面,比以前分析類似的場景要簡單的多。
4. 數據分析過程如果用SQL語句進行統計如何實現
方法和詳細的操作步驟如下:
1、第一步,創建一個測試表,詳細代碼見下圖,轉到下面的步驟。
5. MySql中Sql的執行過程
如果查詢緩存沒有命中,那麼SQL請求會進入分析器,分析器是用來分辨SQL語句的執行目的,其執行過程大致分為兩步:
表1 語法分析關鍵字然後再通過語法規則解析,判斷輸入的SQL 語句是否滿足MySQL語法,並且生成圖5的語法樹。由SQL語句生成的四個單詞中,識別出兩個關鍵字,分別是select 和from。根據MySQL的語法Select 和 from之間對應的是fields 欄位,下面應該掛接username;在from後面跟隨的是Tables欄位,其下掛接的是userinfo。
優化器的作用是對SQL進行優化,生成最有的執行方案。如圖6所示,前面提到的SQL解析器通過語法分析和語法規則生成了SQL語法樹。這個語法樹作為優化器的輸入,而優化器(黃色的部分)包含了邏輯變換和代價優化兩部分的內容。在優化完成以後會生成SQL執行計劃作為整個優化過程的輸出,交給執行器在存儲引擎上執行。
所處的位置如上圖所示,這節的重點在優化器中的邏輯變換和代價優化上。
邏輯變換也就是在關系代數基礎上進行變換,其目的是為了化簡,同時保證SQL變化前後的結果一致,也就是邏輯變化並不會帶來結果集的變化。其主要包括以下幾個方面:
這樣講概念或許有些抽象,通過圖7 來看看邏輯變化如何在SQL中執行的吧。
如圖7所示,從上往下共有4個步驟:
1. 針對存在的SQL語句,首先通過「否定消除」,去掉條件判斷中的「NOT」。語句由原來的「or」轉換成「and」,並且大於小於符號進行變號。藍色部分為修改前的SQL,紅色是修改以後的SQL。2. 等值傳遞,這一步很好理解分別降」t2.a=9」 和」t2.b=5」分別替換掉SQL中對應的值。3. 接下來就是常量表達式計算,將「5+7」計算得到「12」。4. 最後是常量表達式計算後的化簡,將」9<=10」化簡為」true」帶入到最終的SQL表達式中完成優化。
代價優化是用來確定每個表,根據條件是否應用索引,應用哪個索引和確定多表連接的順序等問題。為了完成代價優化,需要找到一個代價最小的方案。因此,優化器是通過基於代價的計算方法來決定如何執行查詢的(Cost-based Optimization)。簡化的過程如下:
這里將配置操作的代價分為MySQL 服務層和MySQL 引擎層,MySQL 服務層主要是定義CPU的代價,而MySQL 引擎層主要定義IO代價。MySQL 5.7 引入了兩個系統表mysql.server_cost和mysql.engine_cost來分別配置這兩個層的代價。如下:MySQL 服務層代價保存在表server_cost中,其具體內容如下:
由上可以看出創建臨時表的代價是很高的,尤其是內部的myisam或innodb臨時表。MySQL 引擎層代價保存在表engine_cost中,其具體內容如下:
目前io_block_read_cost和memory_block_read_cost默認值均為1,實際生產中建議酌情調大memory_block_read_cost,特別是對普通硬碟的場景。MySQL會根據SQL查詢生成的查詢計劃中對應的操作從上面兩張代價表中查找對應的代價值,並且進行累加形成最終執行SQL計劃的代價。再將多種可能的執行計劃進行比較,選取最小代價的計劃執行。
當分析器生成查詢計劃,並且經過優化器以後,就到了執行器。執行器會選擇執行計劃開始執行,但在執行之前會校驗請求用戶是否擁有查詢的許可權,如果沒有許可權,就會返回錯誤信息,否則將會去調用MySQL引擎層的介面,執行對應的SQL語句並且返回結果。例如SQL:「SELECT * FROM userinfo WHERE username = 'Tom';「假設 「username「 欄位沒有設置索引,就會調用存儲引擎從第一條開始查,如果碰到了用戶名字是」 Tom「, 就將結果集返回,沒有查找到就查看下一行,重復上一步的操作,直到讀完整個表或者找到對應的記錄。需要注意SQL語句的執行順序並不是按照書寫順序來的,順序的定義會在分析器中做好,一般是按照如下順序:
如果命中的記錄比較多,應用會從MySql Server一批批獲取數據
本文從MySQL中SQL語句的執行過程作為切入點,首先介紹了查詢請求的執行流程,其中將MySQL的處理分為MySQL Server層和MySQL存儲引擎層。通過介紹SQL語句的流轉,引出了後面要介紹的5大組件,他們分別是:連接器、查詢緩存、分析器、優化器、執行器。後面的內容中對每個組件進行了詳細的介紹。連接器,負責身份認證和許可權鑒別;查詢緩存,將查詢的結果集進行緩存,提高查詢效率;分析器,對SQL語句執行語法分析和語法規則,生成語法樹和執行計劃;優化器,包括邏輯變換和代價優化;執行器,在檢查用戶許可權以後對數據進行逐條查詢,整個過程遵守SQL語句的執行順序。
6. SQL 語句在資料庫中是怎樣執行的
第一步:應用程序把查詢SQL語句發給伺服器端執行
我們在數據層執行SQL語句時,應用程序會連接到相應的資料庫伺服器,把SQL語句發送給伺服器處理。
第二步:伺服器解析請求的SQL語句
SQL計劃緩存,經常用查詢分析器的朋友大概都知道這樣一個事實,往往一個查詢語句在第一次運行的時候需要執行特別長的時間,但是如果你馬上或者在一定時間內運行同樣的語句,會在很短的時間內返回查詢結果。原因是:
伺服器在接收到查詢請求後,並不會馬上去資料庫查詢,而是在資料庫中的計劃緩存中找是否有相對應的執行計劃。如果存在,就直接調用已經編譯好的執行計劃,節省了執行計劃的編譯時間。
如果所查詢的行已經存在於數據緩沖存儲區中,就不用查詢物理文件了,而是從緩存中取數據,這樣從內存中取數據就會比從硬碟上讀取數據快很多,提高了查詢效率。數據緩沖存儲區會在後面提到。
如果查詢語句所包含的數據行已經讀取到數據緩沖存儲區的話,伺服器會直接從數據緩沖存儲區中讀取數據返回給應用程序,避免了從物理文件中讀取,提高查詢速度。
如果數據行沒有在數據緩沖存儲區中,則會從物理文件中讀取記錄返回給應用程序,同時把數據行寫入數據緩沖存儲區中,供下次使用。
FROM子句返回初始結果集。
WHERE子句排除不滿足搜索條件的行。
GROUP BY子句將選定的行收集到GROUP BY子句中各個唯一值的組中。
選擇列表中指定的聚合函數可以計算各組的匯總值。
此外,HAVING子句排除不滿足搜索條件的行。
計算所有的表達式;
使用order by對結果集進行排序。
查找你要搜索的欄位。
如果在SQL計劃緩存中沒有對應的執行計劃,伺服器首先會對用戶請求的SQL語句進行語法效驗,如果有語法錯誤,伺服器會結束查詢操作,並用返回相應的錯誤信息給調用它的應用程序。
注意:此時返回的錯誤信息中,只會包含基本的語法錯誤信息,例如select寫成selec等,錯誤信息中如果包含一列表中本沒有的列,此時伺服器是不會檢查出來的,因為只是語法驗證,語義是否正確放在下一步進行。
語法符合後,就開始驗證它的語義是否正確。例如,表名、列名、存儲過程等等資料庫對象是否真正存在,如果發現有不存在的,就會報錯給應用程序,同時結束查詢。
接下來就是獲得對象的解析鎖,我們在查詢一個表時,首先伺服器會對這個對象加鎖,這是為了保證數據的統一性,如果不加鎖,此時有數據插入,但因為沒有加鎖的原因,查詢已經將這條記錄讀入,而有的插入會因為事務的失敗會回滾,就會形成臟讀的現象。
接下來就是對資料庫用戶許可權的驗證。SQL語句語法,語義都正確,此時並不一定能夠得到查詢結果,如果資料庫用戶沒有相應的訪問許可權,伺服器會報出許可權不足的錯誤給應用程序,在稍大的項目中,往往一個項目裡面會包含好幾個資料庫連接串,這些資料庫用戶具有不同的許可權,有的是只讀許可權,有的是只寫許可權,有的是可讀可寫,根據不同的操作選取不同的用戶來執行。稍微不注意,無論你的SQL語句寫的多麼完善,完美無缺都沒用。
解析的最後一步,就是確定最終的執行計劃。當語法、語義、許可權都驗證後,伺服器並不會馬上給你返回結果,而是會針對你的SQL進行優化,選擇不同的查詢演算法以最高效的形式返回給應用程序。例如在做表聯合查詢時,伺服器會根據開銷成本來最終決定採用hashjoin,mergejoin ,還是loop join,採用哪一個索引會更高效等等。不過它的自動化優化是有限的,要想寫出高效的查詢SQL還是要優化自己的SQL查詢語句。
當確定好執行計劃後,就會把這個執行計劃保存到SQL計劃緩存中,下次在有相同的執行請求時,就直接從計劃緩存中取,避免重新編譯執行計劃。
第三步:語句執行
伺服器對SQL語句解析完成後,伺服器才會知道這條語句到底表態了什麼意思,接下來才會真正的執行SQL語句。
此時分兩種情況:
說明:SQL緩存分好幾種,這里有興趣的朋友可以去搜索一下。有時因為緩存的存在,使得我們很難馬上看出優化的結果,因為第二次執行因為有緩存的存在,會特別快速,所以一般都是先消除緩存,然後比較優化前後的性能表現,這里有幾個常用的方法:
1 DBCC DROPCLEANBUFFERS
2 從緩沖池中刪除所有清除緩沖區。
3 DBCC FREEPROCCACHE
4 從過程緩存中刪除所有元素。
5 DBCC FREESYSTEMCACHE
6 從所有緩存中釋放所有未使用的緩存條目。
SQL Server 2005資料庫引擎會事先在後台清理未使用的緩存條目,以使內存可用於當前條目。但是,可以使用此命令從所有緩存中手動刪除未使用的條目。
這只能基本消除SQL緩存的影響,目前好像沒有完全消除緩存的方案,如果大家有,請指教。
執行順序:
7. SQL語句執行過程詳解
SQL語句執行過程詳解
一條sql,plsql的執行到底是怎樣執行的呢?
一、SQL語句執行原理:
第一步:客戶端把語句發給伺服器端執行當我們在客戶端執行 select 語句時,客戶端會把這條 SQL 語句發送給伺服器端,讓伺服器端的
進程來處理這語句。也就是說,Oracle 客戶端是不會做任何的操作,他的主要任務就是把客戶端產生
的一些 SQL 語句發送給伺服器端。雖然在客戶端也有一個資料庫進程,但是,這個進程的作用跟伺服器
上的進程作用事不相同的。伺服器上的資料庫進程才會對SQL 語句進行相關的處理。不過,有個問題需
要說明,就是客戶端的進程跟伺服器的進程是一一對應的。也就是說,在客戶端連接上伺服器後,在客戶
端與伺服器端都會形成一個進程,客戶端上的我們叫做客戶端進程;而伺服器上的我們叫做伺服器進程。
第二步:語句解析
當客戶端把 SQL 語句傳送到伺服器後,伺服器進程會對該語句進行解析。同理,這個解析的工作,
也是在伺服器端所進行的。雖然這只是一個解析的動作,但是,其會做很多「小動作」。
1. 查詢高速緩存(library cache)。伺服器進程在接到客戶端傳送過來的 SQL 語句時,不
會直接去資料庫查詢。而是會先在資料庫的高速緩存中去查找,是否存在相同語句的執行計劃。如果在
數據高速緩存中,則伺服器進程就會直接執行這個 SQL 語句,省去後續的工作。所以,採用高速數據緩
存的話,可以提高 SQL 語句的查詢效率。一方面是從內存中讀取數據要比從硬碟中的數據文件中讀取
數據效率要高,另一方面,也是因為這個語句解析的原因。
不過這里要注意一點,這個數據緩存跟有些客戶端軟體的數據緩存是兩碼事。有些客戶端軟體為了
提高查詢效率,會在應用軟體的客戶端設置數據緩存。由於這些數據緩存的存在,可以提高客戶端應用軟
件的查詢效率。但是,若其他人在伺服器進行了相關的修改,由於應用軟體數據緩存的存在,導致修改的
數據不能及時反映到客戶端上。從這也可以看出,應用軟體的數據緩存跟資料庫伺服器的高速數據緩存
不是一碼事。
2. 語句合法性檢查(data dict cache)。當在高速緩存中找不到對應的 SQL 語句時,則服
務器進程就會開始檢查這條語句的合法性。這里主要是對 SQL 語句的語法進行檢查,看看其是否合乎
語法規則。如果伺服器進程認為這條 SQL 語句不符合語法規則的時候,就會把這個錯誤信息,反饋給客
戶端。在這個語法檢查的過程中,不會對 SQL 語句中所包含的表名、列名等等進行 SQL 他只是語法
上的檢查。
3. 語言含義檢查(data dict cache)。若 SQL 語句符合語法上的定義的話,則伺服器進程
接下去會對語句中的欄位、表等內容進行檢查。看看這些欄位、表是否在資料庫中。如果表名與列名不
准確的話,則資料庫會就會反饋錯誤信息給客戶端。所以,有時候我們寫 select 語句的時候,若語法
與表名或者列名同時寫錯的話,則系統是先提示說語法錯誤,等到語法完全正確後,再提示說列名或表名
錯誤。
4. 獲得對象解析鎖(control structer)。當語法、語義都正確後,系統就會對我們需要查詢
的對象加鎖。這主要是為了保障數據的一致性,防止我們在查詢的過程中,其他用戶對這個對象的結構發
生改變。
5. 數據訪問許可權的核對(data dict cache)。當語法、語義通過檢查之後,客戶端還不一定
能夠取得數據。伺服器進程還會檢查,你所連接的用戶是否有這個數據訪問的許可權。若你連接上伺服器
的用戶不具有數據訪問許可權的話,則客戶端就不能夠取得這些數據。有時候我們查詢數據的時候,辛辛苦
苦地把 SQL 語句寫好、編譯通過,但是,最後系統返回個 「沒有許可權訪問數據」的錯誤信息,讓我們氣
半死。這在前端應用軟體開發調試的過程中,可能會碰到。所以,要注意這個問題,資料庫伺服器進程先
檢查語法與語義,然後才會檢查訪問許可權。
6. 確定最佳執行計劃 ?。當語句與語法都沒有問題,許可權也匹配的話,伺服器進程還是不會直接對
資料庫文件進行查詢。伺服器進程會根據一定的規則,對這條語句進行優化。不過要注意,這個優化是有
限的。一般在應用軟體開發的過程中,需要對資料庫的 sql 語言進行優化,這個優化的作用要大大地大
於伺服器進程的自我優化。所以,一般在應用軟體開發的時候,資料庫的優化是少不了的。當伺服器進程
的優化器確定這條查詢語句的最佳執行計劃後,就會將這條 SQL 語句與執行計劃保存到數據高速緩存
(library cache)。如此的話,等以後還有這個查詢時,就會省略以上的語法、語義與許可權檢查的步驟,
而直接執行 SQL 語句,提高 SQL 語句處理效率。
第三步:語句執行
語句解析只是對 SQL 語句的語法進行解析,以確保伺服器能夠知道這條語句到底表達的是什麼意
思。等到語句解析完成之後,資料庫伺服器進程才會真正的執行這條 SQL 語句。這個語句執行也分兩
種情況。
一是若被選擇行所在的數據塊已經被讀取到數據緩沖區的話,則伺服器進程會直接把這個數據傳遞
給客戶端,而不是從資料庫文件中去查詢數據。
若數據不在緩沖區中,則伺服器進程將從資料庫文件中查詢相關數據,並把這些數據放入到數據緩沖
區中(buffer cache)。
第四步:提取數據
當語句執行完成之後,查詢到的數據還是在伺服器進程中,還沒有被傳送到客戶端的用戶進程。所以,
在伺服器端的進程中,有一個專門負責數據提取的一段代碼。他的作用就是把查詢到的數據結果返回給
用戶端進程,從而完成整個查詢動作。從這整個查詢處理過程中,我們在資料庫開發或者應用軟體開發過
程中,需要注意以下幾點:
一是要了解資料庫緩存跟應用軟體緩存是兩碼事情。資料庫緩存只有在資料庫伺服器端才存在,在
客戶端是不存在的。只有如此,才能夠保證資料庫緩存中的內容跟資料庫文件的內容一致。才能夠根據
相關的規則,防止數據臟讀、錯讀的發生。而應用軟體所涉及的數據緩存,由於跟資料庫緩存不是一碼事
情,所以,應用軟體的數據緩存雖然可以提高數據的查詢效率,但是,卻打破了數據一致性的要求,有時候
會發生臟讀、錯讀等情況的發生。所以,有時候,在應用軟體上有專門一個功能,用來在必要的時候清除
數據緩存。不過,這個數據緩存的清除,也只是清除本機上的數據緩存,或者說,只是清除這個應用程序
的數據緩存,而不會清除資料庫的數據緩存。
二是絕大部分 SQL 語句都是按照這個處理過程處理的。我們 DBA 或者基於 Oracle 資料庫的
開發人員了解這些語句的處理過程,對於我們進行涉及到 SQL 語句的開發與調試,是非常有幫助的。有
時候,掌握這些處理原則,可以減少我們排錯的時間。特別要注意,資料庫是把數據查詢許可權的審查放在
語法語義的後面進行檢查的。所以,有時會若光用資料庫的許可權控制原則,可能還不能滿足應用軟體許可權
控制的需要。此時,就需要應用軟體的前台設置,實現許可權管理的要求。而且,有時應用資料庫的許可權管
理,也有點顯得繁瑣,會增加伺服器處理的工作量。因此,對於記錄、欄位等的查詢許可權控制,大部分程
序涉及人員喜歡在應用程序中實現,而不是在資料庫上實現。
DBCC DROPCLEANBUFFERS
從緩沖池中刪除所有清除緩沖區。
DBCC FREEPROCCACHE
從過程緩存中刪除所有元素。
DBCC FREESYSTEMCACHE
從所有緩存中釋放所有未使用的緩存條目
SQL語句中的函數、關鍵字、排序等執行順序:
1. FROM 子句返回初始結果集。
2. WHERE 子句排除不滿足搜索條件的行。
3. GROUP BY 子句將選定的行收集到 GROUP BY 子句中各個唯一值的組中。
4. 選擇列表中指定的聚合函數可以計算各組的匯總值。
5. 此外,HAVING 子句排除不滿足搜索條件的行。
6. 計算所有的表達式;
7. 使用 order by 對結果集進行排序。
8. 查找你要搜索的欄位。
二、SQL語句執行完整過程:
1.用戶進程提交一個 sql 語句:
update temp set a=a*2,給伺服器進程。
2.伺服器進程從用戶進程把信息接收到後,在 PGA 中就要此進程分配所需內存,存儲相關的信息,如在會
話內存存儲相關的登錄信息等。
3.伺服器進程把這個 sql 語句的字元轉化為 ASCII 等效數字碼,接著這個 ASCII 碼被傳遞給一個
HASH 函數,並返回一個 hash 值,然後伺服器進程將到shared pool 中的 library cache 中去查找是否存在相
同的 hash 值,如果存在,伺服器進程將使用這條語句已高速緩存在 SHARED POOL 的library cache 中的已
分析過的版本來執行。
4.如果不存在,伺服器進程將在 CGA 中,配合 UGA 內容對 sql,進行語法分析,首先檢查語法的正確性,接
著對語句中涉及的表,索引,視圖等對象進行解析,並對照數據字典檢查這些對象的名稱以及相關結構,並根據
ORACLE 選用的優化模式以及數據字典中是否存在相應對象的統計數據和是否使用了存儲大綱來生成一個
執行計劃或從存儲大綱中選用一個執行計劃,然後再用數據字典核對此用戶對相應對象的執行許可權,最後生成
一個編譯代碼。
5.ORACLE 將這條 sql 語句的本身實際文本、HASH 值、編譯代碼、與此語名相關聯的任何統計數據
和該語句的執行計劃緩存在 SHARED POOL 的 library cache中。伺服器進程通過 SHARED POOL 鎖存
器(shared pool latch)來申請可以向哪些共享 PL/SQL 區中緩存這此內容,也就是說被SHARED POOL 鎖存
器鎖定的 PL/SQL 區中的塊不可被覆蓋,因為這些塊可能被其它進程所使用。
6.在 SQL 分析階段將用到 LIBRARY
CACHE,從數據字典中核對表、視圖等結構的時候,需要將數據
字典從磁碟讀入 LIBRARY
CACHE,因此,在讀入之前也要使用LIBRARY
CACHE 鎖存器(library cache
pin,library cache lock)來申請用於緩存數據字典。 到現在為止,這個 sql 語句已經被編譯成可執行的代碼了,
但還不知道要操作哪些數據,所以伺服器進程還要為這個 sql 准備預處理數據。
7.首先伺服器進程要判斷所需數據是否在 db buffer 存在,如果存在且可用,則直接獲取該數據,同時根據
LRU 演算法增加其訪問計數;如果 buffer 不存在所需數據,則要從數據文件上讀取首先伺服器進程將在表頭部
請求 TM 鎖(保證此事務執行過程其他用戶不能修改表的結構),如果成功加 TM 鎖,再請求一些行級鎖(TX
鎖),如果 TM、TX 鎖都成功加鎖,那麼才開始從數據文件讀數據,在讀數據之前,要先為讀取的文件准備好
buffer 空間。伺服器進程需要掃面 LRU list 尋找 free db buffer,掃描的過程中,伺服器進程會把發現的所有
已經被修改過的 db buffer 注冊到 dirty list 中, 這些 dirty buffer 會通過 dbwr 的觸發條件,隨後會被寫出到
數據文件,找到了足夠的空閑 buffer,就可以把請求的數據行所在的數據塊放入到 db buffer 的空閑區域或者
覆蓋已經被擠出 LRU list 的非臟數據塊緩沖區,並排列在 LRU list 的頭部,也就是在數據塊放入 DB
BUFFER 之前也是要先申請 db buffer 中的鎖存器,成功加鎖後,才能讀數據到 db buffer。
8.記日誌 現在數據已經被讀入到 db buffer 了,現在伺服器進程將該語句所影響的並被讀
入 db buffer 中的這些行數據的 rowid 及要更新的原值和新值及 scn 等信息從 PGA 逐條的寫入 redo log
buffer 中。在寫入 redo log buffer 之前也要事先請求 redo log buffer 的鎖存器,成功加鎖後才開始寫入,當
寫入達到 redo log buffer 大小的三分之一或寫入量達到 1M 或超過三秒後或發生檢查點時或者 dbwr 之前
發生,都會觸發 lgwr 進程把 redo log buffer 的數據寫入磁碟上的 redo file 文件中(這個時候會產生log file
sync 等待事件)
已經被寫入 redofile 的 redo log buffer 所持有的鎖存器會被釋放,並可被後來的寫入信息覆蓋,
redo log buffer是循環使用的。Redo file 也是循環使用的,當一個 redo file 寫滿後,lgwr 進程會自動切換到
下一 redo file(這個時候可能出現 log fileswitch(checkpoint complete)等待事件)。如果是歸檔模式,歸檔進
程還要將前一個寫滿的 redo file 文件的內容寫到歸檔日誌文件中(這個時候可能出現 log file
switch(archiving needed)。
9.為事務建立回滾段 在完成本事務所有相關的 redo log buffer 之後,伺服器進程開始改寫這個 db buffer
的塊頭部事務列表並寫入 scn,然後 包含這個塊的頭部事務列表及 scn 信息的數據副本放入回滾段中,將
這時回滾段中的信息稱為數據塊的「前映像「,這個」前映像「用於以後的回滾、恢復和一致性讀。(回滾段可以
存儲在專門的回滾表空間中,這個表空間由一個或多個物理文件組成,並專用於回滾表空間,回滾段也可在其它
表空間中的數據文件中開辟。
10.本事務修改數據塊 准備工作都已經做好了,現在可以改寫 db buffer 塊的數據內容了,並在塊的頭部寫
入回滾段的地址。
11.放入 dirty list 如果一個行數據多次 update 而未 commit,則在回滾段中將會有多個「前映像「,除了第
一個」前映像「含有 scn 信息外,其他每個「前映像「的頭部都有 scn 信息和「前前映像」回滾段地址。一個
update 只對應一個 scn,然後伺服器進程將在 dirty list 中建立一
條指向此 db buffer 塊的指針(方便 dbwr 進程可以找到 dirty list 的 db buffer 數據塊並寫入數據文件中)。
接著伺服器進程會從數據文件中繼續讀入第二個數據塊,重復前一數據塊的動作,數據塊的讀入、記日誌、建
立回滾段、修改數據塊、放入 dirty list。當 dirty queue 的長度達到閥值(一般是 25%),伺服器進程將通知
dbwr 把臟數據寫出,就是釋放 db buffer 上的鎖存器,騰出更多的 free db buffer。前面一直都是在說明
oracle 一次讀一個數據塊,其實 oracle 可以一次讀入多個數據塊(db_file_multiblock_read_count 來設置一
次讀入塊的個數)
說明:
在預處理的數據已經緩存在 db buffer 或剛剛被從數據文件讀入到 db buffer 中,就要根據 sql 語句
的類型來決定接下來如何操作。
1>如果是 select 語句,則要查看 db buffer 塊的頭部是否有事務,如果有事務,則從回滾段中讀取數據;如
果沒有事務,則比較 select 的 scn 和 db buffer 塊頭部的 scn,如果前者小於後者,仍然要從回滾段中讀取數據;
如果前者大於後者,說明這是一非臟緩存,可以直接讀取這個 db buffer 塊的中內容。
2>如果是 DML 操作,則即使在 db buffer 中找到一個沒有事務,而且 SCN 比自己小的非臟
緩存數據塊,伺服器進程仍然要到表的頭部對這條記錄申請加鎖,加鎖成功才能進行後續動作,如果不成功,則要
等待前面的進程解鎖後才能進行動作(這個時候阻塞是 tx 鎖阻塞)。
用戶 commit 或 rollback 到現在為止,數據已經在 db buffer 或數據文件中修改完
成,但是否要永久寫到數文件中,要由用戶來決定 commit(保存更改到數據文件) rollback 撤銷數據的更改)。
1.用戶執行 commit 命令
只有當 sql 語句所影響的所有行所在的最後一個塊被讀入 db buffer 並且重做信息被寫入 redo log
buffer(僅指日誌緩沖區,而不包括日誌文件)之後,用戶才可以發去 commit 命令,commit 觸發 lgwr 進程,但不
強制立即 dbwr來釋放所有相應 db buffer 塊的鎖(也就是no-force-at-commit,即提交不強制寫),也就是說有
可能雖然已經 commit 了,但在隨後的一段時間內 dbwr 還在寫這條 sql 語句所涉及的數據塊。表頭部的行鎖
並不在 commit 之後立即釋放,而是要等 dbwr 進程完成之後才釋放,這就可能會出現一個用戶請求另一用戶
已經 commit 的資源不成功的現象。
A .從 Commit 和 dbwr 進程結束之間的時間很短,如果恰巧在 commit 之後,dbwr 未結束之前斷電,因為
commit 之後的數據已經屬於數據文件的內容,但這部分文件沒有完全寫入到數據文件中。所以需要前滾。由
於 commit 已經觸發 lgwr,這些所有未來得及寫入數據文件的更改會在實例重啟後,由 smon 進程根據重做日
志文件來前滾,完成之前 commit 未完成的工作(即把更改寫入數據文件)。
B.如果未 commit 就斷電了,因為數據已經在 db buffer 更改了,沒有 commit,說明這部分數據不屬於數
據文件,由於 dbwr 之前觸發 lgwr 也就是只要數據更改,(肯定要先有 log) 所有 DBWR,在數據文件上的修改
都會被先一步記入重做日誌文件,實例重啟後,SMON 進程再根據重做日誌文件來回滾。
其實 smon 的前滾回滾是根據檢查點來完成的,當一個全部檢查點發生的時候,首先讓 LGWR 進程將
redo log buffer 中的所有緩沖(包含未提交的重做信息)寫入重做日誌文件,然後讓 dbwr 進程將 db buffer 已
提交的緩沖寫入數據文件(不強制寫未提交的)。然後更新控制文件和數據文件頭部的 SCN,表明當前資料庫
是一致的,在相鄰的兩個檢查點之間有很多事務,有提交和未提交的。
像前面的前滾回滾比較完整的說法是如下的說明:
A.發生檢查點之前斷電,並且當時有一個未提交的改變正在進行,實例重啟之後,SMON 進程將從上一個
檢查點開始核對這個檢查點之後記錄在重做日誌文件中已提交的和未提交改變,因為
dbwr 之前會觸發 lgwr,所以 dbwr 對數據文件的修改一定會被先記錄在重做日誌文件中。因此,斷電前被
DBWN 寫進數據文件的改變將通過重做日誌文件中的記錄進行還原,叫做回滾,
B. 如果斷電時有一個已提交,但 dbwr 動作還沒有完全完成的改變存在,因為已經提交,提交會觸發 lgwr
進程,所以不管 dbwr 動作是否已完成,該語句將要影響的行及其產生的結果一定已經記錄在重做日誌文件中
了,則實例重啟後,SMON 進程根據重做日誌文件進行前滾.
實例失敗後用於恢復的時間由兩個檢查點之間的間隔大小來決定,可以通個四個參數設置檢查點執行的頻
率:
Log_checkpoint_interval:
決定兩個檢查點之間寫入重做日誌文件的系統物理塊(redo blocks)
的大小,默認值是 0,無限制。
log_checkpoint_timeout:
兩 個 檢 查 點 之 間 的 時 間 長 度(秒)默 認 值 1800s。
fast_start_io_target:
決定了用於恢復時需要處理的塊的多少,默認值是 0,無限制。
fast_start_mttr_target:
直接決定了用於恢復的時間的長短,默認值是 0,無限制(SMON 進程執行的前滾
和回滾與用戶的回滾是不同的,SMON 是根據重做日誌文件進行前滾或回滾,而用戶的回滾一定是根據回滾段
的內容進行回滾的。
在這里要說一下回滾段存儲的數據,假如是 delete 操作,則回滾段將會記錄整個行的數據,假如是 update,
則回滾段只記錄被修改了的欄位的變化前的數據(前映像),也就是沒有被修改的欄位是不會被記錄的,假如是
insert,則回滾段只記錄插入記錄的 rowid。 這樣假如事務提交,那回滾段中簡單標記該事務已經提交;假如是
回退,則如果操作是 delete,回退的時候把回滾段中數據重新寫回數據塊,操作如果是 update,則把變化前數據
修改回去,操作如果是 insert,則根據記錄的 rowid 把該記錄刪除。
2.如果用戶 rollback。
則伺服器進程會根據數據文件塊和 DB BUFFER 中塊的頭部的事務列表和 SCN 以及回滾段地址找到
回滾段中相應的修改前的副本,並且用這些原值來還原當前數據文件中已修改但未提交的改變。如果有多個
「前映像」,伺服器進程會在一個「前映像」的頭部找到「前前映像」的回滾段地址,一直找到同一事務下的最早的
一個「前映像」為止。一旦發出了 COMMIT,用戶就不能rollback,這使得 COMMIT 後 DBWR 進程還沒有
全部完成的後續動作得到了保障。到現在為例一個事務已經結束了。
說明:
TM 鎖:
符合 lock 機制的,用於保護對象的定義不被修改。 TX 鎖:
這個鎖代表一個事務,是行
級鎖,用數據塊頭、數據記錄頭的一些欄位表示,也是符合 lock 機制,有 resource structure、lock
structure、enqueue 演算法。
8. oracle怎樣跟蹤一條sql語句的執行過程
需要建立一張表來記錄
explain plan SET statement_id='name' FOR (這里是你要調試的語句 )
SELECT
A.OPERATION,
OPTIONS,
OBJECT_NAME,
OBJECT_TYPE,
ID,
PARENT_ID
FROM
PLAN_TABLE A
WHERE
STATEMENT_ID='name'
ORDER BY
Id;
ID 'name'是一個標識,你可以自己取,欄位有很多個,以下是各個欄位的解釋(可能格式不對,你可以復制後看):
欄位名 欄位類型 含義
STATEMENT_ID VARCHAR2(30) explain PLAN 語句中所指定的最優STATEMENT_ID 參數值, 如果在EXPLAN PLAN語句中沒有使用SET STATEMENT_ID,那麼此值會被設為NULL。
REMARKS VARCHAR2(80) 與被解釋規劃的各步驟相關聯的注釋最長可達80 位元組
OPERATION VARCHAR2(30) 各步驟所執行內部操作的名稱在某條語句所產生的第一行中該列的可能取值如下DELETE STATEMENT INSERT STATEMENT SELECT STATEMENT UPDATE STATEMENT
OPTIONS VARCHAR2(30) 對OPERATION 列中所描述操作的變種
OBJECT_NODE VARCHAR2(128) 用於訪問對象的資料庫鏈接database link 的名稱對於使用並行執行的本地查詢該列能夠描述操作中輸出的次序
OBJECT_OWNER VARCHAR2(30) 對於包含有表或索引的架構schema 給出其所有者的名稱
OBJECT_NAME VARCHAR2(30) 表或索引的名稱
OBJECT_INSTANCE INTEGER 根據對象出現在原始original 語句中的次序所給出的相應次序編號就原始的語句文本而論其處理順序為自左至右自外向內景象擴張view
OBJECT_TYPE VARCHAR2(30) 用於提供對象描述性信息的修飾符例如索引的NON-UNIQUE
OPTIMIZER VARCHAR2(255) 當前優化程序的模式
ID INTEGER 分配給執行規劃各步驟的編號
PARENT_ID INTEGER 對ID 步驟的輸出進行操作的下一個執行步驟的ID
POSITION INTEGER 對於具有相同PARENT_ID 的步驟其相應的處理次序
COST INTEGER 根據優化程序的基於開銷的方法所估計出的操作開銷值對於使用基於規則方法的語句該列為空該列值沒有特定的測量單位它只是一個用於比較執行規劃開銷大小的權重值
CARDINALITY INTEGER 根據基於開銷的方法對操作所訪問行數的估計值
BYTES INTEGER 根據基於開銷的方法對操作所訪問位元組的估計
=============================================
你按照我說的做,後面用
SELECT
*
FROM
PLAN_TABLE A
WHERE
STATEMENT_ID='name'
結果已經很清楚了,全部滿足你的要求。
各列的具體含義上面已經給出。
9. SQL執行與優化
SQL優化
執行計劃,表關聯查詢順序,優化策略與思路
下面再向前走一些,容我根據自己的認識說一下查詢執行的流程是怎樣的:
1.連接
1.1客戶端發起一條Query請求,監聽客戶端的『連接管理模塊』接收請求
1.2將請求轉發到『連接進/線程模塊』
1.3調用『用戶模塊』來進行授權檢查
1.4通過檢查後,『連接進/線程模塊』從『線程連接池』中取出空閑的被緩存的連接線程和客戶端請求對接,如果失敗則創建一個新的連接請求
2.處理
2.1先查詢緩存,檢查Query語句是否完全匹配,接著再檢查是否具有許可權,都成功則直接取數據返回
2.2上一步有失敗則轉交給『命令解析器』,經過詞法分析,語法分析後生成解析樹
2.3接下來是預處理階段,處理解析器無法解決的語義,檢查許可權等,生成新的解析樹
2.4再轉交給對應的模塊處理
2.5如果是SELECT查詢還會經由『查詢優化器』做大量的優化,生成執行計劃
2.6模塊收到請求後,通過『訪問控制模塊』檢查所連接的用戶是否有訪問目標表和目標欄位的許可權
2.7有則調用『表管理模塊』,先是查看table cache中是否存在,有則直接對應的表和獲取鎖,否則重新打開表文件
2.8根據表的meta數據,獲取表的存儲引擎類型等信息,通過介面調用對應的存儲引擎處理
2.9上述過程中產生數據變化的時候,若打開日誌功能,則會記錄到相應二進制日誌文件中
3.結果
3.1Query請求完成後,將結果集返回給『連接進/線程模塊』
3.2返回的也可以是相應的狀態標識,如成功或失敗等
3.3『連接進/線程模塊』進行後續的清理工作,並繼續等待請求或斷開與客戶端的連接
接下來再走一步,讓我們看看一條SQL語句的前世今生。
首先看一下示例語句
示例語句
執行順序
SQL解析
1. FROM
當涉及多個表的時候,左邊表的輸出會作為右邊表的輸入,之後會生成一個虛擬表VT1。
(1-J1)笛卡爾積
計算兩個相關聯表的笛卡爾積(CROSS JOIN) ,生成虛擬表VT1-J1。
兩次全表掃描
哈希索引,查找復雜度都是 O(1) 。
2. WHERE
對VT1過程中生成的臨時表進行過濾,滿足WHERE子句的列被插入到VT2表中。
注意:
此時因為分組,不能使用聚合運算;也不能使用SELECT中創建的別名;
與ON的區別:
如果有外部列,ON針對過濾的是關聯表,主表(保留表)會返回所有的列;
如果沒有添加外部列,兩者的效果是一樣的;
應用:
對主表的過濾應該放在WHERE;
對於關聯表,先條件查詢後連接則用ON,先連接後條件查詢則用WHERE;
hash join 哈希連接 驅動表和被驅動表都只會訪問0次或1次
應用場景:一個大表一個小表/表上沒有索引/返回結果集比較大
3. GROUP BY
這個子句會把VT2中生成的表按照GROUP BY中的列進行分組。生成VT3表。
注意:
其後處理過程的語句,如SELECT,HAVING,所用到的列必須包含在GROUP BY中,對於沒有出現的,得用聚合函數;
原因:
GROUP BY改變了對表的引用,將其轉換為新的引用方式,能夠對其進行下一級邏輯操作的列會減少;
原作者的理解是:
根據分組欄位,將具有相同分組欄位的記錄歸並成一條記錄,因為每一個分組只能返回一條記錄,除非是被過濾掉了,而不在分組欄位裡面的欄位可能會有多個值,多個值是無法放進一條記錄的,所以必須通過聚合函數將這些具有多值的列轉換成單值;
GROUP BY 重新聚合查詢
4. HAVING
這個子句對VT3表中的不同的組進行過濾,只作用於分組後的數據,滿足HAVING條件的子句被加入到VT4表中。
7.LIMIT
LIMIT子句從上一步得到的VT6虛擬表中選出從指定位置開始的指定行數據。
注意:
offset和rows的正負帶來的影響;
當偏移量很大時效率是很低的,可以這么做:
採用子查詢的方式優化,在子查詢里先從索引獲取到最大id,然後倒序排,再取N行結果集
採用INNER JOIN優化,JOIN子句里也優先從索引獲取ID列表,然後直接關聯查詢獲得最終結果
當前未用到索引,
三次full scan , table1 AS a / table2 AS b / GROUP BY
盡量少做重復的工作
控制同一語句的多次執/減少多次的數據轉換/
杜絕不必要的子查詢和連接表,子查詢在執行計劃一般解釋成外連接,多餘的連接表帶來額外的開銷
關於臨時表和表變數的選擇
臨時表產生使用SELECT INTO和CREATE TABLE + INSERT INTO的選擇,一般情況下,SELECT INTO會比CREATE TABLE + INSERT INTO的方法快很多,但是SELECT INTO會鎖定TEMPDB的系統表SYSOBJECTS、SYSINDEXES、SYSCOLUMNS,在多用戶並發環境下,容易阻塞其他進程,所以建議,在並發系統中,盡量使用CREATE TABLE + INSERT INTO,而大數據量的單個語句使用中,使用SELECT INTO。
子查詢的用法
相關子查詢可以用IN、NOT IN、EXISTS、NOT EXISTS引入
NOT IN、NOT EXISTS的相關子查詢可以改用LEFT JOIN代替寫法
如果保證子查詢沒有重復 ,IN、EXISTS的相關子查詢可以用INNER JOIN 代替
IN``的相關子查詢用EXISTS代替
不要用 COUNT (*)的子查詢判斷是否存在記錄,最好用 LEFT` `JOIN 或者EXISTS,比如有人寫這樣的語句:
建立索引後,並不是每個查詢都會使用索引,在使用索引的情況下,索引的使用效率也會有很大的差別。只要我們在查詢語句中沒有強制指定索引,
不要對索引欄位進行運算,而要想辦法做變換
不要對索引欄位進行格式轉換
不要對索引欄位使用函數
不要對索引欄位進行多欄位連接
join關聯查詢的計算是很復雜的,特別是數據量比較大的情況下,實際情況還是拆解較快的
Join拆解的核心就是利用In關鍵字
要麼用空間換時間,要麼用時間換空間
多表連接的連接條件對索引的選擇有著重要的意義,所以我們在寫連接條件條件的時候需要特別注意。
A、多表連接的時候,連接條件必須寫全,寧可重復,不要缺漏。
B、連接條件盡量使用聚集索引
C、注意ON、WHERE和HAVING部分條件的區別
ON是最先執行, WHERE次之,HAVING最後,因為ON是先把不符合條件的記錄過濾後才進行統計,它就可以減少中間運算要處理的數據,按理說應該速度是最快的,WHERE也應該比 HAVING快點的,因為它過濾數據後才進行SUM,在兩個表聯接時才用ON的,所以在一個表的時候,就剩下WHERE跟HAVING比較了
考慮聯接優先順序:
(1)INNER JOIN
(2)LEFT JOIN (註:RIGHT JOIN 用 LEFT JOIN 替代)
(3)CROSS JOIN
索引並不適用於所有情況:a.少量數據;b.頻繁進行改動的欄位,不適合做索引;c.很少使用的欄位,不需要加索引
索引會提高數據查詢效率,但是會降低「增、刪、改」的效率。當不使用索引的時候,我們進行數據的增刪改,只需要操作源表即可,但是當我們添加索引後,不僅需要修改源表,也需要再次修改索引,很麻煩。
先執行順序, 是否走索引, 有無類型轉換
18000 字的SQL優化大全
步步深入:MySQL架構總覽->查詢執行流程->SQL解析順序
MySQL索引總結(4)——btree與hash區別
10. SQL優化萬能公式:5 大步驟 + 10 個案例
在應用開發的早期,數據量少,開發人員開發功能時更重視功能上的實現,隨著生產數據的增長,很多SQL語句開始暴露出性能問題,對生產的影響也越來越大,有時可能這些有問題的SQL就是整個系統性能的瓶頸。
1、通過慢查日誌等定位那些執行效率較低的SQL語句
2、explain 分析SQL的執行計劃
type由上至下,效率越來越高
Extra
3、show profile 分析
了解SQL執行的線程的狀態及消耗的時間。默認是關閉的,開啟語句「set profiling = 1;」
4、trace
trace分析優化器如何選擇執行計劃,通過trace文件能夠進一步了解為什麼優惠券選擇A執行計劃而不選擇B執行計劃。
5、確定問題並採用相應的措施
案例1、最左匹配
索引
SQL語句
查詢匹配從左往右匹配,要使用order_no走索引,必須查詢條件攜帶shop_id或者索引( shop_id , order_no )調換前後順序
案例2、隱式轉換
索引
SQL語句
隱式轉換相當於在索引上做運算,會讓索引失效。mobile是字元類型,使用了數字,應該使用字元串匹配,否則MySQL會用到隱式替換,導致索引失效。
案例3、大分頁
索引
SQL語句
對於大分頁的場景,可以優先讓產品優化需求,如果沒有優化的,有如下兩種優化方式, 一種是把上一次的最後一條數據,也即上面的c傳過來,然後做「c < xxx」處理,但是這種一般需要改介面協議,並不一定可行。另一種是採用延遲關聯的方式進行處理,減少SQL回表,但是要記得索引需要完全覆蓋才有效果,SQL改動如下
案例4、in + order by
索引
SQL語句
in查詢在MySQL底層是通過n*m的方式去搜索,類似union,但是效率比union高。in查詢在進行cost代價計算時(代價 = 元組數 * IO平均值),是通過將in包含的數值,一條條去查詢獲取元組數的,因此這個計算過程會比較的慢,所以MySQL設置了個臨界值(eq_range_index_pe_limit),5.6之後超過這個臨界值後該列的cost就不參與計算了。因此會導致執行計劃選擇不準確。默認是200,即in條件超過了200個數據,會導致in的代價計算存在問題,可能會導致Mysql選擇的索引不準確。
處理方式,可以( order_status , created_at )互換前後順序,並且調整SQL為延遲關聯。
案例5、范圍查詢阻斷,後續欄位不能走索引
索引
SQL語句
范圍查詢還有「IN、between」
案例6、不等於、不包含不能用到索引的快速搜索。(可以用到ICP)
在索引上,避免使用NOT、!=、>、!、NOT EXISTS、NOT IN、NOT LIKE等
案例7、優化器選擇不使用索引的情況
如果要求訪問的數據量很小,則優化器還是會選擇輔助索引,但是當訪問的數據占整個表中數據的蠻大一部分時(一般是20%左右),優化器會選擇通過聚集索引來查找數據。
查詢出所有未支付的訂單,一般這種訂單是很少的,即使建了索引,也沒法使用索引。
案例8、復雜查詢
如果是統計某些數據,可能改用數倉進行解決;如果是業務上就有那麼復雜的查詢,可能就不建議繼續走SQL了,而是採用其他的方式進行解決,比如使用ES等進行解決。
案例9、asc和desc混用
desc 和asc混用時會導致索引失效
案例10、大數據
對於推送業務的數據存儲,可能數據量會很大,如果在方案的選擇上,最終選擇存儲在MySQL上,並且做7天等有效期的保存。那麼需要注意,頻繁的清理數據,會照成數據碎片,需要聯系DBA進行數據碎片處理。