前言
說到數(shù)據(jù)庫(kù)這個(gè)詞展东,我只能用愛恨交加這個(gè)詞來(lái)形容它。兩年前在自己還單純懵懂的時(shí)候進(jìn)了數(shù)據(jù)庫(kù)的課堂炒俱,聽完數(shù)據(jù)庫(kù)的課盐肃,覺得這是一門再簡(jiǎn)單不過的課程卦停,任何一門編程語(yǔ)言都比SQL要晦澀難懂,任何一門理論課程都比數(shù)據(jù)庫(kù)關(guān)系要復(fù)雜得多恼蓬。直到從被面試官按在地上摩擦惊完,到工作中那一條條令人發(fā)指的慢查詢SQL,這就已經(jīng)完全顛覆了我對(duì)數(shù)據(jù)庫(kù)的看法处硬。在有各種數(shù)據(jù)庫(kù)工具的今天小槐,我們是看不到那簡(jiǎn)單到不能再簡(jiǎn)單的一張表的背后,隱藏著多少數(shù)據(jù)結(jié)構(gòu)的支撐荷辕,也看不到我們隨手敲的一條SELECT凿跳,背后會(huì)有多少算法和數(shù)據(jù)結(jié)構(gòu)在給我們做優(yōu)化,作為一個(gè)有技術(shù)熱情的coder疮方,應(yīng)該需要對(duì)我們每日在用的數(shù)據(jù)庫(kù)做一次深入了解控嗜。
數(shù)據(jù)庫(kù)架構(gòu)
-
如何設(shè)計(jì)一個(gè)關(guān)系型數(shù)據(jù)庫(kù)
這個(gè)問題很寬泛,需要我們對(duì)于整體有一個(gè)掌控骡显,對(duì)我們平時(shí)所用的數(shù)據(jù)庫(kù)要有足夠的了解疆栏,對(duì)整個(gè)數(shù)據(jù)庫(kù)做模塊劃分是這道題的關(guān)鍵,這就更需要我們足夠了解數(shù)據(jù)庫(kù)惫谤,對(duì)數(shù)據(jù)庫(kù)做一個(gè)合理的模塊設(shè)計(jì)壁顶。
-
設(shè)計(jì)
從開始,首先要明白溜歪,數(shù)據(jù)庫(kù)的最最根本的作用是什么——存儲(chǔ)數(shù)據(jù)若专,所以我們需要一個(gè)存儲(chǔ)模塊來(lái)存儲(chǔ)我們的數(shù)據(jù),它可以是一個(gè)文件系統(tǒng)(機(jī)械硬盤蝴猪?SSD固態(tài)硬盤调衰?)。但光有存儲(chǔ)模塊是不夠的自阱,我們還需要一個(gè)程序?qū)嵗?/strong>嚎莉,來(lái)組織或者獲取這些數(shù)據(jù),在程序?qū)嵗形覀冃枰峁┇@取和組織這些數(shù)據(jù)的方式动壤,所以我們需要在程序?qū)嵗袑?shí)現(xiàn)存儲(chǔ)管理模塊萝喘。我們還可以在存儲(chǔ)管理模塊中做一些提升效能的工作,例如同時(shí)讀取多行琼懊、分塊分頁(yè)存儲(chǔ)等阁簸。數(shù)據(jù)庫(kù)作為一款對(duì)性能要求極高的軟件,我們應(yīng)該加入緩存機(jī)制哼丈,來(lái)提高其速度启妹,當(dāng)查詢到緩存中已存在的數(shù)據(jù),我們應(yīng)該直接將其從緩存中讀取醉旦,這樣可以減少硬盤IO次數(shù)饶米,提高效能桨啃。到這里為止,我們已經(jīng)完成了對(duì)數(shù)據(jù)庫(kù)的存儲(chǔ)方面的功能設(shè)計(jì)檬输,但是作為數(shù)據(jù)庫(kù)照瘾,應(yīng)該需要提供給用戶對(duì)數(shù)據(jù)進(jìn)行增刪改查的接口,即平時(shí)所寫的SQL丧慈,所以我們應(yīng)該提供一個(gè)SQL解析模塊析命,來(lái)對(duì)日常用戶所寫的SQL語(yǔ)句進(jìn)行解析,轉(zhuǎn)換成機(jī)器可識(shí)別的指令逃默,我們也可以直接將編譯過的SQL加入緩存鹃愤,下次再有同樣的SQL就直接從緩存中讀取,同樣可以提高效能完域。作為一款成熟的數(shù)據(jù)庫(kù)软吐,需要應(yīng)對(duì)各種復(fù)雜的環(huán)境,要時(shí)刻記錄數(shù)據(jù)庫(kù)的狀態(tài)吟税,所以我們還需要一個(gè)日志管理模塊凹耙,對(duì)操作和錯(cuò)誤信息進(jìn)行記錄。數(shù)據(jù)庫(kù)中需要支持多用戶操作乌妙,但每個(gè)用戶都能操作所有的數(shù)據(jù)使兔,這是不現(xiàn)實(shí)的,所以還需要權(quán)限劃分模塊對(duì)數(shù)據(jù)庫(kù)用戶進(jìn)行權(quán)限管理藤韵。當(dāng)然數(shù)據(jù)庫(kù)說到底也只是一款軟件,是軟件就會(huì)有bug熊经,就會(huì)出故障泽艘,故障不可怕,可怕的是在數(shù)據(jù)庫(kù)這種敏感軟件下對(duì)故障沒有特殊的處理方式镐依,導(dǎo)致數(shù)據(jù)丟失匹涮,畢竟數(shù)據(jù)是無(wú)價(jià)的,所以數(shù)據(jù)庫(kù)應(yīng)該引入容災(zāi)機(jī)制槐壳,在數(shù)據(jù)庫(kù)掛了的時(shí)候然低,對(duì)數(shù)據(jù)進(jìn)行恢復(fù)。還有作為數(shù)據(jù)庫(kù)最重要的兩個(gè)模塊务唐,也是現(xiàn)今任何一個(gè)數(shù)據(jù)庫(kù)都需要考慮的問題——并發(fā)和查找效率雳攘,所以還需引入索引和鎖這兩個(gè)模塊,這就是實(shí)現(xiàn)一個(gè)最基礎(chǔ)的數(shù)據(jù)庫(kù)所必需的幾大模塊枫笛。
-
歸納
綜上對(duì)數(shù)據(jù)庫(kù)設(shè)計(jì)模塊做一個(gè)匯總:
1.存儲(chǔ)模塊
2.程序?qū)嵗?/strong>
2.1存儲(chǔ)管理模塊
2.2緩存機(jī)制
2.3SQL解析模塊
2.4日志管理模塊
2.5權(quán)限劃分模塊
2.6容災(zāi)機(jī)制
2.7索引模塊
2.8鎖模塊
索引
-
為什么要使用索引
要考慮這個(gè)問題吨灭,首先要從最基礎(chǔ)的查找表中數(shù)據(jù)的過程開始說起。通常我們?cè)诓檎乙粋€(gè)序列中的某一個(gè)元素時(shí)刑巧,用到的最簡(jiǎn)單的方式就是遍歷喧兄,數(shù)據(jù)庫(kù)也是一樣无畔,在一張表中查找某一行數(shù)據(jù)時(shí),如果不考慮索引的狀況下吠冤,也會(huì)采用一個(gè)逐行掃描的方式浑彰,只不過數(shù)據(jù)庫(kù)通常以塊或者頁(yè)為單位,所以它通常將整個(gè)塊或者頁(yè)加載進(jìn)內(nèi)存拯辙,然后逐塊輪詢查找到結(jié)果并返回闸昨。如果數(shù)據(jù)庫(kù)中只有少量數(shù)據(jù),那么進(jìn)行全表掃描薄风,速度還是會(huì)很快饵较,但是如果在數(shù)據(jù)量很大的表中,這種方法就不再適用了遭赂,在數(shù)據(jù)量很大的表中循诉,由于逐行掃描代價(jià)變大,通常需要避免采用這種逐行掃描的方式進(jìn)行數(shù)據(jù)查找撇他,數(shù)據(jù)庫(kù)為了使查詢變得高效茄猫,所以引入了索引這種方式對(duì)數(shù)據(jù)進(jìn)行查找。
-
什么樣的信息能成為索引
1.主鍵困肩、唯一鍵划纽、普通鍵
-
索引的數(shù)據(jù)結(jié)構(gòu)
-
二叉查找樹
眾所周知,二叉查找樹是每個(gè)節(jié)點(diǎn)最多由兩個(gè)子樹的樹結(jié)構(gòu)锌畸,而其還有一個(gè)特點(diǎn)是勇劣,在任意一顆樹中,根節(jié)點(diǎn)的左孩子永遠(yuǎn)小于根節(jié)點(diǎn)潭枣,根節(jié)點(diǎn)的右孩子永遠(yuǎn)大于根節(jié)點(diǎn)比默,用二叉查找樹作為索引,確實(shí)可以提高查找效率盆犁,其可以使用二分查找將時(shí)間復(fù)雜度控制在O(lgn)命咐,但是二叉查找樹有一個(gè)顯而易見的缺陷,當(dāng)某種特殊情況(按照某個(gè)特定順序插入樹)發(fā)生時(shí)谐岁,二叉查找樹將變?yōu)橄聢D右側(cè)(線性二叉樹)的狀況:
此時(shí)二叉查找樹查找任意某個(gè)元素時(shí)醋奠,其查找順序與逐行查找無(wú)異,查詢時(shí)間復(fù)雜度又將回到O(n)伊佃,查詢效率無(wú)法保持窜司。 -
B-Tree
B-Tree,平衡多路查找樹锭魔,如果每個(gè)節(jié)點(diǎn)例证,最多有N個(gè)孩子,那么這樣的樹就叫N階B-Tree迷捧,
每個(gè)節(jié)點(diǎn)中主要包含關(guān)鍵字和指向孩子的指針织咧,最多能有幾個(gè)孩子胀葱,取決于節(jié)點(diǎn)的容量和數(shù)據(jù)庫(kù)的相關(guān)配置,通常情況下這個(gè)N是很大的笙蒙。
B-Tree作為一種數(shù)據(jù)結(jié)構(gòu)抵屿,有如下特征:
1.根節(jié)點(diǎn)至少包含兩個(gè)孩子
2.樹中每個(gè)節(jié)點(diǎn)至多含有N個(gè)孩子(N>=2)
3.除根節(jié)點(diǎn)和葉節(jié)點(diǎn)外,其它每個(gè)節(jié)點(diǎn)至少有ceil(N/2)個(gè)孩子捅位。(ceil表示取上限轧葛,例如1.2的上限為2,1.1的上限也為2艇搀,非四舍五入)
4.所有葉子節(jié)點(diǎn)都位于同一層尿扯,即葉子節(jié)點(diǎn)的高度都是一樣的。
5.假設(shè)每個(gè)非終端節(jié)點(diǎn)包含n個(gè)關(guān)鍵字信息(P0,P1...Pn,k1...kn)( a )ki(i=1..n)為關(guān)鍵字焰雕,且關(guān)鍵字按順序升序排序k(i-1)<ki衷笋。
( b )關(guān)鍵字的個(gè)數(shù)必須滿足:[ceil(m/2)-1]<=n<=m-1]。
( c )非葉子節(jié)點(diǎn)的指針:P[1],P[2]...P[M];其中P[1]指向關(guān)鍵字小于K[1]的子樹矩屁,P[N]指向關(guān)鍵字大于K[N-1]的子樹辟宗,其它P[i]指向關(guān)鍵字屬于(K[i-1],K[i])的子樹。
遵守上述規(guī)則吝秕,其目的就是盡量使每個(gè)索引塊都盡可能多的存儲(chǔ)數(shù)據(jù)泊脐,盡可能減少查找次數(shù)以提升效率。
舉個(gè)例子烁峭,模擬一下查找過程容客,以便于理解:假設(shè)我們要查詢關(guān)鍵字為10的數(shù)據(jù),則從根節(jié)點(diǎn)出發(fā),10<17奄薇,于是通過P1進(jìn)入其孩子節(jié)點(diǎn),10>8且10<12,于是通過P2進(jìn)入其孩子節(jié)點(diǎn)胸蛛,最后尋找到10。如果不使用索引纳寂,而使用逐行掃描的方式進(jìn)行查找衫哥,則從0開始至少掃描10次才能查找到10號(hào)數(shù)據(jù),有了索引之后可以看到悲关,查找次數(shù)從10變?yōu)?谎僻,大大提高了查找效率。
如果這里是二叉查找樹寓辱,會(huì)出現(xiàn)極端情況艘绍,使得查找時(shí)間復(fù)雜度為O(n),而如果是B-Tree,由于上述五個(gè)約束秫筏,可以讓節(jié)點(diǎn)通過合并诱鞠、分裂挎挖、上移、下移等操作航夺,使得樹高度較二叉查找樹小蕉朵,查找效率顯然更高。 -
B+ -Tree(MySQL)
B+ -Tree是B-Tree的一個(gè)變體阳掐,其定義基本與B樹相同始衅,除了:
1.非葉子節(jié)點(diǎn)的子樹指針與關(guān)鍵字個(gè)數(shù)相同,其表明B+樹能存儲(chǔ)更多的關(guān)鍵字
2.非葉子節(jié)點(diǎn)的子樹指針P[i]缭保,指向關(guān)鍵字值[K[i],K[i+1])的子樹汛闸。
3.非葉子節(jié)點(diǎn)僅用來(lái)做索引,數(shù)據(jù)到保存在葉子節(jié)點(diǎn)中艺骂。(B+樹的所有檢索都是從根部開始诸老,直到搜索到葉子節(jié)點(diǎn)結(jié)束。)
4.所有葉子節(jié)點(diǎn)均有一個(gè)鏈指針彻亲,指向下一個(gè)葉子節(jié)點(diǎn)孕锄。(方便直接在葉子節(jié)點(diǎn)直接做范圍統(tǒng)計(jì))
B+樹相較于B樹的優(yōu)勢(shì):
1.B+樹的磁盤讀寫代價(jià)更低。
2.B+樹的查詢效率更加穩(wěn)定苞尝。
3.B+樹更有利于對(duì)數(shù)據(jù)庫(kù)的掃描畸肆。
-
Hash
Hash索引是根據(jù)Hash結(jié)構(gòu)的定義,只需要一次運(yùn)算便可以找到數(shù)據(jù)所在位置宙址,不像B+樹或者B樹需要從根結(jié)點(diǎn)出發(fā)尋找數(shù)據(jù)轴脐,所以Hash索引的查詢效率理論上要高于B+樹索引,但是MySQL中并沒有采用這一種索引抡砂,這是由于這種索引除查詢效率之外的缺陷是十分明顯的大咱。
1.僅僅只能滿足"=","IN",不能使用范圍查詢。由于其是由Hash運(yùn)算獲取的數(shù)據(jù)存放位置注益,每次Hash運(yùn)算獲取的是一個(gè)確定的值碴巾,且這個(gè)值并不與數(shù)據(jù)本身的大小有關(guān)系,所以其并不能滿足范圍查詢丑搔。
2.無(wú)法被用來(lái)避免數(shù)據(jù)的排序操作厦瓢。和1的意思差不多,Hash的索引值是由Hash運(yùn)算獲取的啤月,其索引值與數(shù)據(jù)本身的大小并無(wú)明顯關(guān)系煮仇。
3.不能利用部分索引鍵查詢。
4.不能避免表掃描谎仲。由于Hash索引會(huì)產(chǎn)生Hash沖突浙垫,存在Hash沖突的數(shù)據(jù)會(huì)被連接到同一個(gè)鏈表上,當(dāng)大量數(shù)據(jù)被連接到相同鏈表上時(shí),查詢某條數(shù)據(jù)就變成了掃描該鏈表夹姥,時(shí)間復(fù)雜度并不能保證在O(1)杉武。
5.遇到大量Hash值相等的情況后性能并不一定就會(huì)比B-Tree索引高。 -
BitMap索引
位圖索引佃声,當(dāng)表中的某個(gè)字段只有幾種值的時(shí)候艺智,例如:性別,此時(shí)用位圖索引是一個(gè)最佳的選擇圾亏。目前使用位圖索引的比較主流的數(shù)據(jù)庫(kù)有Oracle數(shù)據(jù)庫(kù)十拣。
-
-
密集索引和稀疏索引的區(qū)別
1.密集索引文件中的每個(gè)搜索碼都對(duì)應(yīng)一個(gè)索引值,稀疏索引文件只為索引碼的某些值建立索引項(xiàng)志鹃。
2.密集索引將數(shù)據(jù)存儲(chǔ)與索引放到了一塊夭问,找到索引也就找到了數(shù)據(jù),稀疏索引將數(shù)據(jù)和索引分開存儲(chǔ)曹铃,索引結(jié)構(gòu)的葉子節(jié)點(diǎn)指向數(shù)據(jù)的對(duì)應(yīng)行缰趋。-
關(guān)于MySQL的MyISAM和InnoDB
MyISAM不論是主鍵索引、唯一鍵索引陕见、還是普通索引秘血,都采用的是稀疏索引,而InnoDB必須有且僅有一個(gè)密集索引评甜。
InnoDB密集索引規(guī)則:
1.若一個(gè)主鍵被定義灰粮,該主鍵則作為密集索引。
2.若沒有主鍵被定義忍坷,該表的第一個(gè)唯一非空索引則作為密集索引粘舟。
3.若不滿足以上條件,InnoDB內(nèi)部會(huì)生成一個(gè)隱藏主鍵(密集索引)佩研。
4.非主鍵索引存儲(chǔ)相關(guān)鍵位和其對(duì)應(yīng)的主鍵值柑肴,包含兩次查找。
注:InnoDB如果查詢條件為主鍵索引旬薯,則只需查詢一次晰骑,但是輔助索引需要查詢兩次,先通過輔助索引查詢到主鍵索引绊序,再查詢到數(shù)據(jù)些侍。
從上圖中可以看到,如果一個(gè)索引是聚集索引政模,則其葉子節(jié)點(diǎn)上存放的即是數(shù)據(jù)本身,而如果一個(gè)索引是稀疏索引蚂会,葉子節(jié)點(diǎn)存放的僅是地址淋样,指向?qū)⒁檎业臄?shù)據(jù)。
-
關(guān)于MySQL的MyISAM和InnoDB
-
如何定位并優(yōu)化慢查詢SQL
首先先建立一張表
CREATE DATABASE sqltest; use sqltest; create table tb_test( test_id int primary key auto_increment, test_name varchar(1024), test_date datetime, test_desc varchar(1024) );
在這張表中灌入200w數(shù)據(jù)胁住。
1.根據(jù)慢日志定位慢查詢SQL#查找慢日志 slow_query_log
記錄慢日志SQL運(yùn)行時(shí)間閾值
設(shè)置慢查詢閾值為1秒趁猴,重連數(shù)據(jù)庫(kù)
set global long_query_time = 1;
制造慢查詢:
2.使用explain工具分析SQL
explain select test_name from tb_test order by test_name desc
type:找到數(shù)據(jù)的方式刊咳,根據(jù)效率從高到低排序有如下幾種 system>const>eq_ref>ref>fulltext>ref_or_null>index_merge>unique_subquery>index_subquery>range>index>all
如果type為index或all,表示本次掃描為全表掃描儡司,說明這個(gè)語(yǔ)句是需要優(yōu)化的娱挨。extra:可以用來(lái)輔助type幫助我們進(jìn)行SQL優(yōu)化,extra中出現(xiàn)以下兩項(xiàng)捕犬,意味著MySQL根本不能使用索引跷坝,效率會(huì)受到重大影響,應(yīng)該盡可能對(duì)此進(jìn)行優(yōu)化碉碉。
Using filesort:表示MySQL會(huì)對(duì)結(jié)果使用一個(gè)外部索引排序柴钻,而不是從表里按索引次序讀到相關(guān)內(nèi)容,可能在內(nèi)存或者磁盤上進(jìn)行排序垢粮。MySQL中無(wú)法例用索引完成的排序操作稱為“文件排序”贴届。
Using temporary:表示MySQL在對(duì)查詢結(jié)果排序時(shí)使用臨時(shí)表,常見于排序 order by和分組查詢group by蜡吧。
3.修改SQL毫蚓,盡量讓SQL走索引
我們可以知道,創(chuàng)建表時(shí)昔善,我們將id設(shè)為主鍵元潘,那么id也就自然稱為了索引,所以我們只要修改排序字段為id耀鸦,即可以通過索引排序柬批。
explain select test_id from tb_test order by test_id desc
key:PRIMARY 代表使用了主鍵索引
另一種情況,在特定狀況下一定需要使用name進(jìn)行排序袖订,那還有一種做法氮帐,就是將name字段加索引。#加索引 ALTER TABLE tb_test add index index_name(test_name); #再次分析 explain select test_name from tb_test order by test_name desc;
結(jié)果:
-
聯(lián)合索引的最左匹配原則的成因
上文中只是用了單一索引對(duì)表進(jìn)行排序洛姑,如果使用聯(lián)合索引又會(huì)是什么樣的一種狀況上沐?
最左匹配原則:假設(shè)數(shù)據(jù)表中有兩列,A and B,我們將A楞艾、B設(shè)置為聯(lián)合索引参咙,然后在where語(yǔ)句中調(diào)用where A = ? AND B = ?,該查詢語(yǔ)句會(huì)使用AB聯(lián)合索引硫眯,調(diào)用where A = ?蕴侧,該查詢語(yǔ)句也會(huì)使用AB聯(lián)合索引,但當(dāng)調(diào)用where B = 两入?時(shí)净宵,它將不會(huì)使用AB聯(lián)合索引。
官方定義:1.最左前綴匹配原則,MySQL會(huì)一直向右匹配直到遇到范圍查詢(>择葡、<紧武、between、like)就停止匹配敏储,比如 a=3 and b=4 and c>5 and d=6,如果建立(a,b,c,d)順序的索引阻星,d是無(wú)法使用索引的,如果建立(a,b,d,c)的索引則都可以使用到已添,a妥箕、b、d的順序可以任意調(diào)整酝碳。
2.=和in可以亂序矾踱,比如 a=1 and b=2 and c=3 建立(a,b,c)索引可以任意順序,MySQL的查詢優(yōu)化器會(huì)幫你優(yōu)化成索引可以識(shí)別的形式疏哗。 -
索引是建立的越多越好嗎
答:NO呛讲,數(shù)據(jù)量小的表不需要建立索引,建立會(huì)增加額外的索引開銷返奉。另外數(shù)據(jù)變更需要維護(hù)索引贝搁,因此更多的索引意味著更多的維護(hù)成本。更多的索引還需要消耗更多的空間芽偏。
鎖 and 鎖 and 鎖
-
MyISAM與InnoDB關(guān)于鎖方面的區(qū)別是什么
1.MyISAM默認(rèn)使用表級(jí)鎖雷逆,不支持行級(jí)鎖。
2.InnoDB默認(rèn)使用行級(jí)鎖污尉,也支持表級(jí)鎖膀哲。
3.InnoDB在使用索引時(shí),默認(rèn)使用行級(jí)鎖被碗,但當(dāng)其沒有用到索引時(shí)某宪,默認(rèn)使用表級(jí)鎖。-
表級(jí)鎖
當(dāng)一條數(shù)據(jù)庫(kù)語(yǔ)句對(duì)某條數(shù)據(jù)進(jìn)行操作時(shí)锐朴,默認(rèn)將整張表進(jìn)行加鎖兴喂,其余操作無(wú)法對(duì)表進(jìn)行更改,需等到當(dāng)前操作執(zhí)行完畢釋放鎖后焚志,才能對(duì)該表進(jìn)行更改衣迷。 -
行級(jí)鎖
當(dāng)一條數(shù)據(jù)庫(kù)語(yǔ)句對(duì)某條數(shù)據(jù)或多條數(shù)據(jù)進(jìn)行操作時(shí),默認(rèn)將正在操作的這幾條數(shù)據(jù)進(jìn)行加鎖酱酬,其余操作無(wú)法對(duì)當(dāng)前語(yǔ)句正在操作的這幾條數(shù)據(jù)進(jìn)行操作壶谒,但可以操作其他數(shù)據(jù)。 -
共享鎖和排他鎖
共享鎖:當(dāng)對(duì)某行或某張表上了共享鎖之后膳沽,其它任意語(yǔ)句依然支持上共享鎖佃迄,但不支持上排他鎖泼差。(MySQL使用lock in share mode加共享鎖)
排他鎖:當(dāng)對(duì)某行或某張表上了排他鎖之后,其它任意語(yǔ)句對(duì)這一行或者這一張表進(jìn)行讀或?qū)懚际遣辉试S的呵俏。(MySQL使用 for update 加排他鎖) -
樂觀鎖和悲觀鎖
悲觀鎖:總是假設(shè)最壞的情況,認(rèn)為競(jìng)爭(zhēng)總是存在滔灶,認(rèn)為每次對(duì)數(shù)據(jù)的修改總會(huì)產(chǎn)生沖突普碎,因此每次都會(huì)先上鎖,其他線程阻塞等待釋放鎖录平。
樂觀鎖:總是假設(shè)最好的情況麻车,認(rèn)為競(jìng)爭(zhēng)總是不存在,認(rèn)為每次對(duì)數(shù)據(jù)的修改都不會(huì)產(chǎn)生沖突斗这,因此不會(huì)先上鎖动猬,再最后更新的時(shí)候,比較數(shù)據(jù)是否已經(jīng)被更新表箭,可用版本號(hào)或CAS實(shí)現(xiàn)赁咙。 -
行級(jí)鎖一定比表級(jí)鎖優(yōu)嗎?
答:不一定免钻,鎖的粒度越細(xì)彼水,其消耗的資源代價(jià)越高。行級(jí)鎖在上鎖的時(shí)候需要掃描到某行再進(jìn)行上鎖极舔,這樣的代價(jià)是較大的凤覆。 -
MyISAM和InnoDB各自適合的場(chǎng)景
MyISAM適合的場(chǎng)景:1.頻繁執(zhí)行全表count語(yǔ)句。2.對(duì)數(shù)據(jù)進(jìn)行增刪改的頻率不高拆魏,查詢非常頻繁盯桦。3.沒有事務(wù)
InnoDB適合的場(chǎng)景:1.數(shù)據(jù)增刪改查都相當(dāng)頻繁。2.可靠性要求比較高渤刃,存在事務(wù)拥峦。
-
-
數(shù)據(jù)庫(kù)事務(wù)四大特性ACID
事務(wù):作為單個(gè)邏輯單元執(zhí)行的一個(gè)操作,要么全部完成溪掀,要么全部失敗事镣。
-
原子性
事務(wù)包含的所有操作,要么全部執(zhí)行揪胃,要么全部失敗璃哟。 -
隔離性
多個(gè)事務(wù)并發(fā)執(zhí)行時(shí),一個(gè)事務(wù)的執(zhí)行不應(yīng)該影響其它事務(wù)的執(zhí)行喊递。 -
一致性
事務(wù)應(yīng)保證數(shù)據(jù)庫(kù)狀態(tài)從一個(gè)一致性狀態(tài)轉(zhuǎn)換到另一個(gè)一致性狀態(tài)随闪。(一致性狀態(tài):數(shù)據(jù)庫(kù)的數(shù)據(jù)應(yīng)滿足完整性約束) -
持久性
一個(gè)事務(wù)一旦提交,它的數(shù)據(jù)應(yīng)該永久地保存在數(shù)據(jù)庫(kù)中骚勘。
-
-
事務(wù)隔離級(jí)別以及各級(jí)別下的并發(fā)訪問問題
1.更新丟失 —— 一個(gè)事務(wù)的更新铐伴,引起另一個(gè)事務(wù)提交的丟失撮奏,MySQL所有事務(wù)隔離級(jí)別在數(shù)據(jù)庫(kù)層面上均可避免贪惹。
2.臟讀 —— 一個(gè)事務(wù)讀到另一個(gè)事務(wù)未提交的數(shù)據(jù)攻臀,READ-COMMITTED事務(wù)隔離級(jí)別以上可避免。(ORACLE默認(rèn)隔離級(jí)別為READ-COMMITTED)
3.不可重復(fù)讀——事務(wù)A多次讀取同一數(shù)據(jù)時(shí)管钳,事務(wù)B修改該數(shù)據(jù)户矢,導(dǎo)致事務(wù)A每次讀取到的數(shù)據(jù)結(jié)果不一致玲献,REPEATABLE-READ隔離級(jí)別下可避免。
4.幻讀——事務(wù)A多次讀取同一數(shù)據(jù)時(shí)梯浪,事務(wù)B對(duì)事務(wù)A的影響區(qū)間內(nèi)進(jìn)行增刪操作捌年,導(dǎo)致事務(wù)A讀取到的數(shù)據(jù)一會(huì)多、一會(huì)少挂洛,就像產(chǎn)生幻覺了一樣礼预。SERIALIZABLE事務(wù)隔離級(jí)別可避免。(但實(shí)際上MySQL的InnoDB在REPEATABLE-READ隔離級(jí)別下避免了幻讀的發(fā)生)
-
InnoDB可重復(fù)讀隔離級(jí)別下如何避免幻讀
-
表象
快照讀(非阻塞讀)--偽MVCC虏劲。使用此種機(jī)制避免使我們看到“幻行”托酸。
當(dāng)前讀:select ...lock in share mode,select ...for update,update,delete,insert.即加了鎖的增刪改查語(yǔ)句。由于其讀取的是記錄的最新版本伙单,所以稱為當(dāng)前讀获高。
快照讀:不加鎖的非阻塞讀(非SERIALIZABLE),select吻育∧钛恚基于提升并發(fā)性能的考慮,基于多版本并發(fā)控制布疼。既然是基于多版本摊趾,也就是說快照讀有可能讀到非最新版本的數(shù)據(jù)。
-
內(nèi)在
next-key鎖(行鎖+gap鎖):next-key鎖由兩部分組成游两,行鎖+gap鎖砾层,行鎖即對(duì)單個(gè)行記錄上的鎖。Gap鎖贱案,即對(duì)插入索引間的空隙上鎖肛炮,即鎖定一個(gè)范圍,但不包括記錄本身宝踪。Gap鎖的目的侨糟,是為了防止一個(gè)記錄兩次當(dāng)前讀出現(xiàn)幻讀的情況。Gap鎖只存在與Read-Committed(不包括Read-committed)以上的隔離級(jí)別存在瘩燥。如果查詢時(shí)秕重,where條件全部命中(精確查詢時(shí)),則不會(huì)用Gap鎖厉膀,只會(huì)加記錄鎖溶耘。因?yàn)樵诰_查詢的狀況下二拐,即使在讀結(jié)果集的過程中,另一個(gè)事務(wù)增加一條數(shù)據(jù)凳兵,也不會(huì)增加到當(dāng)前結(jié)果集下百新,只會(huì)在where條件的范圍之外,所以并不會(huì)產(chǎn)生幻讀現(xiàn)象留荔,加行鎖就足夠了吟孙。如果where條件部分命中或者全不命中,則會(huì)加Gap鎖聚蝶。
-
-
RC、RR級(jí)別下的InnoDB的非阻塞讀(快照讀)如何實(shí)現(xiàn)
1.數(shù)據(jù)行里的DB_TRX_ID藻治、DB_ROLL_PTR碘勉、DB_ROW_ID字段,DB_TRX_ID標(biāo)識(shí)最近一次對(duì)本行記錄做修改的事務(wù)ID桩卵,DB_ROLL_PTR回滾指針验靡,當(dāng)事務(wù)回滾時(shí),去往undo日志尋找上一版本的數(shù)據(jù)雏节,DB_ROW_ID行號(hào)(MySQL自動(dòng)創(chuàng)建的隱藏自增主鍵)胜嗓。
2.undo日志:存儲(chǔ)歷史版本的數(shù)據(jù)。當(dāng)某行的某個(gè)字段進(jìn)行修改時(shí)钩乍,首先用排他鎖鎖住該行辞州,然后將該行數(shù)據(jù)拷貝一份放入undolog中,通過行中的DB_ROLL_PTR指針寥粹,指向undolog中的這條數(shù)據(jù)变过,然后修改當(dāng)前行的值,并填寫DB_TRX_ID字段為當(dāng)前事務(wù)的ID涝涤。
3.read view:可見性判斷媚狰。決定當(dāng)前事務(wù)能看到的是哪個(gè)版本的數(shù)據(jù)。
結(jié)語(yǔ)
令人頭禿阔拳。
本文圖片來(lái)自網(wǎng)絡(luò)崭孤,侵刪。
歡迎大家訪問我的個(gè)人博客:Object's Blog