mySQL面試題
MySQL面試題
1.InnoDB索引和MyISAM索引的區(qū)別是什么?
- InnoDB的索引的索引在存儲(chǔ)的時(shí)候,他的索引是和數(shù)據(jù)是存儲(chǔ)在一個(gè)文件里的,MyisAm的索引是另外的一個(gè)文件,和數(shù)據(jù)不是在一個(gè)文件里。
- 索引文件所對(duì)應(yīng)的B+樹(shù)的最葉子結(jié)點(diǎn)記錄的數(shù)據(jù)有所區(qū)別,InnoDB里是存儲(chǔ)的數(shù)據(jù)行的Id,而MyIsAm里存儲(chǔ)的是指針,指向記錄所在頁(yè)的指針
2.一個(gè)表如果沒(méi)有創(chuàng)建索引,還會(huì)去創(chuàng)建B+樹(shù)么?
會(huì),表創(chuàng)建時(shí)會(huì)默認(rèn)創(chuàng)建一個(gè)聚簇索引樹(shù),沒(méi)有Id也會(huì)默認(rèn)生成一個(gè)rowid,然后生成聚簇索引樹(shù)
3.MySQL的binlog有有幾種錄入格式?分別有什么區(qū)別?
有三種格式,statement,row和mixed。
- statement模式下,每一條會(huì)修改數(shù)據(jù)的sql都會(huì)記錄在binlog中。不需要記錄每一行的變化,減少了binlog日志量,節(jié)約了IO,提高性能。由于sql的執(zhí)行是有上下文的,因此在保存的時(shí)候需要保存相關(guān)的信息,同時(shí)還有一些使用了函數(shù)之類(lèi)的語(yǔ)句無(wú)法被記錄復(fù)制。
- row級(jí)別下,不記錄sql語(yǔ)句上下文相關(guān)信息,僅保存哪條記錄被修改。記錄單元為每一行的改動(dòng),基本是可以全部記下來(lái)但是由于很多操作,會(huì)導(dǎo)致大量行的改動(dòng)(比如alter table),因此這種模式的文件保存的信息太多,日志量太大。
- mixed,一種折中的方案,普通操作使用statement記錄,當(dāng)無(wú)法使用statement的時(shí)候使用row。
4.InnoDB和MyisAm有什么區(qū)別?
主要有4個(gè)區(qū)別:
- 第一個(gè)是數(shù)據(jù)存儲(chǔ)的方式不同,MyisAm里的數(shù)據(jù)和索引是分開(kāi)存儲(chǔ)的,而InnoDB是把索引和數(shù)據(jù)文件存放在一個(gè)文件里面。
- 第二個(gè),對(duì)于事務(wù)的支持不同,MyisAm里不支持事務(wù),而InnODB里支持事務(wù)
- 第三,對(duì)于鎖的支持不同,MYisAm里只支持表鎖,而InnoDB里可以根據(jù)不同的情況去支持行鎖,表鎖,間隙鎖,臨鍵鎖。
- 第四,對(duì)外鍵的支持不同,MyisAm里不支持外鍵,而InnDB支持外鍵
5.InnoDB引擎的4大特性
- 插入緩存,Insert buffer
索引數(shù)據(jù)存儲(chǔ)在磁盤(pán)上,主鍵索引由于天然自增,無(wú)須磁盤(pán)的隨機(jī) I/O,只需不斷追加即可。但普通索引大概率無(wú)序,默認(rèn)情況下需要進(jìn)行隨機(jī)磁盤(pán) I/O 操作,效率極差
為了解決普通索引插入效率低下的問(wèn)題,InnoDB 存儲(chǔ)引擎引入 Insert Buffer 的概念,對(duì)于普通索引(非聚集索引)不是直接插入到索引頁(yè)中,而是先判斷插入的非聚集索引頁(yè)是否在緩存池中,
如果在直接插入,否則先放入 Insert buffer 對(duì)象中,然后以一定頻率和輔助索引頁(yè)子節(jié)點(diǎn)進(jìn)行合并操作,此時(shí)通常能將多個(gè)插入合并到一個(gè)操作中,提高插入性能
- 二次寫(xiě),double write
InnoDB 索引頁(yè)一般 16KB 大小,而操作系統(tǒng)寫(xiě)文件以 4KB 為單位,這就導(dǎo)致同一頁(yè)需要分四塊分別寫(xiě)入。此時(shí)就存在寫(xiě)完一塊系統(tǒng)崩潰或者斷電等特殊情況,此時(shí)就導(dǎo)致寫(xiě)入數(shù)據(jù)不完整的問(wèn)題
二次寫(xiě)就是為了解決該問(wèn)題,double write 分為兩部分,一部分 doublewrite buffer,其大小 2MB,另一部分是磁盤(pán)上共享表空間中連續(xù)的 128 個(gè)頁(yè),也是 2,B
每次刷盤(pán)時(shí)這樣處理:先將臟數(shù)據(jù)寫(xiě)入 doublewrite buffer,doublewrite buffer 每次 1MB 寫(xiě)入共享表空間的磁盤(pán)上,完成以上兩步后調(diào)用 fsync 函數(shù),將數(shù)據(jù)同步到各個(gè)表空間
如果操作系統(tǒng)在將頁(yè)寫(xiě)入磁盤(pán)的過(guò)程中崩潰,InnoDB 重啟發(fā)現(xiàn)頁(yè)數(shù)據(jù)損壞后,可以從共享表的 doublewrite 中找到副本,用于數(shù)據(jù)恢復(fù)
- 自適應(yīng)哈希索引,adaptive hash index
InnoDB 雖然主要使用 B+ 樹(shù)作為索引結(jié)構(gòu),但在某些特殊場(chǎng)景下用到哈希索引。InnoDB 會(huì)監(jiān)控對(duì)表上索引的查找,如果發(fā)現(xiàn)某個(gè)索引頻繁被訪問(wèn),則建立哈希索引。
InnoDB 會(huì)自動(dòng)根據(jù)訪問(wèn)的頻率和模式來(lái)為某些頁(yè)建立哈希索引
- 預(yù)讀,read ahead
當(dāng) InnoDB 預(yù)計(jì)某些頁(yè)很快就要被訪問(wèn)時(shí),會(huì)異步加載對(duì)應(yīng)頁(yè)數(shù)據(jù)到緩沖池。該思路就類(lèi)似空間局部性:如果某塊內(nèi)存被訪問(wèn),那么它周?chē)膬?nèi)存大概率也會(huì)被訪問(wèn)。
InnoDB 采用兩種預(yù)讀算法提高 I/O 性能:線性預(yù)讀 和 隨機(jī)預(yù)讀
線性預(yù)讀:以塊為單位,一塊等于64頁(yè)。如果某一塊中的被順序讀取的頁(yè)數(shù)超過(guò)預(yù)定值,則 InnoDB 將會(huì)異步的將下一塊讀取到 buffer pool 中
隨機(jī)預(yù)讀:以頁(yè)為單位,當(dāng)某一個(gè)塊中的一些頁(yè)在 buffer pool 中被發(fā)現(xiàn)時(shí),InnoDB 會(huì)將該塊中的剩余頁(yè)一并讀到 buffer pool 中,目前已廢棄
6.什么是索引?
- 索引是一種特殊的文件(InnoDB數(shù)據(jù)表上的索引是表空間的一個(gè)組成部分),它們包含著對(duì)數(shù)據(jù)表里所有記錄的引用指針。
- 索引是一種數(shù)據(jù)結(jié)構(gòu)。數(shù)據(jù)庫(kù)索引,是數(shù)據(jù)庫(kù)管理系統(tǒng)中一個(gè)排序的數(shù)據(jù)結(jié)構(gòu),以協(xié)助快速查詢、更新數(shù)據(jù)庫(kù)表中數(shù)據(jù)。索引的實(shí)現(xiàn)通常使用B樹(shù)及其變種B+樹(shù)。
- 更通俗的說(shuō),索引就相當(dāng)于目錄。為了方便查找書(shū)中的內(nèi)容,通過(guò)對(duì)內(nèi)容建立索引形成目錄。索引是一個(gè)文件,它是要占據(jù)物理空間的。
7.索引有哪幾種類(lèi)型?
- 主鍵索引: 數(shù)據(jù)列不允許重復(fù),不允許為NULL,一個(gè)表只能有一個(gè)主鍵。
- 唯一索引: 數(shù)據(jù)列不允許重復(fù),允許為NULL值,一個(gè)表允許多個(gè)列創(chuàng)建唯一索引。
- 普通索引: 基本的索引類(lèi)型,沒(méi)有唯一性的限制,允許為NULL值。
- 全文索引: 是目前搜索引擎使用的一種關(guān)鍵技術(shù)。
8.索引的數(shù)據(jù)結(jié)構(gòu)(b樹(shù),hash)
索引的數(shù)據(jù)結(jié)構(gòu)和具體存儲(chǔ)引擎的實(shí)現(xiàn)有關(guān),在MySQL中使用較多的索引有Hash索引,B+樹(shù)索引等,而我們經(jīng)常使用的InnoDB存儲(chǔ)引擎的默認(rèn)索引實(shí)現(xiàn)
為:B+樹(shù)索引。對(duì)于哈希索引來(lái)說(shuō),底層的數(shù)據(jù)結(jié)構(gòu)就是哈希表,因此在絕大多數(shù)需求為單條記錄查詢的時(shí)候,可以選擇哈希索引,查詢性能最快;其余大部
分場(chǎng)景,建議選擇BTree索引。
B樹(shù)索引
mysql通過(guò)存儲(chǔ)引擎取數(shù)據(jù),基本上90%的人用的就是InnoDB了,按照實(shí)現(xiàn)方式分,InnoDB的索引類(lèi)型目前只有兩種:BTREE(B樹(shù))索引和HASH索引。B
樹(shù)索引是Mysql數(shù)據(jù)庫(kù)中使用最頻繁的索引類(lèi)型,基本所有存儲(chǔ)引擎都支持BTree索引。通常我們說(shuō)的索引不出意外指的就是(B樹(shù))索引(實(shí)際是用B+樹(shù)
實(shí)現(xiàn)的,因?yàn)樵诓榭幢硭饕龝r(shí),mysql一律打印BTREE,所以簡(jiǎn)稱(chēng)為B樹(shù)索引)
查詢方式:
主鍵索引區(qū):PI(關(guān)聯(lián)保存的時(shí)數(shù)據(jù)的地址)按主鍵查詢,普通索引區(qū):si(關(guān)聯(lián)的id的地址,然后再到達(dá)上面的地址)。所以按主鍵查詢,速度最快
B+tree性質(zhì):
- n棵子tree的節(jié)點(diǎn)包含n個(gè)關(guān)鍵字,不用來(lái)保存數(shù)據(jù)而是保存數(shù)據(jù)的索引。
- 所有的葉子結(jié)點(diǎn)中包含了全部關(guān)鍵字的信息,及指向含這些關(guān)鍵字記錄的指針,且葉子結(jié)點(diǎn)本身依關(guān)鍵字的大小自小而大順序鏈接。
- 所有的非終端結(jié)點(diǎn)可以看成是索引部分,結(jié)點(diǎn)中僅含其子樹(shù)中的最大(或最小)關(guān)鍵字。
- B+ 樹(shù)中,數(shù)據(jù)對(duì)象的插入和刪除僅在葉節(jié)點(diǎn)上進(jìn)行。
- B+樹(shù)有2個(gè)頭指針,一個(gè)是樹(shù)的根節(jié)點(diǎn),一個(gè)是最小關(guān)鍵碼的葉節(jié)點(diǎn)。
哈希索引
簡(jiǎn)要說(shuō)下,類(lèi)似于數(shù)據(jù)結(jié)構(gòu)中簡(jiǎn)單實(shí)現(xiàn)的HASH表(散列表)一樣,當(dāng)我們?cè)趍ysql中用哈希索引時(shí),主要就是通過(guò)Hash算法(常見(jiàn)的Hash算法有直接定址
法、平方取中法、折疊法、除數(shù)取余法、隨機(jī)數(shù)法),將數(shù)據(jù)庫(kù)字段數(shù)據(jù)轉(zhuǎn)換成定長(zhǎng)的Hash值,與這條數(shù)據(jù)的行指針一并存入Hash表的對(duì)應(yīng)位置;如果發(fā)生
Hash碰撞(兩個(gè)不同關(guān)鍵字的Hash值相同),則在對(duì)應(yīng)Hash鍵下以鏈表形式存儲(chǔ)。
9.創(chuàng)建索引的原則(重中之重)
索引雖好,但也不是無(wú)限制的使用,最好符合一下幾個(gè)原則
- 最左前綴匹配原則,組合索引非常重要的原則,mysql會(huì)一直向右匹配直到遇到范圍查詢(>、<、between、like)就停止匹配,比如a = 1 and b = 2 and
c > 3 and d = 4 如果建立(a,b,c,d)順序的索引,d是用不到索引的,如果建立(a,b,d,c)的索引則都可以用到,a,b,d的順序可以任意調(diào)整。 - 較頻繁作為查詢條件的字段才去創(chuàng)建索引
- 更新頻繁字段不適合創(chuàng)建索引
- 若是不能有效區(qū)分?jǐn)?shù)據(jù)的列不適合做索引列(如性別,男女未知,最多也就三種,區(qū)分度實(shí)在太低
- 盡量的擴(kuò)展索引,不要新建索引。
- 定義有外鍵的數(shù)據(jù)列一定要建立索引。
- 對(duì)于那些查詢中很少涉及的列,重復(fù)值比較多的列不要建立索引。
- 對(duì)于定義為text、image和bit的數(shù)據(jù)類(lèi)型的列不要建立索引。
10.創(chuàng)建索引時(shí)需要注意什么?
- 非空字段:應(yīng)該指定列為NOT NULL,除非你想存儲(chǔ)NULL。在mysql中,含有空值的列很難進(jìn)行查詢優(yōu)化,因?yàn)樗鼈兪沟盟饕⑺饕慕y(tǒng)計(jì)信息以及比較運(yùn)算更加復(fù)雜。
你應(yīng)該用0、一個(gè)特殊的值或者一個(gè)空串代替空值;
- 取值離散大的字段:(變量各個(gè)取值之間的差異程度)的列放到聯(lián)合索引的前面,可以通過(guò)count()函數(shù)查看字段的差異值,返回值越大說(shuō)明字段的唯一值越多字
- 段的離散程度高:索引字段越小越好:數(shù)據(jù)庫(kù)的數(shù)據(jù)存儲(chǔ)以頁(yè)為單位一頁(yè)存儲(chǔ)的數(shù)據(jù)越多一次IO操作獲取的數(shù)據(jù)越大效率越高
11.使用索引查詢一定能提高查詢的性能嗎?為什么
索引需要空間來(lái)存儲(chǔ),也需要定期維護(hù), 每當(dāng)有記錄在表中增減或索引列被修改時(shí),索引本身也會(huì)被修改。 這意味著每條記錄的INSERT,DELETE,UPDATE將為
此多付出4,5 次的磁盤(pán)I/O。 因?yàn)樗饕枰~外的存儲(chǔ)空間和處理,那些不必要的索引反而會(huì)使查詢反應(yīng)時(shí)間變慢。使用索引查詢不一定能提高查詢性能,索引范圍查
詢(INDEX RANGE SCAN)適用于兩種情況:
- 基于一個(gè)范圍的檢索,一般查詢返回結(jié)果集小于表中記錄數(shù)的30%
- 基于非唯一性索引的檢索
12.什么是最左前綴原則?什么是最左匹配原則
顧名思義,就是最左優(yōu)先,在創(chuàng)建多列索引時(shí),要根據(jù)業(yè)務(wù)需求,where子句中使用最頻繁的一列放在最左邊。
最左前綴匹配原則,非常重要的原則,mysql會(huì)一直向右匹配直到遇到范圍查詢(>、<、between、like)就停止匹配,比如a = 1 and b = 2 and c > 3 and d = 4
如果建立(a,b,c,d)順序的索引,d是用不到索引的,如果建立(a,b,d,c)的索引則都可以用到,a,b,d的順序可以任意調(diào)整。
=和in可以亂序,比如a = 1 and b = 2 and c = 3 建立(a,b,c)索引可以任意順序,mysql的查詢優(yōu)化器會(huì)幫你優(yōu)化成索引可以識(shí)別的形式
13.B樹(shù)和B+樹(shù)的區(qū)別
- 在B樹(shù)中,你可以將鍵和值存放在內(nèi)部節(jié)點(diǎn)和葉子節(jié)點(diǎn);但在B+樹(shù)中,內(nèi)部節(jié)點(diǎn)都是鍵,沒(méi)有值,葉子節(jié)點(diǎn)同時(shí)存放鍵和值。
- B+樹(shù)的葉子節(jié)點(diǎn)有一條鏈相連,而B(niǎo)樹(shù)的葉子節(jié)點(diǎn)各自獨(dú)立
14.使用B+樹(shù)的好處
由于B+樹(shù)的內(nèi)部節(jié)點(diǎn)只存放鍵,不存放值,因此,一次讀取,可以在內(nèi)存頁(yè)中獲取更多的鍵,有利于更快地縮小查找范圍。 B+樹(shù)的葉節(jié)點(diǎn)由一條鏈相連,因
此,當(dāng)需要進(jìn)行一次全數(shù)據(jù)遍歷的時(shí)候,B+樹(shù)只需要使用O(logN)時(shí)間找到最小的一個(gè)節(jié)點(diǎn),然后通過(guò)鏈進(jìn)行O(N)的順序遍歷即可。而B(niǎo)樹(shù)則需要對(duì)樹(shù)的每一
層進(jìn)行遍歷,這會(huì)需要更多的內(nèi)存置換次數(shù),因此也就需要花費(fèi)更多的時(shí)間
15.Hash索引和B+樹(shù)所有有什么區(qū)別或者說(shuō)優(yōu)劣呢?
首先要知道Hash索引和B+樹(shù)索引的底層實(shí)現(xiàn)原理:
- hash索引底層就是hash表,進(jìn)行查找時(shí),調(diào)用一次hash函數(shù)就可以獲取到相應(yīng)的鍵值,之后進(jìn)行回表查詢獲得實(shí)際數(shù)據(jù)。B+樹(shù)底層實(shí)現(xiàn)是多路平衡查找樹(shù)。
- 對(duì)于每一次的查詢都是從根節(jié)點(diǎn)出發(fā),查找到葉子節(jié)點(diǎn)方可以獲得所查鍵值,然后根據(jù)查詢判斷是否需要回表查詢數(shù)據(jù)。
那么可以看出他們有以下的不同:
- hash索引進(jìn)行等值查詢更快(一般情況下),但是卻無(wú)法進(jìn)行范圍查詢。因?yàn)樵趆ash索引中經(jīng)過(guò)hash函數(shù)建立索引之后,索引的順序與原順序無(wú)法保持一致,不能支持范圍查詢。
- 而B(niǎo)+樹(shù)的的所有節(jié)點(diǎn)皆遵循(左節(jié)點(diǎn)小于父節(jié)點(diǎn),右節(jié)點(diǎn)大于父節(jié)點(diǎn),多叉樹(shù)也類(lèi)似),天然支持范圍。hash索引不支持使用索引進(jìn)行排序,原理同上。
- hash索引不支持模糊查詢以及多列索引的最左前綴匹配。原理也是因?yàn)閔ash函數(shù)的不可預(yù)測(cè)。AAAA和AAAAB的索引沒(méi)有相關(guān)性。
- hash索引任何時(shí)候都避免不了回表查詢數(shù)據(jù),而B(niǎo)+樹(shù)在符合某些條件(聚簇索引,覆蓋索引等)的時(shí)候可以只通過(guò)索引完成查詢。
- hash索引雖然在等值查詢上較快,但是不穩(wěn)定。性能不可預(yù)測(cè),當(dāng)某個(gè)鍵值存在大量重復(fù)的時(shí)候,發(fā)生hash碰撞,此時(shí)效率可能極差。而B(niǎo)+樹(shù)的查詢效率比較穩(wěn)
定,對(duì)于所有的查詢都是從根節(jié)點(diǎn)到葉子節(jié)點(diǎn),且樹(shù)的高度較低。因此,在大多數(shù)情況下,直接選擇B+樹(shù)索引可以獲得穩(wěn)定且較好的查詢速度。而不需要使用hash索引。
16.數(shù)據(jù)庫(kù)為什么使用B+樹(shù)而不是B樹(shù)?
- B樹(shù)只適合隨機(jī)檢索,而B(niǎo)+樹(shù)同時(shí)支持隨機(jī)檢索和順序檢索;B+樹(shù)空間利用率更高,可減少I(mǎi)/O次數(shù),磁盤(pán)讀寫(xiě)代價(jià)更低。一般來(lái)說(shuō),索引本身也很大,不可能全部存儲(chǔ)在內(nèi)存中,
因此索引往往以索引文件的形式存儲(chǔ)的磁盤(pán)上。這樣的話,索引查找過(guò)程中就要產(chǎn)生磁盤(pán)I/O消耗。B+樹(shù)的內(nèi)部結(jié)點(diǎn)并沒(méi)有指向關(guān)鍵字具體信息的指針,只是作為索引使用,
其內(nèi)部結(jié)點(diǎn)比B樹(shù)小,盤(pán)塊能容納的結(jié)點(diǎn)中關(guān)鍵字?jǐn)?shù)量更多,一次性讀入內(nèi)存中可以查找的關(guān)鍵字也就越多,相對(duì)的,IO讀寫(xiě)次數(shù)也就降低了。而IO讀寫(xiě)次數(shù)是影響索引檢索效率的最大因素; - B+樹(shù)的查詢效率更加穩(wěn)定。B樹(shù)搜索有可能會(huì)在非葉子結(jié)點(diǎn)結(jié)束,越靠近根節(jié)點(diǎn)的記錄查找時(shí)間越短,只要找到關(guān)鍵字即可確定記錄的存在,其性能等價(jià)于在關(guān)鍵字
全集內(nèi)做一次二分查找。而在B+樹(shù)中,順序檢索比較明顯,隨機(jī)檢索時(shí),任何關(guān)鍵字的查找都必須走一條從根節(jié)點(diǎn)到葉節(jié)點(diǎn)的路,所有關(guān)鍵字的查找路徑長(zhǎng)度相同,導(dǎo)
致每一個(gè)關(guān)鍵字的查詢效率相當(dāng)。 - B-樹(shù)在提高了磁盤(pán)IO性能的同時(shí)并沒(méi)有解決元素遍歷的效率低下的問(wèn)題。B+樹(shù)的葉子節(jié)點(diǎn)使用指針順序連接在一起,只要遍歷葉子節(jié)點(diǎn)就可以實(shí)現(xiàn)整棵樹(shù)的遍歷。
而且在數(shù)據(jù)庫(kù)中基于范圍的查詢是非常頻繁的,而B(niǎo)樹(shù)不支持這樣的操作。增刪文件(節(jié)點(diǎn))時(shí),效率更高。因?yàn)锽+樹(shù)的葉子節(jié)點(diǎn)包含所有關(guān)鍵字,并以
有序的鏈表結(jié)構(gòu)存儲(chǔ),這樣可很好提高增刪效率。
17.什么是聚簇索引?何時(shí)使用聚簇索引與非聚簇索引
聚簇索引:將數(shù)據(jù)存儲(chǔ)與索引放到了一塊,找到索引也就找到了數(shù)據(jù)
非聚簇索引:將數(shù)據(jù)存儲(chǔ)于索引分開(kāi)結(jié)構(gòu),索引結(jié)構(gòu)的葉子節(jié)點(diǎn)指向了數(shù)據(jù)的對(duì)應(yīng)行,myisam通過(guò)key_buffer把索引先緩存到內(nèi)存中,當(dāng)需要訪問(wèn)數(shù)據(jù)時(shí)(通過(guò)索
引訪問(wèn)數(shù)據(jù)),在內(nèi)存中直接搜索索引,然后通過(guò)索引找到磁盤(pán)相應(yīng)數(shù)據(jù),這也就是為什么索引不在key buffer命中時(shí),速度慢的原因
澄清一個(gè)概念:innodb中,在聚簇索引之上創(chuàng)建的索引稱(chēng)之為輔助索引,輔助索引訪問(wèn)數(shù)據(jù)總是需要二次查找,非聚簇索引都是輔助索引,像復(fù)合索引、前綴
索引、唯一索引,輔助索引葉子節(jié)點(diǎn)存儲(chǔ)的不再是行的物理位置,而是主鍵值何時(shí)使用聚簇索引與非聚簇索引
18.非聚簇索引一定會(huì)回表查詢嗎?
不一定,這涉及到查詢語(yǔ)句所要求的字段是否全部命中了索引,如果全部命中了索引,那么就不必再進(jìn)行回表查詢。
舉個(gè)簡(jiǎn)單的例子,假設(shè)我們?cè)趩T工表的年齡上建立了索引,那么當(dāng)進(jìn)行selectage from employee where age < 20的查詢時(shí),在索引的葉子節(jié)點(diǎn)上,已經(jīng)包含了
age信息,不會(huì)再次進(jìn)行回表查詢。
19.什么是數(shù)據(jù)庫(kù)事務(wù)?
事務(wù)是一個(gè)不可分割的數(shù)據(jù)庫(kù)操作序列,也是數(shù)據(jù)庫(kù)并發(fā)控制的基本單位,其執(zhí)行的結(jié)果必須使數(shù)據(jù)庫(kù)從一種一致性狀態(tài)變到另一種一致性狀態(tài)。事務(wù)是邏輯上
的一組操作,要么都執(zhí)行,要么都不執(zhí)行
20.事務(wù)的四大特性(ACID)介紹一下?
http://www.rzrgm.cn/hualou/p/12076017.html
- 原子性: 事務(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ì)其有任何影響。
21.什么是臟讀?幻讀?不可重復(fù)讀?
- 臟讀(Drity Read):某個(gè)事務(wù)已更新一份數(shù)據(jù),另一個(gè)事務(wù)在此時(shí)讀取了同一份數(shù)據(jù),由于某些原因,前一個(gè)RollBack了操作,則后一個(gè)事務(wù)所讀取的數(shù)據(jù)就會(huì)是不正確的。
- 不可重復(fù)讀(Non-repeatable read):在一個(gè)事務(wù)的兩次查詢之中數(shù)據(jù)不一致,這可能是兩次查詢過(guò)程中間插入了一個(gè)事務(wù)更新的原有的數(shù)據(jù)
- 幻讀(Phantom Read):一個(gè)事務(wù)在前后兩次查詢同一個(gè)范圍的時(shí)候、后一次查詢看到了前一次查詢未看到的行.
22.什么是事務(wù)的隔離級(jí)別?MySQL的默認(rèn)隔離級(jí)別是什么?
一個(gè)事務(wù)必須與由其他事務(wù)進(jìn)行的資源或數(shù)據(jù)更改相隔離的程度。
- READ-UNCOMMITTED(讀取未提交): 最低的隔離級(jí)別,允許讀取尚未提交的數(shù)據(jù)變更,可能會(huì)導(dǎo)致臟讀、幻讀或不可重復(fù)讀。
- READ-COMMITTED(讀取已提交): 允許讀取并發(fā)事務(wù)已經(jīng)提交的數(shù)據(jù),可以阻止臟讀,但是幻讀或不可重復(fù)讀仍有可能發(fā)生。
- REPEATABLE-READ(可重復(fù)讀): 對(duì)同一字段的多次讀取結(jié)果都是一致的,除非數(shù)據(jù)是被本身事務(wù)自己所修改,可以阻止臟讀和不可重復(fù)讀,但幻讀仍有可能發(fā)生。
- SERIALIZABLE(可串行化): 最高的隔離級(jí)別,完全服從ACID的隔離級(jí)別。所有的事務(wù)依次逐個(gè)執(zhí)行,這樣事務(wù)之間就完全不可能產(chǎn)生干擾,也就是說(shuō),該
級(jí)別可以防止臟讀、不可重復(fù)讀以及幻讀。
這里需要注意的是:Mysql 默認(rèn)采用的 REPEATABLE_READ隔離級(jí)別
事務(wù)隔離機(jī)制的實(shí)現(xiàn)基于鎖機(jī)制和并發(fā)調(diào)度。其中并發(fā)調(diào)度使用的是MVVC(多版本并發(fā)控制),通過(guò)保存修改的舊版本信息來(lái)支持并發(fā)一致性讀和回滾等特性。
因?yàn)楦綦x級(jí)別越低,事務(wù)請(qǐng)求的鎖越少,所以大部分?jǐn)?shù)據(jù)庫(kù)系統(tǒng)的隔離級(jí)別都是READ-COMMITTED(讀取提交內(nèi)容):,但是你要知道的是InnoDB 存儲(chǔ)引擎默
認(rèn)使用 **REPEATABLE-READ(可重讀)**并不會(huì)有任何性能損失。
23.按照鎖的粒度分?jǐn)?shù)據(jù)庫(kù)鎖有哪些?鎖機(jī)制與InnoDB鎖算法
關(guān)系型數(shù)據(jù)庫(kù)中,可以按照鎖的粒度把數(shù)據(jù)庫(kù)鎖分為行級(jí)鎖(INNODB引擎)、表級(jí)鎖(MYISAM引擎)和頁(yè)級(jí)鎖(BDB引擎 )。
MyISAM和InnoDB存儲(chǔ)引擎使用的鎖:行級(jí)鎖,表級(jí)鎖和頁(yè)級(jí)鎖對(duì)比
行級(jí)鎖:行級(jí)鎖是Mysql中鎖定粒度最細(xì)的一種鎖,表示只針對(duì)當(dāng)前操作的行進(jìn)行加鎖。行級(jí)鎖能大大減少數(shù)據(jù)庫(kù)操作的沖突。其加鎖粒度最小,但加鎖的開(kāi)銷(xiāo)也最大。行級(jí)鎖分為共享鎖 和 排他鎖。
特點(diǎn):開(kāi)銷(xiāo)大,加鎖慢;會(huì)出現(xiàn)死鎖;鎖定粒度最小,發(fā)生鎖沖突的概率最低,并發(fā)度也最高。
表級(jí)鎖:表級(jí)鎖是MySQL中鎖定粒度最大的一種鎖,表示對(duì)當(dāng)前操作的整張表加鎖,它實(shí)現(xiàn)簡(jiǎn)單,資源消耗較少,被大部分MySQL引擎支持。最常使用的MYISAM與INNODB都支持表級(jí)鎖定。
表級(jí)鎖定分為表共享讀鎖(共享鎖)與表獨(dú)占寫(xiě)鎖(排他鎖)。
特點(diǎn):開(kāi)銷(xiāo)小,加鎖快;不會(huì)出現(xiàn)死鎖;鎖定粒度大,發(fā)出鎖沖突的概率最高,并發(fā)度最低。
頁(yè)級(jí)鎖:頁(yè)級(jí)鎖是MySQL中鎖定粒度介于行級(jí)鎖和表級(jí)鎖中間的一種鎖。表級(jí)鎖速度快,但沖突多,行級(jí)沖突少,但速度慢。所以取了折衷的頁(yè)級(jí),一次鎖定相鄰的一組記錄。
特點(diǎn):開(kāi)銷(xiāo)和加鎖時(shí)間界于表鎖和行鎖之間;會(huì)出現(xiàn)死鎖;鎖定粒度界于表鎖和行鎖之間,并發(fā)度一般
24.從鎖的類(lèi)別上分MySQL都有哪些鎖呢?
從鎖的類(lèi)別上來(lái)講,有共享鎖和排他鎖。
- 共享鎖: 又叫做讀鎖。 當(dāng)用戶要進(jìn)行數(shù)據(jù)的讀取時(shí),對(duì)數(shù)據(jù)加上共享鎖。共享鎖可以同時(shí)加上多個(gè)。
- 排他鎖: 又叫做寫(xiě)鎖。 當(dāng)用戶要進(jìn)行數(shù)據(jù)的寫(xiě)入時(shí),對(duì)數(shù)據(jù)加上排他鎖。排他鎖
只可以加一個(gè),他和其他的排他鎖,共享鎖都相斥。
鎖的粒度取決于具體的存儲(chǔ)引擎,InnoDB實(shí)現(xiàn)了行級(jí)鎖,頁(yè)級(jí)鎖,表級(jí)鎖。他們的加鎖開(kāi)銷(xiāo)從大到小,并發(fā)能力也是從大到小
25.MySQL中InnoDB引擎的行鎖是怎么實(shí)現(xiàn)的?
InnoDB是基于索引來(lái)完成行鎖
例: select * from tab_with_index where id = 1 for update;
for update 可以根據(jù)條件來(lái)完成行鎖鎖定,并且 id 是有索引鍵的列,如果 id不是索引鍵那么InnoDB將完成表鎖,并發(fā)將無(wú)從談起
InnoDB存儲(chǔ)引擎的鎖的算法有三種
Record lock:?jiǎn)蝹€(gè)行記錄上的鎖
Gap lock:間隙鎖,鎖定一個(gè)范圍,不包括記錄本身
Next-key lock:record+gap 鎖定一個(gè)范圍,包含記錄本身
26.數(shù)據(jù)庫(kù)的樂(lè)觀鎖和悲觀鎖是什么?怎么實(shí)現(xiàn)的?
- 悲觀鎖:假定會(huì)發(fā)生并發(fā)沖突,屏蔽一切可能違反數(shù)據(jù)完整性的操作。在查詢完數(shù)據(jù)的時(shí)候就把事務(wù)鎖起來(lái),直到提交事務(wù)。實(shí)現(xiàn)方式:使用數(shù)據(jù)庫(kù)中的鎖機(jī)制
- 樂(lè)觀鎖:假設(shè)不會(huì)發(fā)生并發(fā)沖突,只在提交操作時(shí)檢查是否違反數(shù)據(jù)完整性。在修改數(shù)據(jù)的時(shí)候把事務(wù)鎖起來(lái),通過(guò)version的方式來(lái)進(jìn)行鎖定。實(shí)現(xiàn)方式:樂(lè)一般會(huì)使用版本號(hào)機(jī)制或CAS算法實(shí)現(xiàn)。
兩種鎖的使用場(chǎng)景
從上面對(duì)兩種鎖的介紹,我們知道兩種鎖各有優(yōu)缺點(diǎn),不可認(rèn)為一種好于另一種,像樂(lè)觀鎖適用于寫(xiě)比較少的情況下(多讀場(chǎng)景),即沖突真的很少發(fā)生的時(shí)
候,這樣可以省去了鎖的開(kāi)銷(xiāo),加大了系統(tǒng)的整個(gè)吞吐量。但如果是多寫(xiě)的情況,一般會(huì)經(jīng)常產(chǎn)生沖突,這就會(huì)導(dǎo)致上層應(yīng)用會(huì)不斷的進(jìn)行retry,這樣反倒是降低了性能,所以一般多寫(xiě)的場(chǎng)景下用悲觀鎖就比較合適。
27.mysql中 in 和 exists 區(qū)別
mysql中的in語(yǔ)句是把外表和內(nèi)表作hash 連接,而exists語(yǔ)句是對(duì)外表作loop循環(huán),每次loop循環(huán)再對(duì)內(nèi)表進(jìn)行查詢。一直大家都認(rèn)為exists比in語(yǔ)句的效率
要高,這種說(shuō)法其實(shí)是不準(zhǔn)確的。這個(gè)是要區(qū)分環(huán)境的。
1. 如果查詢的兩個(gè)表大小相當(dāng),那么用in和exists差別不大。
2. 如果兩個(gè)表中一個(gè)較小,一個(gè)是大表,則子查詢表大的用exists,子查詢表小的用in。
3. not in 和not exists:如果查詢語(yǔ)句使用了not in,那么內(nèi)外表都進(jìn)行全表掃描,沒(méi)有用到索引;而not extsts的子查詢依然能用到表上的索
引。所以無(wú)論那個(gè)表大,用not exists都比not in要快
28.varchar與char的區(qū)別
char的特點(diǎn)
- char表示定長(zhǎng)字符串,長(zhǎng)度是固定的;
- 如果插入數(shù)據(jù)的長(zhǎng)度小于char的固定長(zhǎng)度時(shí),則用空格填充
- 因?yàn)殚L(zhǎng)度固定,所以存取速度要比varchar快很多,甚至能快50%,但正因?yàn)槠溟L(zhǎng)度固定,所以會(huì)占據(jù)多余的空間,是空間換時(shí)間的做法;
varchar的特點(diǎn)
- varchar表示可變長(zhǎng)字符串,長(zhǎng)度是可變的;插入的數(shù)據(jù)是多長(zhǎng),就按照多長(zhǎng)來(lái)存儲(chǔ);
- varchar在存取方面與char相反,它存取慢,因?yàn)殚L(zhǎng)度不固定,但正因如此,不占據(jù)多余的空間,是時(shí)間換空間的做法;
- 對(duì)于varchar來(lái)說(shuō),最多能存放的字符個(gè)數(shù)為65532總之,結(jié)合性能角度(char更快)和節(jié)省磁盤(pán)空間角度(varchar更小),具體
情況還需具體來(lái)設(shè)計(jì)數(shù)據(jù)庫(kù)才是妥當(dāng)?shù)淖龇ā?/span>
29.如何定位及優(yōu)化SQL語(yǔ)句的性能問(wèn)題?創(chuàng)建的索引有沒(méi)有被使用到?或者說(shuō)怎么才可以知道這條語(yǔ)句運(yùn)行很慢的原因?
對(duì)于低性能的SQL語(yǔ)句的定位,最重要也是最有效的方法就是使用執(zhí)行計(jì)劃,MySQL提供了explain命令來(lái)查看語(yǔ)句的執(zhí)行計(jì)劃。 我們知道,不管是哪種數(shù)
據(jù)庫(kù),或者是哪種數(shù)據(jù)庫(kù)引擎,在對(duì)一條SQL語(yǔ)句進(jìn)行執(zhí)行的過(guò)程中都會(huì)做很多相關(guān)的優(yōu)化,對(duì)于查詢語(yǔ)句,最重要的優(yōu)化方式就是使用索引。 而執(zhí)行計(jì)劃,
就是顯示數(shù)據(jù)庫(kù)引擎對(duì)于SQL語(yǔ)句的執(zhí)行的詳細(xì)情況,其中包含了是否使用索引,使用什么索引,使用的索引的相關(guān)信息等。
id :表示一個(gè)查詢中各個(gè)子查詢的執(zhí)行順序
- id相同執(zhí)行順序由上至下。
- id不同,id值越大優(yōu)先級(jí)越高,越先被執(zhí)行。
- id為null時(shí)表示一個(gè)結(jié)果集,不需要使用它查詢,常出現(xiàn)在包含union等查詢語(yǔ)句中。
select_type:每個(gè)子查詢的查詢類(lèi)型,一些常見(jiàn)的查詢類(lèi)型。例如Simple,primary,subquery,union,derived等。
type:訪問(wèn)類(lèi)型,可以看到是否走索引.此列表示關(guān)聯(lián)類(lèi)型或訪問(wèn)類(lèi)型。也就是MySQL決定如何查找表中的行。依次從最優(yōu)到最差分別為:system > const > eq_ref > ref > range > index > all。
- system、const:MySQL對(duì)查詢的某部分進(jìn)行優(yōu)化并把其轉(zhuǎn)化成一個(gè)常量(可以通過(guò)show warnings命令查看結(jié)果)。system是const的一個(gè)特例,表示表里只有一條元組匹配時(shí)為system。
- eq_ref:主鍵或唯一鍵索引被連接使用,最多只會(huì)返回一條符合條件的記錄。簡(jiǎn)單的select查詢不會(huì)出現(xiàn)這種type。
- ref:相比eq_ref,不使用唯一索引,而是使用普通索引或者唯一索引的部分前綴,索引和某個(gè)值比較,會(huì)找到多個(gè)符合條件的行。
- range:通常出現(xiàn)在范圍查詢中,比如in、between、大于、小于等。使用索引來(lái)檢索給定范圍的行。
- index:掃描全索引拿到結(jié)果,一般是掃描某個(gè)二級(jí)索引,二級(jí)索引一般比較少,所以通常比ALL快一點(diǎn)。
- ALL:全表掃描,掃描聚簇索引的所有葉子節(jié)點(diǎn)。
possible_keys:此列顯示在查詢中可能用到的索引。如果該列為NULL,則表示沒(méi)有相關(guān)索引,可以通過(guò)檢查where子句看是否可以添加一個(gè)適當(dāng)?shù)乃饕齺?lái)提高性能。
key:此列顯示MySQL在查詢時(shí)實(shí)際用到的索引。在執(zhí)行計(jì)劃中可能出現(xiàn)possible_keys列有值,而key列為null,這種情況可能是表中數(shù)據(jù)不多,
MySQL認(rèn)為索引對(duì)當(dāng)前查詢幫助不大而選擇了全表查詢。如果想強(qiáng)制MySQL使用或忽視possible_keys列中的索引,在查詢時(shí)可使用force index、ignore index。
key_len: 此列顯示MySQL在索引里使用的字節(jié)數(shù),通過(guò)此列可以算出具體使用了索引中的那些列。索引最大長(zhǎng)度為768字節(jié),當(dāng)長(zhǎng)度過(guò)大時(shí),MySQL會(huì)做一個(gè)類(lèi)似最左前綴處理,
將前半部分字符提取出做索引。當(dāng)字段可以為null時(shí),還需要1個(gè)字節(jié)去記錄。
key_len計(jì)算規(guī)則:
- 字符串:char(n):n個(gè)數(shù)字或者字母占n個(gè)字節(jié),漢字占3n個(gè)字
- varchar(n): n個(gè)數(shù)字或者字母占n個(gè)字節(jié),漢字占3n+2個(gè)字節(jié)。+2字節(jié)用來(lái)存儲(chǔ)字符串長(zhǎng)度。
數(shù)字類(lèi)型:
- tinyint:1字節(jié) smallint:2字節(jié) int:4字節(jié) bigint:8字節(jié)
時(shí)間類(lèi)型
- date:3字節(jié) timestamp:4字節(jié) datetime:8字節(jié)
ref: 此列顯示key列記錄的索引中,表查找值時(shí)使用到的列或常量。常見(jiàn)的有const、字段名
rows: 此列是MySQL在查詢中估計(jì)要讀取的行數(shù)。注意這里不是結(jié)果集的行數(shù)。
Extra:
此列是一些額外信息。常見(jiàn)的重要值如下:
1)Using index:使用覆蓋索引(如果select后面查詢的字段都可以從這個(gè)索引的樹(shù)中獲取,不需要通過(guò)輔助索引樹(shù)找到主鍵,再通過(guò)主鍵去主鍵索引樹(shù)里獲取其它字段值,這種情況一般可以說(shuō)是用到了覆蓋索引)。
2)Using where:使用 where 語(yǔ)句來(lái)處理結(jié)果,并且查詢的列未被索引覆蓋。
3)Using index condition:查詢的列不完全被索引覆蓋,where條件中是一個(gè)查詢的范圍。
4)Using temporary:MySQL需要?jiǎng)?chuàng)建一張臨時(shí)表來(lái)處理查詢。出現(xiàn)這種情況一般是要進(jìn)行優(yōu)化的。
5)Using filesort:將使用外部排序而不是索引排序,數(shù)據(jù)較小時(shí)從內(nèi)存排序,否則需要在磁盤(pán)完成排序。
6)Select tables optimized away:使用某些聚合函數(shù)(比如 max、min)來(lái)訪問(wèn)存在索引的某個(gè)字段時(shí)。
30.大表數(shù)據(jù)查詢,怎么優(yōu)化
- 優(yōu)化shema、sql語(yǔ)句+索引;第二加緩存,memcached, redis;
- 主從復(fù)制,讀寫(xiě)分離;
- 垂直拆分,根據(jù)你模塊的耦合度,將一個(gè)大的系統(tǒng)分為多個(gè)小的系統(tǒng),也就是分布式系統(tǒng);
- 水平切分,針對(duì)數(shù)據(jù)量大的表,這一步最麻煩,最能考驗(yàn)技術(shù)水平,要選擇一個(gè)合理的sharding key, 為了有好的查詢效率,表結(jié)構(gòu)也要改動(dòng),
- 做一定的冗余,應(yīng)用也要改,sql中盡量帶sharding key,將數(shù)據(jù)定位到限定的表上去查,而不是掃描全部的表
31.超大分頁(yè)怎么處理?
超大的分頁(yè)一般從兩個(gè)方向上來(lái)解決.
數(shù)據(jù)庫(kù)層面,這也是我們主要集中關(guān)注的(雖然收效沒(méi)那么大),類(lèi)似于select *from table where age > 20 limit 1000000,10這種查詢其實(shí)也是有可以優(yōu)化的余地
的. 這條語(yǔ)句需要load1000000數(shù)據(jù)然后基本上全部丟棄,只取10條當(dāng)然比較慢. 當(dāng)時(shí)我們可以修改為select * from table where id in (select id from table where age
> 20 limit 1000000,10).這樣雖然也load了一百萬(wàn)的數(shù)據(jù),但是由于索引覆蓋,要查詢的所有字段都在索引中,所以速度會(huì)很快. 同時(shí)如果ID連續(xù)的好,我們還可以select *
from table where id > 1000000 limit 10,效率也是不錯(cuò)的,優(yōu)化的可能性有許多種,但是核心思想都一樣,就是減少load的數(shù)據(jù).從需求的角度減少這種請(qǐng)求…
主要是不做類(lèi)似的需求(直接跳轉(zhuǎn)到幾百萬(wàn)頁(yè)之后的具體某一頁(yè).只允許逐頁(yè)查看或者按照給定的路線走,這樣可預(yù)測(cè),可緩存)以及防止ID泄漏且連續(xù)被人惡意攻擊.
32.主鍵使用自增ID還是UUID?
推薦使用自增ID,不要使用UUID。
因?yàn)樵贗nnoDB存儲(chǔ)引擎中,主鍵索引是作為聚簇索引存在的,也就是說(shuō),主鍵索引的B+樹(shù)葉子節(jié)點(diǎn)上存儲(chǔ)了主鍵索引以及全部的數(shù)據(jù)(按照順序),如果主鍵索
引是自增ID,那么只需要不斷向后排列即可,如果是UUID,由于到來(lái)的ID與原來(lái)的大小不確定,會(huì)造成非常多的數(shù)據(jù)插入,數(shù)據(jù)移動(dòng),然后導(dǎo)致產(chǎn)生很多的內(nèi)
存碎片,進(jìn)而造成插入性能的下降。總之,在數(shù)據(jù)量大一些的情況下,用自增主鍵性能會(huì)好一些。關(guān)于主鍵是聚簇索引,如果沒(méi)有主鍵,InnoDB會(huì)選擇一個(gè)唯一鍵來(lái)作為聚簇索
引,如果沒(méi)有唯一鍵,會(huì)生成一個(gè)隱式的主鍵
33.mysql索引失效的場(chǎng)景?
- 不符合最左匹配原則:
例如有一條sql:select * from table where a =1 and b = 1 and c = 1; 假如我們創(chuàng)建了一個(gè)(a,b,c)的索引,假如我們想用這個(gè)索引,則一定要有a字段的查詢條件,因?yàn)閍是索引的最左邊字段
2.不正確的like查詢:%寫(xiě)在左邊會(huì)導(dǎo)致索引失效
例: select * from table where name like '%tom';
3.對(duì)索引列進(jìn)行了函數(shù)計(jì)算:
例: select * from table where a + 1 = 2;
4.索引列進(jìn)行了類(lèi)型轉(zhuǎn)換:表中是varchar類(lèi)型,傳進(jìn)來(lái)的是數(shù)值會(huì)導(dǎo)致索引失效
5.不等于也會(huì)導(dǎo)致索引失效:
6.錯(cuò)誤的order By 會(huì)導(dǎo)致索引失效:
7. or 連接查詢條件會(huì)導(dǎo)致索引失效
8.select * 會(huì)導(dǎo)致索引失效,而select b 不會(huì)
9.范圍查詢的數(shù)據(jù)量過(guò)大也可能導(dǎo)致索引失效,Mysql預(yù)估全表掃描快的話,也不會(huì)走索引
10.條件中使用is null,或者is not null會(huì)導(dǎo)致索引失效
34.什么是MVCC?
https://www.bilibili.com/video/BV1AW4y1g75F/?spm_id_from=333.337.search-card.all.click&vd_source=912db34243165a8f1c8423819c1adaf4

浙公網(wǎng)安備 33010602011771號(hào)