前言
MySQL 是一款開源軟件半抱,憑借其出色的性能,目前已經(jīng)成為絕大多數(shù)互聯(lián)網(wǎng)公司的首選關(guān)系型數(shù)據(jù)庫。因此程序員不能只懂?dāng)?shù)據(jù)庫的增刪改查和一些簡單的使用技巧疗我,更需要熟練掌握 MySQL 的一些原理知識仔夺,從而更好地應(yīng)對實際工作中遇到的問題琐脏。
本文為大家介紹 MySQL 的知識點包括:
- 了解 MySQL 官網(wǎng)以及 MySQL 下載,MySQL 文檔
- 了解 MySQL 的架構(gòu)
- 了解 MySQL 的存儲引擎
- 掌握 MyISAM 與 InnoDB 引擎的區(qū)別以及數(shù)據(jù)文件存放路徑
- 了解 InnoDB 的邏輯存儲結(jié)構(gòu)
- 掌握 MySQL 中 SQL 語句的執(zhí)行過程
- 了解 MySQL 的緩存機制
- 認(rèn)識數(shù)據(jù)庫索引的本質(zhì)
- 認(rèn)識四種樹的數(shù)據(jù)結(jié)構(gòu)(二叉查找樹缸兔、平衡二叉樹日裙、B-Tree、B+Tree)
- 掌握 MySQL 中 B+Tree 索引的實現(xiàn)方式
- 深入理解數(shù)據(jù)庫索引的幾個概念
- 掌握索引的創(chuàng)建原則以及注意事項
- 深入理解數(shù)據(jù)庫的事務(wù)
- 掌握事務(wù)的 ACID 特性與事務(wù)的隔離級別
- 了解并發(fā)給數(shù)據(jù)庫帶來的問題
- 了解 InnoDB 存儲引擎支持的隔離級別
- 掌握 MySQL 中鎖的機制
- 了解 MySQL 中四個事務(wù)隔離級別的實現(xiàn)
- 掌握 MVCC 的實現(xiàn)原理
- 理解 Undo Log惰蜜、Redo Log 的作用
- 掌握 SQL 語句調(diào)優(yōu)中 explain 的用法
- 掌握 MySQL 常用的調(diào)優(yōu)策略
適合人群: 有 MySQL 使用經(jīng)驗昂拂,想要深入了解 MySQL 的架構(gòu)、索引抛猖、事務(wù)格侯、性能優(yōu)化的程序員。
本文以理論介紹為主财著,大約 1.8 萬字联四,建議大家分章節(jié)閱讀。
一瓢宦、MySQL 官網(wǎng)介紹碎连,了解 MySQL 的整體架構(gòu)
1、MySQL 官網(wǎng)介紹
學(xué)習(xí)了解一個技術(shù)最好的方式是通過官網(wǎng)去了解驮履,官網(wǎng)上有詳細(xì)的文檔資料可供學(xué)習(xí)鱼辙。很對人不習(xí)慣從官網(wǎng)上查找資料廉嚼,甚至不清楚如何在官網(wǎng)上下載程序安裝包,如何找到官方的學(xué)習(xí)文檔倒戏,下面給大家做個簡單的介紹怠噪。面對復(fù)雜的英文文檔,大家不必發(fā)憷杜跷,看的多了就習(xí)慣了傍念,實在看不了,不是還有谷歌翻譯嗎葛闷。
1.1憋槐、MySQL 下載
MySQL 官網(wǎng)地址:https://dev.mysql.com/
打開后界面如下:其中 DOWNLOADS 和 DOCUMENTATION 兩個標(biāo)簽就是下載和文檔的入口。打開 DOWNLOADS 頁面淑趾,找到最下方的 MySQL Community (GPL) Downloads 阳仔,下載社區(qū)版即可,免費使用扣泊。
MySQL 下載地址:https://dev.mysql.com/downloads/mysql/ 打開后點擊:MySQL Community Server 近范,點擊 Looking for previous GA versions? 選擇 MySQL 的歷史通用版本。
下載時需要注冊 Oracle 的賬戶延蟹。
1.2评矩、MySQL 文檔
2阱飘、了解 MySQL 的架構(gòu)
在 MySQL5.1 的中文文檔資料里斥杜,有如下的一張 MySQL 插件式存儲引擎的體系結(jié)構(gòu)圖,通過這張圖(https://www.mysqlzh.com/doc/134.html)俯萌, 我們能很好的了解 MySQL 的整體架構(gòu)果录。
2.1 MySQL 架構(gòu)圖
從以上的架構(gòu)圖我們可以看出,最上面的 Connectors 表示客戶端咐熙,客戶端就是一些具體的鏈接協(xié)議弱恒;下面的 MySQL Server 表示服務(wù)端,其中包括 Connection Pool 棋恼、SQL Interface返弹、Parser、Optimizer爪飘、Storage Engines义起、文件系統(tǒng)(Files、Logs)以及管理服務(wù)师崎。
2.2默终、MySQL 架構(gòu)介紹
從上面的分析可以看出,MySQL 服務(wù)器主要包括 sql 層和存儲引擎層,主要包括以下 6 個部分:
1齐蔽、客戶端的鏈接支持的協(xié)議很多两疚,比如我們在 Java 開發(fā)中經(jīng)常用到的 JDBC,客戶端請求過來首先會由 Connection Pool 進(jìn)行管理含滴,包括權(quán)限認(rèn)證诱渤、鏈接管理,緩存管理等谈况,Connection Pool 最主要的功能就是接收客戶端的請求勺美。
2、客戶端一個 sql 語句請求過來之后碑韵, SQL Interface 會統(tǒng)一接收用戶的 sql 命令赡茸,接下來會交給 Parser 來進(jìn)行解析。
3泼诱、Parser 解析器的目的是把 sql 的文本解析成 selectlex 對象坛掠,隨后把 selectlex 對象提交給優(yōu)化器 Optimizer。
4治筒、優(yōu)化器 Optimizer 會進(jìn)行 sql 執(zhí)行的成本計算,找到最優(yōu)的一個執(zhí)行計劃舷蒲,然后會調(diào)用下方存儲引擎 Storage Engines 的 API 進(jìn)行數(shù)據(jù)的讀寫耸袜。
5、存儲引擎 Storage Engines 最終操作的是最下方的文件系統(tǒng)牲平,包括 Files 和 Logs堤框。
6、圖中左上角的工具模塊包括備份纵柿、主從復(fù)制等功能蜈抓。
二、了解 MySQL 的存儲引擎
存儲引擎的主要工作就是與文件系統(tǒng)進(jìn)行數(shù)據(jù)交互昂儒,比如我們常用的 InnoDB 引擎沟使。
MySQL 的存儲引擎是插件式的,應(yīng)用程序無需針對不同的存儲引擎進(jìn)行對應(yīng)的編碼操作渊跋,MySQL 提供了一套標(biāo)準(zhǔn)的 API 標(biāo)準(zhǔn)腊嗡,MySQL 服務(wù)會自動處理不同的存儲引擎與文件系統(tǒng)的讀寫,用戶層面沒有感知拾酝。
存儲引擎是指定在表上面的燕少,每個表都可以指定其自己的引擎;不管使用什么存儲引擎蒿囤,都會產(chǎn)生一個后綴為 frm 的文件客们,改用來定義表結(jié)構(gòu)。
1、MySQL 的存儲引擎介紹
存儲引擎名稱 | 特點 | |
---|---|---|
1 | MyISAM | Mysql5.5 版本之前的默認(rèn)存儲引擎 |
2 | InnoDB | Mysql5.5 及以后版本的默認(rèn)存儲引擎底挫,目前用的最多的一個存儲引擎 |
3 | CSV | 數(shù)據(jù)存儲以 CSV 文件恒傻,不適用大表或者數(shù)據(jù)的在線處理 |
適用于數(shù)據(jù)的快速導(dǎo)入與導(dǎo)出 | ||
4 | Memory | 數(shù)據(jù)都是存儲在內(nèi)存中,服務(wù)重啟數(shù)據(jù)丟失凄敢,默認(rèn) 16M 空間碌冶, |
可用于查詢結(jié)果內(nèi)存計算,創(chuàng)建臨時表存儲需要計算的數(shù)據(jù) | ||
5 | Archive | 數(shù)據(jù)存儲為 ARZ 文件格式涝缝,只支持 insert 和 select 兩種操作 |
可用于數(shù)據(jù)備份 |
2扑庞、掌握 MyISAM 與 InnoDB 引擎的區(qū)別
Mysql5.5 版本之前默認(rèn)的存儲引擎就是 MyISAM 存儲引擎,MySQL 中比較多的系統(tǒng)表使用 MyISAM 存儲引擎拒逮,系統(tǒng)臨時表也會用到 MyISAM 存儲引擎罐氨,但是在 Mysql5.5 之后默認(rèn)的存儲引擎就是 InnoDB 存儲引擎了。原因主要是 MyISAM 是表級鎖定滩援,限制了數(shù)據(jù)庫讀/寫的性能栅隐;另外一個原因 MyISAM 不支持事務(wù),基于以上兩點玩徊,InnoDB 引擎使用的非常廣泛租悄。
對比 | InnoDB | MyISAM |
---|---|---|
文件存儲方式 | .frm 表定義文件;.ibd 數(shù)據(jù)文件 | .frm 表定義文件恩袱;.myd 數(shù)據(jù)文件泣棋;.myi 索引文件 |
索引方式 | B+ Tree | B+ Tree |
count(*) 操作 | 全表掃描 | 無需掃描 |
鎖機制 | 表鎖、 行鎖 | 表鎖 |
事務(wù) | 支撐事務(wù)的 ACID | 不支持事務(wù) |
常用場景 | 讀寫操作 | 讀多寫少操作畔塔,比如配置表 |
3潭辈、MyISAM 與 InnoDB 數(shù)據(jù)文件存放路徑
-- 查看數(shù)據(jù)存儲的文件路徑
show variables like 'datadir';
4、InnoDB 邏輯存儲結(jié)構(gòu)
MySQL 的存儲結(jié)構(gòu)分為 5 級:表空間澈吨、段把敢、簇、頁谅辣、行修赞。
表空間(TableSpace): 表空間可以看做是 InnoDB 存儲引擎邏輯結(jié)構(gòu)的存儲結(jié)構(gòu)的大容器,所有的數(shù)據(jù)都存放在表空間中屈藐,分為:系統(tǒng)表空間榔组、獨占表空間、通用表空間联逻、臨時表空間搓扯、Undo 表空間。
段(Segment): 表空間是由各個段組成的包归,分為:數(shù)據(jù)段锨推、索引段、回滾段等,段是一個邏輯的概念换可,一個 .ibd 文件(獨立表空間文件)里面會由很多個段組成椎椰。創(chuàng)建一個索引會創(chuàng)建兩個段,一個是索引段(leaf node segment)沾鳄,索引段管理非葉子節(jié)點的關(guān)鍵字和引用數(shù)據(jù)慨飘;一個是數(shù)據(jù)段:(non-leaf node segment),數(shù)據(jù)段管理葉子節(jié)點的行記錄數(shù)據(jù)译荞。
區(qū)/簇(extents): 一個段由很多的簇(也可以叫區(qū))組成瓤的,每個區(qū)的大小是 1MB(64 個連續(xù)的頁),每一個段至少會有一個簇吞歼,一個段所管理的空間大小是無限的圈膏,可以一直擴展下去,但是擴展的最小單位就是簇篙骡。
頁/塊(page): 為了高效管理物理空間稽坤,簇是由連續(xù)的頁組成的空間,一個簇中有 64 個連續(xù)的頁(1MB/16 KB = 64)糯俗,這些頁面在物理上和邏輯上都是連續(xù)的尿褪。頁是 InnoDB 存儲引擎磁盤管理的最小單位,每個頁默認(rèn) 16KB得湘,通過 innodbpagesize 設(shè)置茫多。
行(Row): MySQL5.7 以后默認(rèn)的行格式(Rowformat)為:DYNAMIC,Rowformat 可在創(chuàng)建數(shù)據(jù)表時指定忽刽,大家了解有這么一回事就可以了。
文件系統(tǒng)中夺欲,操作系統(tǒng)和內(nèi)存進(jìn)行文件交換跪帝,最小的單位也是頁,不過文件系統(tǒng)的頁默認(rèn)是 4KB些阅,MySQL 數(shù)據(jù)庫的頁默認(rèn)是 16KB伞剑,目的是為了一次 IO 操作可以獲取更多的數(shù)據(jù)。
三市埋、了解 SQL 語句在 MySQL 中的執(zhí)行過程
作為開發(fā)人員都非常清楚黎泣,當(dāng)我們寫好一個 sql 語句之后,連接到數(shù)據(jù)庫點擊執(zhí)行缤谎,數(shù)據(jù)庫就會返回我們要查詢或者更新的結(jié)果抒倚。但是,數(shù)據(jù)庫服務(wù)器在接收到一個 sql 請求后內(nèi)部是如何處理的坷澡,可能就不太清楚了托呕。這一節(jié)主要為大家講解從客戶端發(fā)起一個 sql 語句的查詢悬嗓,數(shù)據(jù)庫服務(wù)器內(nèi)部的一個處理流程。
1司顿、 MySQL 中 SQL 語句的執(zhí)行過程
一個 MySQL 請求的處理流程圖:從上圖可以看出锰镀,MySQL 的處理流程主要分為 4 個步驟:客戶端與服務(wù)端通信、查詢優(yōu)化處理過程着降、查詢執(zhí)行引擎差油、返回結(jié)果給客戶端。
1.1任洞、客戶端與服務(wù)端通信
一般通信方式有 3 種:單工蓄喇,半雙工,全雙工侈咕。單工就是只能單向傳輸公罕,要么 A 端給 B 端傳輸,要么 B 端給 A 端傳輸耀销;半雙工是可以雙向傳輸?shù)穆ゾ欤峭粫r間只能是一個方向傳輸,也就是說 A 端給 B 端傳輸?shù)臅r候熊尉,B 端只能等待罐柳,反過來也一樣,B 端給 A 端傳輸?shù)臅r候狰住,A 端也只能等待张吉;全雙工是雙向隨便傳輸。
MySQL 客戶端與服務(wù)器的通信方式是半雙工的催植,也就是說肮蛹,我們的一個數(shù)據(jù)庫連接在向數(shù)據(jù)庫發(fā)送數(shù)據(jù)的時候,此時這個數(shù)據(jù)庫連接是不能給客戶端返回數(shù)據(jù)的创南,一定是數(shù)據(jù)返回完畢以后伦忠,客戶端才能再次發(fā)起查詢操作。這也就是我們在做數(shù)據(jù)查詢的時候用 where 條件 和 limit 限制數(shù)據(jù)結(jié)果行數(shù)的原因稿辙,否則客戶端連接需要等到數(shù)據(jù)庫把所有的查詢結(jié)果返回之后昆码,才能進(jìn)行下一個操作。
從上面的分析可以看出邻储,MySQL 數(shù)據(jù)庫半雙工通信模式的一個重要特點是:客戶端一旦開始發(fā)送指令赋咽,服務(wù)端需要接收完畢才能響應(yīng),客戶端只有在完全接收到服務(wù)端響應(yīng)的數(shù)據(jù)后吨娜,才能再次發(fā)送指令脓匿。有點像對講機,這就是為什么電視里看到兩個人對講時萌壳,最后要說一句 over 的原因亦镶,當(dāng)聽到 over 的時候日月,另一端的人就可以按對講鍵進(jìn)行說話了。我們在程序開發(fā)中缤骨,一般會用多個連接進(jìn)行數(shù)據(jù)交互爱咬,通過數(shù)據(jù)庫連接池來進(jìn)行管理,因此對這塊體會可能不夠深刻绊起。
其實 MySQL 的每一個連接都有其對應(yīng)的狀態(tài)來標(biāo)識它目前所處的階段精拟,和線程類似,我們可以通過下面的命令查看數(shù)據(jù)庫連接的狀態(tài):
SHOW [FULL] PROCESSLIST
常用的幾個狀態(tài)描述:
狀態(tài)值 | 狀態(tài)描述 | |
---|---|---|
1 | login | 連接線程的初始狀態(tài)虱歪,直到客戶端已成功通過身份驗證 |
2 | executing | 該線程已開始執(zhí)行一條語句 |
3 | optimizing | 服務(wù)器正在對查詢執(zhí)行初始優(yōu)化 |
4 | Updating | 線程正在搜索或者更新要更新的行 |
5 | Sending data | 正在將數(shù)據(jù)發(fā)送到客戶端蜂绎,一般會執(zhí)行大量的磁盤訪問操作 |
6 | Sorting result | 正在對結(jié)果排序 |
7 | Waiting for commit lock | 正在等待提交鎖 |
當(dāng)發(fā)現(xiàn)數(shù)據(jù)庫連接長時間占用的時候,可以用 kill 命令殺死線程:
kill processlist_id
1.2笋鄙、查詢優(yōu)化處理過程
解析器解析 sql 語句:通過 lex 詞法分析器(就是把一個完整的 SQL 語句分析成獨立的單詞 )师枣、yacc 語法分析器(就是分析是否符合語法規(guī)則,比如單引號是否閉合等)進(jìn)行分析萧落,將 sql 語句按 sql 標(biāo)準(zhǔn)解析成 解析樹(select_lex)對象践美,主要功能是把一個 sql 語句的字符串解析成數(shù)據(jù)庫服務(wù)器可以處理的解析樹對象,便于后續(xù)進(jìn)行預(yù)處理和生成執(zhí)行計劃找岖。
預(yù)處理:預(yù)處理會根據(jù) mysql 的語法規(guī)則對解析樹對象進(jìn)行合法性檢查陨倡,比如檢查表名列名是否存在、檢查名字和別名许布,保證沒有歧義兴革,預(yù)處理之后得到一個新的解析樹。
優(yōu)化器生成執(zhí)行計劃:優(yōu)化器的主要作用就是把這個 sql 語句找到最優(yōu)的執(zhí)行計劃蜜唾,MySQL 的查詢優(yōu)化器和 Oracle 類似杂曲,都是基于成本的計算,優(yōu)化器會嘗試使用不同的執(zhí)行計劃袁余,以便于找到一個最優(yōu)的執(zhí)行計劃(一般隨機讀取 4K 的數(shù)據(jù)庫進(jìn)行分析)解阅。
可以使用以下的命令查看查詢的成本:
show status like 'Last_query_cost';
優(yōu)化器最終會把解析樹變成一個查詢執(zhí)行計劃。MySQL 提供了一個執(zhí)行計劃的工具泌霍,我們在 SQL 語句前面加上 EXPLAIN,就可以看到執(zhí)行計劃的信息述召。
我們在做 sql 調(diào)優(yōu)的時候主要也就是對這部分進(jìn)行處理朱转,在《掌握 SQL 語句調(diào)優(yōu)中 explain 的用法》中會詳細(xì)介紹。
1.3积暖、查詢執(zhí)行引擎
查詢執(zhí)行模塊藤为,也就是查詢執(zhí)行引擎,根據(jù)優(yōu)化器生成的最優(yōu)執(zhí)行計劃調(diào)用對應(yīng)存儲引擎的 API 的進(jìn)行執(zhí)行計劃的執(zhí)行夺刑,并獲取查詢應(yīng)該返回的結(jié)果集缅疟。
1.4分别、返回結(jié)果給客戶端
如果沒有開啟緩存,把查詢到的結(jié)果集返回到客戶端存淫;如果開啟了緩存耘斩,執(zhí)行緩存操作,把結(jié)果集存入緩存桅咆,然后把結(jié)果返回給客戶端括授,即使結(jié)果集是空的,也要返回岩饼。
2荚虚、MySQL 的緩存介紹
一般情況下,我們不會用到數(shù)據(jù)庫自帶的緩存籍茧,所以 MySQL 默認(rèn)是不開啟緩存的版述。只有以讀為主的業(yè)務(wù),數(shù)據(jù)不變化的情況下寞冯,可以開啟數(shù)據(jù)庫的緩存渴析。
查看緩存是否開啟:
show variables like 'query_cache%';
queyrcachetype:on,表示緩存開啟简十,默認(rèn)是關(guān)閉的檬某,可以通過修改 MySQL 配置文件 my.cnf 進(jìn)行調(diào)整,重啟服務(wù)后生效螟蝙。
querycachelimit:1048576恢恼,表示單詞查詢緩存的結(jié)果集大小 1M,超過 1M 則不會緩存胰默。
querycachesize场斑,表示緩存開辟的空間大小
查看緩存操作情況:
show status like 'Qcache%';
Qcache_hits:表示緩存命中次數(shù)
Qcache_inserts:表示緩存寫入次數(shù)
緩存生效的條件是在緩存開啟的情況下,執(zhí)行的 sql 語句字符串一模一樣的時候牵署,可以從緩存直接讀取數(shù)據(jù)漏隐,但是當(dāng)緩存數(shù)據(jù)相關(guān)的表存在數(shù)據(jù)變化的時候,原有的緩存就會失效奴迅,需要重新寫人緩存青责。
MySQL 的緩存開啟后,當(dāng) sql 查詢語句帶有 sqlnocache 關(guān)鍵字或者帶有函數(shù)操作或者單次查詢結(jié)果集超過 querycachelimit 的設(shè)置的值或者查詢系統(tǒng)表時取具,不會用到緩存脖隶。
我們在開發(fā)中,最好不要開啟緩存暇检,將 querycachetype 設(shè)置為 off产阱,querycachesize 設(shè)置為 0;緩存一般會用 Redis 方案來替代块仆。
四构蹬、索引的本質(zhì)與數(shù)據(jù)結(jié)構(gòu)
正確合理的創(chuàng)建索引是提升數(shù)據(jù)庫查詢性能的基礎(chǔ)王暗,因此針對數(shù)據(jù)庫來說,索引是必須要掌握的庄敛。
1俗壹、數(shù)據(jù)庫索引的本質(zhì)
我們一說到索引,如果大家想到是一個類似于字典的目錄铐姚,可以提高數(shù)據(jù)庫查詢的性能策肝,那么這個理解還是太表面了,也就是說對索引的作用有了解隐绵,對它的本質(zhì)還是不夠了解之众。
數(shù)據(jù)庫索引的本質(zhì)是一種數(shù)據(jù)結(jié)構(gòu),是數(shù)據(jù)庫管理系統(tǒng)(DBMS)中一個排序的數(shù)據(jù)結(jié)構(gòu)依许,目的是為了提升對數(shù)據(jù)庫表中的記錄行的檢索速度棺禾,以協(xié)助快速查詢、更新數(shù)據(jù)庫表中數(shù)據(jù)峭跳,而創(chuàng)建的一種數(shù)據(jù)結(jié)構(gòu)膘婶。
實際上,建立索引會占用磁盤空間的索引文件蛀醉,索引也是一張表悬襟,該表保存了主鍵與索引字段,并指向?qū)嶓w表的記錄地址拯刁。
使用索引提升查詢效率的 3 個特點:
1脊岳、索引能極大的減少存儲引擎需要掃描的數(shù)據(jù)量,從而提升查詢效率垛玻;
2割捅、索引可以把隨機 IO 變成順序 IO,從而提升查詢效率帚桩;
3亿驾、索引在進(jìn)行分組、排序操作時避免使用臨時表账嚎,從而提升查詢效率莫瞬;
應(yīng)該選擇一種什么樣的數(shù)據(jù)結(jié)構(gòu),才能實現(xiàn)通過索引來實現(xiàn)數(shù)據(jù)的高效檢索呢郭蕉?MySQL 數(shù)據(jù)庫選擇的是 B+ Tree 作為索引的數(shù)據(jù)結(jié)構(gòu)乏悄,下面我們來看看為什么選擇 B+Tree。
2恳不、四種數(shù)據(jù)結(jié)構(gòu)介紹
2.1、二叉查找樹(Binary Search Tree)
二叉搜索樹的特點:左子樹的鍵值小于根的鍵值开呐,右子樹的鍵值大于根的鍵值烟勋。從上面的 2 個圖來看规求,同樣是 30 個數(shù)字,插入的數(shù)據(jù)順序不一樣卵惦,二叉樹的結(jié)構(gòu)完全不一樣阻肿,圖 1 里查找 30 需要 2 次檢索的 IO 操作,但是在圖 2 里查找 30 需要 7 次檢索的 IO 操作沮尿,在圖 2 里查找 30 類似于全表掃描丛塌。
此處為什么說是一次檢索就有一次 IO 呢,因為要查找的數(shù)據(jù)在文件里畜疾,需要從文件里讀取出來加載到內(nèi)存里進(jìn)行數(shù)據(jù)比較赴邻,如果相等就返回,如果不相等啡捶,假設(shè)比要查找的數(shù)字大姥敛,需要接著往左邊找;假設(shè)比要查找的數(shù)字小瞎暑,需要接著往右邊找彤敛。
以上可以看出,二叉樹的數(shù)據(jù)檢索取決于數(shù)據(jù)的分布情況了赌,不適合用作索引的數(shù)據(jù)結(jié)構(gòu)墨榄,因為二叉樹的深度太深的話,IO 操作會特變多勿她,查詢效率就會很低袄秩,因此若想二叉樹的查詢效率盡可能高,需要這棵二叉樹是平衡的嫂拴,從而引出新的數(shù)據(jù)結(jié)構(gòu)播揪,平衡二叉樹(Balanced Binary Search Tree)或者完全平衡二叉樹(又叫 AVL 樹)。
2.2筒狠、平衡二叉樹
平衡二叉樹(Balanced Binary Search Tree 樹)在符合二叉查找樹的條件下猪狈,滿足某一個節(jié)點的子節(jié)點的高度差不超過 1,也就是相對平衡辩恼;如果整棵樹的所有節(jié)點的高度差不超過 1 就是完全平衡二叉樹(又叫 AVL 樹)雇庙。樹節(jié)點的數(shù)據(jù)存儲結(jié)構(gòu)示意圖:
每個節(jié)點其實就是一個磁盤塊,有 3 部分?jǐn)?shù)據(jù)區(qū)灶伊,一個是關(guān)鍵字:用于存放主鍵或者其他索引的值疆前,一個是數(shù)據(jù)磁盤塊的地址,一個是子節(jié)點的引用聘萨,分別指向父節(jié)點的左子節(jié)點的引用和右子節(jié)點的引用竹椒。
以上面的平衡二叉樹的圖片為例,通過查找關(guān)鍵字為 10 的數(shù)據(jù)記錄米辐,來說明節(jié)點的查找過程說明:
1胸完、先找到節(jié)點 15书释,把節(jié)點 15 加載到內(nèi)存后,與 10 比較赊窥,發(fā)現(xiàn)比 10 大爆惧,那么需要找到 節(jié)點 15 的左子節(jié)點,也就是節(jié)點 8锨能;
2扯再、通過節(jié)點 15 的左子節(jié)點的引用,找到節(jié)點 8址遇,把節(jié)點 8 加載到內(nèi)存后熄阻,與 10 比較,發(fā)現(xiàn)比 10 小傲隶,那么需要找到節(jié)點 8 的右子節(jié)點饺律,也就是節(jié)點 10;
3跺株、通過節(jié)點 8 的右子節(jié)點的引用复濒,找到節(jié)點 10,把節(jié)點 10 加載到內(nèi)存后乒省,與 10 比較巧颈,發(fā)現(xiàn)正好匹配,那么說明要查詢的數(shù)據(jù)就是節(jié)點 10袖扛;
4砸泛、通過節(jié)點 10 的數(shù)據(jù)磁盤地址,找到數(shù)據(jù)區(qū)蛆封,進(jìn)行數(shù)據(jù)的獲取唇礁,返回到客戶端即可。
二叉樹存在數(shù)據(jù)分布不均勻惨篱,出現(xiàn)查詢深度太深盏筐,平衡二叉樹解決了數(shù)據(jù)分布相對平衡的事情,但是用平衡二叉樹作為索引的話還是有問題砸讳,原因主要有兩個:
一個就是數(shù)據(jù)節(jié)點的深度決定了磁盤的 IO 操作次數(shù)琢融,IO 操作非常消耗時間,從上圖可以看出 9 條記錄簿寂,如果查找 30 就需要 4 次 IO 操作漾抬,如果記錄成千上萬,這種 IO 操作是巨大的災(zāi)難常遂。
另外一個就是平衡二叉樹的節(jié)點磁盤塊保存的數(shù)據(jù)量太少纳令,它沒有很好的利用操作系統(tǒng)讀取磁盤 IO 的數(shù)據(jù)交互特性(操作系統(tǒng)去磁盤上讀取的數(shù)據(jù)是 4KB,交互的單位是以頁為單位的),以上的二叉樹數(shù)據(jù)結(jié)構(gòu)一個節(jié)點不可能用滿 4KB 的空間平绩;也沒有利用好磁盤 IO 的預(yù)讀能力(即空間局部性原理)(磁盤預(yù)讀能力就是當(dāng)你一次 IO 讀取到數(shù)據(jù)的頁后坤按,會認(rèn)為你馬上要讀取相鄰的數(shù)據(jù)頁,所以磁盤會多返回幾個頁的數(shù)據(jù)到內(nèi)存)馒过,從而帶來頻繁的 IO 操作。
2.3酗钞、多路平衡查找樹(B-Tree)
多路平衡是指每個節(jié)點可以有多個分支腹忽,也就是可以有多個子節(jié)點,二叉樹就是 2 個子節(jié)點砚作,有幾個子節(jié)點就是幾路窘奏。
多路平衡與二叉平衡相比,可以大大減少樹的深度葫录,從而減少 IO 的操作次數(shù)着裹。
多路平衡查找樹的節(jié)點里的關(guān)鍵字最多有(路數(shù)-1)個,如果用 N 來表示路數(shù)米同,那么關(guān)鍵字的個數(shù)最多就是 N-1 個骇扇。
InnoDB 存儲引擎中有頁(Page)的概念,頁是其磁盤管理的最小單位面粮。InnoDB 存儲引擎中默認(rèn)每個頁的大小為 16KB少孝,可通過參數(shù) innodbpagesize 將頁的大小設(shè)置為 4K、8K熬苍、16K稍走,在 MySQL 中可通過如下命令查看頁的大小: show variables like 'innodb_page_size';
B-Tree 是一個絕對平衡的查找樹虐拓,也就是所有的子節(jié)點都在一個高度上心俗,為了維持樹的絕對平衡,在數(shù)據(jù)插入更新操作時,會通過一些分裂組合的操作來維持城榛,這塊也是比較消耗性能的揪利,這也就是人們常說的索引建立夠用就行,建立索引不易太多的主要原因狠持。因為索引建多了疟位,當(dāng)數(shù)據(jù)插入或者更新的時候,維護(hù)索引的成本也是很大的喘垂。
B-Tree 是為磁盤等外存儲設(shè)備設(shè)計的一種平衡查找樹甜刻,B-Tree 結(jié)構(gòu)的數(shù)據(jù)可以讓系統(tǒng)高效的找到數(shù)據(jù)所在的磁盤塊。關(guān)系型數(shù)據(jù)庫一般都會用 B-Tree 的改進(jìn)版作為索引的數(shù)據(jù)結(jié)構(gòu)正勒。
2.4得院、加強版多路平衡查找樹(B+Tree)
B+Tree 是在 B-Tree 基礎(chǔ)上的一種優(yōu)化,MySQL 數(shù)據(jù)庫的存儲引擎就是用 B+Tree 作為它的索引數(shù)據(jù)結(jié)構(gòu)章贞,MySQL 數(shù)據(jù)庫有幾路取決于索引的字段長度祥绞。在 B+Tree 中,所做的優(yōu)化是:所有數(shù)據(jù)記錄節(jié)點都是按照鍵值大小順序存放在葉子節(jié)點上鸭限,而非葉子節(jié)點上只存儲關(guān)鍵字和子節(jié)點的引用蜕径,非葉子節(jié)點不存數(shù)據(jù)塊的磁盤地址;這樣可以大大加大每個節(jié)點存儲的 key 值數(shù)量里覆,降低 B+Tree 的高深度丧荐。
同時最底層的葉子節(jié)點之間都有一個指針,把上一個節(jié)點的數(shù)據(jù)塊里的最后一個地址指向到下一個節(jié)點的數(shù)據(jù)塊里的第一個地址喧枷,這樣就可以使數(shù)據(jù)庫形成一個有序鏈表虹统,因此可以做到順序 IO 操作。
由于以上的特點隧甚,B+Tree 的全表掃描的能力更強车荔,因為數(shù)據(jù)是放到葉子節(jié)點上的,只需要讀取葉子節(jié)點就可以了戚扳;同時 B+Tree 的排序能力更強忧便,B+Tree 的查詢能力更穩(wěn)定,每次都需要檢索到葉子節(jié)點才能拿到數(shù)據(jù)帽借。
3珠增、MySQL 中 B+Tree 索引的實現(xiàn)方式
3.1、Myisam 存儲引擎中的實現(xiàn)
.frm 表定義文件:每一個存儲引擎都有這個文件
.myd 數(shù)據(jù)文件:d 代表 data砍艾,數(shù)據(jù)庫表中的數(shù)據(jù)保存在這個文件
.myi 索引文件:i 代表 Index蒂教,索引保存在這個文件
Myisam 引擎的數(shù)據(jù)文件與索引文件是分開的,在.myi 索引文件中無論是主鍵索引還是輔助索引脆荷,其葉子節(jié)點保存的是.myd 數(shù)據(jù)文件中的數(shù)據(jù)行的地址指針凝垛,因此 Myisam 引擎獲取數(shù)據(jù)記錄的過程是先從索引文件中拿到數(shù)據(jù)區(qū)的地址懊悯,然后再去數(shù)據(jù)文件中獲取對應(yīng)地址的數(shù)據(jù)返回給客戶端。
3.2梦皮、Innodb 存儲引擎中的實現(xiàn)
.frm 表定義文件:每一個存儲引擎都有這個文件
.ibd 數(shù)據(jù)文件:數(shù)據(jù)庫表中的數(shù)據(jù)保存在這個文件
在 InnoDB 存儲引擎里炭分,它是以主鍵為索引來組織數(shù)據(jù)的存儲的,所以索引文件和數(shù)據(jù)文件是同一個文件剑肯,都在 .ibd 文件里面捧毛。
Innodb 以主鍵為索引來組織數(shù)據(jù)的存儲,如果沒有指定主鍵让网,那么 InnoDB 會默認(rèn)增加主鍵索引岖妄,同時,葉子節(jié)點存放的是整張表的行記錄數(shù)據(jù)寂祥,因為主鍵對應(yīng)的行所有的字段都在葉子節(jié)點上存在,而且索引鍵值的邏輯順序跟數(shù)據(jù)庫表中的數(shù)據(jù)行內(nèi)容存儲順序是一致七兜,所以這種主鍵索引也叫聚集索引丸凭。
Innodb 的輔助索引與聚集索引的區(qū)別在于輔助索引的葉子節(jié)點并不包含行記錄的全部數(shù)據(jù),而是存儲相應(yīng)行數(shù)據(jù)的聚集索引鍵聚集索引鍵(即主鍵的值),當(dāng)通過輔助索引來查詢數(shù)據(jù)時腕铸,InnoDB 存儲引擎會遍歷輔助索引找到主鍵惜犀,然后再通過主鍵在聚集索引中找到完整的行記錄數(shù)據(jù)。
InnoDB 會默認(rèn)增加主鍵索引的規(guī)則: 1狠裹、如果定義了主鍵(PRIMARY KEY)虽界,那么 InnoDB 會選擇主鍵作為聚集索引; 2涛菠、如果沒有定義主鍵莉御,那么 InnoDB 會選擇第一個不包含有 NULL 值的唯一索引作為主鍵索引; 3俗冻、如果唯一索引也沒有定義礁叔,那么 InnoDB 會選擇內(nèi)置 6 個字節(jié)的 ROWID 作為默認(rèn)的聚集索引,采用主鍵遞增的策略生成迄薄。
Innodb 引擎的聚集索引把行記錄數(shù)據(jù)與主鍵全部存起來的原因是考慮到用主鍵做查詢的情況比較多琅关,所以把所有的行記錄聚集起來放到主鍵的葉子節(jié)點上。
Innodb 引擎的輔助索引用主鍵值而不用地址表示讥蔽,主要原因是當(dāng)數(shù)據(jù)變化的時候涣易,如果用的是主鍵,不是地址的話冶伞,不用再維護(hù)輔助索引了新症。
五、深入了解數(shù)據(jù)庫的索引
1碰缔、索引的幾個概念
單列索引: 索引節(jié)點中的關(guān)鍵字是一個列的內(nèi)容账劲,也叫普通索引,比如:用身份證號做索引,用手機號做索引瀑焦。
聯(lián)合索引: 索引節(jié)點中的關(guān)鍵字是多個列的內(nèi)容腌且,經(jīng)常在需要用多個字段查詢的時候用到。比如:用手機號+時間做索引榛瓮,或者手機號+時間+狀態(tài)做索引铺董;也就是說單列索引是特殊的聯(lián)合索引。
唯一索引: 索引列的值必須唯一禀晓,但允許有空值精续;如果是聯(lián)合索引,則列值的組合必須唯一粹懒。
主鍵索引: 當(dāng)給這一張表指定主鍵以后重付,那么這個主鍵就會變成一個主鍵索引,主鍵索引是唯一性索引凫乖,唯一性索引并不一定就是主鍵确垫。
聚集索引(聚簇索引): 聚集索引的 B+Tree 中的葉子節(jié)點存放的是整張表的行記錄數(shù)據(jù),而且索引鍵值的邏輯順序跟數(shù)據(jù)庫表中的數(shù)據(jù)行內(nèi)容存儲順序是一致帽芽,輔助索引與聚集索引的區(qū)別在于輔助索引的葉子節(jié)點并不包含行記錄的全部數(shù)據(jù)删掀,而是存儲相應(yīng)行數(shù)據(jù)的聚集索引鍵,即主鍵的值导街。
輔助索引: 輔助索引也叫非聚集索引披泪,也就是非主鍵索引。
回表掃描: 在 InnoDB 中輔助索引里存放的是主鍵值搬瑰,假設(shè)查詢條件不是主鍵而是輔助索引款票,那么首先需要從輔助索引里找到對應(yīng)的主鍵值,然后從主鍵索引里找到該主鍵值對應(yīng)的數(shù)據(jù)記錄行泽论,這個過程就叫做回表掃描徽职,因為這個過程比基于主鍵索引的查詢多掃描了一棵輔助索引樹,這個過程就叫回表掃描佩厚,意思就是從輔助索引樹里找一圈姆钉,然后再回到主鍵索引里找一圈。
覆蓋索引: 在輔助索引里面抄瓦,不管是單列索引還是聯(lián)合索引潮瓶,如果查詢的數(shù)據(jù)列只用從索引節(jié)點中的關(guān)鍵字就能夠取得,不必從數(shù)據(jù)區(qū)中讀取主鍵值钙姊,再回到主鍵索引查詢記錄行毯辅,這時候使用的索引就叫做覆蓋索引,這樣就避免了回表掃描煞额,所以說思恐,用到覆蓋索引的查詢效率會高一些沾谜,原因就是不用進(jìn)行回表掃描操作,IO 操作次數(shù)減少了胀莹。同時覆蓋索引也可以減少數(shù)據(jù)庫 IO基跑,將隨機 IO 變?yōu)轫樞?IO,因此提高數(shù)據(jù)的查詢效率描焰。
了解了覆蓋索引的概念媳否,我們就能知道為什么數(shù)據(jù)庫開發(fā)一般要求不要用 select * 查詢,除了 select * 查詢需要返回的字段值多荆秦,占用空間以外篱竭,更重要的原因就是,如果業(yè)務(wù)需求只需要查詢索引列的時候步绸,直接查索引字段會直接從索引文件返回內(nèi)容掺逼,而不需要回表掃描,這樣就大大提升了查詢的性能瓤介。
2坪圾、索引的創(chuàng)建原則
在實際開發(fā)過程中,一般人們存在一個誤區(qū)惑朦,就是查詢效率低,我們創(chuàng)建索引解決就行了漓概,也有人把“在經(jīng)常使用的查詢條件上都建立索引“作為解決查詢緩慢的法則漾月,其實通過索引數(shù)據(jù)結(jié)構(gòu)的學(xué)習(xí),大家應(yīng)該能有體會胃珍,索引不是越多越好梁肿,太多的索引反而會影響數(shù)據(jù)庫的性能。
下面介紹索引創(chuàng)建的 3 個原則:
2.1觅彰、最少空間原則
最少空間指的是列的長度小吩蔑,長度越小的列,B+Tree 的路數(shù)就會越多填抬,IO 檢索的次數(shù)就會越少烛芬,查詢的效率就會越高,也就是說索引的效果越好飒责。如果不理解赘娄,請再看一下索引的數(shù)據(jù)結(jié)構(gòu)部分的內(nèi)容。
2.2宏蛉、高離散度列原則
數(shù)據(jù)表中列的離散度公式:count(distinct(列名稱)) / count(*)佛玄,也就是列的所有不相同的值和所有數(shù)據(jù)記錄總行數(shù)的比洒闸,如果列的所有不相同的值和數(shù)據(jù)記錄總行數(shù)約接近,那么列的離散度越好屋确,簡單來說,如果列的重復(fù)值越多柒凉,離散度越低,重復(fù)值越少,離散度越就越高隐砸。
這也就是大家都了解的不要再性別字段上建立索引的主要原因,因為性別字段列的值除了男就是女西采,列的散型太差凰萨,建立索引起不到效果,查詢優(yōu)化器選擇執(zhí)行計劃可能覺得走這樣的索引還不如走全表掃描械馆。
因此我們在創(chuàng)建索引的時候盡量選擇離散度高的列胖眷,也就是選擇列的值幾乎沒有重復(fù)的列做索引效果最好。
2.3霹崎、最左匹配原則
最左匹配原則一般用于聯(lián)合索引中珊搀,聯(lián)合索引在 B+Tree 中是復(fù)合的數(shù)據(jù)結(jié)構(gòu),最左匹配原則就是聯(lián)合索引的檢索比對是從左往右匹配的尾菇,它是按照從左到右的順序來建立搜索樹的境析。
假如一張表用手機號和時間聯(lián)合索引【phone,time】派诬,沒有建立 time 的單列索引劳淆。
那么聯(lián)合索引【phone,time】(phone 在左邊默赂,time 在右邊)沛鸵,就表示:phone 是有序的,time 是無序的缆八;當(dāng) phone 相等的時候曲掰,time 才是有序的。
這個時候我們使用 phone 和 time 作為查詢條件的時候奈辰,B+Tree 會優(yōu)先比較 phone 來確定下一步應(yīng)該搜索的方向栏妖,往左還是往右;如果 phone 相同的時候再比較 time 奖恰;但是如果查詢條件只有 time吊趾,那么就不知道第一步應(yīng)該查哪個節(jié)點,因為建立搜索樹的時候 phone 是在左邊瑟啃,所以用 time 查詢就不會用到索引趾徽。
如果用 phone 作為查詢條件,是否可以用到索引呢翰守,答案是肯定的孵奶,因為 phone 在聯(lián)合索引的左邊,所以用 phone 獨立查詢蜡峰,按照最左匹配原則是可以用到索引的了袁。
從最左匹配原則我們就能發(fā)現(xiàn)朗恳,如果創(chuàng)建了聯(lián)合索引【phone,time】载绿,那么就沒有必要再去創(chuàng)建單列索引【phone】了粥诫,但是如果有用 time 作為查詢條件的情況,就需要創(chuàng)建 time 列的索引【time】崭庸。
3怀浆、索引使用的幾個注意事項
在 where 判斷 order 排序和 join(on)的字段上創(chuàng)建索引
索引字段最好不要為 null 值
索引列的長度能少則少
索引不是查詢字段都要建立索引,也不是越多越好
注意聯(lián)合索引的使用最左匹配原則
選擇索引要選離散度高的列
聯(lián)合索引里最左的列怕享,就不要創(chuàng)建單列索引了执赡,浪費空間不說,還增加了更新操作的索引維護(hù)成本
頻繁更新的值函筋,不要作為主鍵或者索引
索引列上避免使用函數(shù)(replace沙合、substr、concat跌帐、sum)和運算
字符串查詢一定要加引號
like 條件中前面帶 %首懈,同時要保證 % 前面的內(nèi)容離散度要高
避免使用 NOT IN 、<>谨敛、NOT LIKE 等負(fù)向判斷
可以使用 limit 關(guān)鍵字進(jìn)行查詢記錄限制
六究履、深入了解數(shù)據(jù)庫的事務(wù)
1、什么是事務(wù)
事務(wù)是數(shù)據(jù)庫管理系統(tǒng)(DBMS)執(zhí)行過程中的一個邏輯單位脸狸,由一個有限的數(shù)據(jù)庫操作序列構(gòu)成最仑,事務(wù)是不可以再分的最小的數(shù)據(jù)庫工作單元,它包含了一個或者多個 DML 語句肥惭,比如 insert、update紊搪、delete蜜葱。
InnoDB 存儲引擎是支持事務(wù)的,這也是它成為默認(rèn)的存儲引擎的一個重要原因耀石。
最常見的事務(wù)操作就是金融系統(tǒng)的銀行轉(zhuǎn)賬場景牵囤,簡單的說 A 賬戶轉(zhuǎn)出多少金額給 B 賬戶,那么 B 賬戶必須完成對應(yīng)金額的增加滞伟,這個轉(zhuǎn)賬業(yè)務(wù)才算成功揭鳞,否則就出大問題了。
再比如電商系統(tǒng)的下訂單場景梆奈,訂單表和庫存表一定是要在一個事務(wù)里完成的野崇。
事務(wù)里的操作,要么同時成功亩钟,要么同時失敗乓梨,這樣才能保證事務(wù)操作的一致性鳖轰。
2、事務(wù)的四大特性
事務(wù)的四大特性:ACID扶镀。
2.1蕴侣、原子性(Atomicity)
原子性,又叫不可分割性臭觉,它是指一個事務(wù)(transaction)中的所有操作昆雀,要么全部成功,要么全部失敗蝠筑。如果事務(wù)在執(zhí)行過程中發(fā)生錯誤狞膘,就會被回滾(rollback)到事務(wù)執(zhí)行之前的狀態(tài),也就是說要返回原樣菱肖,就像這個事務(wù)從來沒有執(zhí)行過一樣客冈。 原子性的回滾操作在 InnoDB 里面是通過 undolog 來實現(xiàn)的,它記錄了數(shù)據(jù)修改之前的值(邏輯日志)稳强,一旦發(fā)生異常场仲,就可以用 undo log 來實現(xiàn)回滾操作。
2.2退疫、一致性(Consistent)
一致性渠缕,指的是在事務(wù)執(zhí)行前后,數(shù)據(jù)庫的完整性約束沒有被破壞褒繁,事務(wù)執(zhí)行的前后都是合法的數(shù)據(jù)狀態(tài)亦鳞。比如主鍵必須是唯一的,字段長度符合要求等棒坏,也不會因為系統(tǒng)出現(xiàn)異常等原因?qū)е聰?shù)據(jù)前后的狀態(tài)不一致燕差。
除了數(shù)據(jù)庫自身的完整性約束,我們在寫代碼的時候坝冕,還要滿足用戶自定義的完整性徒探。比如:A 賬戶要給 B 賬戶轉(zhuǎn)賬 10000 元,如果 A 賬戶余額減少 10000 元喂窟,B 賬戶余額只增加了 1000 元测暗,這個時候兩個 sql 操作都可以成功,因為它是滿足原子性的磨澡,但是明顯存在業(yè)務(wù)問題碗啄;假設(shè) A 賬戶里沒有 10000 元,那么也是不能夠執(zhí)行轉(zhuǎn)賬操作的稳摄,一般用戶自定義的完整性需要在代碼中做好判斷和控制稚字。
2.3、隔離性(Isolation)
隔離性可以防止多個事務(wù)并發(fā)的讀寫操作時厦酬,由于交叉執(zhí)行而導(dǎo)致數(shù)據(jù)的不一致尉共。因為數(shù)據(jù)庫允許多個并發(fā)事務(wù)同時去讀寫或者修改數(shù)據(jù)庫的同一張表或者同一行數(shù)據(jù)褒傅,如果不加控制,數(shù)據(jù)一定會不一致袄友。隔離性就是指多個并發(fā)事務(wù)對同一張表或者同一行數(shù)據(jù)的讀寫操作殿托,應(yīng)該是互相不干擾的。主要是指一個事務(wù)要操作的數(shù)據(jù)在提交之前剧蚣,對其他事務(wù)應(yīng)該是不可見的支竹,從而可以保證業(yè)務(wù)數(shù)據(jù)的一致性。
數(shù)據(jù)庫的隔離性是通過事務(wù)的隔離級別來實現(xiàn)的鸠按,這塊在后面介紹礼搁。
2.4、持久性(Durable)
持久性是指事務(wù)處理(對數(shù)據(jù)庫的任意的增刪改操作)結(jié)束后目尖,對數(shù)據(jù)的修改就是永久的馒吴,即便數(shù)據(jù)庫宕機也不會丟失(數(shù)據(jù)庫的磁盤文件損壞除外)。
數(shù)據(jù)庫宕機后的恢復(fù)是通過 redolog 來實現(xiàn)的數(shù)據(jù)持久性恢復(fù)瑟曲,數(shù)據(jù)庫在操作數(shù)據(jù)的時候饮戳,會先寫到內(nèi)存的 buffer pool 里面,同時記錄 redo log洞拨。如果在寫到磁盤之前出現(xiàn)異常扯罐,那么重啟后就可以讀取 redo log 的內(nèi)容,然后再寫入到磁盤烦衣,從而保證數(shù)據(jù)的持久性歹河。
其實事務(wù)的原子性、隔離性花吟、持久性秸歧,最后都是為了實現(xiàn)事務(wù)的最終一致性。
3衅澈、MySQL 如何開啟事務(wù)
1键菱、自動開啟事務(wù),自動提交或者回滾事務(wù)
InnoDB 有一個 autocommit 的參數(shù)(分成兩個級別矾麻, session 級別和 global 級別)纱耻。
show variables like 'autocommit';
它的默認(rèn)值是 ON芭梯,如果 autocommit 值是 true/on 的話险耀,我們在操作數(shù)據(jù)的時候,會自動開啟一個事務(wù)玖喘,和自動提交事務(wù)甩牺;如果把 autocommit 設(shè)置成 false/off,那么數(shù)據(jù)庫的事務(wù)就需要我們手動地去開啟和手動地去結(jié)束累奈。
2贬派、手動開啟事務(wù)急但,手動提交或者回滾
手動開啟事務(wù)可以用 begin,也可以用 start transaction搞乏;結(jié)束一個事務(wù)也有兩種方式波桩,可以用 commit 提交一個事務(wù),也可以用 rollback 回滾事務(wù)请敦。
4镐躲、了解并發(fā)給數(shù)據(jù)庫帶來的問題
下面我們來分析一下,假設(shè)沒有事務(wù)的隔離性侍筛,當(dāng)兩個事務(wù)并發(fā)地去操作數(shù)據(jù)庫的表或者行的時候萤皂,那么會產(chǎn)生什么問題?
讀臟數(shù)據(jù): A 事務(wù)修改了一個數(shù)據(jù)(假設(shè) A 從 1 改為 2)匣椰,B 事務(wù)讀取 A 事務(wù)修改后的數(shù)據(jù)(B 讀取到了 2)裆熙,但由于某種原因 A 事務(wù)撤銷了事務(wù)(這個數(shù)據(jù)回滾到 1),此時導(dǎo)致 B 事務(wù)讀取到的數(shù)據(jù)不正確(B 認(rèn)為數(shù)據(jù)是 2)禽笑,這就表示 B 事務(wù)讀取到了臟數(shù)據(jù)入录,一般稱為臟讀。
不可重復(fù)讀 A 事務(wù)讀取數(shù)據(jù)后(假設(shè) A 讀到了 1)蒲每,B 事務(wù)執(zhí)行更新操作(B 把 1 改成了 2)纷跛,然后 B 事務(wù)提交(此時數(shù)據(jù)的值為 2);然后 A 事務(wù)再次讀取數(shù)據(jù)(A 讀到了 2)邀杏,也就是說 A 事務(wù)兩次讀取操作得到了 2 個結(jié)果(第一次是 1贫奠,第二次是 2)。說明在一個事務(wù)里對同一個數(shù)據(jù)讀取望蜡,得到的值不一致唤崭,后面的讀操作獲得了其他它事務(wù)提交的結(jié)果,這就表示 A 事務(wù)不可重復(fù)度脖律。
-
幻讀 A 事務(wù)讀取價格小于 10 的產(chǎn)品數(shù)據(jù)(假設(shè) A 讀到了 100 條數(shù)據(jù))谢肾,B 事務(wù)往產(chǎn)品表里插了一條價格小于 10 的產(chǎn)品,然后 B 事務(wù)提交(此時價格小于 10 的產(chǎn)品有 101 條)小泉;然后 A 事務(wù)再次讀取價格小于 10 的產(chǎn)品數(shù)據(jù)(A 讀到了 101 條數(shù)據(jù))芦疏,也就是說 A 事務(wù)讀取到了 B 事務(wù)新增的記錄(第一次是 100 條,第二次是 101 條)微姊。這就表示 A 事務(wù)發(fā)生了幻讀酸茴。
幻讀與不可重復(fù)讀的區(qū)別: 不可重復(fù)讀是由其他事務(wù)的修改操作引發(fā)的讀不一致,幻讀是由于其他事務(wù)新增或者刪除記錄引發(fā)的讀不一致兢交。從結(jié)果上看薪捍,不可重復(fù)讀和幻讀差不多。但從控制的角度來看, 兩者的區(qū)別就比較大。 對于前者, 只需要鎖住滿足條件的記錄酪穿。 對于后者, 要鎖住滿足條件及其相近的記錄
以上事務(wù)并發(fā)帶來的三大問題凳干,無論是臟讀,還是不可重復(fù)讀被济,或者是幻讀救赐,它們都是數(shù)據(jù)庫的讀一致性的問題,都是在一個事務(wù)里面前后兩次讀取出現(xiàn)了不一致的情況只磷。
5净响、MySQL 事務(wù)的隔離級別
針對并發(fā)給數(shù)據(jù)庫帶來的三個問題讀一致性的問題,SQL92 標(biāo)準(zhǔn)為各個數(shù)據(jù)庫廠商提供一定的事務(wù)隔離級別喳瓣,來解決事務(wù)并發(fā)的問題馋贤。