一年又一年澈段,字節(jié)跳動(dòng) Lark(飛書) 研發(fā)團(tuán)隊(duì)又雙叒叕開始招新生啦囊扳!
【內(nèi)推碼】:GTPUVBA
【內(nèi)推鏈接】:https://job.toutiao.com/s/JRupWVj
【招生對(duì)象】:20年9月后~21年8月前 畢業(yè)的同學(xué)
【報(bào)名時(shí)間】:6.16-7.16(提前批簡歷投遞只有一個(gè)月抓住機(jī)會(huì)哦!)
【畫重點(diǎn)】:提前批和正式秋招不矛盾!面試成功,提前鎖定Offer蔽介;若有失利,額外獲得一次面試機(jī)會(huì)煮寡,正式秋招開啟后還可再次投遞虹蓄。
點(diǎn)擊進(jìn)入我的博客
1 邏輯架構(gòu)
最上層的服務(wù)并不是MySQL所獨(dú)有的,大多數(shù)基于網(wǎng)絡(luò)的客戶端/服務(wù)器的工具都有類似的架構(gòu)洲押。比如連接處理武花、授權(quán)認(rèn)證、安全等等杈帐。
第二層架構(gòu)是MySQL比較有意思的部分。大多數(shù)MySQL的核心服務(wù)功能都在這一層专钉,包括查詢解析挑童、分析、優(yōu)化跃须、緩存以及所有內(nèi)置函數(shù)(例如站叼,日期,時(shí)間菇民,數(shù)學(xué)和加密函數(shù))尽楔,所有跨存儲(chǔ)引擎的功能都在這一層實(shí)現(xiàn):存儲(chǔ)過程,觸發(fā)器第练,視圖等阔馋。
第三層包含了存儲(chǔ)引擎。存儲(chǔ)引擎負(fù)責(zé)MySQL中數(shù)據(jù)的存儲(chǔ)和提取娇掏。服務(wù)器通過API與存儲(chǔ)引擎進(jìn)行通信呕寝。這些接口屏蔽了不同存儲(chǔ)引擎之間的差異,使得這些差異對(duì)上層的查詢過程透明婴梧。
連接管理
每個(gè)客戶端連接都會(huì)在服務(wù)器進(jìn)程中擁有一個(gè)線程下梢,這個(gè)連接的查詢只會(huì)在這個(gè)單獨(dú)的線程中執(zhí)行客蹋,該線程只能輪流在某個(gè)CPU核心或者CPU中運(yùn)行。服務(wù)器會(huì)負(fù)責(zé)緩存線程孽江,因此不需要為每一個(gè)新建的連接創(chuàng)建或者銷毀線程讶坯。
2 鎖
讀寫鎖
讀鎖:讀鎖是共享的(共享鎖),多個(gè)線程在同一時(shí)刻可以同時(shí)讀取一個(gè)資源岗屏;
寫鎖:寫鎖是排他的(排他鎖)辆琅,一個(gè)寫鎖會(huì)阻礙其他線程的讀和寫。
鎖策略
一方面担汤,鎖力度越精細(xì)并發(fā)能力越強(qiáng)涎跨,盡量只鎖定需要修改的部分?jǐn)?shù)據(jù),而不是全部的資源崭歧;
另一方面隅很,鎖的各種操作(獲得鎖、檢查鎖率碾、釋放鎖等)都會(huì)增加系統(tǒng)開銷叔营,影響系統(tǒng)性能。
鎖策略就是在鎖的開銷和數(shù)據(jù)的安全性之間尋求一定的平衡所宰,達(dá)到最高的并發(fā)能力绒尊。
表鎖
表鎖是MySQL最基本的鎖策略,也是開銷最小的策略仔粥,它會(huì)鎖定整張表婴谱。
寫鎖比讀鎖有更高的優(yōu)先級(jí),一個(gè)寫鎖請(qǐng)求可能會(huì)被插入到讀鎖隊(duì)列的前面躯泰。
行級(jí)鎖
行級(jí)鎖可以最大程度的支持并發(fā)處理谭羔,同時(shí)開銷也是最大的。
InnoDB以及其他存儲(chǔ)引擎實(shí)現(xiàn)了行級(jí)鎖麦向。
行級(jí)鎖只在存儲(chǔ)引擎層實(shí)現(xiàn)瘟裸,而MySQL服務(wù)器層完全不了解存儲(chǔ)引擎中的鎖實(shí)現(xiàn)。
3 事務(wù)
事務(wù)就是一組原子性的SQL查詢诵竭,事務(wù)內(nèi)的語句要么全部執(zhí)行成功话告,要么全部執(zhí)行失敗。
可以使用START TRANSACTION
開始一個(gè)事務(wù)卵慰,然后要么使用COMMIT
提交事務(wù)沙郭,要么使用ROLLBACK
撤銷所有修改。
3.1 ACID原則
- 原子性(atomicity):一個(gè)事務(wù)必須被視為一個(gè)不可分割的最小工作單元呵燕,整個(gè)事務(wù)中的所有操作要么全部提交成功棠绘,要么全部失敗回滾。
- 一致性(consistency):數(shù)據(jù)庫總是從一個(gè)一致性的狀態(tài)轉(zhuǎn)換到另外一個(gè)一致性的狀態(tài)。
- 隔離性(isolation):一個(gè)事務(wù)所做的修改在最終提交以前氧苍,對(duì)其他事務(wù)是不可見的夜矗。
- 持久性(durability):一旦事務(wù)提交,則其所做的修改就會(huì)永久保存到數(shù)據(jù)庫中让虐。此時(shí)即使系統(tǒng)崩潰紊撕,修改的數(shù)據(jù)也不會(huì)丟失。持久性是個(gè)有點(diǎn)模糊的概念赡突,因?yàn)閷?shí)際上持久性也分很多不同的級(jí)別对扶。而且不可能有能做到100%的持久性保證的策略(如果數(shù)據(jù)庫本身就能做到真正的持久性,那么備份又怎么能增加持久性呢?)惭缰。
3.2 隔離級(jí)別
- READ UNCOMMITTED(未提交讀):在該級(jí)別浪南,事務(wù)中的修改即使沒有提交,對(duì)其他事務(wù)也都是可見的漱受。READ UNCOMMITTED會(huì)引起臟讀络凿。這個(gè)級(jí)別會(huì)導(dǎo)致很多問題,從性能上來說昂羡,READ UNCOMTED不會(huì)比其他的級(jí)別好太多絮记,但卻缺乏其他級(jí)別的很多好處。除非真的有非常必要的理由虐先,在實(shí)際應(yīng)用中一般很少使用怨愤。
- READ COMMITTED(提交讀):大多數(shù)數(shù)據(jù)庫系統(tǒng)的默認(rèn)隔離級(jí)別都是 READ COMMITTED(但 MySQL不是)。READ COMMITTED滿足前面提到的隔離性的簡單定義——一個(gè)事務(wù)開始時(shí)蛹批,只能“看見”已經(jīng)提交的事務(wù)所做的修改撰洗。換句話說,一個(gè)事務(wù)從開始直到提交之前腐芍,所做的任何修改對(duì)其他事務(wù)都是不可見的了赵。
- REPEATABLE READ(可重復(fù)讀):REPEATABLE READ解決了臟讀的問題。該級(jí)別保證了在同一個(gè)事務(wù)中多次讀取同樣記錄的結(jié)果是一致的甸赃。但是理論上,可重復(fù)讀隔離級(jí)別還是無法解決另外一個(gè)幻讀(Phantom Read)的問題冗酿。InnoDB和XtraDB存儲(chǔ)引擎通過多版本并發(fā)控制解決了幻讀的問題埠对。可重復(fù)讀是 MySQL的默認(rèn)事務(wù)隔離級(jí)別。
- SERIALIZABLE(串行化):SERIALIZABLE是最高的隔離級(jí)別裁替。它通過強(qiáng)制事務(wù)串行執(zhí)行项玛,避免了前面說的幻讀的問題。簡單來說, SERIALIZABLE會(huì)在讀取的每一行數(shù)據(jù)上都加鎖弱判,所以可能導(dǎo)致大量的超時(shí)和鎖爭用的問題襟沮。實(shí)際應(yīng)用中也很少用到這個(gè)隔離級(jí)別,只有在非常需要確保數(shù)據(jù)的一致性而且可以接受沒有并發(fā)的情況下,才考慮采用該級(jí)別开伏。
設(shè)置隔離級(jí)別
可以通過SET TRANSACTION ISOLATION LEVEL
命令來設(shè)置隔離級(jí)別膀跌。新的隔離級(jí)別會(huì)在下一個(gè)事務(wù)開始的時(shí)候生效」塘椋可以在配置文件中設(shè)置整個(gè)數(shù)據(jù)庫的隔離級(jí)別捅伤,也可以只改變當(dāng)前會(huì)話的隔離級(jí)別:SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
。MySQL能夠識(shí)別所有的4個(gè)ANSI隔離級(jí)別, InnoDB引擎也支持所有的隔離級(jí)別巫玻。
3.3 不可重復(fù)讀與幻讀
臟讀
指的是事務(wù)可以讀取未提交的數(shù)據(jù)丛忆。
不可重復(fù)讀
在一個(gè)事務(wù)中,兩次查詢的結(jié)果不一致仍秤,針對(duì)的是update操作熄诡。如下所示,(1)和(2)讀到的值不一樣诗力。
事務(wù)A | 事務(wù)B |
---|---|
START TRANSACTION; |
|
(1)select salary from T_Users where username = 'lucas'; |
|
START TRANSACTION; |
|
update T_Users set salary = 1000 where username = 'lucas'; |
|
COMMIT; |
|
(2)select salary from T_Users where username = 'lucas'; |
|
COMMIT; |
幻讀
指的是當(dāng)某個(gè)事務(wù)在讀取某個(gè)范圍內(nèi)的記錄時(shí)凰浮,另外一個(gè)事務(wù)又在該范圍內(nèi)插入或刪除了記錄,當(dāng)之前的事務(wù)再次讀取該范圍的記錄時(shí)姜骡,會(huì)產(chǎn)生幻行(Phantom Row)导坟,針對(duì)的是insert和delete。如下所示圈澈,(1)和(2)處讀取的記錄行數(shù)不一樣惫周。
事務(wù)A | 事務(wù)B |
---|---|
START TRANSACTION; |
|
(1)select * from T_Users where age between 10 and 20; |
|
START TRANSACTION; |
|
insert into T_Users (age) values (15); |
|
COMMIT; |
|
(2)select * from T_Users where age between 10 and 20; |
|
COMMIT; |
3.4 死鎖
- 死鎖:死鎖是指兩個(gè)或者多個(gè)事務(wù)在同一資源上相互占用,并請(qǐng)求鎖定對(duì)方占用的資源康栈,從而導(dǎo)致惡性循環(huán)的現(xiàn)象递递。當(dāng)多個(gè)事務(wù)試圖以不同的順序鎖定資源時(shí),就可能會(huì)產(chǎn)生死鎖啥么;多個(gè)事務(wù)同時(shí)鎖定同一個(gè)資源時(shí)登舞,也會(huì)產(chǎn)生死鎖。
- 死鎖的產(chǎn)生原因:有些是因?yàn)檎嬲臄?shù)據(jù)沖突悬荣,這種情況通常很難避免菠秒;有些則完全是由于存儲(chǔ)引擎的實(shí)現(xiàn)方式導(dǎo)致的。
- 打破死鎖:死鎖發(fā)生以后氯迂,只有部分或者完全回滾其中一個(gè)事務(wù)践叠,才能打破死鎖。
- 死鎖解決思路:為了解決這種問題嚼蚀,數(shù)據(jù)庫系統(tǒng)實(shí)現(xiàn)了各種死鎖檢測和死鎖超時(shí)機(jī)制禁灼。InnoDB可以檢測到死鎖的循環(huán)依賴,并立即返回一個(gè)錯(cuò)誤。還有一種解決方式轿曙,就是當(dāng)查詢的時(shí)間達(dá)到鎖等待超時(shí)的設(shè)定后放棄鎖請(qǐng)求弄捕,這種方式通常來說不太好僻孝。
- InnoDB策略:將持有最少行級(jí)排他鎖的事務(wù)進(jìn)行回滾。
3.5 事務(wù)日志
- 事務(wù)日志可以幫助提高事務(wù)的效率守谓。使用事務(wù)日志穿铆,存儲(chǔ)引擎在修改表的數(shù)據(jù)時(shí)只需要修改其內(nèi)存拷貝,再把該修改行為記錄到持久在硬盤上的事務(wù)日志中分飞,而不用每次都將修改的數(shù)據(jù)本身持久到磁盤悴务。
- 事務(wù)日志采用的是追加的方式,因此寫日志的操作是磁盤上一小塊區(qū)域內(nèi)的順序IO譬猫,而不像隨機(jī)IO需要在磁盤的多個(gè)地方移動(dòng)磁頭讯檐,所以采用事務(wù)日志的方式相對(duì)來說要快得多。
- 事務(wù)日志持久以后染服,內(nèi)存中被修改的數(shù)據(jù)在后臺(tái)可以慢慢地刷回到磁盤别洪。目前大多數(shù)存儲(chǔ)引擎都是這樣實(shí)現(xiàn)的,我們通常稱之為預(yù)寫式日志(Write- Ahead Logging)柳刮,修改數(shù)據(jù)需要寫兩次磁盤挖垛。
- 如果數(shù)據(jù)的修改已經(jīng)記錄到事務(wù)日志并持久化,但數(shù)據(jù)本身還沒有寫回磁盤秉颗,此時(shí)系統(tǒng)崩潰痢毒,存儲(chǔ)引擎在重啟時(shí)能夠自動(dòng)恢復(fù)這部分修改的數(shù)據(jù)。具體的恢復(fù)方式則視存儲(chǔ)引擎而定蚕甥。
3.6 MySQL的事務(wù)
MySQL提供了兩種事務(wù)型的存儲(chǔ)引擎:InnoDB和NDB Cluster哪替。MyISAM是非事務(wù)的。
自動(dòng)提交(AUTOCOMMIT)
# 查看AUTOCOMMIT的值
SHOW VARIABLES LIKE 'AUTOCOMMIT';
# 設(shè)置AUTOCOMMIT的值,1或者ON表示啟用,0或者OFF表示禁用堕扶。
SET AUTOCOMMIT = 0;
- MySQL默認(rèn)采用自動(dòng)提交模式,即如果不是顯式地開始一個(gè)事務(wù)帅霜,則毎個(gè)査詢都被當(dāng)作一個(gè)事務(wù)執(zhí)行提交操作。
- 當(dāng)AUTOCOMMIT=0時(shí)呼伸,所有的查詢都是在一個(gè)事務(wù)中身冀,直到顯式地執(zhí)行COMMIT提交或者ROLLBACK回滾,該事務(wù)結(jié)束括享,同時(shí)又開始另一個(gè)新事務(wù)闽铐。
- 修改AUTOCOMMIT對(duì)非事務(wù)型的表,比如 MyISAM或者內(nèi)存表不會(huì)有任何影響奶浦。對(duì)這類表來說,沒有COMMIT或者R0 ROLLBACK的概念踢星,也可以說是相當(dāng)于一直處于AUTOCOMMIT啟用的模式澳叉。
- 有一些命令在執(zhí)行之前會(huì)強(qiáng)制執(zhí)行COMMIT提交當(dāng)前的活動(dòng)事務(wù)。典型的例子,就是
數(shù)據(jù)定義語言(DDL)中會(huì)導(dǎo)致大量數(shù)據(jù)改變的操作成洗,比如 ALTER TABLE五督。
事務(wù)中混合使用存儲(chǔ)引擎
- MySQL服務(wù)器層不管理事務(wù),事務(wù)是由下層的存儲(chǔ)引擎實(shí)現(xiàn)的瓶殃。所以在同一個(gè)事務(wù)中充包,使用多種存儲(chǔ)引擎是不可靠的。如果在事務(wù)中混合使用了事務(wù)型和非事務(wù)型的表遥椿,正常提交的情況下不會(huì)有什么問題基矮,但如果該事務(wù)需要回滾,非事務(wù)型的表上的變更就無法撤銷冠场,這會(huì)導(dǎo)致數(shù)據(jù)庫處于不致的狀態(tài),這種情況很難修復(fù)家浇,事務(wù)的最終結(jié)果將無法確定。
隱式和顯示鎖定
- 兩階段鎖定協(xié)議(two-phase locking protocol):InnoDB采用的是兩階段鎖定協(xié)議碴裙,整個(gè)事務(wù)分為兩個(gè)階段钢悲,前一個(gè)階段為加鎖,后一個(gè)階段為解鎖舔株。在加鎖階段莺琳,事務(wù)只能加鎖,也可以操作數(shù)據(jù)载慈,但不能解鎖惭等;直到事務(wù)釋放第一個(gè)鎖,就進(jìn)入解鎖階段娃肿,此過程中事務(wù)只能解鎖咕缎,也可以操作數(shù)據(jù),不能再加鎖料扰。兩階段鎖協(xié)議使得事務(wù)具有較高的并發(fā)度凭豪,因?yàn)榻怄i不必發(fā)生在事務(wù)結(jié)尾。它的不足是沒有解決死鎖的問題晒杈,因?yàn)樗诩渔i階段沒有順序要求嫂伞。
- 隱式鎖定:InnoDB會(huì)根據(jù)隔離級(jí)別在需要的時(shí)候自動(dòng)加鎖。
- 顯示鎖定:在事務(wù)執(zhí)行過程中拯钻,隨時(shí)都可以執(zhí)行鎖帖努,鎖只有在執(zhí)行C0MMT或者ROLLBACK的時(shí)候才會(huì)釋放,并且所有的鎖是在同一時(shí)刻被釋放**粪般。
LOCK TABLES和UNLOCK TABLES
MySQL也支持LOCK TABLES和UNLOCK TABLES語句拼余,這是在服務(wù)器層實(shí)現(xiàn)的,和存儲(chǔ)引擎無關(guān)亩歹。它們有自己的用途匙监,但并不能替代事務(wù)處理凡橱。如果應(yīng)用需要用到事務(wù),還是應(yīng)該選擇事務(wù)型存儲(chǔ)引擎亭姥。
LOCK TABLES和事務(wù)之間相互影響的話稼钩,情況會(huì)變得非常復(fù)雜。因此除了事務(wù)中禁用了AUTOCOMMIT达罗,可以使用LOCK TABLES之外,其他任何時(shí)候都不要顯式地執(zhí)行LOCK TABLES坝撑,不管使用的是什么存儲(chǔ)引擎。
4 多版本并發(fā)控制
什么是多版本并發(fā)控制
- MVCC(Multi-Version Concurrency Control)多版本并發(fā)控制粮揉,是一種并發(fā)控制的方法巡李,可以認(rèn)為MVCC是行級(jí)鎖的一個(gè)變種,但是它在很多情況下避免了加鎖操作滔蝉,因此開銷更低击儡。
- 當(dāng)一個(gè) MVCC 數(shù)據(jù)庫需要更新一條數(shù)據(jù)記錄的時(shí)候,它不會(huì)直接用新數(shù)據(jù)覆蓋舊數(shù)據(jù)蝠引,而是將舊數(shù)據(jù)標(biāo)記為過時(shí)(obsolete)并在別處增加新版本的數(shù)據(jù)阳谍。這樣就會(huì)有存儲(chǔ)多個(gè)版本的數(shù)據(jù),但是只有一個(gè)是最新的螃概。這種方式允許讀者讀取在他讀之前已經(jīng)存在的數(shù)據(jù)矫夯,即使這些在讀的過程中半路被別人修改、刪除了吊洼,也對(duì)先前正在讀的用戶沒有影響训貌。
- 簡單的說,MVCC就是用同一份數(shù)據(jù)臨時(shí)保留多版本的方式冒窍,實(shí)現(xiàn)并發(fā)控制递沪。
- 很多數(shù)據(jù)庫都用其獨(dú)特的實(shí)現(xiàn)機(jī)制,但大都實(shí)現(xiàn)了非阻塞的讀操作综液,寫操作也只鎖定必要的行款慨。
InnoDB的MVCC
- InnoDB的MVCC,是通過在每行記錄后面保存兩個(gè)隱藏的列來實(shí)現(xiàn)的谬莹。一列保存了行的創(chuàng)建時(shí)間檩奠,另一列保存行的過期時(shí)間(不是實(shí)際的時(shí)間值,而是系統(tǒng)版本號(hào))附帽,每開始一個(gè)新的事務(wù),系統(tǒng)版本號(hào)都會(huì)自動(dòng)遞增埠戳。事務(wù)開始時(shí)刻的系統(tǒng)版本號(hào)會(huì)作為事務(wù)的版本號(hào),用來和查詢到的每行記錄的版本號(hào)進(jìn)行比較蕉扮。
- MVCC只在REPEATABLE READ和 READ COMMITTED兩個(gè)隔離級(jí)別下工作整胃。其他兩個(gè)隔離級(jí)別都和MVCC不兼容。
5 InnoDB概覽
- InnoDB的數(shù)據(jù)存儲(chǔ)在表空間(tablespace)中喳钟,表空間是由 InnoDB管理的一個(gè)黑盒子爪模,由一系列的數(shù)據(jù)文件組成欠啤。
- InnoDB采用MVCC來支持高并發(fā),并且實(shí)現(xiàn)了四個(gè)標(biāo)準(zhǔn)的隔離級(jí)別屋灌,其默認(rèn)級(jí)別是REPEATABLE READ(可重復(fù)讀),并且通過間隙鎖(next-key locking)策略防止幻讀的出現(xiàn)应狱。
- InnoDB表是基于聚簇索引建立的共郭,聚簇索引對(duì)主鍵查詢有很高的性能。不過它的二級(jí)索引(secondary index疾呻,非主鍵索引)中必須包含主鍵列除嘹。
- InnodB內(nèi)部做了很多優(yōu)化,包括從磁盤讀取數(shù)據(jù)時(shí)采用的可預(yù)測性預(yù)讀岸蜗,能夠自動(dòng)在內(nèi)存中創(chuàng)建hash索引以加速讀操作的自適應(yīng)哈希索引(adaptive hash index)尉咕,以及能夠加速插入操作的插入緩沖區(qū)(insert buffer)等。
- 作為事務(wù)型的存儲(chǔ)引擎璃岳,InnodB通過一些機(jī)制和工具支持真正的熱備份年缎,如MySQL Enterprise Backup、 XtraBackup等铃慷。