[數據庫]數據庫面試基本知識點《按模塊》

原文鏈接:https://www.cnblogs.com/wenxiaofei/p/9853682.html

個人記錄,僅做學習。。。。。

第一部分 基本概念

1 主鍵、外鍵、超鍵、候選鍵

超鍵: 在關係中能唯一標識元祖的屬性集稱爲關係模式的超鍵。一個屬性可以作爲一個超鍵,多個屬性組合也可以作爲一個超鍵。超鍵包含候選鍵和主鍵。

候選鍵: 是最小超鍵,即沒有冗餘元素的超鍵

主鍵: 數據庫表對儲存數據對象予以唯一和完整標識的數據列或屬性的組合。一個數據列只能有一個主鍵,且主鍵的取值不能缺失,即不能爲空值(Null)。

外鍵: 在一個表中存在的另一個表的主鍵稱爲此表的外鍵。

2 爲什麼用自增列作爲主鍵

  • 如果我們定義了主鍵(PRIMARY KEY),那麼InnoDB會選擇主鍵作爲聚集索引。
  • 如果沒有顯式定義主鍵,則InnoDB會選擇第一個不包含有NULL值的唯一索引作爲主鍵索引
  • 如果也沒有這樣的一個唯一索引,則InnoDB會選擇內置6字節長的ROWID作爲隱含的聚集索引(ROWID隨着行記錄的寫入而主鍵遞增,這個ROWID不像ORACL的ROWID那樣可引用,是隱含的)
  • 數據記錄本身被存於主索引(一顆B+Tree)的葉子節點上。這就要求同一個葉子節點內(大小爲一個內存頁或磁盤頁)的各條數據記錄按主鍵順序存放,因此每當有一條新的記錄插入時,MySQL會根據其主鍵將其插入適當的節點和位置,如果頁面達到裝載因子(InnoDB默認爲15/16),則開闢一個新的頁(節點)
  • 如果使用非自增主鍵(如身份證號和學號等),由於每次插入主鍵的值近似於隨機,因此每次新紀錄都要被插到現有索引頁的中間某個位置,此時MySQL不得不爲了將新紀錄插到合適的位置而移動數據,甚至目標頁面可能被回寫到磁盤上而從緩存中清掉,此時又要從磁盤上讀回來,這增加了很多開銷,同時頻繁的移動、分頁操作造成了大量的碎片,得到了不夠緊湊的索引結構,後續不得不通過OPTIMIZE TABLE 來重建表並優化填充頁面。

3 觸發器的作用?

觸發器是一種特殊的存儲過程,主要是通過事件來觸發而被執行的。它可以強化約束,來維護數據的完整性和一致性,可以跟蹤數據庫內的操作從而不允許味精許可的更新和變化,可以聯級運算(如某表上的觸發器包含對另一個表的數據操作,而該操作又會導致該表觸發器被觸發)。

4 什麼是存儲過程?用什麼來調用?

存儲過程是一個預編譯的SQL語句,優點是允許模塊化的設計,就是說只需創建一次,以後在該程序中就可以調用多次。如果某次操作需要執行多次SQL,使用存儲過程比單純SQL語句執行要快。

調用:
(1) 可以用一個命令對象來調用存儲過程
(2) 可以供外部程序調用,比如:Java程序

5 存儲過程的優缺點?

優點:
(1)存儲過程是預編譯過的,執行效率高
(2)存儲過程的代碼直接存放於數據庫中,通過存儲過程名直接調用,減少網絡通訊
(3)安全性高,執行存儲過程需要有一定權限的用戶
(4)存儲過程可以重複使用,可以減少數據庫開發人員的工作量

缺點: 移植性差

6 存儲過程與函數的區別與聯繫

原文鏈接:https://www.cnblogs.com/karrya/p/11600130.html

函數:
函數與存儲過程相似,也是數據庫中存儲的已命名PL-SQL程序塊。
函數的主要特徵是它必須有一個返回值,通過return 來指定函數的返回類型。
在函數的任何地方可以通過return expression 語句從函數返回,返回類型必須和聲明的返回類型一致。

函數和存儲過程的優點

(1)共同使用的代碼可以只需要被編寫一次,而被需要該代碼的任何程序(.net, c++.java,也可以使用DLL庫)
(2)這種模塊化的方法使得一個複雜的問題、大的程序逐步簡化成幾個簡單的、小的程序部分,進行分別編寫,因此程序的結構更加清晰,簡單,也容易實現。
(3)可以在各個開發者之間提供處理數據、控制流程、提示信息等方面的一致性。
(4)節省內存空間。它們以一種壓縮的形式被存儲在外存中,當被調用時才被放入內存進行處理。而且多個用戶在調用同一個存儲過程或函數時,只需要加載一次即可。
(5)提高數據的安全性和完整性。通過把一些對數據的操作方法放到存儲過程或函數中,就可以通過是否授予用戶有執行該語句的權限,來限制某些用戶對數據庫進行這些操作。

函數和存儲過程的區別

(1)存儲過程 用戶在數據庫中完成特定操作或者任務(如插入、刪除等)函數 用於返回特定的數據。
(2) 存儲過程聲明用procedure,函數用function。
(3)存儲過程不需要返回類型, 函數 必須要返回類型。
(4)存儲過程可作爲獨立的pl-sql執行, 函數不能作爲獨立的pl-sql執行,必須作爲表達式的一部分。
(5)存儲過程只能通過out和in/out 來返回值,函數除了ut和in/out 外,還可以使用return返回值
(6)sql語句(DML或SELECT)中不可調用存儲過程,而函數可以。

適用場合

(1)如果需要返回多個值和不返回值,就使用存儲過程; 如果只需要返回一個值,就使用函數。
(2)存儲過程 一般用於執行一個指定的動作, 函數 一般用於計算和返回一個值。

(3)可以在SQL內部調用函數來完成複雜的計算問題,但不能調用存儲過程。

存儲過程與存儲函數的區別和聯繫

相同點:
(1) 創建語法結構相似, 都可以攜帶多個傳入參數和傳出參數
(2) 都是一次編譯,多次執行

不同點:
(1)存儲過程定義關鍵字用procedure, 函數定義用function
(2)存儲過程中不能用return 返回值, 但函數可以,而且函數中必須有return 子句
(3)執行方式略有不同,存儲過程的執行方式用兩種(一是使用execute ,二是使用begin和end)。
函數除了存儲過程的兩種方式外,還可以當作表達式使用,例如放在select中(select f1()from dual)

總結:如果只有一個返回值,用存儲函數,否則,一般用存儲過程。


7 什麼叫視圖?遊標是什麼?

視圖:
是一種虛擬的表,具有和物理表相同的功能。
可以對視圖進行增、改、查操作,視圖通常是有一個表或者多個表的行或列的子集。
對視圖的修改會影響基本表,它使得我們獲取數據更容易,相比多表查詢。

遊標:
是對查詢出來的結果集作爲一個單元來有效的處理。
遊標可以定在該單元中的特定行,從結果集的當前行檢索一行或多行,可以對結果集當前行做修改。
一般不使用遊標,但是需要逐條處理數據的時候,遊標顯得十分重要。

8 視圖的優缺點

優點:
(1)方便對數據庫的訪問,因爲視圖可以有選擇性的選擇數據庫裏的一部分
(2)用戶通過簡單的查詢可以從複雜查詢裏得到結果
(3)維護數據的獨立性,試圖可以從多個表檢索數據
(4)對於相同的數據可產生不同的視圖

缺點:
性能:
查詢視圖時,必須把視圖的查詢轉化成對基本表的查詢,如果這個視圖是由一個複雜的多表查詢所定義,那麼就無法更改數據

9 drop, truncate, delete 區別

drop直接刪掉表
truncate 刪除表中數據,再插入時自增長id又從1開始
delete刪除表中數據,可以加where 子句

  • (1) delete 語句執行刪除的過程是每次從表中刪除一行,並且同時將該行的刪除操作作爲事務記錄在日誌中保存以便進行回滾操作。
    truncate table 則一次性的從表中刪除所有的數據並不把單獨的刪除操作記錄記入日誌保存,刪除行是不能恢復的,並且在刪除的過程中不會激活與表有關的刪除觸發器,執行速度快。

  • (2)表和索引所佔空間
    當表被truncate 後,這個表和索引所佔用的空間會恢復到初始大小
    delete操作不會減少表或索引所佔用的空間
    drop語句將表所佔用的空間全釋放掉

  • (3) 就速度,一般而言,drop > truncate > delete

  • (4)應用範圍
    truncate 只能對table,
    delete 可以是table和view

  • (5)truncate 和delete 只刪除數據, drop則刪除整個表(結構和數據)

  • (6) truncate 是不帶where 的delete:只刪除數據,而不刪除表的結構(定義)
    drop語句將刪除表的結構被依賴的約束(constrain),觸發器(trigger),索引(index);依賴於該表的存儲過程/函數將被保留,但其狀態會變爲:invalid。

  • (7)delete語句爲DML(data maintain Language)
    這個操作將會被放到rollback segment 中,事務提交後才生效,如果有相應的tigger,執行的時候將被觸發。

  • (8)truncate、drop是DDL(data define language)
    操作立即生效,原數據不放到rollback segment中, 不能回滾

  • (9)在沒有備份情況下,謹慎使用drop與truncate。
    要刪除部分數據行採用delete且注意結合where來約束影響範圍,回滾段要足夠大。
    要刪除表用drop
    若想保留表而將表中數據刪除,如果與事務無關,用truncate即可實現,如果和事務有關,或想觸發trigger,還是用delete。

  • (10)truncate table 表名, 速度快,而且效率高,因爲:
    truncate table 在功能上與不帶where子句的delete語句相同,二者均刪除表中的全部行。
    但truncate table 比delete 速度快,且使用的系統和事務日誌資源少
    delete 語句每次刪除一行,並在事務日誌中爲所刪除的每行記錄一項
    truncate table 通過釋放存儲表數據所用的數據頁來刪除數據,並且只在事務日誌中記錄頁的釋放。

  • (11) TRUNCATE TABLE 刪除表中的所有行,但表結構及其列、約束、索引等保持不變。新行標識所用的計數值重置爲該列的種子。如果想保留標識計數值,請改用 DELETE。如果要刪除表定義及其數據,請使用 DROP TABLE 語句。

  • (12) 對於由 FOREIGN KEY 約束引用的表,不能使用 TRUNCATE TABLE,而應使用不帶 WHERE 子句的 DELETE 語句。由於 TRUNCATE TABLE 不記錄在日誌中,所以它不能激活觸發器。

總結
1、在速度上,一般來說,drop> truncate > delete。
2、在使用drop和truncate時一定要注意,雖然可以恢復,但爲了減少麻煩,還是要慎重。
3、如果想刪除部分數據用delete,注意帶上where子句,回滾段要足夠大;
如果想刪除表,當然用drop;
如果想保留表而將所有數據刪除,如果和事務無關,用truncate即可;
如果和事務有關,或者想觸發trigger,還是用delete;
如果是整理表內部的碎片,可以用truncate跟上reuse stroage,再重新導入/插入數據。

10 什麼是臨時表,臨時表什麼時候刪除?

臨時表可以手動刪除
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;

11 非關係型數據庫和關係型數據區別,優勢比較?

非關係型數據庫的優勢:
性能:NOSQL 是基於鍵值對的,可以想象成表中的主鍵和值的對應關係,而且不需要經過SQL層的解析,所以性能非常高
可拓展性:同樣也是因爲基於鍵值對,數據之間沒有耦合性,所以非常容易水平拓展

關係型數據庫的優勢:

複雜查詢:可以用SQL語句方便的在一個表以及多個表之間做非常複雜的數據查詢
事務支持:使得對於安全性能很高的數據訪問要求得以實現

其它:
(1)對於這兩類數據庫,對方的優勢就是自己弱勢,反之亦然
(2)NOSQL 數據庫慢慢開始具備SQL數據庫的一些複雜查詢功能,比如MongoDB
(3)對於事務的支持也可以用一些系統級的原子操作來實現例如樂觀鎖之類的方法來曲線救國,比如Redis set nx。

12 數據庫範式,根據某個場景設計數據表?

目前關係數據庫有六種範式:第一範式(1NF)、第二範式(2NF)、第三範式(3NF)、巴斯-科德範式(BCNF)、第四範式(4NF)和第五範式(5NF,又稱完美範式)

13 什麼是內連接、外連接、交叉連接、笛卡爾積等?

內連接:只連接匹配的行
左外連接: 包含左邊表的全部行(不管右邊的表中是否存在與他們匹配的行),以及右邊表中全部匹配的行
右外連接:包含右邊表的全部行(不管左邊的表中是否存在與它們匹配的行),以及左邊表中全部匹配的行

例如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

全外連接:包含左右兩個表的全部行,不管另外一邊的表中是否存在與它們匹配的行
交叉連接:生成笛卡爾積- 它不使用任何匹配或者選取條件,而是直接將一個數據源中的每個行與另一個數據源的每個行都一一匹配

例如:
SELECT type,pub_name FROM titles CROSS JOIN publishers ORDER BY type

14 varchar 和char 的使用場景?

  • (1)char 的長度是不可變的,而varchar的長度是可變的
    定義一個char[10] 和 varchar[10]
    如果存進去的是“csdn”,那麼char所佔的長度依然爲19,除了字符“csdn”外,後面跟6個空格,
    varchar就立馬馬長度變爲4了,去數據的時候,char類型的要用trim()去掉多餘的空格,而varchar是不需要的

  • (2)char的存取速度要比varchar要快得多,因爲其長度固定,方便程序的存儲與查找
    char也爲此付出的是空間的代價,因爲其長度固定,所以難免會有多餘的空格佔位符佔據空間,可謂是1以空間換取時間效率。
    varchar是1以空間效率爲首位。

  • (3)char的存儲方式:對英文字符(ASCII)佔用1個字節,對漢字佔用2個字節
    varchar的存儲方式是:對英文字符(ASCII)佔用2個字節,對漢字也佔用2個字節

  • (4)兩者的存儲數據都非unicode的字符數據。

15 SQL語言分類

SQL語言共分爲四大類:
(1)數據查詢語言DQL
(2)數據操縱語言DML
(3)數據定義語言DDL
(4)數據控制語言DCL

  • (1)數據查詢語言DQL
    數據查詢語言DQL基本機構是由SELECT子句,FROM子句,WHERE子句組成的查詢塊:
    SELECT
    FROM
    WHERE

  • (2)數據操縱語言DML
    數據操縱語言DML 主要有三種形式:
    一是插入:INSERT
    二是更新:UPDATE
    三是刪除:DELETE

  • (3)數據定義語言DDL

數據定義語言DDL用來創建數據庫中的各種對象–表、視圖、索引、同義詞、聚簇等如CREATE TABLE/VIEW/INDEX/SYN/CLUSTER

DDL操作是隱性提交的,不能rollback

  • (4)數據控制語言DCL
    數據控制語言DCL 用來授予或回收訪問數據庫的某項特權,並控制數據庫操縱事務發生的時間及效果,對數據庫實行監視等。如:
    一.GRANT :授權
    二.ROLLBACK[WORK] TO [SAVEPOINT]:回退到某一點。
    —回滾:ROLLBACK; 回滾命令使數據庫狀態回到上次最後提交的狀態,其格式爲:SQL>ROLLBACK
    三. COMMIT[WORK]:提交

在數據庫的插入、刪除和修改操作時,只有當事務在提交到數據庫時纔算完成。
在事務提交前,只有操作數據庫的這個人才能有權看到所做的事情,別人自幼在最後提交完成後纔可以看到。
提交數據有三種類型:顯式提交、隱式提交及自動提交。
一:顯式提交
用COMMIT命令直接完成的提交爲顯式提交。其格式爲:
SQL>COMMIT
二:隱式提交
用SQL命令間接完成的提交爲隱式提交。這些命令是:
ALTER,ADUIT,COMMIT,CONNECT,CREATE,DISCONNECT, DROP, EXIT, GRANT,NOAUDT,QUIT,REVOKE,RENAME
三. 自動提交
若把AUTOCOMMIT設置爲ON,則在插入、修改、刪除語句執行後,系統將自動進行提交,這就是自動提交。其格式爲:
SQL>SET AUTOCOMMIT ON

16 like % 和 - 的區別

通配符的分類:
% 百分號通配符:表示任何字符出現任意次數(可以是0次)
_ 下劃線通配符:表示只能匹配單個字符,不能多也不能少,就是一個字符
like 操作符:LIKE作用是指示mysql後面的搜索模式是利用通配符而不是直接相等匹配進行比較

注意: 如果在使用like操作符時,後面的沒有使用通用匹配符效果是和=一致的,SELECT * FROM products WHERE products.prod_name like ‘1000’;
只能匹配的結果爲1000,而不能匹配像JetPack 1000這樣的結果.

%通配符使用: 匹配以"yves"開頭的記錄:(包括記錄"yves") SELECT FROM products WHERE products.prod_name like ‘yves%’;
匹配包含"yves"的記錄(包括記錄"yves") SELECT FROM products WHERE products.prod_name like ‘%yves%’;
匹配以"yves"結尾的記錄(包括記錄"yves",不包括記錄"yves ",也就是yves後面有空格的記錄,這裏需要注意) SELECT * FROM products WHERE products.prod_name like ‘%yves’;

通配符使用: SELECT FROM products WHERE products.prod_name like ‘_yves’; 匹配結果爲: 像"yyves"這樣記錄.
SELECT FROM products WHERE products.prodname like 'yves’; 匹配結果爲: 像"yvesHe"這樣的記錄.(一個下劃線只能匹配一個字符,不能多也不能少)

注意事項:

注意大小寫,在使用模糊匹配時,也就是匹配文本時,mysql是可能區分大小的,也可能是不區分大小寫的,這個結果是取決於用戶對MySQL的配置方式.如果是區分大小寫,那麼像YvesHe這樣記錄是不能被"yves__“這樣的匹配條件匹配的.
注意尾部空格,”%yves"是不能匹配"heyves "這樣的記錄的.
注意NULL,%通配符可以匹配任意字符,但是不能匹配NULL,也就是說SELECT * FROM products WHERE products.prod_name like '%;是匹配不到products.prod_name爲NULL的的記錄.

技巧與建議:

正如所見, MySQL的通配符很有用。但這種功能是有代價的:通配符搜索的處理一般要比前面討論的其他搜索所花時間更長。這裏給出一些使用通配符要記住的技巧。

  • 不要過度使用通配符。如果其他操作符能達到相同的目的,應該 使用其他操作符。
  • 在確實需要使用通配符時,除非絕對有必要,否則不要把它們用 在搜索模式的開始處。把通配符置於搜索模式的開始處,搜索起 來是最慢的。
  • 仔細注意通配符的位置。如果放錯地方,可能不會返回想要的數.

17 count(*)、count(1)、count(column)的區別

count() 對行的數目進行計算,包含NULL
count(column)對特定的列的值具有的行數進行計算,不包含NULL值
count()還有一種使用方式,count(1)這個用法和count(
)的結果是一樣的

性能問題:
(1)任何情況下SELECT COUNT() FROM tablename 是最優選擇
(2)儘量減少SELECT COUNT(
) FROM tablename WHERE COL = ‘value’這種查詢
(3)杜絕SELECT COUNT(COL) FROM tablename WHERE COL2 = ‘value’的出現
如果表沒有主鍵,那麼count(1)比count(*)快。

如果有主鍵,那麼count(主鍵,聯合主鍵)比count(*)快。

如果表只有一個字段,count(*)最快。

count(1)跟count(主鍵)一樣,只掃描主鍵。count(*)跟count(非主鍵)一樣,掃描整個表。明顯前者更快一些。

18 最左前綴原則

解釋一下最左前綴原則: 2.當b+樹的數據項是複合的數據結構,比如(name,age,sex)的時候,b+數是按照從左到右的順序來建立搜索樹的,比如當(張三,20,F)這樣的數據來檢索的時候,b+樹會優先比較name來確定下一步的所搜方向,如果name相同再依次比較age和sex,最後得到檢索的數據;但當(20,F)這樣的沒有name的數據來的時候,b+樹就不知道下一步該查哪個節點,因爲建立搜索樹的時候name就是第一個比較因子,必須要先根據name來搜索才能知道下一步去哪裏查詢。比如當(張三,F)這樣的數據來檢索時,b+樹可以用name來指定搜索方向,但下一個字段age的缺失,所以只能把名字等於張三的數據都找到,然後再匹配性別是F的數據了, 這個是非常重要的性質,即索引的最左匹配特性。


第二部分 索引

1 什麼是索引?

數據庫索引,是數據庫管理系統中一個排序的數據結構,索引的實現通常使用B數及其變種B+樹
在數據之外,數據庫系統還維護着滿足特定查找算法的數據結構,
這些數據結構以某種方式引用(指向)數據,這樣就可以在這些數據結構上實現高級查找算法。這種數據結構,就是索引。

2 索引的作用?

索引作用: 協助快速查詢,更新數據表中數據。
爲表設置索引是要付出代價的:
一是增加了數據庫的存儲空間
二是在插入和修改數據時要話費較多的時間(因爲索引也要隨之變動)

3 索引的優點缺點是什麼?

創建索引可以大大提高系統的性能(優點)
(1)通過創建唯一性索引,可以保證數據庫表中每一行數據的唯一性
(2)可以大大加快數據的檢索速度,這也是創建索引的最主要的原因
(3)可以加速表和表之間的連接,特別是在實現數據的參考完整性方面特別有意義
(4)在使用分組和排序子句進行數據檢索時,同樣可以顯著減少查詢中分組和排序的時間
(5)通過使用索引,可以在查詢的過程中,使用優化隱藏器,提高系統的性能

增加索引也有許多不利的方面(缺點)
(1)創建索引和維護索引要耗費時間,這種時間隨着數據量的增加而增加
(2)索引需要佔物理空間,除了數據表佔數據空間之外,每一個索引還要佔一定的物理空間,如果要建立聚簇索引,那麼需要的空間就會更大。
(3)當對錶中的數據進行增加、刪除和修改的時候,索引也要動態的維護,這樣就降低了數據的維護速度。

4 哪些列適合建立索引,哪些不適合建索引?

索引是建立在數據庫表中的某些列的上面。在創建索引的時候,應該考慮在哪些列上可以創建索引,哪些列上不能創建索引。

一般來說,應該在這些列上創建索引:
(1)在經常需要搜索的列上,可以加快搜索的速度
(2)在作爲主鍵的列上,強制該列的唯一性和組織表中數據的排列結構
(3)在經常用在連接的列上,這些列主要是一些外鍵,可以加快連接的速度
(4)在經常需要根據範圍進行搜索的列上創建索引,因爲索引已經排序,其指定的範圍是連續的
(5)在經常需要排序的列上創建索引,因爲索引已經排序,這樣查詢可以利用索引的排序,加快排序查詢時間
(6)在經常使用在WHERE子句中的列上面創建索引,加快條件的判斷速度

對於有些列不應該創建索引:
(1)對於那些在查詢中很少使用或者參考的列不應該創建索引。
這是因爲,既然這些列很少使用到,因此有索引或者無索引,並不能提高查詢速度。
相反,由於增加了索引,反而降低列系統的維護速度和增大了空間需求

(2)對於那些只有很少數據值的列也不應該增加索引
這是因爲,由於這些列的取值很少,例如人事表的性別列,在查詢的結果中,結果集的數據行佔列表中數據行的很大比例,
即需要在表中搜索的數據行的比例很大。增加索引,並不能明顯加快檢索速度。

(3)對於那些定義爲text,image 和 bit 數據類型的列不應該增加索引
這是因爲,這些列的數據量要麼相當大,要麼取值很少

(4)當修改性能遠遠大於檢索性能時,不應該創建索引
這是因爲,修改性能和檢索性能是互相矛盾的。
當增加索引時,會提高檢索性能,但是會降低修改性能。當減少索引時,會提高修改性能,降低檢索性能。
因此,當修改性能遠遠大於檢索性能時,不應該創建索引。

5 什麼樣的字段適合建索引?

唯一、不爲空、經常被查詢的字段

6 MySQL B+Tree 和 Hash 索引的區別?

Hash 索引和B+樹索引的特點

  • Hash 索引結構的特殊性,其檢索效率非常高,索引的檢索可以一次定位
  • B+樹索引需要從根節點到枝節點,最後才能訪問到頁節點這樣的多次IO訪問

爲什麼不都用Hash索引而使用B+ 樹索引?

  • (1)Hash索引僅僅能滿足“=” , “IN” 和 “”查詢,不能使用範圍查詢,
    因爲經過相應的Hash算法處理之後的Hash值的大小關係,並不能保證和Hash運算前完全一樣

  • (2)Hash 索引無法被用來避免數據的排序操作,
    因爲Hash值的大小關係並不一定和Hash運算前的鍵值完全一樣

  • (3)Hash索引不能利用部分索引鍵查詢,
    對於組合索引,Hash索引在計算Hash值的時候是組合索引鍵合併後再一起計算Hash值,而不是單獨計算Hash值,
    所以通過組合索引的前面一個或者幾個索引鍵進行查詢的時候,Hash索引也無法被利用

  • (4)Hash索引在任何時候都不能避免表掃描,
    由於不同索引鍵存在相同Hash值,所以即使取滿足某個Hash鍵值的數據的記錄條數,也無法從Hash索引中直接完成查詢,還是要回表查詢數據

  • (5)Hash索引遇到大量的Hash值相等的情況後性能並不一定就會比B+樹索引高

補充:

(1)MySQL 中,只有HEAP/MEMORY 引擎才顯示支持Hash索引。
(2)常用的InnoDB引擎中默認使用的是B+樹索引,它會實時監控表上索引的使用情況,
如果認爲建立哈希索引可以提高查詢效率,則自動在內存中“自適應哈希索引緩衝區”建立哈希索引(在InnoDB中默認開啓自適應哈希索引)
通過觀察搜索模式,MySQL會利用index key 的前綴建立哈希索引,
如果一個表幾乎大部分都在緩衝池中,那麼建立一個哈希索引能夠加快等值查詢

B+樹索引和哈希索引的明顯區別是:
(1) 如果是等值查詢,那麼哈希索引明顯有絕對優勢,因爲只需經過一次算法即可找到相應的鍵值;
當然了,這個前提是,鍵值都是唯一的,如果鍵值不是唯一的,就需要先找到該鍵所在位置,然後再根據鏈表往後掃描,直到找到相應的數據;
(2) 如果是範圍查詢檢索,這時候哈希索引毫無用武之地了,
因爲原先是有序的鍵值,經過哈希算法後,有可能變成不連續的了,就沒辦法再利用索引完成範圍查詢檢索
同理,哈希索引沒辦法利用索引完成排序,以及like“XXX%” 這樣的部分模糊查詢(這種模糊查詢,其實本質上也是範圍查詢)
(3) 哈希索引也不支持多列聯合索引的最左匹配原則

(4)B+樹索引的關鍵字檢索效率比較平均,不像B樹那樣波動幅度大,在有大量重複鍵值情況下,哈希索引的效率也是極低的,因爲存在所謂的哈希碰撞蘇問題

(5) 在大多數場景下,都會有範圍查詢、排序、分組等查詢特徵,用B+樹索引就可以了

7 B樹和B+樹的區別

B樹,每個節點都存儲key和data,所有節點組成這棵樹,並且葉子節點指針爲null,葉子節點不包含任何關鍵字信息。

B+樹,所有的葉子節點中包含了全部關鍵字的信息,及指向含有這些關鍵字記錄的指針,且葉子節點本身依關鍵字的大小自小而大的順序鏈接,所有的非終端節點可以看成是索引部分,節點中僅含有其子樹根節點中最大(或最小)關鍵字。(而B樹的非終端節點也包含需要查找的有效信息)。

8 爲什麼說B+樹比B樹更適合實際應用中操作系統的文件索引和數據庫索引?

  • 1 B+ 的磁盤讀寫代價更低
    B+的內部節點並沒有指向關鍵字具體信息的指針,因此其內部節點相對B樹更小。
    如果把所有同一內部節點的關鍵字存放在同一盤塊中,那麼盤塊所能容納的關鍵字數量也越多。
    一次性讀入內存中的查找需要的關鍵字也就越多。相對來說IO讀寫次數也就降低了。

  • 2 B+tree 的查詢效率更加穩定
    由於非終端結點並不是最終指向文件內容的結點,而只是葉子結點中關鍵字的索引。
    所以任何關鍵字的查找必須走一條從根節點到葉子結點的路。
    所有關鍵字查詢的路徑長度相同,導致每一個數據的查詢效率相當。

9 聚集索引和非聚集索引區別

聚合索引(clustered index)

聚集索引表記錄的排列順序和索引的排列順序一致,所以查詢效率快。
只要找到第一個索引值記錄,其餘就連續性的記錄在物理也一樣連續存放。
聚集索引對應的缺點就是修改慢,因爲爲了保證表中記錄的物理和索引順序一致,在記錄插入的時候,會對數據頁重新排序。

聚集索引類似於新華字典中用拼音去查找漢字,拼音檢索表與索引序列都是按照a~z排列的,就像相同的邏輯順序與物理順序一樣,當你需要查找a, ai 兩個讀音的字,或者想一次尋找多個傻(sha)的同音字時, 也許往後翻幾頁,或緊接着下一行就得到結果了。

非聚合索引(nonclustered index)

非聚集索引 ** 指定了表中記錄的邏輯順序,但是記錄的物理和索引不一定一致。**
兩種索引都採用B+ 樹結構,非聚集索引的葉子層並不和實際數據頁相重疊,而採用葉子層包含一個指向表中的記錄在數據頁中的指針方式。
非聚集索引層次多,不會造成數據重排。

非 聚集索引類似在新華字典上通過偏旁部首來查詢漢字,檢索表也許是按照橫、豎、撇來排列的,但是由於正文是a~z 的拼音順序,
所以就類似於邏輯地址與物理地址的不對應。
同時適用的情況就在於分組,大數目的不同值,頻繁更新的列中,這些情況即不適合聚集索引。

根本區別

聚集索引和非聚集索引的根本區別是表記錄的排列順序和索引的排列順序是否一致。


第三部分:事務

1 什麼是事務?

事務是對數據庫中一系列操作進行統一的回滾或者提交的操作,主要用來保證數據的完整性和一致性。

2 事務四大特性(ACID) 原子性、一致性、隔離型、持久性?

  • 原子性(Atomicity)
    原子性是指事務所包含的所有操作要麼全部成功,要麼全部失敗回滾,
    因此事務的操作如果成功就必須要完全應用到數據庫,如果操作失敗則不能對數據庫有任何影響。

  • 一致性(Consistency)
    事務開始前和結束後,數據庫的完整性約束沒有被破壞。比如A向B轉賬,不可能A扣了錢,B卻沒有收到

  • 隔離性(Isolation)
    隔離性是當多個用戶併發訪問數據庫時,比如操作同一張表時,數據庫爲每一個用戶開啓的事務,不可能被其他事務的操作所幹擾,多個併發事務之間要相互隔離。
    同一時間,只允許一個事務請求同一數據,不同的事務之間彼此沒有任何干擾。
    比如A正在從一張銀行卡中取錢,在A取錢的過程結束前,B不能向這張卡轉賬。

  • 持久性 (Durability)
    持久性是指一個事務一旦被提交了,那麼對數據庫中的數據的改變就是永久性的,
    即便是在數據庫系統遇到故障的情況下也不會丟失提交事務的操作。

3 事務的併發?事務隔離級別,每個級別會引發什麼問題?MySQL默認是哪個級別?

從理論上來說,事務應該彼此完全隔離,以避免併發事務所導致的問題,
然後,那樣會對性能產生極大的影響,因此事務必須按順序運行,
在實際開發中,爲了提升性能,事務會以較低的隔離級別運行,事務的隔離級別可以通過隔離事務屬性指定。

  • 事務的併發問題
    (1)髒讀
    事務A讀取了事務B更新的數據,然後B回滾操作,那麼A讀取到的數據是髒數據

(2)不可重複讀
事務A多次讀取同一數據,事務B在事務A多次讀取的過程中,對數據做了更新並提交,導致事務A多次讀取同一數據時,結果導致本事務先後兩次讀到的數據結果會不一致。

(3)幻讀
幻讀解決了不重複讀,保證了同一個事務裏,查詢的結果都是事務開始時的狀態(一致性)。
例如:事務T1對一個表中所有的行的某個數據項做了從“1” 修改爲“2”的操作,
這時事務T2又對這個表中插入了一行數據,而這個數據項的數值還是爲“1”並且提交給數據庫。
而操作事務T1的用戶如果再查看剛剛修改的數據,會發現還有跟沒修改的一樣,
其實這行是從事務T2中添加的,就好像產生幻覺一樣,這就是發生了幻讀。

小結:不可重複讀和幻讀很容易混淆,不可重複讀側重於修改,幻讀側重於新增或刪除。
解決不可重複讀的問題只需鎖住滿足條件的行,解決幻讀需要鎖表。

  • 事務的隔離級別
    (1)讀未提交
    另一個事務修改了數據,但尚未提交,而本事務中的SELECT會讀到這些未被提交的數據髒讀

(2)不可重複讀
事務A多次讀取同一數據,事務B在事務A多次讀取的過程中,對數據作了更新並提交,
導致事務A多次讀取同一數據時,結果因此本事務先後兩次讀到的數據結果會不一致。

(3)可重複讀
在同一個事務裏,SELECT的結果是事務開始時時間點的狀態,
因此,同樣的SELECT操作讀到的結果會是一致的。
但是,會有幻讀現象。

(4)串行化
最高的隔離級別,在這個隔離級別下,不會產生任何異常。
併發的事務,就像事務是在一個個按照順序執行一樣。

特別注意

MySQL默認的事務隔離級別爲repeatable-read
MySQL支持4種事務隔離級別
事務的隔離級別要得到底層數據庫引擎的支持,而不是程序或框架的支持

Oracle 支持2種事務隔離級別:Read_COMMITED,SERIALIZABLE
SQL 規範所規定的標準,不同的數據庫具體的實現可能會有些差異

MySQL中默認事務隔離級別是“可重複讀”時並不會鎖住讀取到的行
事務隔離級別爲未提交讀時, 寫數據只會鎖住相應的行
事務隔離級別爲可重複讀時,寫數據會鎖住整張表
事務隔離級別爲
串行化時
,讀寫數據都會鎖住整張表

隔離級別越高,越能保證數據的完整性和一致性,但是對併發性能的影響也越大,魚和熊掌不可兼得啊
對於多數應用程序,可以優先考慮把數據庫系統的隔離級別設爲Read Committed,它能夠避免髒讀取,而且具有較好的併發性能。
儘管它會導致不可重複讀、幻讀這些併發問題,在可能出現這類問題的個別場合,可以由應用程序採用悲觀鎖或樂觀鎖來控制。

4 嵌套事務

什麼是嵌套事務?
嵌套是子事務套在父事務中執行,子事務是父事務的一部分,
在進入子事務之前,父事務建立一個回滾點,叫save point
然後執行子事務,這個子事務的執行也算是父事務的一部分,
然後子事務執行結束,父事務繼續執行。
重點就在於那個save point

看幾個問題就明白啦~~~~

如果子事務發生回滾,會發生什麼?
父事務會回滾到進入子事務之前建立的save point,然後嘗試其他的事務或者其他的業務邏輯,
父事務之前的操作不會受到影響,更不會自動回滾。

如果父事務回滾,會發生什麼?
父事務回滾,子事務也會跟着回滾!!
爲什麼呢?因爲父事務結束之前,子事務是不會提交的,我們說子事務是父事務的一部分,正是這個道理。

那麼?
事務的提交,是什麼情況?
是父事務先提交,然後子事務提交,還是子事務先提交,父事務再提交?
答案是第二種情況,
還是那句話,子事務是父事務的一部分,由父事務統一提交。


第四部分 存儲引擎

1 MySQL常見的兩種存儲引擎(InnoDB,MyISAM)的區別?

  • (1)InnoDB支持事務, MyISAM不支持,這一點非常之重要。
    事務是一種高級的處理方式,如在一些列增刪改中只要哪個出錯還可以回滾還原,而MyISAM就不可以了。

  • (2)MyISAM適合查詢以及插入爲主的應用

  • (3)InnoDB適合頻繁修改及涉及到安全性較高的應用。

  • (4)InnoDB支持外鍵,MyISAM不支持。

  • (5)從MySQL5.5.5 以後,InnoDB是默認引擎。

  • (6)InnoDB不支持FULLTEXT類型的索引

  • (7)InnoDB中不保存表的行數,
    如select count() from table 時,InnoDB需要掃描一遍整個表來計算有多少行
    但是MyISAM只要簡單的讀出保存好的行數即可。
    注意的是,當count()語句包含where條件時MyISAM也需要掃描整個表

  • (8)對於自增長的字段,InnoDB中必須包含只有該字段的索引,
    但是在MyISAM表中可以和其他字段一起建立聯合索引。

  • (9)DELETE From table時,InnoDB不會重新建立表,而是一行一行的刪除,效率非常慢。
    MyISAM則會重建表。

  • (10)InnoDB支持行鎖。(某些情況下還是鎖整表,如 update table set a=1 where user like ‘%lee%’。

2 MySQL 存儲引擎MyISAM與InnoDB如何選擇?

MySQL有多種存儲引擎,每種存儲引擎有各自的優缺點,可以擇優選擇使用:MyISAM、InnoDB、MERGE、MEMORY(HEAP)、BDB(BerkeleyDB)、EXAMPLE、FEDERATED、ARCHIVE、CSV、BLACKHOLE。

雖然MySQL裏的存儲引擎不只是MyISAM與InnoDB這兩個,但常用的就是兩個。

關於MySQL 數據庫提供的兩種存儲引擎,MyISAM與InnoDB選擇使用:
(1)InnoDB會支持一些關係數據庫的高級功能,如事務功能和行級鎖,MyISAM不支持

如果你的應用程序一定要使用事務,毫無疑問你要選擇InnoDB引擎。
但是要注意,InnoDB的行級鎖是有條件的。在where條件沒有使用主鍵時,照樣會鎖全表。
比如 delete from table 這樣的刪除語句。

如果你的應用程序對查詢性能要求較高,就要使用MyISAM了。
MyISAM索引和數據是分開的,而且其索引是壓縮的,可以更好的利用內存,所以它的查詢性能明顯優於InnoDB。
壓縮後的索引也能節約一些磁盤空間。MyISAM擁有全文索引的功能,這可以極大的優化LIKE查詢的效率。

有人說MyISAM只能用於小型應用,其實這是一種偏見,
如果數據量比較大,這是需要通過升級架構來解決,比如分表分庫,而不是單純的依賴存儲引擎。
現在一般都選用InnoDB了,主要是MyISAM的全表鎖,讀寫串行問題,併發效率鎖表,效率低,
MyISAM對於讀寫密集型應用一般不會去選用的。

MEMORY存儲引擎
MEMORY 是MySQL中一類特殊的存儲引擎,它使用存儲在內存中的內容來創建表,而且數據全部放在內存中。這些特性與前面的兩個很不同。

每個基於MEMORY存儲引擎的表實際對應一個磁盤文件,該文件的文件名與表名相同,類型爲frm類型。
該文件中只存儲表的結構,而其數據文件,都是存儲在內存中,這樣有利於數據的快速處理,提高整個表的效率。
值得注意的是,服務器需要有足夠的內存來維持MEMORY存儲引擎的表的使用。
如果不需要了,可以釋放內存,甚至刪除不需要的表。

MEMORY默認使用哈希索引。速度比使用B型樹索引快。當然如果你想用B型樹索引,可以在創建索引時指定。

注意 MEMORY用到的很少,因爲它是把數據存到內存中,如果內存出現異常就會影響數據。
如果重啓或者關機,所有的數據都會消失,因此,基於MEMORY的表的生命週期很短,一般是一次性的。

3 MySQL的MyISAM 與InnoDB兩種搜索引擎在事務,鎖級別,各自的使用場景?

  • 事務處理方面
    **MyISAM:**強調的是性能,每次查詢具有原子性,其執行速度比InnoDB類型更快,但是不提供事務支持
    **InnoDB:**提供事務支持事務,外部鍵等高級數據庫功能。
    具有事務(commit)、回滾(rollback)和崩潰修復能力(crash recovery capabilities)的事務安全transaction-safe (ACID compliant))型表

  • 鎖級別
    **MyISAM:**只支持表級鎖,用戶在操作MyISAM表時,select,update,delete,insert語句都會給表自動加鎖,
    如果加鎖以後的表滿足insert併發的情況下,可以在表的尾部插入新的數據
    **InnoDB:**支持事務和行級鎖,是innoDB最大的特色。行鎖大幅度提高了多用戶併發操作的可能。
    但是InnoDB的行鎖,只是在WHERE的主鍵是有效的,非主鍵的WHERE都會鎖全表的。


第五部分 優化

1 查詢語句不同元素(where, join,limit,group by等)執行先後順序?

  • 查詢用到的關鍵詞主要包含六個,並且他們的順序依次爲select–from–where–group by – having – order by

其中select 和 from 是必須的,其他關鍵詞是可選的,這六個關鍵字的執行順序並不是一樣的,按照下面順序來執行:

(1)from : 需要從哪個數據表中檢索數據
(2)where:過濾表中的數據
(3)group by:如何將上面過濾出的數據分組
(4)having:對上面已經分組的數據進行過濾的條件
(5)select:查看結果集中的哪個列,或列的計算結果
(6)order by:按照什麼樣的順序來查看返回的數據

2 from 後面的表關聯,是自右向左解析,而where條件的解析順序是自下而上的

也就是說,在寫SQL語句的時候,儘量把數據量小的表放在最右邊來進行關聯(用小表去匹配大表),
而把能篩選出小量數據的條件放在where語句的最左邊(用小表去匹配大表)

3 使用explain優化sql和索引?

對於複雜、效率低的sql語句,我們通常是使用explain sql來分析sql語句,這個語句我們可以打印出,語句的執行。這樣方便我們分析,進行優化。

**table:**顯示這一行的數據是關於哪張表的

**type:**這是重要的列,顯示連接使用來何種類型。從最好到最差的連接類型爲const、eq_reg、ref、range、index和ALL

**all:**full table scan; MySQL將遍歷全表以找到匹配的行

index: index scan; index 和all的區別在於index類型只遍歷索引

**range:**索引範圍掃描,對索引的掃描開始於某一點,返回匹配值的行,常見於between等查詢。

**ref:**非唯一性索引掃描,返回匹配某個單獨值的所有行,常見於使用非唯一索引即唯一索引的非唯一前綴進行查找

**eq_ref:**唯一性索引掃描,對於每個索引鍵,表中只有一條記錄與之匹配,常用於主鍵或者唯一索引掃描

**const,system:**當MySQL對某查詢某部分進行優化,並轉爲一個常量時,使用這些訪問類型。如果將主鍵置於where列表中,MySQL就能將該查詢轉化爲一個常量。

**possible_keys:**顯示可能應用在這張表中的索引。如果爲空,沒有可能的索引。可以爲相關的域從where語句中選擇一個合適的語句

**key:**實際使用的索引。如果爲NULL,則沒有使用索引。很少的情況下,MySQL會選擇優化不足的索引。這種情況下,可以在SELECT語句中使用USE INDEX(indexname)來強制使用一個索引或者用IGNORE INDEX
(indexname)來強制MySQL忽略索引

**key_len:**使用的索引的長度。在不損失精確性的情況下,長度越短越好

**ref:**顯示索引的哪一列被使用了。如果可能的話,是一個常數

rows: MySQL認爲必須檢查的用來返回請求數據的行數

Extra: 關於MySQL如何解析查詢的額外信息。

3 MySQL慢查詢怎麼解決?

  • slow_query_log
    慢查詢開啓狀態
  • slow_query_log_file
    慢查詢日誌存放的位置(這個目錄需要MySQL的運行賬號的可寫權限,一般設置爲MySQL的數據存放目錄)
  • long_query_time
    查詢超過多少秒才記錄

第六部分 數據庫鎖

  • MySQL 都有什麼鎖?
    MySQL有三種鎖的級別:頁級、表級、行級

(1)表級鎖:開銷小,加鎖快;不會出現死鎖;鎖定粒度大,發生鎖衝突的概率最高,併發度最低
(2)行級鎖:開銷大,加鎖慢;會出現死鎖;鎖定粒度最小,發生鎖衝突的概率最低,併發度也最高
(3)頁面鎖:開銷和加鎖時間介於表鎖和行鎖之間;會出現死鎖;鎖定粒度介於表鎖和行鎖之間,併發度一般

  • 什麼情況下會造成死鎖?
    死鎖 : 是指兩個或兩個以上的進程在執行過程中,因爭奪資源而造成的一種互相等待的現象,若無外力作用,它們都將無法推進下去,此時稱系統處於死鎖狀態或者系統產生了死鎖,這些永遠在互相等待的進程稱爲死鎖進程。

表級鎖不會產生死鎖,所以產生死鎖主要還是在針對於最常用的InnoDB。

死鎖的關鍵在於: 兩個(或以上)的Session 加鎖的順序不一致。
那麼對應的解決死鎖問題的關鍵就是: 讓不同的session加鎖有次序。

死鎖的解決辦法?
(1)查出的線程殺死Kill
SELECT trx_MySQL_thread_id FROM information_schema.INNODB_TRX;

(2)設置鎖的超時時間
InnoDB行鎖的等待時間,單位秒。可在會話級別設置,RDS實例該參數的默認值爲50(秒)
生產環境不推薦使用過大的 innodb_lock_wait_timeout參數值
該參數支持在會話級別修改,方便應用在會話級別單獨設置某些特殊操作的行鎖等待超時時間,如下:
set innodb_lock_wait_timeout=1000; —設置當前會話 Innodb 行鎖等待超時時間,單位秒。

(3)指定獲取鎖的順序

2 有哪些鎖(樂觀鎖悲觀鎖),select時怎麼加排他鎖?

  • 悲觀鎖(Pession Lock)
    悲觀鎖特點:先獲取鎖,再進行業務操作。
    即“悲觀”的認爲獲取鎖是非常有可能失敗的,因此要先確保獲取鎖成功再進行業務操作。
    通常所說的一鎖二查三更新 即指的使用悲觀鎖。
    通常來講在數據庫上的悲觀鎖需要數據庫本身提供支持,即通過常用的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中用悲觀鎖 務必要確定了索引,而不是全表掃描。

  • 樂觀鎖(Optimistic Lock)
    樂觀鎖,也叫樂觀併發控制,它假設多用戶併發的事務在處理時不會彼此影響,各事務能夠在不產生鎖的情況下處理各自影響的那部分數據。在提交數據更新之前,每個事務會先檢查該事務讀取數據後,有沒有其他的事務又修改了該數據。如果其他事務有更新的話,那麼當前正在提交的事務會進行回滾。

樂觀鎖的特點是先進行業務操作,不到萬不得已不去拿鎖。 即“樂觀” 的認爲拿鎖多半是會成功的,因此在進行完業務操作需要實際更新數據的最後一步去拿一下鎖就好。
樂觀鎖在數據庫傷的實現完全是邏輯的,不需要數據庫提供特殊的支持。

** 一般的做法是在需要鎖的數據上增加一個版本號,或者時間戳。**

注意

樂觀鎖在不發生取鎖失敗的情況下開銷比悲觀鎖小,但是一旦發生失敗回滾開銷則比較大,因此適合用在取鎖失敗概率比較小的場景,可以提升系統併發性能

樂觀鎖還適用於一些比較特殊的場景,例如在業務操作過程中無法和數據庫保持連接等悲觀鎖無法適用的地方。

  • 總結
    悲觀鎖和樂觀鎖是數據庫用來保證數據併發安全防止更新丟失的兩種方法,
    例子在select… for update前加個事務就可以防止更新丟失。
    悲觀鎖和樂觀鎖大部分場景下差異不大,一些獨特場景下有一些差別,一般從如下幾個方面來判斷:
    (1)響應速度
    如果需要非常高的響應速度,建議採用樂觀鎖方案,成功就執行,不成功就失敗,不需要等待其他併發去釋放鎖

(2)衝突頻率
如果衝突頻率非常高,建議採用悲觀鎖,保證成功率,如果衝突頻率大,樂觀鎖要多次嘗試才能成功,代價比較大。

(3)重試代價
如果重試代價大,建議採用悲觀鎖。


第七部分 主從複製及高併發

1 數據庫的三種主從複製方式

  • (1)同步複製
    所謂的同步複製,意思是master的變化,必須等待slave1,slave2…slaven 完成後才能返回。
    這樣,顯然不可取,也不是MySQL複製的默認設置。
    比如,在WEB前端頁面上,用戶增加了條記錄,需要等待很長時間。

  • (2)異步複製
    如同AJAX請求一樣,master只需完成自己的數據庫操作即可。
    至於slaves是否收到二進制日誌,是否完成操作,不用關心MySQL的默認設置。

  • (3)半同步複製
    master 只保證slaves中的一個操作成功,就返回,其他slave不管。
    這個功能,是由google 爲MySQL引入的。

2 數據庫主從複製分析的7個問題

問題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掛了,某些slaves掛了,那麼應用程序就要修改了。

能不能讓應用程序與MySQL的主從複製架構沒有太多關係呢?
找一個組件,application program 只需要跟它打交道,用它來完成MySQL的代理,實現SQL語句的路由。

MySQL proxy 並不負責,怎麼從衆多的slaves挑一個?
可以交給另一個組件,比如haproxy 來完成。

這就是所謂的MySQL READ WRITE SPLITE, MySQL的讀寫分離。

問題四:如果MySQL proxy , direct , master他們中的某些掛了怎麼辦?

總統一般都會弄個副總統,以防不測。同時,可以給這些關鍵的節點來個備份。

問題五: 當master的二進制日誌每產生一個事件,都需要發往slave,如果我們有N個slave,那是發N次,還是隻發一次?如果只發一次,發給了slave-1,那slave-2,slave-3,…它們怎麼辦?

顯然,應該發N次。
實際上,在MySQL master 內部,維護多個線程,每一個線程負責將二進制日誌文件發往對應的salve。
master 既要負責寫操作,還得維護N個線程,負擔會很重。
可以這樣,salve 是master的從, slave1 又是 slave2, slave3 的主,同時slave1 不再負責select。
slave1將master的複製線程的負擔,轉移到自己的身上。

這就是所謂的多級複製的概念。

問題6:當一個select發往MySQL proxy,可能這次由slave-2響應,下次由slave-3響應,這樣的話,就無法利用查詢緩存了

應該找一個共享式的緩存,比如memcache來解決。
將slave2, slave3…這些查詢的結果都緩存至memcache中。

問題7:隨着應用的日益增長,讀操作很多,我們可以擴展slave,但是如果master滿足不了寫操作了,怎麼辦呢?

scale on ?更好的服務器? 沒有最好的,只有更好的,太貴了。。。
scale out ? 主從複製架構已經滿足不了。
可以分庫【垂直拆分】,分表【水平拆分】。

3 MySQL高併發環境解決方案

MySQL高併發環境解決方案:分庫, 分表, 分佈式, 增加二級緩存…

需求分析:互聯網單位每天大量數據讀取、寫入,併發性高
現有解決方式:水平分庫分表,由單點分佈到多點數據庫中,從而降低單點數據庫壓力
集羣方案:解決DB宕機帶來的單點DB 不能訪問問題
讀寫分離策略:極大限度提高了應用中Read數據的速度和併發量,無法解決高寫入壓力

4 數據庫崩潰時事務的恢復機制(REDO日誌和UNDO日誌)

Undo Log:
Undo Log 是爲了實現事務的原子性,在MySQL 數據庫 InnoDB存儲引擎中,還用了Undo Log 來實現多版本併發控制(簡稱:MVCC)

事務的原子性(Atomicity)事務中的所有操作,要麼全部完成,要麼不做任何操作,不能只做部分操作。
如果在執行的過程中發生了錯誤,要回滾(RollBack)到事務開始前的狀態,就好像這個事務從來沒有執行過。

原理Undo Log 的原理很簡單,爲了滿足事務的原子性,在操作任何數據之前,首先將數據備份到一個地方(這個存儲數據備份的地方稱爲UndoLog)。然後進行數據的修改。
如果出現了錯誤或者用戶執行了ROLLBACK語句,系統可以利用Undo Log中的備份將數據恢復到事務開始之前的狀態。

**缺陷:**每個事務提交前將數據和UndoLog寫入磁盤,這樣會導致大量的磁盤IO,因此性能很低。
如果能夠將數據緩存一段時間,就能減少IO提高性能,但是這樣就會喪失事務的持久性。
因此引入了另外一種機制來實現持久化,即Redo Log。

Redo Log
原理和Undo Log相反, Redo Log 記錄的是新數據的備份。在事務提交前,只要將Redo Log 持久化即可,不需要將數據持久化。
當系統崩潰時,雖然數據沒有持久化,但是 Redo Log 已經持久化,系統可以根據Redo Log的內容,將所有數據恢復到最新的狀態。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章