獲取腦圖方式請看最下面!
Mysql
1.mysql語句執行流程
查詢語句
-
1.通信協議
-
1.通信類型
- 1.同步通信:依賴於被調用方,受限與被調用方的性能,也就是說線程會阻塞,等待數據庫的返回。
- 2.異步通信:可以避免應用阻塞等待,但是不能節省SQL執行時間;如果併發過高,每個SQL執行都要建立一個連接,會給服務器造成很大壓力,如果要異步,必須要考慮連接池。
-
2.連接方式
- 1.長連接(默認):
- 2.短連接:
-
3.查詢連接相關配置
-
1.wait_timeout 非交互式超時時間,如JDBC
-
2.interactive_timeout 交互式超時時間,如數據庫工具
-
3.查看當前有多少個連接 show global status like ‘Thread%’;
- 1.Threads_cached:緩存中的線程連接數
- 2.Threads_connected:當前打開的連接數。
- 3.Threads_created:爲處理連接創建的線程數。
- 4.Threads_running:非睡眠狀態的連接數,通常指併發連接數。
-
4.查看SQL的執行狀態, SHOW PROCESSLIST;
-
State
- Sleep:線程正在等待客戶端,以向他發送一個新語句
- Query:線程正在執行查詢或往客戶端發送請求
- Locked:該查詢被其他查詢鎖定
- Copying to tmp table on disk:臨時結果集合大於tmp_table_size 線程把內存格式持久化到磁盤
- Sending data:線程正在爲Select語句處理行,同時正在向客戶端發送數據
- Sorting for group/order:線程正在分類,以滿足group/order要求
-
Info:執行的語句
-
以及一些數據庫用戶信息
-
-
5.允許客戶端最大連接151個,最大可設置成16384,show variables like ‘max_connections’;
-
-
4.支持的通信協議
- 1.Unix Socket:比如在服務器上,如果沒有指定-h,它就用socket方式登陸(本地連接)
- 2.TCP/IP:如果指定-h參數,就會用TCP/IP協議
- 3.命名管道:
- 4.內存共享:
-
5.通信方式(採用半雙工)
- 1.單工:數據單向傳輸,比如遙控器
- 2.半雙工:數據雙向傳輸,但不能同時傳輸,比如對講機,
- 3.全雙工:數據雙向傳輸,可以同時傳輸,比如打電話
-
-
2.查詢緩存:mysql自帶緩存模塊,緩存默認是關閉的,8.0版本中被移除了
- 爲什麼不推薦使用?
-
3.語法解析&預處理:基於SQL語法進行詞法和語義的解析
- 1.詞法解析:把一個完整的SQL語句打碎成一個個單詞
- 2.語法解析:對SQL做一些語法檢查,比如單引號沒有閉合,然後根據MySQL定義的語法規則,根據SQL語句生成一個數據結構,這個數據結構爲解析樹
- 3.預處理器:詞法與語法解析只能知道SQL語句是否符合MySQL的語法規則,(而無法判斷表是否存在、列名是否存在,檢查名字和別名保證沒有歧義),預處理後得到一個新的解析樹。
-
4.查詢優化器(Optimizer)&查詢執行計劃:
-
查詢優化器:Last_query_cost 可以查看開銷。
得到解析樹之後,是不是就執行SQL語句了呢?
這裏有一個問題,一條SQL語句是不是隻有一種執行方式,或者說數據庫最終執行的SQL是不是就是爲們發送的SQL?
這個答案是否行的,一條SQL語句是可以有很多種執行方式的,最終返回相同的結果,他們是等價的,但是如果有這麼多執行方式,這些方式是怎麼得到的?最終選擇哪一種去執行?根據什麼判斷標準去選擇?-
1.什麼是優化器?
- 根據解析樹生成不同的執行計劃,然後選擇最優的執行計劃,MySQL是基於開銷的優化器,哪種執行計劃開銷最小,就用哪種。
-
2.優化器可以做什麼?
- 1.對多張表進行關聯查詢的時候,以哪個表的數據作爲基準表
- 2.多個索引可以使用的時候,選擇哪個索引
-
3.優化器是怎麼得到執行計劃的?
-
1.開啓優化器的追蹤(默認關閉)SHOW VARIABLES LIKE ‘optimizer_trace’;
-
2.執行一個SQL語句,優化器會生成執行計劃
-
3.優化器分析的過程已經記錄到系統表裏 set optimizer_trace=“enabled=off”;
steps主要分爲3部分
1.準備階段(join_preparation-SQL)
2.優化階段(join_optimization-SQL)
3.執行階段(join_execution-SQL)
-
-
4.優化器得到的結果:優化器最終會把解析樹變成一個查詢執行計劃,查詢執行計劃是一個數據結構
-
-
執行計劃
-
-
5.存儲引擎
-
1.存儲引擎介紹:MySQL支持多種存儲引擎,他們是可以替換的,所以是插件式的存儲引擎,存儲引擎是以表爲單位的,創建表後還可以修改存儲引擎
-
2.查看存儲引擎 如何查看存儲引擎呢?show table status from
- Transactions 是否支持事務
- XA 協議用來實現分佈式事物(分爲本地資源管理器/事務管理器)
- Savepoints 實現子事務。創建一個savepoints後,事務可以回滾到這個點,不會影響到之前的操作。
-
3.存儲引擎的比較show engines ;
-
InnoDB
mysql 5.7 中的默認存儲引擎。InnoDB 是一個事務安全(與 ACID 兼容)的 MySQL 存儲引擎,它具有提交、回滾和崩潰恢復功能來保護用戶數據。InnoDB 行級鎖(不升級 爲更粗粒度的鎖)和 Oracle 風格的一致非鎖讀提高了多用戶併發性和性能。InnoDB 將 用戶數據存儲在聚集索引中,以減少基於主鍵的常見查詢的 I/O。爲了保持數據完整性,
InnoDB 還支持外鍵引用完整性約束。 特點:
支持事務,支持外鍵,因此數據的完整性、一致性更高。 支持行級別的鎖和表級別的鎖。 支持讀寫併發,寫不阻塞讀(MVCC)。 特殊的索引存放方式,可以減少 IO,提升查詢效率。 適合:經常更新的表,存在併發讀寫或者有事務處理的業務系統。 -
MyISAM
特點:
支持表級別的鎖(插入和更新會鎖表)。不支持事務。 擁有較高的插入(insert)和查詢(select)速度。
存儲了表的行數(count 速度更快)。
(怎麼快速向數據庫插入 100 萬條數據?我們有一種先用 MyISAM 插入數據,然後
修改存儲引擎爲 InnoDB 的操作。) 適合:只讀之類的數據分析的項目。 -
Memory
把數據放在內存裏面,讀寫的速度很快,但是數據庫重啓或者崩潰,數據會全部消 失。只適合做臨時表。
將表中的數據存儲到內存中。 -
CSV
它的表實際上是帶有逗號分隔值的文本文件。csv 表允許以 csv 格式導入或轉儲數據, 以便與讀寫相同格式的腳本和應用程序交換數據。因爲 csv 表沒有索引,所以通常在正 常操作期間將數據保存在 innodb 表中,並且只在導入或導出階段使用 csv 表。
特點:不允許空行,不支持索引。格式通用,可以直接編輯,適合在不同數據庫之 間導入導出。 -
Archive
這些緊湊的未索引的表用於存儲和檢索大量很少引用的歷史、存檔或安全審計信息。 特點:不支持索引,不支持 update delete。
-
-
如何選擇存儲引擎?
- 如果對數據一致性要求高,需要事務支持,可以選擇InnoDB
- 如果數據查詢多更新少,對查詢性能要求比較高,可以選擇 MyISAM。
- 如果需要一個用於查詢的臨時表,可以選擇 Memory。
- 如果所有的存儲引擎都不能滿足你的需求,可以用C語言開發一個存儲引擎。
-
-
6.執行引擎:使用執行計劃操作存儲引擎,它利用存儲引擎提供的相應的API來完成操作。
更新語句:基本流程和查詢流程是一致的,它也要經過解析器、優化器、執行器處理,區別在於拿到符合條件的數據之後的操作
簡化過程:
1.事務開始,從內存或磁盤讀取到這條數據,返回給Server的執行器
2.將查詢到的行進行修改
3.將修改的結果更新到內存Buffer pool中
4.記錄 redo log ,並將記錄狀態置爲prepare狀態
5.修改後,提交事務
6.server 層 寫入bin log 中
7.執行 commit
8.將 redo log裏的事務記錄置爲 commit 狀態
-
1.緩衝池Buffer Pool
InnooDB的數據都是放在磁盤上的,InnoDB 操作數據有一個最小的邏輯單 位,叫做頁(索引頁和數據頁),我們對於數據的操作不是直接操作磁盤,因爲磁盤的速度慢,InnoDB使用了一種緩衝池的技術,也就是把磁盤讀到的頁放到一塊內存區域裏
下次讀取相同的頁,先判斷是不是在緩存池裏面,如果是直接讀取,不用再次訪問磁盤。
修改數據的時候,先修改緩衝池裏的頁。內存的數據頁和磁盤數據不一致的時候,我們把它叫做髒頁。InnoDB裏面有專門的線程把Buffer Pool的數據寫入到磁盤,每隔一段時間就一次性的把多個修改寫入磁盤,這個動作叫做刷髒
Buffer Pool 是 InnoDB 裏面非常重要的一個結構,它的內部又分成幾塊區域-
Buffer Pool,默認128M,採用LRU算法來管理緩衝池(鏈表實現,分成來young和old),經過淘汰的數據就是熱點數據
Buffer Pool 緩存的是頁面信息,包括數據頁、索引頁。
-
Change Buffer 寫緩衝區,如果這個數據頁不是唯一索引,不存在數據重複的情況,可以先把修改記錄在內存的緩衝池中,從而提升更新語句(insert、delete、update)執行速度
5.5 之前叫 Insert Buffer 插入緩衝,現在也能支 持 delete 和 update。
最後把 Change Buffer 記錄到數據頁的操作叫做 merge。什麼時候發生 merge?
有幾種情況:在訪問這個數據頁的時候,或者通過後臺線程、或者數據庫 shut down、 redo log 寫滿時觸發。
如果數據庫大部分索引都是非唯一索引,並且業務是寫多讀少,不會在寫數據後立刻讀取,幾句可以使用Change Buffer,寫多讀少的業務,可以調大這個值
innodb_change_buffer_max_size 默認佔比 25% -
Adaptive Hash Index:自適應哈希索引,對於一些熱點數據頁,InnoDB會自動建立自適應hash索引,在B+Tree索引的基礎上創建。
-
log buffer(redo log緩存區):用來處理數據庫宕機或者重啓,這些數據丟失,如果寫到一半,甚至可能會破壞數據文件導致數據庫不可用。
爲了避免這個問題,InnoDB 把所有對頁面的修改操作專門寫入redo log,並且在數據庫啓動時從redo log進行恢復操作(實現 crash-safe)——用它來實現事務的持久性。
默認2個文件,每個48M,這種日誌和磁盤配合的整個過程就是WAL(Write-Ahead Logging),它的關鍵點就是先寫日誌,再寫磁盤。-
爲什麼要先寫日誌再寫磁盤?考慮 隨機IO和順序IO,寫日誌比較快
我們先來了解一下隨機 I/O 和順序 I/O 的概念。 磁盤的最小組成單元是扇區,通常是 512 個字節。 操作系統和內存打交道,最小的單位是頁 Page。 操作系統和磁盤打交道,讀寫磁盤,最小的單位是塊 Block。
刷盤是隨機 I/O,而記錄日誌是順序 I/O,順序 I/O 效率更高。因此先把修改寫入日誌,可以延遲刷盤時機,進而提升系統吞吐。
當然redo log也不是每一次都直接寫入磁盤,Log buffer專門用來保存即將要寫入日誌的數據,默認16M,它一樣可以接收磁盤IO -
什麼時候寫入redo log中?
在我們寫入數據到磁盤的時候,操作系統本身是有緩存的。flush 就是把操作系統緩 衝區寫入到磁盤。log buffer 寫入磁盤的時機,由一個參數控制,默認是 1。
0(延遲寫):log buffer 將每秒一次的寫入log 中,並且log 的flush操作同時進行,該模式下,在事務提交的時候,不會主動出發寫入磁盤操作。
1(默認,實時寫,實時刷):每次事務提交時log buffer的數據寫入log中,並且刷到磁盤中
2.(實時寫,延遲刷):每次事務提交會把log buffer的數據寫入log中,但是flush 操作並不會同時進行,該模式下MySQL會每秒執行一次flush操作
-
-
後臺線程:刷新buffer內存池中的數據到磁盤
- master thread: 負責刷新緩存數據到磁盤並協調調度其它後臺進程。
- IO thread:分爲 insert buffer、log、read、write 進程。分別用來處理 insert buffer、 redo log、讀寫請求的 IO 回調。
- purge thread:用來回收 undo 頁
- page cleaner thread:用來刷新髒頁。
-
-
2.redo log
-
redo log的特點
1.redo log是InnoDB存儲引擎實現的,並不是所有存儲引擎都有
2.不是記錄數據頁更新之後的狀態,而是記錄這個頁做了什麼改動,屬於物理日誌。
3.redo log大小是固定的,前面的內容會被覆蓋,所有日誌組成一個環形結構。
當 write pos 和 check point相遇時說明寫滿,
-
2.MySQL體系結構
1.結構講解
- Connector:用來支持各種語言和SQL的交互
- Management Serveices & Utilities:系統管理和控制工具,包括備份恢復、MySQL複製、集羣等等
- Connection Pool:連接池,管理需要緩衝的資源,包括用戶密碼、權限、線程等
- SQL Interface:用來接收用戶的SQL命令,返回用戶需要的查詢結果
- Parser:用來解析SQL語句
- Optimizer:查詢優化器
- Cache and Buffer:查詢存儲,除了行記錄的緩存之外,還有表緩存,Key緩存,權限緩存等
- Pluggable Storage Engines:插件式存儲引擎,它提供API給服務層使用
2.架構分層
-
連接層:管理連接,權限驗證
客戶端要連接到MySQL服務器的3306端口,必須要跟服務端建立連接,那麼管理所有的連接,驗證客戶端的身份和權限,這些功能就在連接層完成
-
服務層:語句分析->執行執行計劃->執行引擎->返回結果
連接層會把 SQL 語句交給服務層,這裏面又包含一系列的流程:
比如查詢緩存的判斷、根據 SQL 調用相應的接口,對我們的 SQL 語句進行詞法和語 法的解析(比如關鍵字怎麼識別,別名怎麼識別,語法有沒有錯誤等等)。
然後就是優化器,MySQL 底層會根據一定的規則對我們的 SQL 語句進行優化,最 後再交給執行器去執行。 -
存儲引擎層:存儲數據,提供讀寫接口
存儲引擎就是我們的數據真正存放的地方,在 MySQL 裏面支持不同的存儲引擎。 再往下就是內存或者磁盤。
3.表空間分類
-
系統表空間:所有表共享系統表空間
-
數據字典:由內部系統表組成,存儲表和索引的元數據
-
雙寫緩衝區:我們不是有 redo log 嗎?但是有個問題,如果這個頁本身已經損壞了,用它來做崩潰恢復是沒有意義的。所以在對於應用 redo log 之前,需要一個頁的副本。如果出現了寫入失效,就用頁的副本來還原這個頁,然後再應用 redo log。這個頁的副本就是double write,InnoDB 的雙寫技術。通過它實現了數據頁的可靠性。
InnoDB 的頁和操作系統的頁大小不一致,InnoDB 頁大小一般爲 16K,操作系統頁 大小爲 4K,InnoDB 的頁寫入到磁盤時,一個頁需要分 4 次寫,如果存儲引擎正在寫入頁的數據到磁盤時發生宕機,可能出現頁只寫了一部分的情況,這種情況是部分寫失效,可能會導致數據丟失。
- 內存的double寫
- 磁盤的double寫
-
Change Buffer
-
undo Logs:默認存放在系統表空間裏
-
-
獨佔表空間:每張表會開闢一個表空間,這個文件就是數據目錄下的 ibd 文件,存放表的索引和數據
-
通用表空間
-
臨時表空間:存儲臨時表的數據,包括用戶創建的臨時表,和磁盤的內部臨時表
-
undo log(撤銷日誌/回滾日誌):記錄了數據發生之前的數據狀態,如果修改時出現異常,可以用undo log 來實現回滾操作。
在執行undo 的時候,僅僅將數據從邏輯上恢復至事務之前的狀態,而不是從物理頁面的操作實現的,數據邏輯日誌。
redo log和undo log與事務相關,統稱爲事務日誌。
undo log的數據默認在系統表空間 ibdata1中,因爲共享表空間不會自動收縮,也可以單獨創建一個undo的表空間-
如何組織Undo Log 鏈
詳細請參考https://blog.csdn.net/m0_37645820/article/details/89814582
-
4.數據庫完整性約束
-
1.域完整性:限制此單元格的數據正確,不對照此列的其他單元格比較(非空約束、默認值約束)
- 1.非空約束
- 2.默認值約束
-
2.實體完整性
- 主鍵約束
- 唯一約束
- 自動增長列
-
3.參照完整性:表與表的關係,插入、更新、刪除等操作都要與另外一張表對照,組織不正確的操作
3.Binlog:記錄了所有的 DDL 和 DML 語句
binlog 以事件的形式記錄了所有的 DDL 和 DML 語句(因爲它記錄的是操作而不是 數據值,屬於邏輯日誌),可以用來做主從複製和數據恢復
跟redo log 不一樣,它的文件內容是追加的,沒有大小限制。
1.導出成sql語句,把所有操作重新執行,實現數據恢復
2.實現主從複製,從主服務器讀取bin log ,然後執行一遍
4.索引
1.什麼是索引:數據庫中的一個排序的數據結構,可以快速的查詢。
2.索引類型
-
普通索引:沒有任何限制
-
唯一索引:要求鍵值不能重複,主鍵是特殊的唯一索引
-
全文索引:針對比較大的數據,只有文本類型的字段可以創建全文索引,比如char/varchar/text
select * from table where match(content) against(‘content’ IN NATURAL LANGUAGE MODE);
3.索引結構(B+樹)
-
特點
- 1.關鍵字的數量是跟樹相等的
- 2.根節點和枝節點中都不會存儲數據,只有葉子節點才存儲數據
- 3.數據節點相連,形成一條有序的鏈表
- 4.它時根據左閉右開[ )來檢索數據的
-
計算一個Page存儲多少數據
假設一條記錄是1K,一個葉子節點可以存儲16條數據,非葉子節點可以存儲多少哥指針?
假設索引字段是bigint,長度爲8字節,指針大小在innoDB 源碼中爲6字節,這樣一共14字節。
非葉子節點(1頁)存儲16384/14=1170個單元,代表1170個指針。
樹深度爲2的時候,共有11701170個葉子節點
可以存儲的數據爲11701170*16,在查詢數據時,一次頁的查找代表一次IO,也就是說,一張2000萬左右的表查詢最多需要3次IO。-
假設索引字段是bigint
- 假設索引字段是bigint
-
4.索引存儲選型樹
-
B+樹存儲的特點
- 1.它是 B Tree 的變種,B Tree 能解決的問題,它都能解決。B樹解決了每個節點存儲更多關鍵字;路數更多的問題
- 2.掃庫、掃表能力更強,遍歷數據節點的指針就可以了
- 3.磁盤讀寫能力相對於B 樹來說更強,根節點和葉子節點不保存數據,所以一個節點可以存儲更多的關鍵字,加載的關鍵字也多
- 4.排序能力更強
- 5.效率穩定,葉子節點存儲數據,IO次數穩定
-
爲什麼不用紅黑樹?
- 1.只有兩路
- 2.不夠平衡
5.索引方式
-
Hash
-
特點
它的時間複雜度是 O(1),查詢速度比較快。因爲哈希索引裏面的數據不是 按順序存儲的,所以不能用於排序。
查詢的數據會根據鍵值計算hash碼,所以只能支持等值查詢,不支持範圍查詢
如果字段重複很多的時候,會有大量的哈希衝突(拉鍊表法解決),降低查詢效率 -
如何建立
InnoDB 只支持顯式創建 B+Tree 索引,對於一些熱點數據頁, InnoDB 會自動建立自適應 Hash 索引也就是在 B+Tree 索引基礎上建立 Hash 索引, 這個過程對於客戶端是不可控制的,隱式的。
-
-
B+Tree只能顯式創建
6.實現形式
-
MySQL架構:MySQL是一個支持插件式存儲的數據庫,每個表都可以指定不用的存儲引擎,最常用的是MyISAM,InnoDB
-
數據存儲文件:MySQL數據是以文件的形式存儲在磁盤中的,目錄在 datadir參數可以查詢到,每個數據庫都有一個目錄,每個表都有.frm文件(存儲表結構)
-
MyISAM
- .MYD 存放數據
- .MYI 存放索引
- 特點:輔助索引和主鍵索引存儲和檢索數據的方式沒有任何區別,是索引文件裏面找到磁盤地址,然後到數據文件裏獲取數據
-
innoDB
-
.ibd 存放數據與索引
-
特點:以主鍵爲索引來組織數據存儲的,索引文件和數據文件是一個文件,數據存儲在葉子節點上。輔助索引存儲的值是主鍵的內容,可能需要回表二次檢索,如果沒有主鍵,那麼會選擇第一個不包含NULL值的唯一索引作爲主鍵,如果沒有則會選擇內置6字節長的RowId作爲隱藏的聚集索引,他會隨着記錄而主鍵子增
爲什麼在輔助索引裏面存儲的是主鍵值而不是主鍵的磁盤地址呢?如果主鍵的數據 類型比較大,是不是比存地址更消耗空間呢?
1.是因爲有分裂和合並的操作,這個時候鍵值的地址會發生變化,還需要將輔助索引進行同步。
2.InnoDB本身的以主鍵爲主排序的表,對InnoDB來講,其在磁盤的順序已經由主鍵的順序決定了,不能在按照其他順序排序。
-
-
7.聚集索引:索引的邏輯順序與表數據行的物理存儲順序一致
比如字典的目錄 是按拼音排序的,內容也是按拼音排序的,按拼音排序的這種目錄就叫聚集索引,按照部首的就不是聚集索引。
在 InnoDB 裏面,它組織數據的方式叫做叫做(聚集)索引組織表,所以主鍵索引是聚集索引,非主鍵都是非聚集索引。
8.索引使用原則
-
1.列的離散度:列的全部不同值和所有數據行的比例,離散度越大越適合創建索引。
如果在 B+Tree 裏面的重複值太多,MySQL 的優化器發現走索引跟使用全表掃描差
不了多少的時候,就算建了索引,也不一定會走索引。 -
2.聯合索引最左匹配原則:建立聯合索引一定把最常用的列放在左邊
-
3.覆蓋索引:使用輔助索引查詢,查詢數據列只用從索引中就能取得,不必從數據區中讀取,這時候使用的索引就是覆蓋索引,避免了回表操作
-
4.索引條件下推(ICP)
索引的比較是在存儲引擎進行的,數據記錄的比較是在Server層進行的
只適用於二級索引-
開啓索引下推:set optimizer_switch=‘index_condition_pushdown=on’;
-
使用的場景
1.對於二級索引
2.select的列不使用覆蓋索引
3.多條件查詢(where中多條件,where + order by)+聯合索引- 1.過濾like的模糊匹配
- 2.進行聯合索引的排序
-
-
5.索引上不能使用函數,會導致索引失效
9.索引的創建原則
- 1.用於 where 判斷 order 排序和 join 的(on)字段上創建索引
- 2.索引的個數不要過多——浪費空間,更新變慢
- 3.區分度低的字段,例如性別,不要建索引——離散度太低,導致掃描行數過多。
- 4.頻繁更新的值,不要作爲主鍵或者索引——頁分裂
- 5.組合索引把散列性高(區分度高)的值放在前面
- 6.創建複合索引,而不是修改單列索引。
- 7.過長的字段,創建前綴索引——前綴的離散度儘可能符合要求
- 8.不建議用無序的值作爲聚集索引——會產生存儲的碎片化
10.什麼時候用不到索引
- 1.索引列上使用函數、表達式、計算(±*/)
- 2.查詢時索引類型被隱式轉換,比如索引是string類型,查詢時用int類型
- 3.like條件中前面帶%
- 4.負向查詢,Not like 不能,<>和NOT IN在某些情況下可以
- 5.有些情況也不可以使用索引,由於是基於cost的,用不用索引最終由優化器說了算。
5.語句優化
1.主要是索引相關,儘量用到索引
2.in與exists區別
-
in:優先查詢子查詢,再查詢主查詢,適用於子查詢數據量小
SELECT * FROM A WHERE id IN (SELECT id FROM B);
以上in()中的查詢只執行一次,它查詢出B中的所有的id並緩存起來,然後檢查A表中查詢出的id在緩存中是否存在,如果存在則將A的查詢數據加入到結果集中,直到遍歷完A表中所有的結果集爲止。 -
exists:優先查詢主查詢,主查詢的數據集去子查詢exists中去尋找,如果存在添加到結果集
SELECT * FROM a WHERE EXISTS(SELECT 1 FROM b WHERE B.id = A.id);
EXISTS()查詢會執行SELECT * FROM A查詢,執行A.length次,並不會將EXISTS()查詢結果結果進行緩存,因爲EXISTS()查詢返回一個布爾值true或flase,它只在乎EXISTS()的查詢中是否有記錄,與具體的結果集無關。
EXISTS()查詢是將主查詢的結果集放到子查詢中做驗證,根據驗證結果是true或false來決定主查詢數據結果是否得以保存。
6.事務:數據庫管理系統執行過程中的一個邏輯單位,由一個或者多個操作構成。
1.事務的特性
-
原子性:事務要麼成功,要麼失敗(利用undo 日誌來實現回滾操作)
以轉賬的場景爲例,一個賬戶的餘額減少,對應一個賬戶的增加,這兩個一 定是同時成功或者同時失敗的。
-
一致性:事務前後數據的完整性必須保持一致
-
隔離性:多個用戶併發訪問庫,每個用戶開啓一個事務,不能被其他事務的操作所幹擾
-
持久性:事務一旦被提交,它對數據庫中數據的改變就是永久性的,通過redo log 和 double write雙寫緩衝來實現的,double write實現了數據庫崩潰,數據頁本身不會被破壞
2.事務隔離級別
- 未提交讀(read-uncommitted)髒讀、不可重複讀、幻讀都不可避免
- 提交讀(read-committed)不可重複讀、幻讀都不可避免
- 可重複讀(repeatable-read)
- 串行化(serializable)
3.事務併發帶來的問題
總結:無論是髒讀、不可重複讀、幻讀,他們都是數據庫的讀一致性文帝,都是在一個事務裏面前後兩次讀取結果出現不一致情況,目前有兩種解決方案,LBCC (基於鎖的併發控制):僅僅通過鎖來實現事務隔離,意味着不支持併發的讀寫操作,會極大影響操作數據的效率;MVCC(多版本併發控制)核心思想:可以查到這個事務開始之前已經存在的數據,即使後面修改或刪除,當前事務查詢結果都是一樣的。在這個事務之後的新增數據,是查不到的。
-
髒讀
一個事務裏面,由於其他的時候修改了 數據並且沒有提交,而導致了前後兩次讀取數據不一致的情況,這種事務併發的問題
-
不可重複讀
一個事務讀取到了其他事務已提交的數據導致前後兩次讀取數據不一致的情況
-
幻讀
一個事務前後兩次讀取數據數據不一致,是由於其他事務插入數據造成的
4.事務併發帶來問題的解決方案
-
MVCC:undo log 實現的
我可以查到在我這個事務開始之前已經存在的數據,即使它 在後面被修改或者刪除了。在我這個事務之後新增的數據,我是查不到的。
-
解決方案:InnoDB爲每行記錄都實現了兩個隱藏的字段,DB_TRX_ID 6字節,事務編號,記錄操作的事務ID;DB_ROLL_PTR 7字節 ,回滾指針 數據被刪除活記錄爲舊數據的時候,記錄當前事務ID
-
案例
https://www.processon.com/view/link/5d29999ee4b07917e2e09298 MVCC 演示圖
- 1.只能查找版本號小於等於當前事務版本的數據行,這樣可以確保事務讀取的行,要麼是在事務開始前已經存在的,要麼是事務自身插入或者修改過的。
- 2.行的刪除版本要麼未定義,要麼大於當前事務版本號,這可以確保事務讀取到的行,在事務開始之前未被刪除
-
InnoDB 鎖
鎖的粒度
- 行鎖:鎖住一張表
- 表鎖:鎖住一行記錄
鎖的類型
-
共享鎖(Shared Locks)
-
排它鎖(Exclusive Locks)
- 自動加排它鎖,比如增刪改都會加排它鎖
- 手動加排它鎖,for update
-
意向鎖
- 如果一張表上面至少有一個意向共享鎖,說明有其他的事務給其中的某些數據行加上了共享鎖。
- 如果一張表上面至少有一個意向排他鎖,說明有其他的事務給其中的某些數據行加上了排他鎖。
行鎖的原理
-
沒有索引的表、沒有非NULL唯一鍵約束:會進行全表掃描,然後把每一個隱藏的聚集索引(ROW_ID,6字節)都鎖住
-
有索引的表
- 聚簇索引:直接通過索引鎖定數據行
- 其他索引:找到存儲的主鍵id(顯式ID與隱式ID),在通過主鍵鎖定數據行
鎖的算法
-
記錄鎖(Record Lock):當我們對於唯一性的索引(包括唯一索引和主鍵索引)使用等值查詢,精準匹配到一條記錄的時候,這個時候使用的就是記錄鎖,比如where id=1
-
間隙鎖(Gap Lock):當我們查詢的記錄不存在,沒有命中任何一個 record,無論是用等值 查詢還是範圍查詢的時候,它使用的都是間隙鎖。
-
關閉間隙鎖
如果要關閉間隙鎖,就是把事務隔離級別設置成 RC,並且innodb_locks_unsafe_for_binlog 設置爲 ON。
-
左開右開的區間範圍
-
-
臨鍵鎖(Next-key Lock):當我們使用了範圍查詢,不僅僅命中了 Record 記錄,還包含了 Gap 間隙,在這種情況下我們使用的就是臨鍵鎖
- 左開右閉的區間範圍
鎖的退化
- 1.唯一性索引等值查詢匹配到一條記錄的時候,退化成記錄鎖。
- 2.沒有匹配到任何記錄的時候,退化成間隙鎖
鎖與事務隔離級別的關係
-
事務隔離級別
-
未提交讀(read-uncommitted)(不加鎖)
-
提交讀(read-committed)
- 普通的select都是快照讀,使用MVCC實現
- 加鎖的 select 都使用記錄鎖,因爲基本不用 Gap Lock。
- 外鍵約束檢查以及重複鍵檢查時會使用間隙鎖(Gap Lock)
-
可重複讀(repeatable-read)
- 普通的select都是快照讀,使用MVCC實現
- 加鎖的select以及更新操作等語句使用當前讀,底層使用記錄鎖、間隙所、臨鍵鎖
-
串行化(serializable)
- 所有的 select 語句都會被隱式的轉化爲in share mode,會和update、delete等操作互斥
-
ReadView
- 在RU隔離級別下,直接讀取版本最新的記錄。
- 在RR隔離級別下,每個事務開始時,會將當前系統中所有的活躍事務拷貝到一個列表生成ReadView
- 在RC隔離級別下,每個語句開始時,會將當前系統中的所有活躍事務拷貝到一個列表生成ReadView
關注公衆號,每週都有新內容
獲取腦圖請回復公衆號“mysql”獲取PDF版本腦圖,需要補充的知識點可以進行留言,逐步進行完善。
最後感謝大家的關注