MySQL重要知識(shí)點(diǎn)--圈住,面試要考

mysql數(shù)據(jù)庫(kù)使用了Innodb存儲(chǔ)引擎就有了事務(wù)特性。

什么是事務(wù)?

事務(wù)是邏輯上的一組操作,要么都執(zhí)行,要么都不執(zhí)行。

事務(wù)的四大特性:(ACID)

原子性:事務(wù)是最小的執(zhí)行單位,不允許分割。事務(wù)的原子性確保動(dòng)作要么全部完成,要么完全不起作用;

一致性:執(zhí)行事務(wù)前后,數(shù)據(jù)保持一致,多個(gè)事務(wù)對(duì)同一個(gè)數(shù)據(jù)讀取的結(jié)果是相同的;

隔離性:并發(fā)訪問(wèn)數(shù)據(jù)庫(kù)時(shí),一個(gè)用戶的事務(wù)不被其他事務(wù)所干擾,各并發(fā)事務(wù)之間數(shù)據(jù)庫(kù)是獨(dú)立的;

持久性:一個(gè)事務(wù)被提交之后。它對(duì)數(shù)據(jù)庫(kù)中數(shù)據(jù)的改變是持久的,即使數(shù)據(jù)庫(kù)發(fā)生故障也不應(yīng)該對(duì)其有任何影響。

那么ACID實(shí)現(xiàn)的原理是怎么樣的呢:

問(wèn)題一:Mysql怎么保證一致性的

從數(shù)據(jù)庫(kù)層面,數(shù)據(jù)庫(kù)通過(guò)原子性、隔離性、持久性來(lái)保證一致性。也就是說(shuō)ACID四大特性之中,C(一致性)是目的,A(原子性)、I(隔離性)、D(持久性)是手段,是為了保證一致性,數(shù)據(jù)庫(kù)提供的手段。數(shù)據(jù)庫(kù)必須要實(shí)現(xiàn)AID三大特性,才有可能實(shí)現(xiàn)一致性。例如,原子性無(wú)法保證,顯然一致性也無(wú)法保證。從應(yīng)用層面,通過(guò)代碼判斷數(shù)據(jù)庫(kù)數(shù)據(jù)是否有效,然后決定回滾還是提交數(shù)據(jù)。

問(wèn)題二: Mysql怎么保證原子性的?

答,是利用Innodb的undo log。undo log名為回滾日志,是實(shí)現(xiàn)原子性的關(guān)鍵,當(dāng)事務(wù)回滾時(shí)能夠撤銷所有已經(jīng)成功執(zhí)行的sql語(yǔ)句,他需要記錄你要回滾的相應(yīng)日志信息。

例如

(1)當(dāng)你delete一條數(shù)據(jù)的時(shí)候,就需要記錄這條數(shù)據(jù)的信息,回滾的時(shí)候,insert這條舊數(shù)據(jù)

(2)當(dāng)你update一條數(shù)據(jù)的時(shí)候,就需要記錄之前的舊值,回滾的時(shí)候,根據(jù)舊值執(zhí)行update操作

(3)當(dāng)年insert一條數(shù)據(jù)的時(shí)候,就需要這條記錄的主鍵,回滾的時(shí)候,根據(jù)主鍵執(zhí)行delete操作

undo log記錄了這些回滾需要的信息,當(dāng)事務(wù)執(zhí)行失敗或調(diào)用了rollback,導(dǎo)致事務(wù)需要回滾,便可以利用undo log中的信息將數(shù)據(jù)回滾到修改之前的樣子。

問(wèn)題三: Mysql怎么保證持久性的?

OK,是利用Innodb的redo log。

正如之前說(shuō)的,Mysql是先把磁盤(pán)上的數(shù)據(jù)加載到內(nèi)存中,在內(nèi)存中對(duì)數(shù)據(jù)進(jìn)行修改,再刷回磁盤(pán)上。如果此時(shí)突然宕機(jī),內(nèi)存中的數(shù)據(jù)就會(huì)丟失。

怎么解決這個(gè)問(wèn)題?

簡(jiǎn)單啊,事務(wù)提交前直接把數(shù)據(jù)寫(xiě)入磁盤(pán)就行啊。

這么做有什么問(wèn)題?

1.只修改一個(gè)頁(yè)面里的一個(gè)字節(jié),就要將整個(gè)頁(yè)面刷入磁盤(pán),太浪費(fèi)資源了。畢竟一個(gè)頁(yè)面16kb大小,你只改其中一點(diǎn)點(diǎn)東西,就要將16kb的內(nèi)容刷入磁盤(pán),聽(tīng)著也不合理。

2.畢竟一個(gè)事務(wù)里的SQL可能牽涉到多個(gè)數(shù)據(jù)頁(yè)的修改,而這些數(shù)據(jù)頁(yè)可能不是相鄰的,也就是屬于隨機(jī)IO。顯然操作隨機(jī)IO,速度會(huì)比較慢。

于是,決定采用redo log解決上面的問(wèn)題。當(dāng)做數(shù)據(jù)修改的時(shí)候,不僅在內(nèi)存中操作,還會(huì)在redo log中記錄這次操作。當(dāng)事務(wù)提交的時(shí)候,會(huì)將redo log日志進(jìn)行刷盤(pán)(redo log一部分在內(nèi)存中,一部分在磁盤(pán)上)。當(dāng)數(shù)據(jù)庫(kù)宕機(jī)重啟的時(shí)候,會(huì)將redo log中的內(nèi)容恢復(fù)到數(shù)據(jù)庫(kù)中,再根據(jù)undo log和binlog內(nèi)容決定回滾數(shù)據(jù)還是提交數(shù)據(jù)。

采用redo log的好處?

其實(shí)好處就是將redo log進(jìn)行刷盤(pán)比對(duì)數(shù)據(jù)頁(yè)刷盤(pán)效率高,具體表現(xiàn)如下?

1:redo log體積小,畢竟只記錄了哪一頁(yè)修改了啥,因此體積小,刷盤(pán)快。

2:redo log是一直往末尾進(jìn)行追加,屬于順序IO。效率顯然比隨機(jī)IO來(lái)的快。

問(wèn)題四: Mysql怎么保證隔離性的?

是利用的是鎖和MVCC機(jī)制。MVCC,即多版本并發(fā)控制(Multi Version Concurrency Control),一個(gè)行記錄數(shù)據(jù)有多個(gè)版本對(duì)快照數(shù)據(jù),這些快照數(shù)據(jù)在undo log中。

如果一個(gè)事務(wù)讀取的行正在做DELELE或者UPDATE操作,讀取操作不會(huì)等行上的鎖釋放,而是讀取該行的快照版本。由于MVCC機(jī)制在可重復(fù)讀(Repeateable Read)和讀已提交(Read Commited)的MVCC表現(xiàn)形式不同,就不贅述了。但是有一點(diǎn)說(shuō)明一下,在事務(wù)隔離級(jí)別為讀已提交(Read Commited)時(shí),一個(gè)事務(wù)能夠讀到另一個(gè)事務(wù)已經(jīng)提交的數(shù)據(jù),是不滿足隔離性的。但是當(dāng)事務(wù)隔離級(jí)別為可重復(fù)讀(Repeateable Read)中,是滿足隔離性的。

mysql數(shù)據(jù)庫(kù)在并發(fā)操作時(shí)會(huì)出現(xiàn)的問(wèn)題:

*臟讀(Dirty read): **當(dāng)一個(gè)事務(wù)正在訪問(wèn)數(shù)據(jù)并且對(duì)數(shù)據(jù)進(jìn)行了修改,而這種修改還沒(méi)有提交到數(shù)據(jù)庫(kù)中,這時(shí)另外一個(gè)事務(wù)也訪問(wèn)了這個(gè)數(shù)據(jù),然后使用了這個(gè)數(shù)據(jù)。因?yàn)檫@個(gè)數(shù)據(jù)是還沒(méi)有提交的數(shù)據(jù),那么另外一個(gè)事務(wù)讀到的這個(gè)數(shù)據(jù)是“臟數(shù)據(jù)”,依據(jù)“臟數(shù)據(jù)”所做的操作可能是不正確的。

**丟****失修改(Lost to modify): **指在一個(gè)事務(wù)讀取一個(gè)數(shù)據(jù)時(shí),另外一個(gè)事務(wù)也訪問(wèn)了該數(shù)據(jù),那么在第一個(gè)事務(wù)中修改了這個(gè)數(shù)據(jù)后,第二個(gè)事務(wù)也修改了這個(gè)數(shù)據(jù)。這樣第一個(gè)事務(wù)內(nèi)的修改結(jié)果就被丟失,因此稱為丟失修改。例如:事務(wù)1讀取某表中的數(shù)據(jù)A=20,事務(wù)2也讀取A=20,事務(wù)1修改A=A-1,事務(wù)2也修改A=A-1,最終結(jié)果A=19,事務(wù)1的修改被丟失。

**不可重復(fù)讀(Unrepeatableread): **指在一個(gè)事務(wù)內(nèi)多次讀同一數(shù)據(jù)。在這個(gè)事務(wù)還沒(méi)有結(jié)束時(shí),另一個(gè)事務(wù)也訪問(wèn)該數(shù)據(jù)。那么,在第一個(gè)事務(wù)中的兩次讀數(shù)據(jù)之間,由于第二個(gè)事務(wù)的修改導(dǎo)致第一個(gè)事務(wù)兩次讀取的數(shù)據(jù)可能不太一樣。這就發(fā)生了在一個(gè)事務(wù)內(nèi)兩次讀到的數(shù)據(jù)是不一樣的情況,因此稱為不可重復(fù)讀。

**幻讀(Phantom read): **幻讀與不可重復(fù)讀類似。它發(fā)生在一個(gè)事務(wù)(T1)讀取了幾行數(shù)據(jù),接著另一個(gè)并發(fā)事務(wù)(T2)插入了一些數(shù)據(jù)時(shí)。在隨后的查詢中,第一個(gè)事務(wù)(T1)就會(huì)發(fā)現(xiàn)多了一些原本不存在的記錄,就好像發(fā)生了幻覺(jué)一樣,所以稱為幻讀。

不可重復(fù)度和幻讀區(qū)別:

不可重復(fù)讀的重點(diǎn)是修改,幻讀的重點(diǎn)在于新增或者刪除。

例1(同樣的條件, 你讀取過(guò)的數(shù)據(jù), 再次讀取出來(lái)發(fā)現(xiàn)值不一樣了 ):事務(wù)1中的A先生讀取自己的工資為 1000的操作還沒(méi)完成,事務(wù)2中的B先生就修改了A的工資為2000,導(dǎo) 致A再讀自己的工資時(shí)工資變?yōu)?2000;這就是不可重復(fù)讀。

例2(同樣的條件, 第1次和第2次讀出來(lái)的記錄數(shù)不一樣 ):假某工資單表中工資大于3000的有4人,事務(wù)1讀取了所有工資大于3000的人,共查到4條記錄,這時(shí)事務(wù)2 又插入了一條工資大于3000的記錄,事務(wù)1再次讀取時(shí)查到的記錄就變?yōu)榱?條,這樣就導(dǎo)致了幻讀。

那如果面試官問(wèn)你:你們項(xiàng)目Mysql默認(rèn)的事務(wù)隔離級(jí)別是什么呀?是可重復(fù)讀(Repeatable Read)么,那互聯(lián)網(wǎng)項(xiàng)目中Mysql也是用默認(rèn)隔離級(jí)別,不做修改么?OK,不是的,我們?cè)陧?xiàng)目中一般用讀已提交(Read Commited)這個(gè)隔離級(jí)別!what!居然是讀已提交,網(wǎng)上不是說(shuō)這個(gè)隔離級(jí)別存在不可重復(fù)讀和幻讀問(wèn)題么?不用管么?

首先從主從復(fù)制,主從是基于什么復(fù)制的?

是基于binlog復(fù)制的!這里不想去搬binlog的概念了,就簡(jiǎn)單理解為binlog是一個(gè)記錄數(shù)據(jù)庫(kù)更改的文件吧~

binlog有幾種格式?

OK,三種,分別是

statement:記錄的是修改SQL語(yǔ)句

row:記錄的是每行實(shí)際數(shù)據(jù)的變更

mixed:statement和row模式的混合

那Mysql在5.0這個(gè)版本以前,binlog只支持STATEMENT這種格式!而這種格式在讀已提交(Read Commited)這個(gè)隔離級(jí)別下主從復(fù)制是有bug的,因此Mysql將可重復(fù)讀(Repeatable Read)作為默認(rèn)的隔離級(jí)別!

有什么bug呢?

就是在master上執(zhí)行的順序?yàn)橄葎h后插!而此時(shí)binlog為STATEMENT格式,它記錄的順序?yàn)橄炔搴髣h!從(slave)同步的是binglog,因此從機(jī)執(zhí)行的順序和主機(jī)不一致!就會(huì)出現(xiàn)主從不一致!

如何解決?

解決方案有兩種!

(1)隔離級(jí)別設(shè)為可重復(fù)讀(Repeatable Read),在該隔離級(jí)別下引入間隙鎖。當(dāng)Session 1執(zhí)行delete語(yǔ)句時(shí),會(huì)鎖住間隙。那么,Ssession 2執(zhí)行插入語(yǔ)句就會(huì)阻塞住!

(2)將binglog的格式修改為row格式,此時(shí)是基于行的復(fù)制,自然就不會(huì)出現(xiàn)sql執(zhí)行順序不一樣的問(wèn)題!奈何這個(gè)格式在mysql5.1版本開(kāi)始才引入。因此由于歷史原因,mysql將默認(rèn)的隔離級(jí)別設(shè)為可重復(fù)讀(Repeatable Read),保證主從復(fù)制不出問(wèn)題!

那么,當(dāng)我們了解完mysql選可重復(fù)讀(Repeatable Read)作為默認(rèn)隔離級(jí)別的原因后,接下來(lái)我們將其和讀已提交(Read Commited)進(jìn)行對(duì)比,來(lái)說(shuō)明為什么在互聯(lián)網(wǎng)項(xiàng)目為什么將隔離級(jí)別設(shè)為讀已提交(Read Commited)!

首先說(shuō)項(xiàng)目中是不用讀未提交(Read UnCommitted)串行化(Serializable)兩個(gè)隔離級(jí)別,原因有二

采用讀未提交(Read UnCommitted),一個(gè)事務(wù)讀到另一個(gè)事務(wù)未提交讀數(shù)據(jù),這個(gè)不用多說(shuō)吧,從邏輯上都說(shuō)不過(guò)去!

采用串行化(Serializable),每個(gè)次讀操作都會(huì)加鎖,快照讀失效,一般是使用mysql自帶分布式事務(wù)功能時(shí)才使用該隔離級(jí)別!(筆者從未用過(guò)mysql自帶的這個(gè)功能,因?yàn)檫@是XA事務(wù),是強(qiáng)一致性事務(wù),性能不佳!互聯(lián)網(wǎng)的分布式方案,多采用最終一致性的事務(wù)解決方案!)

究竟隔離級(jí)別是用讀已經(jīng)提交呢還是可重復(fù)讀?

可重復(fù)讀(Repeatable Read),簡(jiǎn)稱為RR;

讀已提交(Read Commited),簡(jiǎn)稱為RC;

緣由一:在RR隔離級(jí)別下,存在間隙鎖,導(dǎo)致出現(xiàn)死鎖的幾率比RC大的多!

緣由二:在RR隔離級(jí)別下,條件列未命中索引會(huì)鎖表!而在RC隔離級(jí)別下,只鎖行

緣由三:在RC隔離級(jí)別下,半一致性讀(semi-consistent)特性增加了update操作的并發(fā)性!

在5.1.15的時(shí)候,innodb引入了一個(gè)概念叫做“semi-consistent”,減少了更新同一行記錄時(shí)的沖突,減少鎖等待。

所謂半一致性讀就是,一個(gè)update語(yǔ)句,如果讀到一行已經(jīng)加鎖的記錄,此時(shí)InnoDB返回記錄最近提交的版本,由MySQL上層判斷此版本是否滿足update的where條件。若滿足(需要更新),則MySQL會(huì)重新發(fā)起一次讀操作,此時(shí)會(huì)讀取行的最新版本(并加鎖)!

兩個(gè)疑問(wèn)

在RC級(jí)別下,不可重復(fù)讀問(wèn)題需要解決么?

不用解決,這個(gè)問(wèn)題是可以接受的!畢竟你數(shù)據(jù)都已經(jīng)提交了,讀出來(lái)本身就沒(méi)有太大問(wèn)題!Oracle的默認(rèn)隔離級(jí)別就是RC,你們改過(guò)Oracle的默認(rèn)隔離級(jí)別么?

在RC級(jí)別下,主從復(fù)制用什么binlog格式?

OK,在該隔離級(jí)別下,用的binlog為row格式,是基于行的復(fù)制!Innodb的創(chuàng)始人也是建議binlog使用該格式!


Innodb存儲(chǔ)引擎的索引原理:

主要分 聚簇索引和非聚簇索引

我們自己用語(yǔ)句創(chuàng)建的索引,如復(fù)合索引、前綴索引、唯一索引,都是屬于非聚簇索引,在有的書(shū)籍中,又將其稱為輔助索引(secondary index)。在后文中,我們稱其為非聚簇索引,其數(shù)據(jù)結(jié)構(gòu)為B+樹(shù)。

在Innodb中,聚簇索引默認(rèn)就是主鍵索引。在Mysql中是沒(méi)有語(yǔ)句來(lái)另外生成的。在Innodb中,Mysql中的數(shù)據(jù)是按照主鍵的順序來(lái)存放的。那么聚簇索引就是按照每張表的主鍵來(lái)構(gòu)造一顆B+樹(shù),葉子節(jié)點(diǎn)存放的就是整張表的行數(shù)據(jù)。由于表里的數(shù)據(jù)只能按照一顆B+樹(shù)排序,因此一張表只能有一個(gè)聚簇索引。

如果沒(méi)有主鍵,則按照下列規(guī)則來(lái)建聚簇索引

1.沒(méi)有主鍵時(shí),會(huì)用一個(gè)唯一且不為空的索引列做為主鍵,成為此表的聚簇索引

2.如果沒(méi)有這樣的索引,InnoDB會(huì)隱式定義一個(gè)主鍵來(lái)作為聚簇索引。

MySQL數(shù)據(jù)庫(kù)會(huì)根據(jù)你的建立的索引字段生成一顆新的B+樹(shù)。我們每加一個(gè)索引,就會(huì)增加表的體積, 占用磁盤(pán)存儲(chǔ)空間。然而,注意看葉子節(jié)點(diǎn),非聚簇索引的葉子節(jié)點(diǎn)并不是真實(shí)數(shù)據(jù),它的葉子節(jié)點(diǎn)依然是索引節(jié)點(diǎn),存放的是該索引字段的值以及對(duì)應(yīng)的主鍵索引(聚簇索引)。再說(shuō)個(gè)小知識(shí):MySQL表 一個(gè)表最多可創(chuàng)建16個(gè)索引,最大索引長(zhǎng)度256字節(jié)。如果索引太多,插入性能就會(huì)下降是因?yàn)槎嗉右粋€(gè)索引,就會(huì)多生成一顆非聚簇索引樹(shù)。這時(shí)可以考慮使用組合索引,利用前綴索引進(jìn)行優(yōu)化。

問(wèn)題來(lái)了什么是最左前綴原則?

假設(shè),我們對(duì)(a,b)字段建立了聯(lián)合索引 ,他們是按照a來(lái)進(jìn)行排序,在a相等的情況下,才按b來(lái)排序。

因此,我們可以看到a是有序的1,1,2,2,3,3。而b是一種全局無(wú)序,局部相對(duì)有序狀態(tài)!

什么意思呢?

從全局來(lái)看,b的值為1,2,1,4,1,2,是無(wú)序的,因此直接執(zhí)行b = 2這種查詢條件沒(méi)有辦法利用索引。

從局部來(lái)看,當(dāng)a的值確定的時(shí)候,b是有序的。例如a = 1時(shí),b值為1,2是有序的狀態(tài)。當(dāng)a=2時(shí)候,b的值為1,4也是有序狀態(tài)。因此,你執(zhí)行a = 1 and b = 2是a,b字段能用到索引的。而你執(zhí)行a > 1 and b = 2時(shí),a字段能用到索引,b字段用不到索引。因?yàn)閍的值此時(shí)是一個(gè)范圍,不是固定的,在這個(gè)范圍內(nèi)b值不是有序的,因此b字段用不上索引。綜上所示,最左匹配原則,在遇到范圍查詢的時(shí)候,就會(huì)停止匹配。

為什么mysql的索引要用B+樹(shù)數(shù)據(jù)結(jié)構(gòu)呢:

B+樹(shù)和B樹(shù)的特點(diǎn),我們做一個(gè)總結(jié)

(1)B樹(shù)的樹(shù)內(nèi)存儲(chǔ)數(shù)據(jù),因此查詢單條數(shù)據(jù)的時(shí)候,B樹(shù)的查詢效率不固定,最好的情況是O(1)。我們可以認(rèn)為在做單一數(shù)據(jù)查詢的時(shí)候,使用B樹(shù)平均性能更好。但是,由于B樹(shù)中各節(jié)點(diǎn)之間沒(méi)有指針相鄰,因此B樹(shù)不適合做一些數(shù)據(jù)遍歷操作。

(2)B+樹(shù)的數(shù)據(jù)只出現(xiàn)在葉子節(jié)點(diǎn)上,因此在查詢單條數(shù)據(jù)的時(shí)候,查詢速度非常穩(wěn)定。因此,在做單一數(shù)據(jù)的查詢上,其平均性能并不如B樹(shù)。但是,B+樹(shù)的葉子節(jié)點(diǎn)上有指針進(jìn)行相連,因此在做數(shù)據(jù)遍歷的時(shí)候,只需要對(duì)葉子節(jié)點(diǎn)進(jìn)行遍歷即可,這個(gè)特性使得B+樹(shù)非常適合做范圍查詢。

因此,我們可以做一個(gè)推論:沒(méi)準(zhǔn)是Mysql中數(shù)據(jù)遍歷操作比較多,所以用B+樹(shù)作為索引結(jié)構(gòu)。而Mongodb是做單一查詢比較多,數(shù)據(jù)遍歷操作比較少,所以用B樹(shù)作為索引結(jié)構(gòu)。

那么為什么Mysql做數(shù)據(jù)遍歷操作多?而Mongodb做數(shù)據(jù)遍歷操作少呢?

因?yàn)镸ysql是關(guān)系型數(shù)據(jù)庫(kù),而Mongodb是非關(guān)系型數(shù)據(jù)。

由于關(guān)系型數(shù)據(jù)庫(kù)和非關(guān)系型數(shù)據(jù)的設(shè)計(jì)方式上的不同。導(dǎo)致在關(guān)系型數(shù)據(jù)中,遍歷操作比較常見(jiàn),因此采用B+樹(shù)作為索引,比較合適。而在非關(guān)系型數(shù)據(jù)庫(kù)中,單一查詢比較常見(jiàn),因此采用B樹(shù)作為索引,比較合適。

顯示的sql語(yǔ)句加鎖

(insert into select)復(fù)制表時(shí)候會(huì)用到,加鎖

select *** lock in share mode樂(lè)觀鎖共享鎖

select ***?for update 悲觀鎖 排它鎖

1. 明確指定主鍵,并且有此數(shù)據(jù),row lock

2.明確指定主鍵,無(wú)此數(shù)據(jù),不 lock

3.(無(wú)主鍵,table lock)

4.(明確指定索引,并且有此數(shù)據(jù),row lock)

使用悲觀鎖要注意盡量別產(chǎn)生間隙鎖,否則非常影響性能。 二級(jí)索引? 非唯一索引

樂(lè)觀鎖一般會(huì)使用版本號(hào)機(jī)制或CAS算法實(shí)現(xiàn)。

1. 版本號(hào)機(jī)制

一般是在數(shù)據(jù)表中加上一個(gè)數(shù)據(jù)版本號(hào)version字段,表示數(shù)據(jù)被修改的次數(shù),當(dāng)數(shù)據(jù)被修改時(shí),version值會(huì)加一。當(dāng)線程A要更新數(shù)據(jù)值時(shí),在讀取數(shù)據(jù)的同時(shí)也會(huì)讀取version值,在提交更新時(shí),若剛才讀取到的version值為當(dāng)前數(shù)據(jù)庫(kù)中的version值相等時(shí)才更新,否則重試更新操作,直到更新成功。

2. CAS算法

compare and swap(比較與交換),是一種有名的無(wú)鎖算法。無(wú)鎖編程,即不使用鎖的情況下實(shí)現(xiàn)多線程之間的變量同步,也就是在沒(méi)有線程被阻塞的情況下實(shí)現(xiàn)變量的同步,所以也叫非阻塞同步(Non-blocking Synchronization)。CAS算法涉及到三個(gè)操作數(shù)

需要讀寫(xiě)的內(nèi)存值 V

進(jìn)行比較的值 A

擬寫(xiě)入的新值 B

當(dāng)且僅當(dāng) V 的值等于 A時(shí),CAS通過(guò)原子方式用新值B來(lái)更新V的值,否則不會(huì)執(zhí)行任何操作(比較和替換是一個(gè)原子操作)。一般情況下是一個(gè)自旋操作,即不斷的重試

鎖機(jī)制與InnoDB鎖算法

InnoDB 支持行級(jí)鎖(row-level locking)和表級(jí)鎖,默認(rèn)為行級(jí)鎖

表級(jí)鎖和行級(jí)鎖對(duì)比:

表級(jí)鎖:Mysql中鎖定粒度最大的一種鎖,對(duì)當(dāng)前操作的整張表加鎖,實(shí)現(xiàn)簡(jiǎn)單,資源消耗也比較少,加鎖快,不會(huì)出現(xiàn)死鎖。其鎖定粒度最大,觸發(fā)鎖沖突的概率最高,并發(fā)度最低,MyISAM和 InnoDB引擎都支持表級(jí)鎖。

行級(jí)鎖:Mysql中鎖定粒度最小的一種鎖,只針對(duì)當(dāng)前操作的行進(jìn)行加鎖。行級(jí)鎖能大大減少數(shù)據(jù)庫(kù)操作的沖突。其加鎖粒度最小,并發(fā)度高,但加鎖的開(kāi)銷也最大,加鎖慢,會(huì)出現(xiàn)死鎖。

InnoDB存儲(chǔ)引擎的鎖的算法有三種:

Record lock:?jiǎn)蝹€(gè)行記錄上的鎖

Gap lock:間隙鎖,鎖定一個(gè)范圍,不包括記錄本身

Next-key lock:record+gap 鎖定一個(gè)范圍,包含記錄本身

相關(guān)知識(shí)點(diǎn):

innodb對(duì)于行的查詢使用next-key lock

Next-locking keying為了解決Phantom Problem幻讀問(wèn)題

當(dāng)查詢的索引含有唯一屬性時(shí),將next-key lock降級(jí)為record key

Gap鎖設(shè)計(jì)的目的是為了阻止多個(gè)事務(wù)將記錄插入到同一范圍內(nèi),而這會(huì)導(dǎo)致幻讀問(wèn)題的產(chǎn)生

有兩種方式顯式關(guān)閉gap鎖:(除了外鍵約束和唯一性檢查外,其余情況僅使用record lock) A. 將事務(wù)隔離級(jí)別設(shè)置為RC B. 將參數(shù)innodb_locks_unsafe_for_binlog設(shè)置為1

面試官問(wèn)你如何進(jìn)行數(shù)據(jù)庫(kù)優(yōu)化的?別慌:

第一階段 優(yōu)化sql和索引

(1)用慢查詢?nèi)罩径ㄎ粓?zhí)行效率低的SQL語(yǔ)句

(2)用explain分析SQL的執(zhí)行計(jì)劃

(3)確定問(wèn)題,采取相應(yīng)的優(yōu)化措施,建立索引啊,等

我就不舉例了,因?yàn)槿绾蝺?yōu)化SQL的文章,一抓一大把,再貼過(guò)來(lái),讀者看著也累。

第二階段 搭建緩存

在優(yōu)化sql無(wú)法解決問(wèn)題的情況下,才考慮搭建緩存。畢竟你使用緩存的目的,就是將復(fù)雜的、耗時(shí)的、不常變的執(zhí)行結(jié)果緩存起來(lái),降低數(shù)據(jù)庫(kù)的資源消耗。

這里需要注意的是:搭建緩存后,系統(tǒng)的復(fù)雜性增加了。你需要考慮很多問(wèn)題,比如:

緩存和數(shù)據(jù)庫(kù)一致性問(wèn)題?(比如是更緩存,還是刪緩存),

緩存擊穿、緩存穿透、緩存雪崩問(wèn)題如何解決?是否有做緩存預(yù)熱的必要。不過(guò)我猜,大部分中小公司應(yīng)該都沒(méi)考慮。

第三階段 讀寫(xiě)分離

緩存也搞不定的情況下,搞主從復(fù)制,上讀寫(xiě)分離。在應(yīng)用層,區(qū)分讀寫(xiě)請(qǐng)求。或者利用現(xiàn)成的中間件mycat或者altas等做讀寫(xiě)分離。

需要注意的是,只要你敢說(shuō)你用了主從架構(gòu),有三個(gè)問(wèn)題,你要準(zhǔn)備:

(1)主從的好處?

回答:實(shí)現(xiàn)數(shù)據(jù)庫(kù)備份,實(shí)現(xiàn)數(shù)據(jù)庫(kù)負(fù)載均衡,提交數(shù)據(jù)庫(kù)可用性

(2)主從的原理?

主庫(kù)有一個(gè)log dump線程,將binlog傳給從庫(kù)

從庫(kù)有兩個(gè)線程,一個(gè)I/O線程,一個(gè)SQL線程,I/O線程讀取主庫(kù)傳過(guò)來(lái)的binlog內(nèi)容并寫(xiě)入到relay log,SQL線程從relay log里面讀取內(nèi)容,寫(xiě)入從庫(kù)的數(shù)據(jù)庫(kù)。

(3)如何解決主從一致性?

回答:這個(gè)問(wèn)題,我不建議在數(shù)據(jù)庫(kù)層面解決該問(wèn)題。根據(jù)CAP定理,主從架構(gòu)本來(lái)就是一種高可用架構(gòu),是無(wú)法滿足一致性的

哪怕你采用同步復(fù)制模式或者半同步復(fù)制模式,都是弱一致性,并不是強(qiáng)一致性。所以,推薦還是利用緩存,來(lái)解決該問(wèn)題。

步驟如下:

1、自己通過(guò)測(cè)試,計(jì)算主從延遲時(shí)間,建議mysql版本為5.7以后,因?yàn)閙ysql自5.7開(kāi)始,多線程復(fù)制功能比較完善,一般能保證延遲在1s內(nèi)。不過(guò)話說(shuō)回來(lái),mysql現(xiàn)在都出到8.x了,還有人用5.x的版本么。

2、數(shù)據(jù)庫(kù)的寫(xiě)操作,先寫(xiě)數(shù)據(jù)庫(kù),再寫(xiě)cache,但是有效期很短,就比主從延時(shí)的時(shí)間稍微長(zhǎng)一點(diǎn)。

3、讀請(qǐng)求的時(shí)候,先讀緩存,緩存不存在(這時(shí)主從同步已經(jīng)完成),再讀數(shù)據(jù)庫(kù)。

第四階段 利用分區(qū)表

說(shuō)句實(shí)在話,你們面試的時(shí)候,其實(shí)可以略過(guò)這個(gè)階段。因?yàn)楹芏嗷ヂ?lián)網(wǎng)公司都不建議用分區(qū)表,我自己也不太建議用分區(qū)表,采用這個(gè)分區(qū)表,坑太多。

這里引用一下其他文章的回答:

什么是mysql的分區(qū)表?

回答:所有數(shù)據(jù)還在一個(gè)表中,但物理存儲(chǔ)根據(jù)一定的規(guī)則放在不同的文件中。這個(gè)是mysql支持的功能,業(yè)務(wù)代碼不需要改動(dòng),但是sql語(yǔ)句需要改動(dòng),sql條件需要帶上分區(qū)的列。

缺點(diǎn)

(1)分區(qū)鍵設(shè)計(jì)不太靈活,如果不走分區(qū)鍵,很容易出現(xiàn)全表鎖

(2)在分區(qū)表使用ALTER TABLE?…?ORDER BY,只能在每個(gè)分區(qū)內(nèi)進(jìn)行order by。

(3)分區(qū)表的分區(qū)鍵創(chuàng)建索引,那么這個(gè)索引也將被分區(qū)。分區(qū)鍵沒(méi)有全局索引一說(shuō)。

(4)自己分庫(kù)分表,自己掌控業(yè)務(wù)場(chǎng)景與訪問(wèn)模式,可控。分區(qū)表,研發(fā)寫(xiě)了一個(gè)sql,都不確定該去哪個(gè)分區(qū)查,不太可控。

...不列舉了,不推薦

第五階段 垂直拆分

上面四個(gè)階段都沒(méi)搞定,就來(lái)垂直拆分了。垂直拆分的復(fù)雜度還是比水平拆分小的。將你的表,按模塊拆分為不同的小表。大家應(yīng)該都看過(guò)《大型網(wǎng)站架構(gòu)演變之路》,這種類型的文章或者書(shū)籍,基本都有提到這一階段。

如果你有幸能夠在什么運(yùn)營(yíng)商、銀行等公司上班,你會(huì)發(fā)現(xiàn)他們一個(gè)表,幾百個(gè)字段都是很常見(jiàn)的事情。所以,應(yīng)該要進(jìn)行拆分,拆分原則一般是如下三點(diǎn):

(1)把不常用的字段單獨(dú)放在一張表。

(2)把常用的字段單獨(dú)放一張表

(3)經(jīng)常組合查詢的列放在一張表中(聯(lián)合索引)。

第六階段 水平拆分

OK,水平拆分是最麻煩的一個(gè)階段,拆分后會(huì)有很多的問(wèn)題,我再?gòu)?qiáng)調(diào)一次,水平拆分一定是最最最最后的選擇。從某種意義上,我覺(jué)得還不如垂直拆分。因?yàn)槟阌么怪辈鸱?,分成不同模塊后,發(fā)現(xiàn)單模塊的壓力過(guò)大,你完全可以給該模塊單獨(dú)做優(yōu)化,例如提高該模塊的機(jī)器配置等。如果是水平拆分,拆成兩張表,代碼需要變動(dòng),然后發(fā)現(xiàn)兩張表還不行,再變代碼,再拆成三張表的?水平拆分模塊間耦合性太強(qiáng),成本太大,不是特別推薦。

(全文參考煙哥MySQL數(shù)據(jù)庫(kù)雜文拼接)

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容