進了互聯網公司,成天也就是搬磚,等到了面試的時候,發現數據庫方面,忘得一塌糊塗,抽時間整理了一些數據庫方面的題。歡迎你們向我推薦你在面試過程當中遇到的問題,我會把你們推薦的問題添加到下面的經常使用面試題清單中供你們參考。前端
原子性(Atomicity)面試
一致性(Consistency)算法
隔離性(Isolation)sql
同一時間,只容許一個事務請求同一數據,不一樣的事務之間彼此沒有任何干擾。好比A正在從一張銀行卡中取錢,在A取錢的過程結束前,B不能向這張卡轉帳。數據庫
關於事務的隔離性數據庫提供了多種隔離級別,稍後會介紹到。 持久性(Durability)緩存
從理論上來講, 事務應該彼此徹底隔離, 以免併發事務所致使的問題,然而, 那樣會對性能產生極大的影響, 由於事務必須按順序運行, 在實際開發中, 爲了提高性能, 事務會以較低的隔離級別運行, 事務的隔離級別能夠經過隔離事務屬性指定。安全
一、髒讀:事務A讀取了事務B更新的數據,而後B回滾操做,那麼A讀取到的數據是髒數據性能優化
二、不可重複讀:事務 A 屢次讀取同一數據,事務 B 在事務A屢次讀取的過程當中,對數據做了更新並提交,致使事務A屢次讀取同一數據時,結果所以本事務前後兩次讀到的數據結果會不一致。服務器
三、幻讀:幻讀解決了不重複讀,保證了同一個事務裏,查詢的結果都是事務開始時的狀態(一致性)。session
例如:事務T1對一個表中全部的行的某個數據項作了從「1」修改成「2」的操做 這時事務T2又對這個表中插入了一行數據項,而這個數據項的數值仍是爲「1」而且提交給數據庫。 而操做事務T1的用戶若是再查看剛剛修改的數據,會發現還有跟沒有修改同樣,其實這行是從事務T2中添加的,就好像產生幻覺同樣,這就是發生了幻讀。
小結:不可重複讀的和幻讀很容易混淆,不可重複讀側重於修改,幻讀側重於新增或刪除。解決不可重複讀的問題只需鎖住知足條件的行,解決幻讀須要鎖表。
事務隔離級別 | 髒讀 | 不可重複讀 | 幻讀 |
---|---|---|---|
讀未提交 read-uncommitted | 是 | 是 | 是 |
不可重複讀 read-committed | 否 | 是 | 是 |
可重複讀 repeatable-read | 否 | 否 | 是 |
串行化 serializable | 否 | 否 | 否 |
MySQL默認的事務隔離級別爲repeatable-read
隔離級別越高,越能保證數據的完整性和一致性,可是對併發性能的影響也越大,魚和熊掌不可兼得啊。對於多數應用程序,能夠優先考慮把數據庫系統的隔離級別設爲Read Committed,它可以避免髒讀取,並且具備較好的併發性能。儘管它會致使不可重複讀、幻讀這些併發問題,在可能出現這類問題的個別場合,能夠由應用程序採用悲觀鎖或樂觀鎖來控制。
MySQL有多種存儲引擎,每種存儲引擎有各自的優缺點,能夠擇優選擇使用:MyISAM、InnoDB、MERGE、MEMORY(HEAP)、BDB(BerkeleyDB)、EXAMPLE、FEDERATED、ARCHIVE、CSV、BLACKHOLE
。
雖然MySQL裏的存儲引擎不僅是MyISAM與InnoDB這兩個,但經常使用的就是兩個。
兩種存儲引擎的大體區別表如今:
select count(*) from table
時,InnoDB須要掃描一遍整個表來計算有多少行,可是MyISAM只要簡單的讀出保存好的行數便可。注意的是,當count(*)語句包含where條件時MyISAM也須要掃描整個表。DELETE FROM table
時,InnoDB不會從新創建表,而是一行一行的 刪除,效率很是慢。MyISAM則會重建表。update table set a=1 where user like '%lee%'
。有人說MyISAM只能用於小型應用,其實這只是一種偏見。若是數據量比較大,這是須要經過升級架構來解決,好比分表分庫,而不是單純地依賴存儲引擎。
如今通常都是選用innodb了,主要是MyISAM的全表鎖,讀寫串行問題,併發效率鎖表,效率低,MyISAM對於讀寫密集型應用通常是不會去選用的。
MEMORY是MySQL中一類特殊的存儲引擎。它使用存儲在內存中的內容來建立表,並且數據所有放在內存中。這些特性與前面的兩個很不一樣。
每一個基於MEMORY存儲引擎的表實際對應一個磁盤文件。該文件的文件名與表名相同,類型爲frm類型。該文件中只存儲表的結構。而其數據文件,都是存儲在內存中,這樣有利於數據的快速處理,提升整個表的效率。值得注意的是,服務器須要有足夠的內存來維持MEMORY存儲引擎的表的使用。若是不須要了,能夠釋放內存,甚至刪除不須要的表。
MEMORY默認使用哈希索引。速度比使用B型樹索引快。固然若是你想用B型樹索引,能夠在建立索引時指定。
注意,MEMORY用到的不多,由於它是把數據存到內存中,若是內存出現異常就會影響數據。若是重啓或者關機,全部數據都會消失。所以,基於MEMORY的表的生命週期很短,通常是一次性的。
事務處理上方面
鎖級別
1.查詢中用到的關鍵詞主要包含六個,而且他們的順序依次爲 select--from--where--group by--having--order by
其中select和from是必須的,其餘關鍵詞是可選的,這六個關鍵詞的執行順序 與sql語句的書寫順序並非同樣的,而是按照下面的順序來執行
2.from後面的表關聯,是自右向左解析 而where條件的解析順序是自下而上的。
也就是說,在寫SQL文的時候,儘可能把數據量小的表放在最右邊來進行關聯(用小表去匹配大表),而把能篩選出小量數據的條件放在where語句的最左邊 (用小表去匹配大表)
時表能夠手動刪除:
DROP TEMPORARY TABLE IF EXISTS temp_tb;複製代碼
臨時表只在當前鏈接可見,當關閉鏈接時,MySQL會自動刪除表並釋放全部空間。所以在不一樣的鏈接中能夠建立同名的臨時表,而且操做屬於本鏈接的臨時表。
建立臨時表的語法與建立表語法相似,不一樣之處是增長關鍵字TEMPORARY,如:
CREATE TEMPORARY TABLE tmp_table (
NAME VARCHAR (10) NOT NULL,
time date NOT NULL
);
select * from tmp_table;複製代碼
那爲何你們不都用Hash索引而還要使用B+樹索引呢?
MySQL中,只有HEAP/MEMORY引擎才顯示支持Hash索引。
經常使用的InnoDB引擎中默認使用的是B+樹索引,它會實時監控表上索引的使用狀況,若是認爲創建哈希索引能夠提升查詢效率,則自動在內存中的「自適應哈希索引緩衝區」創建哈希索引(在InnoDB中默認開啓自適應哈希索引),經過觀察搜索模式,MySQL會利用index key的前綴創建哈希索引,若是一個表幾乎大部分都在緩衝池中,那麼創建一個哈希索引可以加快等值查詢。
若是是等值查詢,那麼哈希索引明顯有絕對優點,由於只須要通過一次算法便可找到相應的鍵值;固然了,這個前提是,鍵值都是惟一的。若是鍵值不是惟一的,就須要先找到該鍵所在位置,而後再根據鏈表日後掃描,直到找到相應的數據;
若是是範圍查詢檢索,這時候哈希索引就毫無用武之地了,由於原先是有序的鍵值,通過哈希算法後,有可能變成不連續的了,就沒辦法再利用索引完成範圍查詢檢索;
同理,哈希索引沒辦法利用索引完成排序,以及like ‘xxx%’ 這樣的部分模糊查詢(這種部分模糊查詢,其實本質上也是範圍查詢);
哈希索引也不支持多列聯合索引的最左匹配規則;
B+樹索引的關鍵字檢索效率比較平均,不像B樹那樣波動幅度大,在有大量重複鍵值狀況下,哈希索引的效率也是極低的,由於存在所謂的哈希碰撞問題。
在大多數場景下,都會有範圍查詢、排序、分組等查詢特徵,用B+樹索引就能夠了。
<,<=,=,>,>=,between,in
聚合索引(clustered index) / 非聚合索引(nonclustered index)
根本區別
彙集索引和非彙集索引的根本區別是表記錄的排列順序和與索引的排列順序是否一致。
彙集索引
彙集索引表記錄的排列順序和索引的排列順序一致,因此查詢效率快,只要找到第一個索引值記錄,其他就連續性的記錄在物理也同樣連續存放。彙集索引對應的缺點就是修改慢,由於爲了保證表中記錄的物理和索引順序一致,在記錄插入的時候,會對數據頁從新排序。
彙集索引相似於新華字典中用拼音去查找漢字,拼音檢索表於書記順序都是按照a~z排列的,就像相同的邏輯順序於物理順序同樣,當你須要查找a,ai兩個讀音的字,或是想一次尋找多個傻(sha)的同音字時,也許向後翻幾頁,或緊接着下一行就獲得結果了。
非彙集索引
非彙集索引制定了表中記錄的邏輯順序,可是記錄的物理和索引不必定一致,兩種索引都採用B+樹結構,非彙集索引的葉子層並不和實際數據頁相重疊,而採用葉子層包含一個指向表中的記錄在數據頁中的指針方式。非彙集索引層次多,不會形成數據重排。
非彙集索引相似在新華字典上經過偏旁部首來查詢漢字,檢索表也許是按照橫、豎、撇來排列的,可是因爲正文中是a~z的拼音順序,因此就相似於邏輯地址於物理地址的不對應。同時適用的狀況就在於分組,大數目的不一樣值,頻繁更新的列中,這些狀況即不適合彙集索引。
悲觀鎖的特色是先獲取鎖,再進行業務操做,即「悲觀」的認爲獲取鎖是很是有可能失敗的,所以要先確保獲取鎖成功再進行業務操做。一般所說的「一鎖二查三更新」即指的是使用悲觀鎖。一般來說在數據庫上的悲觀鎖須要數據庫自己提供支持,即經過經常使用的select … for update操做來實現悲觀鎖。當數據庫執行select for update時會獲取被select中的數據行的行鎖,所以其餘併發執行的select for update若是試圖選中同一行則會發生排斥(須要等待行鎖被釋放),所以達到鎖的效果。select for update獲取的行鎖會在當前事務結束時自動釋放,所以必須在事務中使用。
這裏須要注意的一點是不一樣的數據庫對select for update的實現和支持都是有所區別的,例如oracle支持select for update no wait,表示若是拿不到鎖馬上報錯,而不是等待,MySQL就沒有no wait這個選項。另外MySQL還有個問題是select for update語句執行中全部掃描過的行都會被鎖上,這一點很容易形成問題。所以若是在MySQL中用悲觀鎖務必要肯定走了索引,而不是全表掃描。
樂觀鎖,也叫樂觀併發控制,它假設多用戶併發的事務在處理時不會彼此互相影響,各事務可以在不產生鎖的狀況下處理各自影響的那部分數據。在提交數據更新以前,每一個事務會先檢查在該事務讀取數據後,有沒有其餘事務又修改了該數據。若是其餘事務有更新的話,那麼當前正在提交的事務會進行回滾。
樂觀鎖的特色先進行業務操做,不到萬不得已不去拿鎖。即「樂觀」的認爲拿鎖多半是會成功的,所以在進行完業務操做須要實際更新數據的最後一步再去拿一下鎖就好。
樂觀鎖在數據庫上的實現徹底是邏輯的,不須要數據庫提供特殊的支持。通常的作法是在須要鎖的數據上增長一個版本號,或者時間戳,而後按照以下方式實現:
樂觀鎖(給表加一個版本號字段) 這個並非樂觀鎖的定義,給表加版本號,是數據庫實現樂觀鎖的一種方式。
1. SELECT data AS old_data, version AS old_version FROM …;
2. 根據獲取的數據進行業務操做,獲得new_data和new_version
3. UPDATE SET data = new_data, version = new_version WHERE version = old_version
if (updated row > 0) {
// 樂觀鎖獲取成功,操做完成
} else {
// 樂觀鎖獲取失敗,回滾並重試
}複製代碼
樂觀鎖在不發生取鎖失敗的狀況下開銷比悲觀鎖小,可是一旦發生失敗回滾開銷則比較大,所以適合用在取鎖失敗機率比較小的場景,能夠提高系統併發性能
樂觀鎖還適用於一些比較特殊的場景,例如在業務操做過程當中沒法和數據庫保持鏈接等悲觀鎖沒法適用的地方。
悲觀鎖和樂觀鎖是數據庫用來保證數據併發安全防止更新丟失的兩種方法,例子在select ... for update
前加個事務就能夠防止更新丟失。悲觀鎖和樂觀鎖大部分場景下差別不大,一些獨特場景下有一些差異,通常咱們能夠從以下幾個方面來判斷。
響應速度:若是須要很是高的響應速度,建議採用樂觀鎖方案,成功就執行,不成功就失敗,不須要等待其餘併發去釋放鎖。
衝突頻率:若是衝突頻率很是高,建議採用悲觀鎖,保證成功率,若是衝突頻率大,樂觀鎖會須要屢次重試才能成功,代價比較大。
重試代價:若是重試代價大,建議採用悲觀鎖。
1. 性能
NOSQL是基於鍵值對的,能夠想象成表中的主鍵和值的對應關係,並且不須要通過SQL層的解析,因此性能很是高。
2. 可擴展性
一樣也是由於基於鍵值對,數據之間沒有耦合性,因此很是容易水平擴展。
1. 複雜查詢
能夠用SQL語句方便的在一個表以及多個表之間作很是複雜的數據查詢。
2. 事務支持
使得對於安全性能很高的數據訪問要求得以實現。
對於這兩類數據庫,對方的優點就是本身的弱勢,反之亦然。
NOSQL數據庫慢慢開始具有SQL數據庫的一些複雜查詢功能,好比MongoDB。
對於事務的支持也能夠用一些系統級的原子操做來實現例如樂觀鎖之類的方法來曲線救國,好比Redis set nx。
第一範式是最基本的範式。若是數據庫表中的全部字段值都是不可分解的原子值,就說明該數據庫表知足了第一範式。
第一範式的合理遵循須要根據系統的實際需求來定。好比某些數據庫系統中須要用到「地址」這個屬性,原本直接將「地址」屬性設計成一個數據庫表的字段就行。可是若是系統常常會訪問「地址」屬性中的「城市」部分,那麼就非要將「地址」這個屬性從新拆分爲省份、城市、詳細地址等多個部分進行存儲,這樣在對地址中某一部分操做的時候將很是方便。這樣設計纔算知足了數據庫的第一範式,以下表所示。
上表所示的用戶信息遵循了第一範式的要求,這樣在對用戶使用城市進行分類的時候就很是方便,也提升了數據庫的性能。
第二範式在第一範式的基礎之上更進一層。第二範式須要確保數據庫表中的每一列都和主鍵相關,而不能只與主鍵的某一部分相關(主要針對聯合主鍵而言)。也就是說在一個數據庫表中,一個表中只能保存一種數據,不能夠把多種數據保存在同一張數據庫表中。
好比要設計一個訂單信息表,由於訂單中可能會有多種商品,因此要將訂單編號和商品編號做爲數據庫表的聯合主鍵。
第三範式須要確保數據表中的每一列數據都和主鍵直接相關,而不能間接相關。
好比在設計一個訂單數據表的時候,能夠將客戶編號做爲一個外鍵和訂單表創建相應的關係。而不能夠在訂單表中添加關於客戶其它信息(好比姓名、所屬公司等)的字段。
同步複製
異步複製
半同步複製
問題1:master的寫操做,slaves被動的進行同樣的操做,保持數據一致性,那麼slave是否能夠主動的進行寫操做?
假設slave能夠主動的進行寫操做,slave又沒法通知master,這樣就致使了master和slave數據不一致了。所以slave不該該進行寫操做,至少是slave上涉及到複製的數據庫不能夠寫。實際上,這裏已經揭示了讀寫分離的概念。
問題2:主從複製中,能夠有N個slave,但是這些slave又不能進行寫操做,要他們幹嗎?
以實現數據備份。
相似於高可用的功能,一旦master掛了,可讓slave頂上去,同時slave提高爲master。
異地容災,好比master在北京,地震掛了,那麼在上海的slave還能夠繼續。
主要用於實現scale out,分擔負載,能夠將讀的任務分散到slaves上。
【極可能的狀況是,一個系統的讀操做遠遠多於寫操做,所以寫操做發向master,讀操做發向slaves進行操做】
問題3:主從複製中有master,slave1,slave2,...等等這麼多MySQL數據庫,那好比一個JAVA WEB應用到底應該鏈接哪一個數據庫?
當 然,咱們在應用程序中能夠這樣,insert/delete/update
這些更新數據庫的操做,用connection(for master)
進行操做,select用connection(for slaves)
進行操做。那咱們的應用程序還要完成怎麼從slaves選擇一個來執行select,例如使用簡單的輪循算法。
這樣的話,至關於應用程序完成了SQL語句的路由,並且與MySQL的主從複製架構很是關聯,一旦master掛了,某些slave掛了,那麼應用程序就要修改了。能不能讓應用程序與MySQL的主從複製架構沒有什麼太多關係呢?
找一個組件,application program只須要與它打交道,用它來完成MySQL的代理,實現SQL語句的路由。
MySQL proxy並不負責,怎麼從衆多的slaves挑一個?能夠交給另外一個組件(好比haproxy)來完成。
這就是所謂的MySQL READ WRITE SPLITE,MySQL
的讀寫分離。
問題4:若是MySQL proxy , direct , master他們中的某些掛了怎麼辦?
總統通常都會弄個副總統,以防不測。一樣的,能夠給這些關鍵的節點來個備份。
問題5:當master的二進制日誌每產生一個事件,都須要發往slave,若是咱們有N個slave,那是發N次,仍是隻發一次?
若是隻發一次,發給了slave-1,那slave-2,slave-3,...它們怎麼辦?
顯 然,應該發N次。實際上,在MySQL master內部,維護N個線程,每個線程負責將二進制日誌文件發往對應的slave。master既要負責寫操做,還的維護N個線程,負擔會很重。能夠這樣,slave-1是master的從,slave-1又是slave-2,slave-3,...的主,同時slave-1再也不負責select。 slave-1將master的複製線程的負擔,轉移到本身的身上。這就是所謂的多級複製的概念。
問題6:當一個select發往MySQL proxy,可能此次由slave-2響應,下次由slave-3響應,這樣的話,就沒法利用查詢緩存了。
應該找一個共享式的緩存,好比memcache來解決。將slave-2,slave-3,...這些查詢的結果都緩存至mamcache中。
問題7:隨着應用的日益增加,讀操做不少,咱們能夠擴展slave,可是若是master知足不了寫操做了,怎麼辦呢?
scale on ?更好的服務器? 沒有最好的,只有更好的,太貴了。。。
scale out ? 主從複製架構已經知足不了。
能夠分庫【垂直拆分】,分表【水平拆分】。
對於複雜、效率低的sql語句,咱們一般是使用explain sql 來分析sql語句,這個語句能夠打印出,語句的執行。這樣方便咱們分析,進行優化
const、eq_reg、ref、range、index
和ALL
between ,< ,>
等查詢;內鏈接查詢操做列出與鏈接條件匹配的數據行,它使用比較運算符比較被鏈接列的 列值。
內鏈接分三種:
(=)
運算符比較被鏈接列的列值,其查詢結 果中列出被鏈接表中的全部列,包括其中的重複列。例,下面使用等值鏈接列出authors和publishers表中位於同一城市的做者和出版社:
SELECT * FROM authors AS a INNER JOIN publishers AS p ON a.city=p.city 複製代碼
不等鏈接: 在鏈接條件使用除等於運算符之外的其它比較運算符比較被鏈接的 列的列值。這些運算符包括>、>=、<=、<、!>、!<
和<>
。
天然鏈接:在鏈接條件中使用等於(=)運算符比較被鏈接列的列值,但它使用選 擇列表指出查詢結果集合中所包括的列,並刪除鏈接表中的重複列。
例,在選擇列表中刪除authors 和publishers 表中重複列(city和state):
SELECT a.*,p.pub_id,p.pub_name,p.country FROM authors AS a INNER JOIN publishers AS p ON a.city=p.city複製代碼
外鏈接,返回到查詢結果集合中的不只包含符合鏈接條件的行,並且還包括左表(左外鏈接或左鏈接)、右表(右外鏈接或右鏈接)或兩個邊接表(全外鏈接)中的全部數據行。
例如1:
SELECT a.*,b.* FROM luntan LEFT JOIN usertable as b ON a.username=b.username複製代碼
例如2:
SELECT a.*,b.* FROM city as a FULL OUTER JOIN user as b ON a.username=b.username複製代碼
交叉鏈接不帶 WHERE
子句,它返回被鏈接的兩個表全部數據行的「笛卡爾積」,返回到結果集合中的數據行數等於第一個表中符合查詢條件的數據行數乘以第二個表中符合查詢條件的數據行數。
例,titles表中有6類圖書,而publishers表中有8家出版社,則下 列交叉鏈接檢索到的記錄數將等於6*8=48行。
例如:
SELECT type,pub_name FROM titles CROSS JOIN publishers ORDER BY type複製代碼
笛卡爾積是兩個表每個字段相互匹配,去掉where
或者inner join
的等值 得出的結果就是笛卡爾積。笛卡爾積也等同於交叉鏈接。
MySQL有三種鎖的級別:頁級、表級、行級。
死鎖的關鍵在於:兩個(或以上)的Session加鎖的順序不一致。
那麼對應的解決死鎖問題的關鍵就是:讓不一樣的session加鎖有次序。
SELECT trx_MySQL_thread_id FROM information_schema.INNODB_TRX;複製代碼
Innodb 行鎖的等待時間,單位秒。可在會話級別設置,RDS 實例該參數的默認值爲 50(秒)。
生產環境不推薦使用過大的 innodb_lock_wait_timeout
參數值
該參數支持在會話級別修改,方便應用在會話級別單獨設置某些特殊操做的行鎖等待超時時間,以下:
set innodb_lock_wait_timeout=1000; —設置當前會話 Innodb 行鎖等待超時時間,單位秒。複製代碼
char的長度是不可變的,而varchar的長度是可變的。
定義一個char[10]和varchar[10]。
若是存進去的是‘csdn’,那麼char所佔的長度依然爲10,除了字符‘csdn’外,後面跟六個空格,varchar就立馬把長度變爲4了,取數據的時候,char類型的要用trim()去掉多餘的空格,而varchar是不須要的。
char的存取數度仍是要比varchar要快得多,由於其長度固定,方便程序的存儲與查找。
char也爲此付出的是空間的代價,由於其長度固定,因此不免會有多餘的空格佔位符佔據空間,可謂是以空間換取時間效率。
varchar是以空間效率爲首位。
char的存儲方式是:對英文字符(ASCII)佔用1個字節,對一個漢字佔用兩個字節。
varchar的存儲方式是:對每一個英文字符佔用2個字節,漢字也佔用2個字節。
二者的存儲數據都非unicode的字符數據。
MySQL 高併發環境解決方案 分庫 分表 分佈式 增長二級緩存。。。。。
需求分析:互聯網單位 天天大量數據讀取,寫入,併發性高。
Undo Log是爲了實現事務的原子性,在MySQL數據庫InnoDB存儲引擎中,還用了Undo Log來實現多版本併發控制(簡稱:MVCC)。
事務的原子性(Atomicity)事務中的全部操做,要麼所有完成,要麼不作任何操做,不能只作部分操做。若是在執行的過程當中發生了錯誤,要回滾(Rollback)到事務開始前的狀態,就像這個事務歷來沒有執行過。
原理Undo Log的原理很簡單,爲了知足事務的原子性,在操做任何數據以前,首先將數據備份到一個地方(這個存儲數據備份的地方稱爲UndoLog)。而後進行數據的修改。若是出現了錯誤或者用戶執行了ROLLBACK語句,系統能夠利用Undo Log中的備份將數據恢復到事務開始以前的狀態。
之因此能同時保證原子性和持久化,是由於如下特色:
缺陷:每一個事務提交前將數據和Undo Log寫入磁盤,這樣會致使大量的磁盤IO,所以性能很低。
若是可以將數據緩存一段時間,就能減小IO提升性能。可是這樣就會喪失事務的持久性。所以引入了另一種機制來實現持久化,即Redo Log。