【面试】网易游戏面试题目整理及答案(3)
網(wǎng)易游戲面試題目整理及答案(3)
- 數(shù)據(jù)庫部分
- MySQL 事務(wù)
- MySQL鎖機制
- MySQL調(diào)優(yōu)
- MySQL分區(qū)、分表、分庫
- 主從復(fù)制
- 其他問題
數(shù)據(jù)庫部分
MySQL 事務(wù)
答:MySQL 事務(wù)主要用于處理操作量大,復(fù)雜度高的數(shù)據(jù)。事務(wù)是由一組SQL語句組成的邏輯處理單元,具有4個屬性,通常簡稱為事務(wù)的ACID屬性。
- A (Atomicity) 原子性:整個事務(wù)中的所有操作,要么全部完成,要么全部不完成,不可能停滯在中間某個環(huán)節(jié)。事務(wù)在執(zhí)行過程中發(fā)生錯誤,會被回滾(Rollback)到事務(wù)開始前的狀態(tài),就像這個事務(wù)從來沒有執(zhí)行過一樣
- C (Consistency) 一致性:在事務(wù)開始之前和事務(wù)結(jié)束以后,數(shù)據(jù)庫的完整性約束沒有被破壞
- I (Isolation)隔離性:一個事務(wù)的執(zhí)行不能其它事務(wù)干擾。即一個事務(wù)內(nèi)部的操作及使用的數(shù)據(jù)對其它并發(fā)事務(wù)是隔離的,并發(fā)執(zhí)行的各個事務(wù)之間不能互相干擾
- D (Durability) 持久性:在事務(wù)完成以后,該事務(wù)所對數(shù)據(jù)庫所作的更改便持久的保存在數(shù)據(jù)庫之中,并不會被回滾
并發(fā)事務(wù)處理帶來的問題
- 更新丟失(Lost Update):事務(wù)A和事務(wù)B選擇同一行,然后基于最初選定的值更新該行時,由于兩個事務(wù)都不知道彼此的存在,就會發(fā)生丟失更新問題
- 臟讀(Dirty Reads):事務(wù)A讀取了事務(wù)B更新的數(shù)據(jù),然后B回滾操作,那么A讀取到的數(shù)據(jù)是臟數(shù)據(jù)
- 不可重復(fù)讀(Non-Repeatable Reads):事務(wù) A 多次讀取同一數(shù)據(jù),事務(wù)B在事務(wù)A多次讀取的過程中,對數(shù)據(jù)作了更新并提交,導(dǎo)致事務(wù)A- 多次讀取同一數(shù)據(jù)時,結(jié)果不一致。
- 幻讀(Phantom Reads):幻讀與不可重復(fù)讀類似。它發(fā)生在一個事務(wù)A讀取了幾行數(shù)據(jù),接著另一個并發(fā)事務(wù)B插入了一些數(shù)據(jù)時。在隨后的查詢中,事務(wù)A就會發(fā)現(xiàn)多了一些原本不存在的記錄,就好像發(fā)生了幻覺一樣,所以稱為幻讀。
幻讀和不可重復(fù)讀的區(qū)別:
- 不可重復(fù)讀的重點是修改:在同一事務(wù)中,同樣的條件,第一次讀的數(shù)據(jù)和第二次讀的數(shù)據(jù)不一樣。(因為中間有其他事務(wù)提交了修改)
- 幻讀的重點在于新增或者刪除:在同一事務(wù)中,同樣的條件,,第一次和第二次讀出來的記錄數(shù)不一樣。(因為中間有其他事務(wù)提交了插入/刪除)
并發(fā)事務(wù)處理帶來的問題的解決辦法:
- “更新丟失”通常是應(yīng)該完全避免的。但防止更新丟失,并不能單靠數(shù)據(jù)庫事務(wù)控制器來解決,需要應(yīng)用程序?qū)σ碌臄?shù)據(jù)加必要的鎖來解決,因此,防止更新丟失應(yīng)該是應(yīng)用的責(zé)任。
- “臟讀” 、 “不可重復(fù)讀”和“幻讀” ,其實都是數(shù)據(jù)庫讀一致性問題,必須由數(shù)據(jù)庫提供一定的事務(wù)隔離機制來解決:
①一種是加鎖:在讀取數(shù)據(jù)前,對其加鎖,阻止其他事務(wù)對數(shù)據(jù)進行修改。
②另一種是數(shù)據(jù)多版本并發(fā)控制(MultiVersion Concurrency Control,簡稱 MVCC 或 MCC),也稱為多版本數(shù)據(jù)庫:不用加任何鎖, 通過一定機制生成一個數(shù)據(jù)請求時間點的一致性數(shù)據(jù)快照 (Snapshot), 并用這個快照來提供一定級別 (語句級或事務(wù)級) 的一致性讀取。從用戶的角度來看,好象是數(shù)據(jù)庫可以提供同一數(shù)據(jù)的多個版本。
答:數(shù)據(jù)庫事務(wù)的隔離級別有4種,由低到高分別為
- READ-UNCOMMITTED(讀未提交): 最低的隔離級別,允許讀取尚未提交的數(shù)據(jù)變更,可能會導(dǎo)致臟讀、幻讀或不可重復(fù)讀。
- READ-COMMITTED(讀已提交): 允許讀取并發(fā)事務(wù)已經(jīng)提交的數(shù)據(jù),可以阻止臟讀,但是幻讀或不可重復(fù)讀仍有可能發(fā)生。
- REPEATABLE-READ(可重復(fù)讀): 對同一字段的多次讀取結(jié)果都是一致的,除非數(shù)據(jù)是被本身事務(wù)自己所修改,可以阻止臟讀和不可重復(fù)讀,但幻讀仍有可能發(fā)生。
- SERIALIZABLE(可串行化): 最高的隔離級別,完全服從ACID的隔離級別。所有的事務(wù)依次逐個執(zhí)行,這樣事務(wù)之間就完全不可能產(chǎn)生干擾,也就是說,該級別可以防止臟讀、不可重復(fù)讀以及幻讀。
查看當(dāng)前數(shù)據(jù)庫的事務(wù)隔離級別:
數(shù)據(jù)庫的事務(wù)隔離越嚴格,并發(fā)副作用越小,但付出的代價就越大,因為事務(wù)隔離實質(zhì)上就是使事務(wù)在一定程度上“串行化”進行,這顯然與“并發(fā)”是矛盾的。同時,不同的應(yīng)用對讀一致性和事務(wù)隔離程度的要求也是不同的,比如許多應(yīng)用對“不可重復(fù)讀”和“幻讀”并不敏感,可能更關(guān)心數(shù)據(jù)并發(fā)訪問的能力。
Read uncommitted:讀未提交,就是一個事務(wù)可以讀取另一個未提交事務(wù)的數(shù)據(jù)。
Read committed:讀提交,顧名思義,就是一個事務(wù)要等另一個事務(wù)提交后才能讀取數(shù)據(jù)。解決臟讀問題。
Repeatable read:重復(fù)讀,就是在開始讀取數(shù)據(jù)(事務(wù)開啟)時,不再允許修改操作。MySQL的默認事務(wù)隔離級別。解決可能的不可重復(fù)讀問題。不可重復(fù)讀對應(yīng)的是修改,即UPDATE操作。但是可能還會有幻讀問題。因為幻讀問題對應(yīng)的是插入INSERT操作,而不是UPDATE操作。
Serializable 序列化:Serializable 是最高的事務(wù)隔離級別,在該級別下,事務(wù)串行化順序執(zhí)行,可以避免臟讀、不可重復(fù)讀與幻讀。簡單來說,Serializable會在讀取的每一行數(shù)據(jù)上都加鎖,所以可能導(dǎo)致大量的超時和鎖爭用問題。這種事務(wù)隔離級別效率低下,比較耗數(shù)據(jù)庫性能,一般不使用。
需要說明的是,事務(wù)隔離級別和數(shù)據(jù)訪問的并發(fā)性是對立的,事務(wù)隔離級別越高并發(fā)性就越差。所以要根據(jù)具體的應(yīng)用來確定合適的事務(wù)隔離級別,這個地方?jīng)]有萬能的原則。
MySQL InnoDB 存儲引擎的默認支持的隔離級別是 REPEATABLE-READ(可重讀)。我們可以通過SELECT @@tx_isolation;命令來查看,MySQL 8.0 該命令改為SELECT @@transaction_isolation;
這里需要注意的是:與 SQL 標準不同的地方在于InnoDB 存儲引擎在 REPEATABLE-READ(可重讀)事務(wù)隔離級別下使用的是Next-Key Lock 算法,因此可以避免幻讀的產(chǎn)生,這與其他數(shù)據(jù)庫系統(tǒng)(如 SQL Server)是不同的。所以說InnoDB 存儲引擎的默認支持的隔離級別是 REPEATABLE-READ(可重讀)已經(jīng)可以完全保證事務(wù)的隔離性要求,即達到了 SQL標準的 SERIALIZABLE(可串行化)隔離級別,而且保留了比較好的并發(fā)性能。
因為隔離級別越低,事務(wù)請求的鎖越少,所以大部分數(shù)據(jù)庫系統(tǒng)的隔離級別都是READ-COMMITTED(讀已提交):,但是你要知道的是InnoDB 存儲引擎默認使用 REPEATABLE-READ(可重讀)并不會有任何性能損失。
答:MySQL的大多數(shù)事務(wù)型存儲引擎實現(xiàn)都不是簡單的行級鎖。基于提升并發(fā)性考慮,一般都同時實現(xiàn)了多版本并發(fā)控制(MVCC),包括Oracle、PostgreSQL。只是實現(xiàn)機制各不相同。可以認為 **MVCC 是行級鎖的一個變種,但它在很多情況下避免了加鎖操作,因此開銷更低。**雖然實現(xiàn)機制有所不同,但大都實現(xiàn)了非阻塞的讀操作,寫操作也只是鎖定必要的行。**MVCC 的實現(xiàn)是通過保存數(shù)據(jù)在某個時間點的快照來實現(xiàn)的。**也就是說不管需要執(zhí)行多長時間,每個事物看到的數(shù)據(jù)都是一致的。典型的MVCC實現(xiàn)方式,分為樂觀(optimistic)并發(fā)控制和悲觀(pressimistic)并發(fā)控制。下邊通過 InnoDB的簡化版行為來說明 MVCC 是如何工作的。
InnoDB 的 MVCC,是通過在每行記錄后面保存兩個隱藏的列來實現(xiàn)。這兩個列,一個保存了行的創(chuàng)建時間,一個保存行的過期時間(刪除時間)。當(dāng)然存儲的并不是真實的時間,而是系統(tǒng)版本號(system version number)。每開始一個新的事務(wù),系統(tǒng)版本號都會自動遞增。事務(wù)開始時刻的系統(tǒng)版本號會作為事務(wù)的版本號,用來和查詢到的每行記錄的版本號進行比較。
REPEATABLE READ(可重讀)隔離級別下MVCC如何工作:
- SELECT
InnoDB會根據(jù)以下兩個條件檢查每行記錄:
1.InnoDB只查找版本早于當(dāng)前事務(wù)版本的數(shù)據(jù)行,這樣可以確保事務(wù)讀取的行,要么是在開始事務(wù)之前已經(jīng)存在要么是事務(wù)自身插入或者修改過的
2.行的刪除版本號要么未定義,要么大于當(dāng)前事務(wù)版本號,這樣可以確保事務(wù)讀取到的行在事務(wù)開始之前未被刪除
只有符合上述兩個條件的才會被查詢出來 - INSERT:InnoDB為新插入的每一行保存當(dāng)前系統(tǒng)版本號作為行版本號
- DELETE:InnoDB為刪除的每一行保存當(dāng)前系統(tǒng)版本號作為行刪除標識
- UPDATE:InnoDB為插入的一行新紀錄保存當(dāng)前系統(tǒng)版本號作為行版本號,同時保存當(dāng)前系統(tǒng)版本號到原來的行作為刪除標識
保存這兩個額外系統(tǒng)版本號,使大多數(shù)操作都不用加鎖。使數(shù)據(jù)操作簡單,性能很好,并且也能保證只會讀取到符合要求的行。不足之處是每行記錄都需要額外的存儲空間,需要做更多的行檢查工作和一些額外的維護工作。
MVCC 只在 COMMITTED READ(讀提交)和REPEATABLE READ(可重復(fù)讀)兩種隔離級別下工作。
InnoDB 使用日志來減少提交事務(wù)時的開銷。因為日志中已經(jīng)記錄了事務(wù),就無須在每個事務(wù)提交時把緩沖池的臟塊刷新(flush)到磁盤中。事務(wù)修改的數(shù)據(jù)和索引通常會映射到表空間的隨機位置,所以刷新這些變更到磁盤需要很多隨機 IO。
InnoDB 假設(shè)使用常規(guī)磁盤,隨機IO比順序IO昂貴得多,因為一個IO請求需要時間把磁頭移到正確的位置,然后等待磁盤上讀出需要的部分,再轉(zhuǎn)到開始位置。
InnoDB 用日志把隨機IO變成順序IO。一旦日志安全寫到磁盤,事務(wù)就持久化了,即使斷電了,InnoDB可以重放日志并且恢復(fù)已經(jīng)提交的事務(wù)。
InnoDB 使用一個后臺線程智能地刷新這些變更到數(shù)據(jù)文件。這個線程可以批量組合寫入,使得數(shù)據(jù)寫入更順序,以提高效率。
事務(wù)日志可以幫助提高事務(wù)效率:
- 使用事務(wù)日志,存儲引擎在修改表的數(shù)據(jù)時只需要修改其內(nèi)存拷貝,再把該修改行為記錄到持久在硬盤上的事務(wù)日志中,而不用每次都將修改的數(shù)據(jù)本身持久到磁盤。
- 事務(wù)日志采用的是追加的方式,因此寫日志的操作是磁盤上一小塊區(qū)域內(nèi)的順序I/O,而不像隨機I/O需要在磁盤的多個地方移動磁頭,所以采用事務(wù)日志的方式相對來說要快得多。
- 事務(wù)日志持久以后,內(nèi)存中被修改的數(shù)據(jù)在后臺可以慢慢刷回到磁盤。
- 如果數(shù)據(jù)的修改已經(jīng)記錄到事務(wù)日志并持久化,但數(shù)據(jù)本身沒有寫回到磁盤,此時系統(tǒng)崩潰,存儲引擎在重啟時能夠自動恢復(fù)這一部分修改的數(shù)據(jù)。
目前來說,大多數(shù)存儲引擎都是這樣實現(xiàn)的,我們通常稱之為預(yù)寫式日志(Write-Ahead Logging),修改數(shù)據(jù)需要寫兩次磁盤。
答:事務(wù)的實現(xiàn)是基于數(shù)據(jù)庫的存儲引擎。不同的存儲引擎對事務(wù)的支持程度不一樣。MySQL 中支持事務(wù)的存儲引擎有 InnoDB 和 NDB。事務(wù)的實現(xiàn)就是如何實現(xiàn)ACID特性。事務(wù)的隔離性是通過鎖實現(xiàn),而事務(wù)的原子性、一致性和持久性則是通過事務(wù)日志實現(xiàn) 。
補充問題:事務(wù)是如何通過日志來實現(xiàn)的?
答:事務(wù)日志包括:重做日志redo和回滾日志undo
1)redo log(重做日志) 實現(xiàn)持久化和原子性
在innoDB的存儲引擎中,事務(wù)日志通過重做(redo)日志和innoDB存儲引擎的日志緩沖(InnoDB Log Buffer)實現(xiàn)。事務(wù)開啟時,事務(wù)中的操作,都會先寫入存儲引擎的日志緩沖中,在事務(wù)提交之前,這些緩沖的日志都需要提前刷新到磁盤上持久化,這就是DBA們口中常說的“日志先行”(Write-Ahead Logging)。當(dāng)事務(wù)提交之后,在Buffer Pool中映射的數(shù)據(jù)文件才會慢慢刷新到磁盤。此時如果數(shù)據(jù)庫崩潰或者宕機,那么當(dāng)系統(tǒng)重啟進行恢復(fù)時,就可以根據(jù)redo log中記錄的日志,把數(shù)據(jù)庫恢復(fù)到崩潰前的一個狀態(tài)。未完成的事務(wù),可以繼續(xù)提交,也可以選擇回滾,這基于恢復(fù)的策略而定。
在系統(tǒng)啟動的時候,就已經(jīng)為redo log分配了一塊連續(xù)的存儲空間,以順序追加的方式記錄Redo Log,通過順序IO來改善性能。所有的事務(wù)共享redo log的存儲空間,它們的Redo Log按語句的執(zhí)行順序,依次交替的記錄在一起。
2)undo log(回滾日志) 實現(xiàn)一致性
undo log 主要為事務(wù)的回滾服務(wù)。在事務(wù)執(zhí)行的過程中,除了記錄redo log,還會記錄一定量的undo log。undo log記錄了數(shù)據(jù)在每個操作前的狀態(tài),如果事務(wù)執(zhí)行過程中需要回滾,就可以根據(jù)undo log進行回滾操作。單個事務(wù)的回滾,只會回滾當(dāng)前事務(wù)做的操作,并不會影響到其他的事務(wù)做的操作。
Undo記錄的是已部分完成并且寫入硬盤的未完成的事務(wù),默認情況下回滾日志是記錄下表空間中的(共享表空間或者獨享表空間)
二種日志均可以視為一種恢復(fù)操作,redo_log是恢復(fù)提交事務(wù)修改的頁操作,而undo_log是回滾行記錄到特定版本。二者記錄的內(nèi)容也不同,redo_log是物理日志,記錄頁的物理修改操作,而undo_log是邏輯日志,根據(jù)每行記錄進行記錄。
補充問題:MySQL 有多少種日志嗎?
- 錯誤日志:記錄出錯信息,也記錄一些警告信息或者正確的信息。
- 查詢?nèi)罩?/strong>:記錄所有對數(shù)據(jù)庫請求的信息,不論這些請求是否得到了正確的執(zhí)行。
- 慢查詢?nèi)罩?/strong>:設(shè)置一個閾值,將運行時間超過該值的所有SQL語句都記錄到慢查詢的日志文件中。
- 二進制日志:記錄對數(shù)據(jù)庫執(zhí)行更改的所有操作。
- 中繼日志:中繼日志也是二進制日志,用來給slave 庫恢復(fù)
- 事務(wù)日志:重做日志redo和回滾日志undo
答:分布式事務(wù)的實現(xiàn)方式有很多,既可以采用 InnoDB 提供的原生的事務(wù)支持,也可以采用消息隊列來實現(xiàn)分布式事務(wù)的最終一致性。MySQL 從 5.0.3 InnoDB 存儲引擎開始支持XA協(xié)議的分布式事務(wù)。**一個分布式事務(wù)會涉及多個行動,這些行動本身是事務(wù)性的。所有行動都必須一起成功完成,或者一起被回滾。**在MySQL中,使用分布式事務(wù)涉及一個或多個資源管理器和一個事務(wù)管理器。
如圖,MySQL 的分布式事務(wù)模型分為三塊:應(yīng)用程序(AP)、資源管理器(RM)、事務(wù)管理器(TM):
- 應(yīng)用程序:定義了事務(wù)的邊界,指定需要做哪些事務(wù);
- 資源管理器:提供了訪問事務(wù)的方法,通常一個數(shù)據(jù)庫就是一個資源管理器;
- 事務(wù)管理器:協(xié)調(diào)參與了全局事務(wù)中的各個事務(wù)。
分布式事務(wù)采用兩段式提交(two-phase commit)的方式:
- 第一階段所有的事務(wù)節(jié)點開始準備,告訴事務(wù)管理器ready。
- 第二階段事務(wù)管理器告訴每個節(jié)點是commit還是rollback。如果有一個節(jié)點失敗,就需要全局的節(jié)點全部rollback,以此保障事務(wù)的原子性。
MySQL鎖機制
答:鎖是計算機協(xié)調(diào)多個進程或線程并發(fā)訪問某一資源的機制。在數(shù)據(jù)庫中,除傳統(tǒng)的計算資源(如CPU、RAM、I/O等)的爭用以外,數(shù)據(jù)也是一種供許多用戶共享的資源。數(shù)據(jù)庫鎖定機制簡單來說,就是數(shù)據(jù)庫為了保證數(shù)據(jù)的一致性,而使各種共享資源在被并發(fā)訪問變得有序所設(shè)計的一種規(guī)則。
1. 鎖的分類:
1)從對數(shù)據(jù)操作的類型分類:
- 讀鎖(共享鎖):針對同一份數(shù)據(jù),多個讀操作可以同時進行,不會互相影響
- 寫鎖(排他鎖):當(dāng)前寫操作沒有完成前,它會阻斷其他寫鎖和讀鎖
2)從對數(shù)據(jù)操作的粒度分類:
為了盡可能提高數(shù)據(jù)庫的并發(fā)度,每次鎖定的數(shù)據(jù)范圍越小越好,理論上每次只鎖定當(dāng)前操作的數(shù)據(jù)的方案會得到最大的并發(fā)度,但是管理鎖是很耗資源的事情(涉及獲取,檢查,釋放鎖等動作),因此數(shù)據(jù)庫系統(tǒng)需要在高并發(fā)響應(yīng)和系統(tǒng)性能兩方面進行平衡,這樣就產(chǎn)生了“鎖粒度(Lock granularity)”的概念。 - 表級鎖:開銷小,加鎖快;不會出現(xiàn)死鎖;鎖定粒度大,發(fā)生鎖沖突的概率最高,并發(fā)度最低(MyISAM 和 MEMORY 存儲引擎采用的是表級鎖);
- 行級鎖:開銷大,加鎖慢;會出現(xiàn)死鎖;鎖定粒度最小,發(fā)生鎖沖突的概率最低,并發(fā)度也最高(InnoDB 存儲引擎既支持行級鎖也支持表級鎖,但默認情況下是采用行級鎖);
- 頁面鎖:開銷和加鎖時間界于表鎖和行鎖之間;會出現(xiàn)死鎖;鎖定粒度界于表鎖和行鎖之間,并發(fā)度一般。
適用:從鎖的角度來說,表級鎖更適合于以查詢?yōu)橹?/strong>,只有少量按索引條件更新數(shù)據(jù)的應(yīng)用,如Web應(yīng)用;而行級鎖則更適合于有大量按索引條件并發(fā)更新少量不同數(shù)據(jù),同時又有并發(fā)查詢的應(yīng)用,如一些在線事務(wù)處理(OLTP)系統(tǒng)。
2. MyISAM表鎖
MyISAM 的表鎖有兩種模式:
- 表共享讀鎖 (Table Read Lock):不會阻塞其他用戶對同一表的讀請求,但會阻塞對同一表的寫請求;
- 表獨占寫鎖 (Table Write Lock):會阻塞其他用戶對同一表的讀和寫操作;
MyISAM 表的讀操作與寫操作之間,以及寫操作之間是串行的。當(dāng)一個線程獲得對一個表的寫鎖后, 只有持有鎖的線程可以對表進行更新操作。其他線程的讀、 寫操作都會等待,直到鎖被釋放為止。默認情況下,寫鎖比讀鎖具有更高的優(yōu)先級:當(dāng)一個鎖釋放時,這個鎖會優(yōu)先給寫鎖隊列中等候的獲取鎖請求,然后再給讀鎖隊列中等候的獲取鎖請求。
3. InnoDB行鎖
InnoDB 實現(xiàn)了以下兩種類型的行鎖:
- 共享鎖(S):允許一個事務(wù)去讀一行,阻止其他事務(wù)獲得相同數(shù)據(jù)集的排他鎖。
- 排他鎖(X):允許獲得排他鎖的事務(wù)更新數(shù)據(jù),阻止其他事務(wù)取得相同數(shù)據(jù)集的共享讀鎖和排他寫鎖。
為了允許行鎖和表鎖共存,實現(xiàn)多粒度鎖機制,InnoDB 還有兩種內(nèi)部使用的意向鎖(Intention Locks),這兩種意向鎖都是表鎖: - 意向共享鎖(IS):事務(wù)打算給數(shù)據(jù)行加行共享鎖,事務(wù)在給一個數(shù)據(jù)行加共享鎖前必須先取得該表的 IS 鎖。
- 意向排他鎖(IX):事務(wù)打算給數(shù)據(jù)行加行排他鎖,事務(wù)在給一個數(shù)據(jù)行加排他鎖前必須先取得該表的 IX 鎖。
索引失效會導(dǎo)致行鎖變表鎖。比如 vchar 查詢不寫單引號的情況。
1)加鎖機制
樂觀鎖與悲觀鎖是兩種并發(fā)控制的思想,可用于解決丟失更新問題
樂觀鎖會“樂觀地”假定大概率不會發(fā)生并發(fā)更新沖突,訪問、處理數(shù)據(jù)過程中不加鎖,只在更新數(shù)據(jù)時再根據(jù)版本號或時間戳判斷是否有沖突,有則處理,無則提交事務(wù)。用數(shù)據(jù)版本(Version)記錄機制實現(xiàn),這是樂觀鎖最常用的一種實現(xiàn)方式
悲觀鎖會“悲觀地”假定大概率會發(fā)生并發(fā)更新沖突,訪問、處理數(shù)據(jù)前就加排他鎖,在整個數(shù)據(jù)處理過程中鎖定數(shù)據(jù),事務(wù)提交或回滾后才釋放鎖。另外與樂觀鎖相對應(yīng)的,悲觀鎖是由數(shù)據(jù)庫自己實現(xiàn)了的,要用的時候,我們直接調(diào)用數(shù)據(jù)庫的相關(guān)語句就可以了。
2)鎖模式(InnoDB有三種行鎖的算法) - 記錄鎖(Record Locks):單個行記錄上的鎖。對索引項加鎖,鎖定符合條件的行。其他事務(wù)不能修改和刪除加鎖項;
它會在 id=1 的記錄上加上記錄鎖,以阻止其他事務(wù)插入,更新,刪除 id=1 這一行
在通過 主鍵索引 與 唯一索引 對數(shù)據(jù)行進行 UPDATE 操作時,也會對該行數(shù)據(jù)加記錄鎖:
- 間隙鎖(Gap Locks):當(dāng)我們使用范圍條件而不是相等條件檢索數(shù)據(jù),并請求共享或排他鎖時,InnoDB會給符合條件的已有數(shù)據(jù)記錄的索引項加鎖。對于鍵值在條件范圍內(nèi)但并不存在的記錄,叫做“間隙”。
InnoDB 也會對這個“間隙”加鎖,這種鎖機制就是所謂的間隙鎖。
對索引項之間的“間隙”加鎖,鎖定記錄的范圍(對第一條記錄前的間隙或最后一條將記錄后的間隙加鎖),不包含索引項本身。其他事務(wù)不能在鎖范圍內(nèi)插入數(shù)據(jù),這樣就防止了別的事務(wù)新增幻影行。
間隙鎖基于非唯一索引,它鎖定一段范圍內(nèi)的索引記錄。間隙鎖基于下面將會提到的Next-Key Locking 算法,請務(wù)必牢記:使用間隙鎖鎖住的是一個區(qū)間,而不僅僅是這個區(qū)間中的每一條數(shù)據(jù)。
即所有在(1,10)區(qū)間內(nèi)的記錄行都會被鎖住,所有id 為 2、3、4、5、6、7、8、9 的數(shù)據(jù)行的插入會被阻塞,但是 1 和 10 兩條記錄行并不會被鎖住。
GAP鎖的目的,是為了防止同一事務(wù)的兩次當(dāng)前讀,出現(xiàn)幻讀的情況
- 臨鍵鎖(Next-key Locks):臨鍵鎖,是記錄鎖與間隙鎖的組合,它的封鎖范圍,既包含索引記錄,又包含索引區(qū)間。(臨鍵鎖的主要目的,也是為了避免幻讀(Phantom Read)。如果把事務(wù)的隔離級別降級為RC,臨鍵鎖則也會失效。)
Next-Key 可以理解為一種特殊的間隙鎖,也可以理解為一種特殊的算法。通過臨建鎖可以解決幻讀的問題。每個數(shù)據(jù)行上的非唯一索引列上都會存在一把臨鍵鎖,當(dāng)某個事務(wù)持有該數(shù)據(jù)行的臨鍵鎖時,會鎖住一段左開右閉區(qū)間的數(shù)據(jù)。需要強調(diào)的一點是,InnoDB 中行級鎖是基于索引實現(xiàn)的,臨鍵鎖只與非唯一索引列有關(guān),在唯一索引列(包括主鍵列)上不存在臨鍵鎖。
對于行的查詢,都是采用該方法,主要目的是解決幻讀的問題。
補充問題:select for update有什么含義,會鎖表還是鎖行還是其他?
for update 僅適用于InnoDB,且必須在事務(wù)塊(BEGIN/COMMIT)中才能生效。在進行事務(wù)操作時,通過“for update”語句,MySQL會對查詢結(jié)果集中每行數(shù)據(jù)都添加排他鎖,其他線程對該記錄的更新與刪除操作都會阻塞。排他鎖包含行鎖、表鎖。
InnoDB這種行鎖實現(xiàn)特點意味著:只有通過索引條件檢索數(shù)據(jù),InnoDB才使用行級鎖,否則,InnoDB將使用表鎖!假設(shè)有個表單 products ,里面有id跟name二個欄位,id是主鍵。
- 明確指定主鍵,并且有該記錄,則row lock
- 明確指定主鍵,若沒有該記錄,則無lock
- 無主鍵, table lock
- 主鍵不明確, table lock
- 主鍵不明確,table lock
注1: FOR UPDATE僅適用于InnoDB,且必須在交易區(qū)塊(BEGIN/COMMIT)中才能生效。注2: 要測試鎖定的狀況,可以利用MySQL的Command Mode ,開二個視窗來做測試。
補充問題:MySQL 遇到過死鎖問題嗎,你是如何解決的?
4.死鎖
死鎖產(chǎn)生:
- 死鎖是指兩個或多個事務(wù)在同一資源上相互占用,并請求鎖定對方占用的資源,從而導(dǎo)致惡性循環(huán)
- 當(dāng)事務(wù)試圖以不同的順序鎖定資源時,就可能產(chǎn)生死鎖。多個事務(wù)同時鎖定同一個資源時也可能會產(chǎn)生死鎖
- 鎖的行為和順序和存儲引擎相關(guān)。以同樣的順序執(zhí)行語句,有些存儲引擎會產(chǎn)生死鎖有些不會——死鎖有雙重原因:真正的數(shù)據(jù)沖突;存儲引擎的實現(xiàn)方式。
**檢測死鎖:**數(shù)據(jù)庫系統(tǒng)實現(xiàn)了各種死鎖檢測和死鎖超時的機制。InnoDB存儲引擎能檢測到死鎖的循環(huán)依賴并立即返回一個錯誤。
**死鎖恢復(fù):**死鎖發(fā)生以后,只有部分或完全回滾其中一個事務(wù),才能打破死鎖,InnoDB目前處理死鎖的方法是,將持有最少行級排他鎖的事務(wù)進行回滾。所以事務(wù)型應(yīng)用程序在設(shè)計時必須考慮如何處理死鎖,多數(shù)情況下只需要重新執(zhí)行因死鎖回滾的事務(wù)即可。
**外部鎖的死鎖檢測:**發(fā)生死鎖后,InnoDB 一般都能自動檢測到,并使一個事務(wù)釋放鎖并回退,另一個事務(wù)獲得鎖,繼續(xù)完成事務(wù)。但在涉及外部鎖,或涉及表鎖的情況下,InnoDB 并不能完全自動檢測到死鎖, 這需要通過設(shè)置鎖等待超時參數(shù) innodb_lock_wait_timeout 來解決
**死鎖影響性能:**死鎖會影響性能而不是會產(chǎn)生嚴重錯誤,因為InnoDB會自動檢測死鎖狀況并回滾其中一個受影響的事務(wù)。在高并發(fā)系統(tǒng)上,當(dāng)許多線程等待同一個鎖時,死鎖檢測可能導(dǎo)致速度變慢。有時當(dāng)發(fā)生死鎖時,禁用死鎖檢測(使用innodb_deadlock_detect配置選項)可能會更有效,這時可以依賴innodb_lock_wait_timeout設(shè)置進行事務(wù)回滾。
MyISAM避免死鎖:
在自動加鎖的情況下,MyISAM 總是一次獲得 SQL 語句所需要的全部鎖,所以 MyISAM 表不會出現(xiàn)死鎖。
InnoDB避免死鎖:
- 為了在單個InnoDB表上執(zhí)行多個并發(fā)寫入操作時避免死鎖,可以在事務(wù)開始時通過為預(yù)期要修改的每個元祖(行)使用SELECT ... FOR UPDATE語句來獲取必要的鎖,即使這些行的更改語句是在之后才執(zhí)行的。
- 在事務(wù)中,如果要更新記錄,應(yīng)該直接申請足夠級別的鎖,即排他鎖,而不應(yīng)先申請共享鎖、更新時再申請排他鎖,因為這時候當(dāng)用戶再申請排他鎖時,其他事務(wù)可能又已經(jīng)獲得了相同記錄的共享鎖,從而造成鎖沖突,甚至死鎖
- 如果事務(wù)需要修改或鎖定多個表,則應(yīng)在每個事務(wù)中以相同的順序使用加鎖語句。在應(yīng)用中,如果不同的程序會并發(fā)存取多個表,應(yīng)盡量約定以相同的順序來訪問表,這樣可以大大降低產(chǎn)生死鎖的機會
- 通過SELECT ... LOCK IN SHARE MODE獲取行的讀鎖后,如果當(dāng)前事務(wù)再需要對該記錄進行更新操作,則很有可能造成死鎖。
- 改變事務(wù)隔離級別
如果出現(xiàn)死鎖,可以用 show engine innodb status;命令來確定最后一個死鎖產(chǎn)生的原因。返回結(jié)果中包括死鎖相關(guān)事務(wù)的詳細信息,如引發(fā)死鎖的 SQL 語句,事務(wù)已經(jīng)獲得的鎖,正在等待什么鎖,以及被回滾的事務(wù)等。據(jù)此可以分析死鎖產(chǎn)生的原因和改進措施。
MySQL調(diào)優(yōu)
1. 影響MySQL的性能因素:
- 業(yè)務(wù)需求對MySQL的影響(合適合度)
- 存儲定位對MySQL的影響
- 系統(tǒng)各種配置及規(guī)則數(shù)據(jù)
- 活躍用戶的基本信息數(shù)據(jù)
- 活躍用戶的個性化定制信息數(shù)據(jù)
- 準實時的統(tǒng)計信息數(shù)據(jù)
- 其他一些訪問頻繁但變更較少的數(shù)據(jù)
- 二進制多媒體數(shù)據(jù)
- 流水隊列數(shù)據(jù)‘
- 超大文本數(shù)據(jù)
- 不適合放進MySQL的數(shù)據(jù)
- 需要放進緩存的數(shù)據(jù) - Schema設(shè)計對系統(tǒng)的性能影響
- 盡量減少對數(shù)據(jù)庫訪問的請求
- 盡量減少無用數(shù)據(jù)的查詢請求 - 硬件環(huán)境對系統(tǒng)性能的影響
2.性能分析
1)MySQL Query Optimizer - MySQL 中有專門負責(zé)優(yōu)化 SELECT 語句的優(yōu)化器模塊,主要功能:通過計算分析系統(tǒng)中收集到的統(tǒng)計信息,為客戶端請求的 Query 提供他認為最優(yōu)的執(zhí)行計劃(他認為最優(yōu)的數(shù)據(jù)檢索方式,但不見得是 DBA 認為是最優(yōu)的,這部分最耗費時間)
- 當(dāng)客戶端向 MySQL 請求一條 Query,命令解析器模塊完成請求分類,區(qū)別出是 SELECT 并轉(zhuǎn)發(fā)給 MySQL Query Optimize r時,MySQL Query Optimizer 首先會對整條 Query 進行優(yōu)化,處理掉一些常量表達式的預(yù)算,直接換算成常量值。并對 Query 中的查詢條件進行簡化和轉(zhuǎn)換,如去掉一些無用或顯而易見的條件、結(jié)構(gòu)調(diào)整等。然后分析 Query 中的 Hint 信息(如果有),看顯示 Hint 信息是否可以完全確定該 Query 的執(zhí)行計劃。如果沒有 Hint 或 Hint 信息還不足以完全確定執(zhí)行計劃,則會讀取所涉及對象的統(tǒng)計信息,根據(jù) Query 進行寫相應(yīng)的計算分析,然后再得出最后的執(zhí)行計劃。
2)MySQL常見瓶頸 - CPU:CPU在飽和的時候一般發(fā)生在數(shù)據(jù)裝入內(nèi)存或從磁盤上讀取數(shù)據(jù)時候
- IO:磁盤I/O瓶頸發(fā)生在裝入數(shù)據(jù)遠大于內(nèi)存容量的時候
- 服務(wù)器硬件的性能瓶頸:top,free,iostat 和 vmstat來查看系統(tǒng)的性能狀態(tài)
3)性能下降SQL慢 執(zhí)行時間長 等待時間長 原因分析 - 查詢語句寫的爛
- 索引失效(單值、復(fù)合)
- 關(guān)聯(lián)查詢太多join(設(shè)計缺陷或不得已的需求)
- 服務(wù)器調(diào)優(yōu)及各個參數(shù)設(shè)置(緩沖、線程數(shù)等)
4)MySQL常見性能分析手段
在優(yōu)化MySQL時,通常需要對數(shù)據(jù)庫進行分析,常見的分析手段有慢查詢?nèi)罩?/strong>,EXPLAIN 分析查詢,profiling分析以及show命令查詢系統(tǒng)狀態(tài)及系統(tǒng)變量,通過定位分析性能的瓶頸,才能更好的優(yōu)化數(shù)據(jù)庫系統(tǒng)的性能。
性能瓶頸定位
我們可以通過 show 命令查看 MySQL 狀態(tài)及變量,找到系統(tǒng)的瓶頸:
Explain(執(zhí)行計劃)
是什么:使用 Explain 關(guān)鍵字可以模擬優(yōu)化器執(zhí)行SQL查詢語句,從而知道 MySQL 是如何處理你的 SQL 語句的。分析你的查詢語句或是表結(jié)構(gòu)的性能瓶頸。
能干什么:
- 表的讀取順序
- 數(shù)據(jù)讀取操作的操作類型
- 哪些索引可以使用
- 哪些索引被實際使用
- 表之間的引用
- 每張表有多少行被優(yōu)化器查詢
怎么玩: - Explain + SQL語句
- 執(zhí)行計劃包含的信息(如果有分區(qū)表的話還會有partitions)
各字段解釋 - id(select 查詢的序列號,包含一組數(shù)字,表示查詢中執(zhí)行select子句或操作表的順序)
- id相同,執(zhí)行順序從上往下
- id全不同,如果是子查詢,id的序號會遞增,id值越大優(yōu)先級越高,越先被執(zhí)行
- id部分相同,執(zhí)行順序是先按照數(shù)字大的先執(zhí)行,然后數(shù)字相同的按照從上往下的順序執(zhí)行
- select_type(查詢類型,用于區(qū)別普通查詢、聯(lián)合查詢、子查詢等復(fù)雜查詢)
- SIMPLE :簡單的select查詢,查詢中不包含子查詢或UNION
- PRIMARY:查詢中若包含任何復(fù)雜的子部分,最外層查詢被標記為PRIMARY
- SUBQUERY:在select或where列表中包含了子查詢
- DERIVED:在from列表中包含的子查詢被標記為DERIVED,MySQL會遞歸執(zhí)行這些子查詢,把結(jié)果放在臨時表里
- UNION:若第二個select出現(xiàn)在UNION之后,則被標記為UNION,若UNION包含在from子句的子查詢中,外層select將被標記為 DERIVED
- UNION RESULT:從UNION表獲取結(jié)果的select
- table(顯示這一行的數(shù)據(jù)是關(guān)于哪張表的)
- type(顯示查詢使用了那種類型,從最好到最差依次排列 system > const > eq_ref > ref > fulltext > ref_or_null > index_merge > unique_subquery > index_subquery > range > index > ALL )
tip: 一般來說,得保證查詢至少達到range級別,最好到達ref- system:表只有一行記錄(等于系統(tǒng)表),是 const 類型的特例,平時不會出現(xiàn)
- const:表示通過索引一次就找到了,const 用于比較 primary key 或 unique 索引,因為只要匹配一行數(shù)據(jù),所以很快,如將主鍵置于 where 列表中,mysql 就能將該查詢轉(zhuǎn)換為一個常量
- eq_ref:唯一性索引掃描,對于每個索引鍵,表中只有一條記錄與之匹配,常見于主鍵或唯一索引掃描
- ref:非唯一性索引掃描,范圍匹配某個單獨值得所有行。本質(zhì)上也是一種索引訪問,他返回所有匹配某個單獨值的行,然而,它可能也會找到多個符合條件的行,多以他應(yīng)該屬于查找和掃描的混合體
- range:只檢索給定范圍的行,使用一個索引來選擇行。key列顯示使用了哪個索引,一般就是在你的where語句中出現(xiàn)了between、<、>、in等的查詢,這種范圍掃描索引比全表掃描要好,因為它只需開始于索引的某一點,而結(jié)束于另一點,不用掃描全部索引
- index:Full Index Scan,index于ALL區(qū)別為index類型只遍歷索引樹。通常比ALL快,因為索引文件通常比數(shù)據(jù)文件小。(也就是說雖然all和index都是讀全表,但index是從索引中讀取的,而all是從硬盤中讀的)
- ALL:Full Table Scan,將遍歷全表找到匹配的行
- possible_keys(顯示可能應(yīng)用在這張表中的索引,一個或多個,查詢涉及到的字段若存在索引,則該索引將被列出,但不一定被查詢實際使用)
- key
- 實際使用的索引,如果為NULL,則沒有使用索引
- 查詢中若使用了覆蓋索引,則該索引和查詢的 select 字段重疊,僅出現(xiàn)在key列表中
- key_len
表示索引中使用的字節(jié)數(shù),可通過該列計算查詢中使用的索引的長度。在不損失精確性的情況下,長度越短越好- key_len顯示的值為索引字段的最大可能長度,并非實際使用長度,即key_len是根據(jù)表定義計算而得,不是通過表內(nèi)檢索出的
- ref(顯示索引的哪一列被使用了,如果可能的話,是一個常數(shù)。哪些列或常量被用于查找索引列上的值)
- rows(根據(jù)表統(tǒng)計信息及索引選用情況,大致估算找到所需的記錄所需要讀取的行數(shù))
- Extra(包含不適合在其他列中顯示但十分重要的額外信息)
- using filesort: 說明mysql會對數(shù)據(jù)使用一個外部的索引排序,不是按照表內(nèi)的索引順序進行讀取。**mysql中無法利用索引完成的排序操作稱為“文件排序”。**常見于order by和group by語句中
- Using temporary:使用了臨時表保存中間結(jié)果,mysql在對查詢結(jié)果排序時使用臨時表。常見于排序order by和分組查詢group by。
- using index:表示相應(yīng)的select操作中使用了覆蓋索引,避免訪問了表的數(shù)據(jù)行,效率不錯,如果同時出現(xiàn)using where,表明索引被用來執(zhí)行索引鍵值的查找;否則索引被用來讀取數(shù)據(jù)而非執(zhí)行查找操作
- using where:使用了where過濾
- using join buffer:使用了連接緩存
- impossible where:where子句的值總是false,不能用來獲取任何元祖
- select tables optimized away:在沒有g(shù)roup by子句的情況下,基于索引優(yōu)化操作或?qū)τ贛yISAM存儲引擎優(yōu)化COUNT(*)操作,不必等到執(zhí)行階段再進行計算,查詢執(zhí)行計劃生成的階段即完成優(yōu)化
- distinct:優(yōu)化distinct操作,在找到第一匹配的元祖后即停止找同樣值的動作
示例:
1 第一行(執(zhí)行順序4):id列為1,表示是union里的第一個select,select_type列的primary表示該查詢?yōu)橥鈱硬樵?#xff0c;table列被標記為,表示查詢結(jié)果來自一個衍生表,其中derived3中3代表該查詢衍生自第三個select查詢,即id為3的select。【select d1.name…】
2 第二行(執(zhí)行順序2):id為3,是整個查詢中第三個select的一部分。因查詢包含在from中,所以為derived。【select id,name from t1 where other_column=’’】
3 第三行(執(zhí)行順序3):select列表中的子查詢select_type為subquery,為整個查詢中的第二個select?!緎elect id from t3】
4 第四行(執(zhí)行順序1):select_type為union,說明第四個select是union里的第二個select,最先執(zhí)行【select name,id from t2】
5 第五行(執(zhí)行順序5):代表從union的臨時表中讀取行的階段,table列的<union1,4>表示用第一個和第四個select的結(jié)果進行union操作?!緝蓚€結(jié)果union操作】
慢查詢?nèi)罩?/strong> 開啟慢查詢?nèi)罩?/strong> 也可set文件位置,系統(tǒng)會默認給一個缺省文件host_name-slow.log 注:log-slow-queries 參數(shù)為慢查詢?nèi)罩敬娣诺奈恢?#xff0c;一般這個目錄要有 MySQL 的運行帳號的可寫權(quán)限,一般都將這個目錄設(shè)置為 MySQL 的數(shù)據(jù)存放目錄;long_query_time=2 中的 2 表示查詢超過兩秒才記錄;在my.cnf或者 my.ini 中添加 log-queries-not-using-indexes 參數(shù),表示記錄下沒有使用索引的查詢。 也可使用 pt-query-digest 分析 RDS MySQL 慢查詢?nèi)罩?/strong> Show Profile 分析查詢 補充問題:查詢中哪些情況不會使用索引? 查詢優(yōu)化 當(dāng) B 表的數(shù)據(jù)集必須小于 A 表的數(shù)據(jù)集時,用 in 優(yōu)于 exists 當(dāng) A 表的數(shù)據(jù)集小于B表的數(shù)據(jù)集時,用 exists優(yōu)于用 in order by關(guān)鍵字優(yōu)化 GROUP BY關(guān)鍵字優(yōu)化 數(shù)據(jù)類型優(yōu)化 怎么玩 分區(qū)類型及操作 按照 range 來分,就是每個庫一段連續(xù)的數(shù)據(jù),這個一般是按比如時間范圍來的,比如交易表啊,銷售表啊等,可以根據(jù)年月來存放數(shù)據(jù)??赡軙a(chǎn)生熱點問題,大量的流量都打在最新的數(shù)據(jù)上了。 LIST分區(qū):類似于按RANGE分區(qū),每個分區(qū)必須明確定義。它們的主要區(qū)別在于,LIST分區(qū)中每個分區(qū)的定義和選擇是基于某列的值從屬于一個值列表集中的一個值,而RANGE分區(qū)是從屬于一個連續(xù)區(qū)間值的集合。 HASH分區(qū):基于用戶定義的表達式的返回值來進行選擇的分區(qū),該表達式使用將要插入到表中的這些行的列值進行計算。這個函數(shù)可以包含MySQL 中有效的、產(chǎn)生非負整數(shù)值的任何表達式。 KEY分區(qū):類似于按HASH分區(qū),區(qū)別在于KEY分區(qū)只支持計算一列或多列,且MySQL服務(wù)器提供其自身的哈希函數(shù)。必須有一列或多列包含整數(shù)值。 看上去分區(qū)表很帥氣,為什么大部分互聯(lián)網(wǎng)還是更多的選擇自己分庫分表來水平擴展咧? 補充問題:隨著業(yè)務(wù)的發(fā)展,業(yè)務(wù)越來越復(fù)雜,應(yīng)用的模塊越來越多,總的數(shù)據(jù)量很大,高并發(fā)讀寫操作均超過單個數(shù)據(jù)庫服務(wù)器的處理能力怎么辦? 優(yōu)點: 分庫分表后的難題:分布式事務(wù)的問題,數(shù)據(jù)的完整性和一致性問題。 復(fù)制的基本原則 復(fù)制的最大問題:延時
以上是生活随笔為你收集整理的【面试】网易游戏面试题目整理及答案(3)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
MySQL 的慢查詢?nèi)罩臼?MySQL 提供的一種日志記錄,它用來記錄在 MySQL 中響應(yīng)時間超過閾值的語句,具體指運行時間超過 long_query_time 值的 SQL,則會被記錄到慢查詢?nèi)罩局小?/p>
mysql> show variables like '%slow_query_log%';
查看開啟狀態(tài)
mysql> set global slow_query_log='ON';
mysql> set global slow_query_log_file='/var/lib/mysql/hostname-slow.log';
mysql> set global long_query_time=2;
使用set操作開啟慢查詢?nèi)罩局粚Ξ?dāng)前數(shù)據(jù)庫生效,如果MySQL重啟則會失效。
[mysqld]
slow_query_log = ON
slow_query_log_file = /var/lib/mysql/hostname-slow.log
long_query_time = 3
修改配置文件my.cnf或my.ini,在[mysqld]一行下面加入兩個配置參數(shù)
可以用 select sleep(4) 驗證是否成功開啟。
在生產(chǎn)環(huán)境中,如果手工分析日志,查找、分析SQL,還是比較費勁的,所以MySQL提供了日志分析工具mysqldumpslow。通過 mysqldumpslow --help 查看操作幫助信息:
mysqldumpslow -s r -t 10 /var/lib/mysql/hostname-slow.log
mysqldumpslow -s c -t 10 /var/lib/mysql/hostname-slow.log
mysqldumpslow -s t -t 10 -g "left join" /var/lib/mysql/hostname-slow.log
mysqldumpslow -s r -t 10 /var/lib/mysql/hostname-slow.log | more
通過慢日志查詢可以知道哪些 SQL 語句執(zhí)行效率低下,通過 explain 我們可以得知 SQL 語句的具體執(zhí)行情況,索引使用等,還可以結(jié)合Show Profile命令查看執(zhí)行狀態(tài)。
1.是否支持,看看當(dāng)前的mysql版本是否支持
mysql>Show variables like 'profiling'; --默認是關(guān)閉,使用前需要開啟
2.開啟功能,默認是關(guān)閉,使用前需要開啟
mysql>set profiling=1;
3.運行SQL
4.查看結(jié)果
mysql> show profiles; ±---------±-----------±--------------------------------+ | Query_ID | Duration | Query | ±---------±-----------±--------------------------------+ | 1 | 0.00385450 | show variables like “profiling” | | 2 | 0.00170050 | show variables like “profiling” | | 3 | 0.00038025 | select * from t_base_user | ±---------±-----------±--------------------------------+
5)性能優(yōu)化
索引優(yōu)化
1.全值匹配我最愛
2.最佳左前綴法則,比如建立了一個聯(lián)合索引(a,b,c),那么其實我們可利用的索引就有(a), (a,b), (a,b,c)
3.不在索引列上做任何操作(計算、函數(shù)、(自動or手動)類型轉(zhuǎn)換),會導(dǎo)致索引失效而轉(zhuǎn)向全表掃描
4.存儲引擎不能使用索引中范圍條件右邊的列
5.盡量使用覆蓋索引(只訪問索引的查詢(索引列和查詢列一致)),減少select
6.is null ,is not null 也無法使用索引
7.like “xxxx%” 是可以用到索引的,like “%xxxx” 則不行(like “%xxx%” 同理)。like以通配符開頭(’%abc…’)索引失效會變成全表掃描的操作,
8.字符串不加單引號索引失效
9.少用or,用它來連接時會索引失效
10.<,<=,=,>,>=,BETWEEN,IN 可用到索引,<>,not in ,!= 則不行,會導(dǎo)致全表掃描
一般性建議
永遠小標驅(qū)動大表(小的數(shù)據(jù)集驅(qū)動大的數(shù)據(jù)集)
注意:A表與B表的ID字段應(yīng)建立索引。
MySQL 支持的數(shù)據(jù)類型非常多,選擇正確的數(shù)據(jù)類型對于獲取高性能至關(guān)重要。不管存儲哪種類型的數(shù)據(jù),下面幾個簡單的原則都有助于做出更好的選擇。
簡單就好:簡單的數(shù)據(jù)類型通常需要更少的CPU周期。例如,整數(shù)比字符操作代價更低,因為字符集和校對規(guī)則(排序規(guī)則)使字符比較比整型比較復(fù)雜。MySQL分區(qū)、分表、分庫
一般情況下我們創(chuàng)建的表對應(yīng)一組存儲文件,使用MyISAM存儲引擎時是一個.MYI和.MYD文件,使用Innodb存儲引擎時是一個.ibd和.frm(表結(jié)構(gòu))文件。
當(dāng)數(shù)據(jù)量較大時(一般千萬條記錄級別以上),MySQL的性能就會開始下降,這時我們就需要將數(shù)據(jù)分散到多組存儲文件,保證其單個文件的執(zhí)行效率
能干嘛
首先查看當(dāng)前數(shù)據(jù)庫是否支持分區(qū)
SHOW VARIABLES LIKE '%partition%';
show plugins;
range 來分,好處在于說,擴容的時候很簡單。
hash 分發(fā),好處在于說,可以平均分配每個庫的數(shù)據(jù)量和請求壓力;壞處在于說擴容起來比較麻煩,會有一個數(shù)據(jù)遷移的過程,之前的數(shù)據(jù)需要重新計算 hash 值重新分配到不同的庫或表
答:這個時候就出現(xiàn)了數(shù)據(jù)分片,數(shù)據(jù)分片指按照某個維度將存放在單一數(shù)據(jù)庫中的數(shù)據(jù)分散地存放至多個數(shù)據(jù)庫或表中。數(shù)據(jù)分片的有效手段就是對關(guān)系型數(shù)據(jù)庫進行分庫和分表。
區(qū)別于分區(qū)的是,分區(qū)一般都是放在單機里的,用的比較多的是時間范圍分區(qū),方便歸檔。只不過分庫分表需要代碼實現(xiàn),分區(qū)則是mysql內(nèi)部實現(xiàn)。分庫分表和分區(qū)并不沖突,可以結(jié)合使用。
補充問題:說說分庫與分表的設(shè)計。
1)MySQL分表
分表有兩種分割方式,一種垂直拆分,另一種水平拆分。
垂直分表,通常是按照業(yè)務(wù)功能的使用頻次,把主要的、熱門的字段放在一起做為主要表。然后把不常用的,按照各自的業(yè)務(wù)屬性進行聚集,拆分到不同的次要表中;主要表和次要表的關(guān)系一般都是一對一的。
單表的容量不超過500W,否則建議水平拆分。是把一個表復(fù)制成同樣表結(jié)構(gòu)的不同表,然后把數(shù)據(jù)按照一定的規(guī)則劃分,分別存儲到這些表中,從而保證單表的容量不會太大,提升性能;當(dāng)然這些結(jié)構(gòu)一樣的表,可以放在一個或多個數(shù)據(jù)庫中。
水平分割的幾種方法:
1.使用MD5哈希,做法是對UID進行md5加密,然后取前幾位(我們這里取前兩位),然后就可以將不同的UID哈希到不同的用戶表(user_xx)中了。
2.還可根據(jù)時間放入不同的表,比如:article_201601,article_201602。
3.按熱度拆分,高點擊率的詞條生成各自的一張表,低熱度的詞條都放在一張大表里,待低熱度的詞條達到一定的貼數(shù)后,再把低熱度的表單獨拆分成一張表。
4.根據(jù)ID的值放入對應(yīng)的表,第一個表user_0000,第二個100萬的用戶數(shù)據(jù)放在第二 個表user_0001中,隨用戶增加,直接添加用戶表就行了。
2)MySQL分庫
問題:為什么要分庫?
答:數(shù)據(jù)庫集群環(huán)境后都是多臺 slave,基本滿足了讀取操作; 但是寫入或者說大數(shù)據(jù)、頻繁的寫入操作對master性能影響就比較大,這個時候,單庫并不能解決大規(guī)模并發(fā)寫入的問題,所以就會考慮分庫。
問題:分庫是什么?
答:一個庫里表太多了,導(dǎo)致了海量數(shù)據(jù),系統(tǒng)性能下降,把原本存儲于一個庫的表拆分存儲到多個庫上, 通常是將表按照功能模塊、關(guān)系密切程度劃分出來,部署到不同庫上。
但是它無法解決單表數(shù)據(jù)量太大的問題
數(shù)據(jù)操作維度問題:用戶、交易、訂單各個不同的維度,用戶查詢維度、產(chǎn)品數(shù)據(jù)分析維度的不同對比分析角度。**跨庫聯(lián)合查詢的問題,**可能需要兩次查詢 跨節(jié)點的count、order by、group by以及聚合函數(shù)問題,可能需要分別在各個節(jié)點上得到結(jié)果后在應(yīng)用程序端進行合并。額外的數(shù)據(jù)管理負擔(dān),如:訪問數(shù)據(jù)表的導(dǎo)航定位。 額外的數(shù)據(jù)運算壓力,如:需要在多個節(jié)點執(zhí)行,然后再合并,計算程序編碼開發(fā)難度提升,沒有太好的框架解決,更多依賴業(yè)務(wù)看如何分,如何合,是個難題。主從復(fù)制
1)master將改變記錄到二進制日志(binary log)。這些記錄過程叫做二進制日志事件,binary log events;
2)salve 將 master 的 binary log events 拷貝到它的中繼日志(relay log);
3)slave 重做中繼日志中的事件,將改變應(yīng)用到自己的數(shù)據(jù)庫中。MySQL 復(fù)制是異步且是串行化的。其他問題
關(guān)于索引:由于索引需要額外的維護成本,因為索引文件是單獨存在的文件,所以當(dāng)我們對數(shù)據(jù)的增加,修改,刪除,都會產(chǎn)生額外的對索引文件的操作,這些操作需要消耗額外的IO,會降低增/改/刪的執(zhí)行效率。所以,在我們刪除數(shù)據(jù)庫百萬級別數(shù)據(jù)的時候,查詢MySQL官方手冊得知刪除數(shù)據(jù)的速度和創(chuàng)建的索引數(shù)量是成正比的。所以我們想要刪除百萬數(shù)據(jù)的時候:
1)可以先刪除索引(此時大概耗時三分多鐘)
2)然后刪除其中無用數(shù)據(jù)(此過程需要不到兩分鐘)
3)刪除完成后重新創(chuàng)建索引(此時數(shù)據(jù)較少了)創(chuàng)建索引也非???#xff0c;約十分鐘左右。
與之前的直接刪除絕對是要快速很多,更別說萬一刪除中斷,一切刪除會回滾。那更是坑了。總結(jié)
- 上一篇: 【基础】ARM芯片上电取第一条指令流程
- 下一篇: 全国智能网联与无人驾驶职教联盟筹建工作正