Java程式設計師從笨鳥到菜鳥(二十)MySQL面試題
mysql
資料庫是被廣泛應用的關係型資料庫,體積小、支援多處理器、開源並免費的特性使其在中小型網站中的使用率尤其高。
分三個個方面:
- 庫表設計
- 慢SQL問題
- 面試題
#一、庫表設計
1.1 引擎選擇
mysql
常用引擎包括:MYISAM
、Innodb
、Memory
、MERGE
MYISAM
:全表鎖,擁有較高的執行速度,不支援事務,不支援外來鍵,併發效能差,佔用空間相對較小,對事務完整性沒有要求,以select、insert為主的應用基本上可以使用這引擎Innodb
:行級鎖,提供了具有提交、回滾和崩潰回覆能力的事務安全,支援自動增長列,支援外來鍵約束,併發能力強,佔用空間是MYISAM的2.5倍,處理效率相對會差一些Memory
:全表鎖,儲存在內容中,速度快,但會佔用和資料量成正比的記憶體空間且資料在mysql
重啟時會丟失,預設使用HASH索引,檢索效率非常高,但不適用於精確查詢,主要用於那些內容變化不頻繁的程式碼表MERGE
:是一組MYISAM表的組合
絕大部分是選用
innodb
引擎,特殊業務再考慮MYISAM
、Memory
,例如全文索引或極高的執行效率
1.2 分表方法
在資料庫使用過程中,為了減小資料庫伺服器的負擔、縮短查詢時間,會考慮做分表設計
分表分為兩種:一種是縱向分表(將本來可以在同一個表的內容,認為劃分儲存為多個不同結構的表)和橫向分表(把大的表結構,橫向切割為同樣結構的不同表)
縱向分表
常見的分表方式:根據活躍度、重要性分表,主要解決如下問題:
- 表與表之間的資源爭用問題
- 鎖爭用機率小
- 實現核心與非核心的分級儲存
- 解決資料庫同步壓力問題
橫向分表
根據某些特定規則來劃分資料量表,主要解決如下問題:
- 單表過大造成的效能問題
- 單表過大造成的單伺服器空間問題
1.3 索引問題
索引是對資料庫表中一個或多個列的值進行排序的結構,建立索引有助於快速獲取資訊。
mysql
有4種不同的索引:
- 主鍵索引(PRIMARY)
- 唯一索引(UNIQUE)
- 普通索引(INDEX)
- 全文索引(FULLTEXT)
索引並非是越多越好,建立索引也需要耗費資源,一是增加了資料庫的儲存空間,二是在插入和刪除時要花費較多的時間維護索引
#二. 慢SQL的問題
2.1 導致慢SQL的原因
從出現的概率由大到小如下:
- SQL編寫問題
- 鎖
- 業務例項相互干擾對IO/CPU資源的競爭
- 伺服器硬體
- MYSQL BUG
2.2 由SQL編寫導致的慢SQL優化
需要注意與索引有關的規則:
- 欄位型別轉換導致不用索引,如字串型別不用引號,數字型別用引號等
- mysql 不支援函式轉換,欄位面前不能加函式
- 不要在欄位面前加減運算
- 字串比較長的可以考慮索引一部分減少索引檔案的大小,提高寫入效率
- like %在前面用不到索引,需要寫在檢索關鍵字的後面
- 根據聯合索引的第二個及以後的欄位單獨查詢用不到索引
- 不要使用select *
- 排序儘量使用升序
- or的查詢儘快用union代替
- 複合索引高選擇性的欄位排在前面
- order by/group by 欄位包括在索引當中減少排序,效率會更高
- 刪除表所有記錄用truncate,不要用delete
- 不要讓mysql幹多餘的事情,例如計算
問:一張表,裡面id自增長主鍵,已經向表中插入了10條資料之後,刪除了8,9,10的記錄,再把mysql
重啟,之後再插入一條資料,那麼資料id值是多少,8還是11.
- 如果是MYISAM,結果是11,如果是innodb,id為8,兩種型別的儲存引擎所儲存的最大ID記錄的方式不同,MYISAN表將最大的id記錄到資料檔案裡,重啟之後自增主鍵的最大id值不會丟失,而innodb則把最大的ID值記錄到記憶體中,重啟之後最大id值將會丟失
三、面試題
3.1 四個特性及含義
四個基本要素(acid):原子性
、一致性
、隔離性
、永續性
原子性
(Actomicity):整個事務中的操作要麼全完成,要麼全部不完成,不能滯留在中間某個環節,事務在執行過程發生錯誤,會被回滾(Rollback)到事務開始前的狀態,就像沒執行過一樣一致性
(Consistent):在事務開始和結束之後,資料庫的完整性約束沒有被破壞隔離性
(Isolation):使事務在給定的時間內執行的唯一操作,為了防止事務之間的混淆,必須序列化或序列化請求,使得在同一時間內僅有一個請求用於同一資料永續性
(Durable):在事務完成以後,該事務所對資料庫所做的更改持久儲存到資料庫之中,並不會被回滾
3.2 drop、delete、truncate的區別
drop直接刪掉表、truncate刪除表中的資料,再插入資料自增長id又從1開始、delete刪除表中的資料,可以加where語句
- delete語句刪除表中的某一行。並同時將改行的刪除操作作為事務記錄在日誌中,以便後續進行回滾;truncate則是一次性刪除表中所有的資料並不會單獨把操作記錄日誌儲存,是不能恢復的
- 表和索引所佔的空間:truncate之後空間會恢復初始大小,delete操作不會減少表索引所佔的空間,drop將釋放所有的佔用空間
- 一般而言 drop>truncate>delete
- truncate和delete只刪除資料,drop直接刪除整個表
- 在沒有備份的情況下,慎用drop和truncate
- truncate速度快、效率高
3.3 索引
索引是資料庫管理系統中一個排序的資料結構,已助快速查詢、更新資料庫表中資料,建立語句alter table tablename add index(id);
優點:
- 建立唯一索引,可以保證每一行資料的唯一性
- 大大加快檢索速度
- 加速表之間的連線
- 使用分組和排序字句進行資料檢索時,可以顯著減少查詢中分組和排序的時間
缺點:
- 建立索引需要佔用物理空間
- 對錶的資料進行增刪改查時,索引也要動態維護,降低了資料的維護速度
- 建立和維護索引耗費時間,時間隨著資料量的增加而增加
不需要建立索引
- 查詢中很少使用或者是參考的列
- 資料量很少的列
- 對於那些定義text、image和bit資料型別
- 修改效能遠遠大於修改效能,修改效能和檢索效能是互相矛盾的
3.4 內連線、、左連線(左外)、右連線(右外)、全連線(全外)
內連線
:
說明:組合兩個表中的記錄,返回關聯欄位的記錄,返回兩個表交集部分
左連線
:
說明:left join 是left outer join的簡寫,左外連線是外連線的一種,左外連線:左表的資料全顯示,右表顯示符合搜尋條件的記錄,右表記錄不足的地方均為null
右連線
:
說明:right join 是right outer join的簡寫,右外連線是外連線的一種,右外連線:右表的資料全顯示,左表顯示符合搜尋條件的記錄,左表記錄不足的地方均為null
- 關鍵字:right join on / right outer join on
- 語句:select * from a_table a right join b_table b on a.id = b.id
全連線
:
目前mysql不支援
3.5 行級鎖、表級鎖、樂觀鎖、悲觀鎖
加鎖原因:放置更新丟失,並不能單靠資料庫事務控制器來解決,需要應用程式對更新的資料加必要的鎖來解決
表級鎖
:每次操作都鎖定在整張表,開銷少,加鎖塊,不會出現死鎖,鎖定粒度大,發生鎖衝突的概率最高,併發度最低行級鎖
:每次操作鎖定一行資料,開銷大,加鎖慢,會出現死鎖,鎖定粒度小,發生鎖衝突的概率最低,併發度最高頁面鎖
:開銷和加鎖時間介於兩者之間,會出現死鎖,併發度一般悲觀鎖
:假定會發生併發衝突,遮蔽一切可能違反資料完整性的操作樂觀鎖
:假設不會發生併發衝突,只在提交操作時檢查是否違反資料完整性
樂觀鎖
更適合解決衝突概率極小的情況,而悲觀鎖
則適合解決併發競爭激烈的情況,儘量使用行鎖
,縮小加鎖粒度,以提高併發處理能力,即使加行鎖
的時間比表鎖
要長
3.6 併發事務處理帶來的問題
相對於序列處理來說,併發事務處理能大大增加資料庫資源的利用率,提高資料庫系統的事務吞吐量,從而可以支援更多的使用者
更新丟失
:當兩個或多個事務選擇同一行,然後基於最初的值選定該行進行更新該行時,由於事務之間沒有聯絡,就會發生丟失更新的問題,如果在一個編輯人員完成並提交事務之前,另一個編輯人員不能訪問同一個檔案,就可以避免這類問題髒讀
:一個事務在對一條資料進行修改,在事務完成並提交前,記錄就處於不一致的狀態,這是另外一個事務也來讀取同一條記錄,如果不加以控制,第二個事務讀取了這些“髒”資料,並據此做進一步處理,就會產生未提交資料的依關係不可重複讀
:一個事務在讀取某些資料後的某個時間在,再次讀取的時候,發現數據發生改變,或者某些記錄被刪除了幻讀
:一個事務按相同的查詢條件重複讀取以前檢索過的資料,卻發現了其它事務插入了滿足其查詢條件的新資料
跟新丟失可以通過加鎖來解決,“髒讀”、“不可重複讀”、“幻讀”屬於資料庫一致性問題,由資料庫提供一定的事務隔離機制來解決
事務隔離方式實現
- 在讀取資料前,對其進行加鎖,阻止事務對資料進行修改
- 不用加任何鎖,通過一定機制生成一個數據請求時間點的一致性資料快照,並用這個快照提供一定級別的一致性讀取,這種方式也叫
多版本併發控制
簡稱MVCC或MCC
事務隔離度級別
資料庫的隔離越嚴格,併發副作用越小,付出的代價也就越大,隔離的實質就是在一定程度上使事務“序列化”,這顯然與“併發”是矛盾的
隔離級別 | 讀取資料一致性 | 髒讀 | 不可重複讀 | 幻讀 |
---|---|---|---|---|
未提交讀(Read uncommitted) | 最低級別,只能保證讀取不損壞資料 | 是 | 是 | 是 |
已提交讀(Read committed) | 語句級 | 否 | 是 | 是 |
可重複讀(Repeatable read) | 事務級 | 否 | 否 | 是 |
可序列化(Serializable) | 最高級別,事務級 | 否 | 否 | 否 |
3.7 innodb和myisam的區別
- innodb支援事務,myisam不支援
- innodb支援行級鎖,myisam支援表級鎖
- innodb支援外來鍵,myisam不支援
- innodb支援MVCC,myisam不支援
- innodb不支援全文索引,myisam支援
- 應用場景:
MyISAM:做很多count計算、插入不頻繁、查詢非常頻繁、沒有事務、查詢快
InnoDB:對事務要求比較高、可靠性要求高、表更新相當頻繁、併發寫入高
7.DELETE操作:
MyISAM:先drop表,然後重建表
InnoDB:一行一行刪除
8.查詢表的行數不同:
MyISAM:只是簡單的讀出儲存好的行數
InnoDB:不儲存具體行數,執行count(*)時要掃描一整個表來計算有多少行
3.8 innodb引擎的4大特性
- 插入快取(insert buffer)
- 二次寫(double write)
- 自適應雜湊索引
- 預讀
3.9 MyISAM會比InnoDB的查詢要快
InnoDB在做SELECT的時候,要維護的東西比MyISAM引擎多
- 資料塊:InnoDB要快取,MyISAM只快取索引塊
- InnoDB定址要對映到塊再到行,MyISAM記錄的直接是檔案的OFFSET,定位比InniDB要快
- InnoDB還要維護MVCC一致
3.10 varchar和char的區別以及varchar(50)50的含義
- 區別:char是一種固定長度的型別,varchar則是一種可變長度的型別
- 含義:最多存放50個字元,varchar(50)和varchar(200)儲存hello所佔空間一樣,但後者在排序時會消耗更多的記憶體,因為order by col採用fixed_length計算col長度
- int(20)20的含義:是指顯示字元的長度,最大為255,仍佔4位元組儲存,儲存範圍不變
3.11 innodb的事務與日誌實現方式
- 日誌種類
- 錯誤日誌:記錄出錯資訊,也記錄一些警告或是正確的資訊
- 查詢日誌:記錄所有對資料請求資訊,不論是否得到正確執行
- 慢查詢日誌:設定一個閾值,將執行時間超過該值的所有SQL語句都記錄進去
- 二進位制日誌:記錄對資料庫執行更改的所有操作
- 事務是如何通過日誌實現的
事務日誌是通過redo和innodb的儲存引擎日誌緩衝來實現的,當開始一個事務的時候,會記錄該事務的lsn號,當事務執行時,會往innodb儲存引擎日誌快取裡面插入事務日誌,當事務提交時,必須將儲存引擎的日誌快取寫入磁碟,也就是寫資料前,要先寫日誌
3.12 innodb引擎的行鎖怎樣實現的
是基於索引
來完成行鎖
例如:select * from a_table where id = 1 for update;
for update 可以根據條件來完成行鎖定,並且id是有索引鍵的列,如果id不是索引鍵,那麼innodb將完成表鎖
四、MYSQL 效能優化
1、為查詢快取優化查詢
當很多相同的查詢被執行了多次的時候,這些結果會被放到一個快取中,這樣後續的查詢就直接訪問快取結果。
需要使用一個變數來代替 MYSQL 的函式,從而開啟快取
2、explain select 語句
explain 顯示了 mysql 如何使用索引處理 select 語句及連線表,可以幫助選擇更好的索引和寫出更優化的查詢語句
explain select * from table a;
3、當只要一行資料時使用 LIMIT 1
當查詢表的時候,已經知道只會有一條結果,加上 LIMIT 1 後,MySQL 引擎會在查詢到一條資料之後停止搜尋
select 1 from user where id = 1 limit 1;
4、為搜尋欄位建索引
**5、避免使用 select ***
6、為每張表設定一個主鍵
應該為每張表都設定一個 ID 作為主鍵,而且最好是 INT 型的,並設定 auto_increment 自增長,使用 VARCHAR 作為主鍵會是效能下降;但是關聯表的外來鍵除外
7、使用 ENUM 而不是 VARCHAR
ENUM 型別是非常快和緊湊的,實際上,其儲存的是 TINYINT,其外表上顯示為一個字串,當知道一些欄位的取值是有限而且固定的,應該使用 ENUM
8、使用 procedure analyse() 取得建議
procedure analuse() 會讓 MYSQL 幫你去分析欄位和其實際資料,並會給一些建議,資料越多,建議就越準確
9、儘可能使用 not null
null 也需要額外的空間,並且在進行比較的時候,你的程式會變得更復雜
10、PreparedStatement
可以提升效能和安全
11、查詢資料量大無緩衝查詢
當使用指令碼執行 SQL 語句的時候,程式會直到這個 SQL 語句有返回結果,然後才會往下繼續執行,可以使用無緩衝查詢來改變行為。尤其是那些會產生大量結果的查詢,不需要等待所有結果返回,只需要第一行的資料返回的時候,就可以馬上開始工作於查詢結果了
12、把 IP 地址存成 UNSIGNED INT
使用一個無符號整形 UNSIGNED INT 來存放 IP,只需要四個位元組 32 位儲存
// 字串 IP 轉成整形
select INET_ATON('127.0.0.1') from dual;
// 整形轉字串 IP
select INET_NTOA('2130706433') from dual;
13、使用固定長度的表
固定長度的表會提高效能,因為MySQL搜尋的會更快一些,因為這些固定的長度是很容易計算下一個資料的偏移量的,所以讀取的自然也會很快。而如果欄位不是定長的,那麼,每一次要找下一條的話,需要程式找到主鍵,但是定長欄位會浪費一些空間,無論是否在使用,都要分配那麼多的空間
14、垂直分割
將一個表中不常用的欄位放在另一張表
15、拆分大的 DELETE 或 INSERT 語句
使用 limit 來限制
16、選擇正確的儲存引擎
對於查詢非常頻繁,使用 MYISAM 效率高一些,但是寫入資料比較頻繁的話,使用 InnoDB ,一般選用 InnoDB
五、儲存過程
5.1 定義:
儲存在資料庫目錄中的一段宣告性 SQL 語句
5.2 優點:
1、有助於提高應用程式的效能,儲存過程編譯之後,就儲存在資料庫中
2、有助於減少應用程式和資料庫伺服器之間的流量,應用程式不必傳送多個冗長的 SQL 語句,而只能傳送儲存過程的名稱和引數
3、儲存的程式是安全的
5.3 缺點:
1、使用大量儲存過程,記憶體使用量會大大增加
2、儲存過程的構造使得開發具有複雜業務邏輯的儲存過程變得更加困難
3、很難除錯儲存過程
4、開發和維護儲存過程不容易