本文主要受衆爲開發人員,因此不涉及到MySQL的服務部署等操做,且內容較多,你們準備好耐心和瓜子礦泉水.mysql
前一陣系統的學習了一下MySQL,也有一些實際操做經驗,偶然看到一篇和MySQL相關的面試文章,發現其中的一些問題本身也回答很差,雖然知識點大部分都知道,可是沒法將知識串聯起來.web
所以決定搞一個MySQL靈魂100問,試着用回答問題的方式,讓本身對知識點的理解更加深刻一點.面試
此文不會事無鉅細的從select的用法開始講解mysql,主要針對的是開發人員須要知道的一些MySQL的知識點,主要包括索引,事務,優化等方面,以在面試中高頻的問句形式給出答案.redis
1. 什麼是索引?sql
索引是一種數據結構,能夠幫助咱們快速的進行數據的查找.數據庫
2. 索引是個什麼樣的數據結構呢?編程
索引的數據結構和具體存儲引擎的實現有關, 在MySQL中使用較多的索引有Hash索引,B+樹索引等,而咱們常用的InnoDB存儲引擎的默認索引實現爲:B+樹索引.緩存
3. Hash索引和B+樹全部有什麼區別或者說優劣呢?安全
首先要知道Hash索引和B+樹索引的底層實現原理:微信
hash索引底層就是hash表,進行查找時,調用一次hash函數就能夠獲取到相應的鍵值,以後進行回表查詢得到實際數據.B+樹底層實現是多路平衡查找樹.對於每一次的查詢都是從根節點出發,查找到葉子節點方能夠得到所查鍵值,而後根據查詢判斷是否須要回表查詢數據.
那麼能夠看出他們有如下的不一樣:
由於在hash索引中通過hash函數創建索引以後,索引的順序與原順序沒法保持一致,不能支持範圍查詢.而B+樹的的全部節點皆遵循(左節點小於父節點,右節點大於父節點,多叉樹也相似),自然支持範圍.
所以,在大多數狀況下,直接選擇B+樹索引能夠得到穩定且較好的查詢速度.而不須要使用hash索引.
4. 上面提到了B+樹在知足聚簇索引和覆蓋索引的時候不須要回表查詢數據,什麼是聚簇索引?
在B+樹的索引中,葉子節點可能存儲了當前的key值,也可能存儲了當前的key值以及整行的數據,這就是聚簇索引和非聚簇索引. 在InnoDB中,只有主鍵索引是聚簇索引,若是沒有主鍵,則挑選一個惟一鍵創建聚簇索引.若是沒有惟一鍵,則隱式的生成一個鍵來創建聚簇索引.
當查詢使用聚簇索引時,在對應的葉子節點,能夠獲取到整行數據,所以不用再次進行回表查詢.
5. 非聚簇索引必定會回表查詢嗎?
不必定,這涉及到查詢語句所要求的字段是否所有命中了索引,若是所有命中了索引,那麼就沒必要再進行回表查詢.
舉個簡單的例子,假設咱們在員工表的年齡上創建了索引,那麼當進行select age from employee where age < 20
的查詢時,在索引的葉子節點上,已經包含了age信息,不會再次進行回表查詢.
6. 在創建索引的時候,都有哪些須要考慮的因素呢?
創建索引的時候通常要考慮到字段的使用頻率,常常做爲條件進行查詢的字段比較適合.若是須要創建聯合索引的話,還須要考慮聯合索引中的順序.此外也要考慮其餘方面,好比防止過多的全部對錶形成太大的壓力.這些都和實際的表結構以及查詢方式有關.
7. 聯合索引是什麼?爲何須要注意聯合索引中的順序?
MySQL可使用多個字段同時創建一個索引,叫作聯合索引.在聯合索引中,若是想要命中索引,須要按照創建索引時的字段順序挨個使用,不然沒法命中索引.
具體緣由爲:
MySQL使用索引時須要索引有序,假設如今創建了"name,age,school"的聯合索引,那麼索引的排序爲: 先按照name排序,若是name相同,則按照age排序,若是age的值也相等,則按照school進行排序.
當進行查詢時,此時索引僅僅按照name嚴格有序,所以必須首先使用name字段進行等值查詢,以後對於匹配到的列而言,其按照age字段嚴格有序,此時可使用age字段用作索引查找,,,以此類推.所以在創建聯合索引的時候應該注意索引列的順序,通常狀況下,將查詢需求頻繁或者字段選擇性高的列放在前面.此外能夠根據特例的查詢或者表結構進行單獨的調整.
8. 建立的索引有沒有被使用到?或者說怎麼才能夠知道這條語句運行很慢的緣由?
MySQL提供了explain命令來查看語句的執行計劃,MySQL在執行某個語句以前,會將該語句過一遍查詢優化器,以後會拿到對語句的分析,也就是執行計劃,其中包含了許多信息.能夠經過其中和索引有關的信息來分析是否命中了索引,例如possilbe_key,key,key_len等字段,分別說明了此語句可能會使用的索引,實際使用的索引以及使用的索引長度.
9. 那麼在哪些狀況下會發生針對該列建立了索引可是在查詢的時候並無使用呢?
以上狀況,MySQL沒法使用索引.
1. 什麼是事務?
理解什麼是事務最經典的就是轉帳的栗子,相信你們也都瞭解,這裏就再也不說一邊了.
事務是一系列的操做,他們要符合ACID特性.最多見的理解就是:事務中的操做要麼所有成功,要麼所有失敗.可是隻是這樣還不夠的.
2. ACID是什麼?能夠詳細說一下嗎?
A=Atomicity
原子性,就是上面說的,要麼所有成功,要麼所有失敗.不可能只執行一部分操做.
C=Consistency
系統(數據庫)老是從一個一致性的狀態轉移到另外一個一致性的狀態,不會存在中間狀態.
I=Isolation
隔離性: 一般來講:一個事務在徹底提交以前,對其餘事務是不可見的.注意前面的一般來講加了紅色,意味着有例外狀況.
D=Durability
持久性,一旦事務提交,那麼就永遠是這樣子了,哪怕系統崩潰也不會影響到這個事務的結果.
3. 同時有多個事務在進行會怎麼樣呢?
多事務的併發進行通常會形成如下幾個問題:
4. 怎麼解決這些問題呢?MySQL的事務隔離級別瞭解嗎?
MySQL的四種隔離級別以下:
這就是上面所說的例外狀況了,這個隔離級別下,其餘事務能夠看到本事務沒有提交的部分修改.所以會形成髒讀的問題(讀取到了其餘事務未提交的部分,而以後該事務進行了回滾).
這個級別的性能沒有足夠大的優點,可是又有不少的問題,所以不多使用.
其餘事務只能讀取到本事務已經提交的部分.這個隔離級別有 不可重複讀的問題,在同一個事務內的兩次讀取,拿到的結果居然不同,由於另一個事務對數據進行了修改.
可重複讀隔離級別解決了上面不可重複讀的問題(看名字也知道),可是仍然有一個新問題,就是 幻讀,當你讀取id> 10 的數據行時,對涉及到的全部行加上了讀鎖,此時例外一個事務新插入了一條id=11的數據,由於是新插入的,因此不會觸發上面的鎖的排斥,那麼進行本事務進行下一次的查詢時會發現有一條id=11的數據,而上次的查詢操做並無獲取到,再進行插入就會有主鍵衝突的問題.
這是最高的隔離級別,能夠解決上面提到的全部問題,由於他強制將因此的操做串行執行,這會致使併發性能極速降低,所以也不是很經常使用.
5. Innodb使用的是哪一種隔離級別呢?
InnoDB默認使用的是可重複讀隔離級別.
6. 對MySQL的鎖瞭解嗎?
當數據庫有併發事務的時候,可能會產生數據的不一致,這時候須要一些機制來保證訪問的次序,鎖機制就是這樣的一個機制.
就像酒店的房間,若是你們隨意進出,就會出現多人搶奪同一個房間的狀況,而在房間上裝上鎖,申請到鑰匙的人才能夠入住而且將房間鎖起來,其餘人只有等他使用完畢才能夠再次使用.
7. MySQL都有哪些鎖呢?像上面那樣子進行鎖定豈不是有點阻礙併發效率了?
從鎖的類別上來說,有共享鎖和排他鎖.
共享鎖: 又叫作讀鎖. 當用戶要進行數據的讀取時,對數據加上共享鎖.共享鎖能夠同時加上多個.
排他鎖: 又叫作寫鎖. 當用戶要進行數據的寫入時,對數據加上排他鎖.排他鎖只能夠加一個,他和其餘的排他鎖,共享鎖都相斥.
用上面的例子來講就是用戶的行爲有兩種,一種是來看房,多個用戶一塊兒看房是能夠接受的. 一種是真正的入住一晚,在這期間,不管是想入住的仍是想看房的都不能夠.
鎖的粒度取決於具體的存儲引擎,InnoDB實現了行級鎖,頁級鎖,表級鎖.
他們的加鎖開銷從大大小,併發能力也是從大到小.
1. 爲何要儘可能設定一個主鍵?
主鍵是數據庫確保數據行在整張表惟一性的保障,即便業務上本張表沒有主鍵,也建議添加一個自增加的ID列做爲主鍵.設定了主鍵以後,在後續的刪改查的時候可能更加快速以及確保操做數據範圍安全.
2. 主鍵使用自增ID仍是UUID?
推薦使用自增ID,不要使用UUID.
由於在InnoDB存儲引擎中,主鍵索引是做爲聚簇索引存在的,也就是說,主鍵索引的B+樹葉子節點上存儲了主鍵索引以及所有的數據(按照順序),若是主鍵索引是自增ID,那麼只須要不斷向後排列便可,若是是UUID,因爲到來的ID與原來的大小不肯定,會形成很是多的數據插入,數據移動,而後致使產生不少的內存碎片,進而形成插入性能的降低.
總之,在數據量大一些的狀況下,用自增主鍵性能會好一些.
圖片來源於《高性能MySQL》: 其中默認後綴爲使用自增ID,__uuid
爲使用UUID爲主鍵的測試,測試了插入100w行和300w行的性能._
關於主鍵是聚簇索引,若是沒有主鍵,InnoDB會選擇一個惟一鍵來做爲聚簇索引,若是沒有惟一鍵,會生成一個隱式的主鍵.
If you define a PRIMARY KEY on your table, InnoDB uses it as the clustered index.If you do not define a PRIMARY KEY for your table, MySQL picks the first UNIQUE index that has only NOT NULL columns as the primary key and InnoDB uses it as the clustered index.
3. 字段爲何要求定義爲not null?
MySQL官網這樣介紹:
NULL columns require additional space in the rowto record whether their values are NULL. For MyISAM tables, each NULL columntakes one bit extra, rounded up to the nearest byte.
null值會佔用更多的字節,且會在程序中形成不少與預期不符的狀況.
4. 若是要存儲用戶的密碼散列,應該使用什麼字段進行存儲?
密碼散列,鹽,用戶身份證號等固定長度的字符串應該使用char而不是varchar來存儲,這樣能夠節省空間且提升檢索效率.
1. MySQL支持哪些存儲引擎?
MySQL支持多種存儲引擎,好比InnoDB,MyISAM,Memory,Archive等等.在大多數的狀況下,直接選擇使用InnoDB引擎都是最合適的,InnoDB也是MySQL的默認存儲引擎.
1. MySQL中的varchar和char有什麼區別.
char是一個定長字段,假如申請了char(10)
的空間,那麼不管實際存儲多少內容.該字段都佔用10個字符,而varchar是變長的,也就是說申請的只是最大長度,佔用的空間爲實際字符長度+1,最後一個字符存儲使用了多長的空間.
在檢索效率上來說,char > varchar,所以在使用中,若是肯定某個字段的值的長度,可使用char,不然應該儘可能使用varchar.例如存儲用戶MD5加密後的密碼,則應該使用char.
2. varchar(10)和int(10)表明什麼含義?
varchar的10表明了申請的空間長度,也是能夠存儲的數據的最大長度,而int的10只是表明了展現的長度,不足10位以0填充.也就是說,int(1)和int(10)所能存儲的數字大小以及佔用的空間都是相同的,只是在展現時按照長度展現.
3. MySQL的binlog有有幾種錄入格式?分別有什麼區別?
有三種格式,statement,row和mixed.
此外,新版的MySQL中對row級別也作了一些優化,當表結構發生變化的時候,會記錄語句而不是逐行記錄.
4. 超大分頁怎麼處理?
超大的分頁通常從兩個方向上來解決.
select * from table where age > 20 limit 1000000,10
這種查詢其實也是有能夠優化的餘地的. 這條語句須要load1000000數據而後基本上所有丟棄,只取10條固然比較慢. 當時咱們能夠修改成select * from table where id in (select id from table where age > 20 limit 1000000,10)
.這樣雖然也load了一百萬的數據,可是因爲索引覆蓋,要查詢的全部字段都在索引中,因此速度會很快. 同時若是ID連續的好,咱們還能夠select * from table where id > 1000000 limit 10
,效率也是不錯的,優化的可能性有許多種,可是核心思想都同樣,就是減小load的數據.解決超大分頁,其實主要是靠緩存,可預測性的提早查到內容,緩存至redis等k-V數據庫中,直接返回便可.
在阿里巴巴《Java開發手冊》中,對超大分頁的解決辦法是相似於上面提到的第一種.
5. 關心過業務系統裏面的sql耗時嗎?統計過慢查詢嗎?對慢查詢都怎麼優化過?
在業務系統中,除了使用主鍵進行的查詢,其餘的我都會在測試庫上測試其耗時,慢查詢的統計主要由運維在作,會按期將業務中的慢查詢反饋給咱們.
慢查詢的優化首先要搞明白慢的緣由是什麼? 是查詢條件沒有命中索引?是load了不須要的數據列?仍是數據量太大?
因此優化也是針對這三個方向來的,
6. 上面提到橫向分表和縱向分表,能夠分別舉一個適合他們的例子嗎?
橫向分表是按行分表.假設咱們有一張用戶表,主鍵是自增ID且同時是用戶的ID.數據量較大,有1億多條,那麼此時放在一張表裏的查詢效果就不太理想.咱們能夠根據主鍵ID進行分表,不管是按尾號分,或者按ID的區間分都是能夠的. 假設按照尾號0-99分爲100個表,那麼每張表中的數據就僅有100w.這時的查詢效率無疑是能夠知足要求的.
縱向分表是按列分表.假設咱們如今有一張文章表.包含字段id-摘要-內容
.而系統中的展現形式是刷新出一個列表,列表中僅包含標題和摘要,當用戶點擊某篇文章進入詳情時才須要正文內容.此時,若是數據量大,將內容這個很大且不常用的列放在一塊兒會拖慢原表的查詢速度.咱們能夠將上面的表分爲兩張.id-摘要
,id-內容
.當用戶點擊詳情,那主鍵再來取一次內容便可.而增長的存儲量只是很小的主鍵字段.代價很小.
固然,分表其實和業務的關聯度很高,在分表以前必定要作好調研以及benchmark.不要按照本身的猜測盲目操做.
7. 什麼是存儲過程?有哪些優缺點?
存儲過程是一些預編譯的SQL語句。一、更加直白的理解:存儲過程能夠說是一個記錄集,它是由一些T-SQL語句組成的代碼塊,這些T-SQL語句代碼像一個方法同樣實現一些功能(對單表或多表的增刪改查),而後再給這個代碼塊取一個名字,在用到這個功能的時候調用他就好了。二、存儲過程是一個預編譯的代碼塊,執行效率比較高,一個存儲過程替代大量T_SQL語句 ,能夠下降網絡通訊量,提升通訊速率,能夠必定程度上確保數據安全
可是,在互聯網項目中,實際上是不太推薦存儲過程的,比較出名的就是阿里的《Java開發手冊》中禁止使用存儲過程,我我的的理解是,在互聯網項目中,迭代太快,項目的生命週期也比較短,人員流動相比於傳統的項目也更加頻繁,在這樣的狀況下,存儲過程的管理確實是沒有那麼方便,同時,複用性也沒有寫在服務層那麼好.
8. 說一說三個範式
第一範式: 每一個列都不能夠再拆分.第二範式: 非主鍵列徹底依賴於主鍵,而不能是依賴於主鍵的一部分.第三範式: 非主鍵列只依賴於主鍵,不依賴於其餘非主鍵.
在設計數據庫結構的時候,要儘可能遵照三範式,若是不遵照,必須有足夠的理由.好比性能. 事實上咱們常常會爲了性能而妥協數據庫的設計.
9. MyBatis中的#和$有什麼區別?
亂入了一個奇怪的問題.....我只是想單獨記錄一下這個問題,由於出現頻率過高了.
會將傳入的內容當作字符串,而$會直接將傳入值拼接在sql語句中.
因此#能夠在必定程度上預防sql注入攻擊.
做者:呼延十
連接:https://juejin.im/post/5d3513...
關注Java編程鴨微信公衆號,後臺回覆:碼農大禮包 能夠獲取最新整理的技術資料一份。涵蓋Java 框架學習、架構師學習等!