日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

【面试】网易游戏面试题目整理及答案(3)

發(fā)布時間:2023/12/14 编程问答 29 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【面试】网易游戏面试题目整理及答案(3) 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

網(wǎng)易游戲面試題目整理及答案(3)

  • 數(shù)據(jù)庫部分
    • MySQL 事務(wù)
    • MySQL鎖機制
    • MySQL調(diào)優(yōu)
    • MySQL分區(qū)、分表、分庫
    • 主從復(fù)制
    • 其他問題

數(shù)據(jù)庫部分

MySQL 事務(wù)

  • 事務(wù)的隔離級別有哪些?MySQL的默認隔離級別是什么?什么是幻讀,臟讀,不可重復(fù)讀呢?MySQL事務(wù)的四大特性以及實現(xiàn)原理。MVCC熟悉嗎,它的底層原理?
    答: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ù)的多個版本。
  • 事務(wù)隔離級別
    答:數(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ù)隔離級別:
    show variables like 'tx_isolation'

    數(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(可重讀)并不會有任何性能損失。

  • MVCC 多版本并發(fā)控制
    答: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ù)讀)兩種隔離級別下工作。

  • 事務(wù)日志
    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)
    答:事務(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
  • MySQL對分布式事務(wù)的支持
    答:分布式事務(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鎖機制

  • 數(shù)據(jù)庫的樂觀鎖和悲觀鎖?MySQL 中有哪幾種鎖,列舉一下?MySQL中InnoDB引擎的行鎖是怎么實現(xiàn)的?MySQL 間隙鎖有沒有了解,死鎖有沒有了解,寫一段會造成死鎖的 sql 語句,死鎖發(fā)生了如何解決,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ù)不能修改和刪除加鎖項;
    SELECT * FROM table WHERE id = 1 FOR UPDATE;

    它會在 id=1 的記錄上加上記錄鎖,以阻止其他事務(wù)插入,更新,刪除 id=1 這一行
    在通過 主鍵索引 與 唯一索引 對數(shù)據(jù)行進行 UPDATE 操作時,也會對該行數(shù)據(jù)加記錄鎖:

    -- id 列為主鍵列或唯一索引列 UPDATE SET age = 50 WHERE id = 1;
    • 間隙鎖(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ù)
    SELECT * FROM table WHERE id BETWEN 1 AND 10 FOR UPDATE;

    即所有在(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
    SELECT * FROM products WHERE id='3' FOR UPDATE; SELECT * FROM products WHERE id='3' and type=1 FOR UPDATE;
    • 明確指定主鍵,若沒有該記錄,則無lock
    SELECT * FROM products WHERE id='-1' FOR UPDATE;
    • 無主鍵, table lock
    SELECT * FROM products WHERE name='Mouse' FOR UPDATE;
    • 主鍵不明確, table lock
    SELECT * FROM products WHERE id<>'3' FOR UPDATE;
    • 主鍵不明確,table lock
    SELECT * FROM products WHERE id LIKE '3' FOR UPDATE;

    注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)

  • 日常工作中你是怎么優(yōu)化SQL的?SQL優(yōu)化的一般步驟是什么,怎么看執(zhí)行計劃(explain),如何理解其中各個字段的含義?如何寫sql能夠有效的使用到復(fù)合索引?一條sql執(zhí)行過長的時間,你如何優(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)的瓶頸:
    Mysql> show status ——顯示狀態(tài)信息(擴展show status like ‘XXX’) Mysql> show variables ——顯示系統(tǒng)變量(擴展show variables like ‘XXX’) Mysql> show innodb status ——顯示InnoDB存儲引擎的狀態(tài) Mysql> show processlist ——查看當(dāng)前SQL執(zhí)行,包括執(zhí)行狀態(tài)、是否鎖表等 Shell> mysqladmin variables -u username -p password——顯示系統(tǒng)變量 Shell> mysqladmin extended-status -u username -p password——顯示狀態(tài)信息

    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>
    MySQL 的慢查詢?nèi)罩臼?MySQL 提供的一種日志記錄,它用來記錄在 MySQL 中響應(yīng)時間超過閾值的語句,具體指運行時間超過 long_query_time 值的 SQL,則會被記錄到慢查詢?nèi)罩局小?/p>

    • long_query_time 的默認值為10,意思是運行10秒以上的語句
    • 默認情況下,MySQL數(shù)據(jù)庫沒有開啟慢查詢?nèi)罩?#xff0c;需要手動設(shè)置參數(shù)開啟
      查看開啟狀態(tài)
    mysql> show variables like '%slow_query_log%';

    開啟慢查詢?nèi)罩?/strong>

    • 臨時配置:
    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文件位置,系統(tǒng)會默認給一個缺省文件host_name-slow.log
    使用set操作開啟慢查詢?nèi)罩局粚Ξ?dāng)前數(shù)據(jù)庫生效,如果MySQL重啟則會失效。

    • 永久配置
      修改配置文件my.cnf或my.ini,在[mysqld]一行下面加入兩個配置參數(shù)
    [mysqld] slow_query_log = ON slow_query_log_file = /var/lib/mysql/hostname-slow.log long_query_time = 3

    注: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ù),表示記錄下沒有使用索引的查詢。
    可以用 select sleep(4) 驗證是否成功開啟。
    在生產(chǎn)環(huán)境中,如果手工分析日志,查找、分析SQL,還是比較費勁的,所以MySQL提供了日志分析工具mysqldumpslow。通過 mysqldumpslow --help 查看操作幫助信息:

    • 得到返回記錄集最多的10個SQL
    mysqldumpslow -s r -t 10 /var/lib/mysql/hostname-slow.log
    • 得到訪問次數(shù)最多的10個SQL
    mysqldumpslow -s c -t 10 /var/lib/mysql/hostname-slow.log
    • 得到按照時間排序的前10條里面含有左連接的查詢語句
    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

    也可使用 pt-query-digest 分析 RDS MySQL 慢查詢?nèi)罩?/strong>

    Show Profile 分析查詢
    通過慢日志查詢可以知道哪些 SQL 語句執(zhí)行效率低下,通過 explain 我們可以得知 SQL 語句的具體執(zhí)行情況,索引使用等,還可以結(jié)合Show Profile命令查看執(zhí)行狀態(tài)。

    • Show Profile 是 MySQL 提供可以用來分析當(dāng)前會話中語句執(zhí)行的資源消耗情況??梢杂糜赟QL的調(diào)優(yōu)的測量
    • 默認情況下,參數(shù)處于關(guān)閉狀態(tài),并保存最近15次的運行結(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 | ±---------±-----------±--------------------------------+
    • converting HEAP to MyISAM 查詢結(jié)果太大,內(nèi)存都不夠用了往磁盤上搬了。
    • create tmp table 創(chuàng)建臨時表,這個要注意
    • Copying to tmp table on disk 把內(nèi)存臨時表復(fù)制到磁盤
    • locked
    • 診斷SQL,show profile cpu,block io for query id(上一步前面的問題SQL數(shù)字號碼)
    • 日常開發(fā)需要注意的結(jié)論
    1.是否支持,看看當(dāng)前的mysql版本是否支持 mysql>Show variables like 'profiling'; --默認是關(guān)閉,使用前需要開啟 2.開啟功能,默認是關(guān)閉,使用前需要開啟 mysql>set profiling=1; 3.運行SQL 4.查看結(jié)果

    補充問題:查詢中哪些情況不會使用索引?
    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)致全表掃描
    一般性建議

    • 對于單鍵索引,盡量選擇針對當(dāng)前query過濾性更好的索引
    • 在選擇組合索引的時候,當(dāng)前Query中過濾性最好的字段在索引字段順序中,位置越靠前越好。
    • 在選擇組合索引的時候,盡量選擇可以能夠包含當(dāng)前query中的where字句中更多字段的索引
    • 盡可能通過分析統(tǒng)計信息和調(diào)整query的寫法來達到選擇合適索引的目的
    • 少用Hint強制索引

    查詢優(yōu)化
    永遠小標驅(qū)動大表(小的數(shù)據(jù)集驅(qū)動大的數(shù)據(jù)集)

    slect * from A where id in (select id from B)`等價于 #等價于 select id from B select * from A where A.id=B.id

    當(dāng) B 表的數(shù)據(jù)集必須小于 A 表的數(shù)據(jù)集時,用 in 優(yōu)于 exists

    select * from A where exists (select 1 from B where B.id=A.id) #等價于 select * from A select * from B where B.id = A.id`

    當(dāng) A 表的數(shù)據(jù)集小于B表的數(shù)據(jù)集時,用 exists優(yōu)于用 in
    注意:A表與B表的ID字段應(yīng)建立索引。

    order by關(guān)鍵字優(yōu)化

    • order by子句,盡量使用 Index 方式排序,避免使用 FileSort 方式排序
    • MySQL 支持兩種方式的排序,FileSort 和 Index,Index效率高,它指 MySQL 掃描索引本身完成排序,FileSort 效率較低;
    • ORDER BY 滿足兩種情況,會使用Index方式排序;①ORDER BY語句使用索引最左前列 ②使用where子句與ORDER BY子句條件列組合滿足索引最左前列
    • 盡可能在索引列上完成排序操作,遵照索引建的最佳最前綴
    • 如果不在索引列上,filesort 有兩種算法,mysql就要啟動雙路排序和單路排序
      • 雙路排序:MySQL 4.1之前是使用雙路排序,字面意思就是兩次掃描磁盤,最終得到數(shù)據(jù)
      • 單路排序:從磁盤讀取查詢需要的所有列,按照order by 列在 buffer對它們進行排序,然后掃描排序后的列表進行輸出,效率高于雙路排序
    • 優(yōu)化策略
      • 增大sort_buffer_size參數(shù)的設(shè)置
      • 增大max_lencth_for_sort_data參數(shù)的設(shè)置

    GROUP BY關(guān)鍵字優(yōu)化

    • group by實質(zhì)是先排序后進行分組,遵照索引建的最佳左前綴
    • 當(dāng)無法使用索引列,增大 max_length_for_sort_data 參數(shù)的設(shè)置,增大sort_buffer_size參數(shù)的設(shè)置
    • where高于having,能寫在where限定的條件就不要去having限定了

    數(shù)據(jù)類型優(yōu)化
    MySQL 支持的數(shù)據(jù)類型非常多,選擇正確的數(shù)據(jù)類型對于獲取高性能至關(guān)重要。不管存儲哪種類型的數(shù)據(jù),下面幾個簡單的原則都有助于做出更好的選擇。

    • 更小的通常更好:一般情況下,應(yīng)該盡量使用可以正確存儲數(shù)據(jù)的最小數(shù)據(jù)類型。
      簡單就好:簡單的數(shù)據(jù)類型通常需要更少的CPU周期。例如,整數(shù)比字符操作代價更低,因為字符集和校對規(guī)則(排序規(guī)則)使字符比較比整型比較復(fù)雜。
    • 盡量避免NULL:通常情況下最好指定列為NOT NULL

    MySQL分區(qū)、分表、分庫

  • MySQL分區(qū)
    一般情況下我們創(chuàng)建的表對應(yīng)一組存儲文件,使用MyISAM存儲引擎時是一個.MYI和.MYD文件,使用Innodb存儲引擎時是一個.ibd和.frm(表結(jié)構(gòu))文件。
    當(dāng)數(shù)據(jù)量較大時(一般千萬條記錄級別以上),MySQL的性能就會開始下降,這時我們就需要將數(shù)據(jù)分散到多組存儲文件,保證其單個文件的執(zhí)行效率
    能干嘛
    • 邏輯數(shù)據(jù)分割
    • 提高單一的寫和讀應(yīng)用速度
    • 提高分區(qū)范圍讀查詢的速度
    • 分割數(shù)據(jù)能夠有多個不同的物理文件路徑
    • 高效的保存歷史數(shù)據(jù)

    怎么玩
    首先查看當(dāng)前數(shù)據(jù)庫是否支持分區(qū)

    • MySQL5.6以及之前版本:
    SHOW VARIABLES LIKE '%partition%';
    • MySQL5.6:
    show plugins;

    分區(qū)類型及操作

    • RANGE分區(qū):基于屬于一個給定連續(xù)區(qū)間的列值,把多行分配給分區(qū)。mysql將會根據(jù)指定的拆分策略,,把數(shù)據(jù)放在不同的表文件上。相當(dāng)于在文件上,被拆成了小塊.但是,對外給客戶的感覺還是一張表,透明的。

    按照 range 來分,就是每個庫一段連續(xù)的數(shù)據(jù),這個一般是按比如時間范圍來的,比如交易表啊,銷售表啊等,可以根據(jù)年月來存放數(shù)據(jù)??赡軙a(chǎn)生熱點問題,大量的流量都打在最新的數(shù)據(jù)上了。
    range 來分,好處在于說,擴容的時候很簡單。

    • LIST分區(qū):類似于按RANGE分區(qū),每個分區(qū)必須明確定義。它們的主要區(qū)別在于,LIST分區(qū)中每個分區(qū)的定義和選擇是基于某列的值從屬于一個值列表集中的一個值,而RANGE分區(qū)是從屬于一個連續(xù)區(qū)間值的集合。

    • HASH分區(qū)基于用戶定義的表達式的返回值來進行選擇的分區(qū),該表達式使用將要插入到表中的這些行的列值進行計算。這個函數(shù)可以包含MySQL 中有效的、產(chǎn)生非負整數(shù)值的任何表達式。
      hash 分發(fā),好處在于說,可以平均分配每個庫的數(shù)據(jù)量和請求壓力;壞處在于說擴容起來比較麻煩,會有一個數(shù)據(jù)遷移的過程,之前的數(shù)據(jù)需要重新計算 hash 值重新分配到不同的庫或表

    • KEY分區(qū):類似于按HASH分區(qū),區(qū)別在于KEY分區(qū)只支持計算一列或多列,且MySQL服務(wù)器提供其自身的哈希函數(shù)。必須有一列或多列包含整數(shù)值。

    看上去分區(qū)表很帥氣,為什么大部分互聯(lián)網(wǎng)還是更多的選擇自己分庫分表來水平擴展咧?

    • 分區(qū)表,分區(qū)鍵設(shè)計不太靈活,如果不走分區(qū)鍵,很容易出現(xiàn)全表鎖
    • 一旦數(shù)據(jù)并發(fā)量上來,如果在分區(qū)表實施關(guān)聯(lián),就是一個災(zāi)難
    • 自己分庫分表,自己掌控業(yè)務(wù)場景與訪問模式,可控。分區(qū)表,研發(fā)寫了一個sql,都不確定mysql是怎么玩的,不太可控

    補充問題:隨著業(yè)務(wù)的發(fā)展,業(yè)務(wù)越來越復(fù)雜,應(yīng)用的模塊越來越多,總的數(shù)據(jù)量很大,高并發(fā)讀寫操作均超過單個數(shù)據(jù)庫服務(wù)器的處理能力怎么辦?
    答:這個時候就出現(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)系一般都是一對一的。
    • 水平拆分(數(shù)據(jù)分片)
      單表的容量不超過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)系密切程度劃分出來,部署到不同庫上

    優(yōu)點:

    • 減少增量數(shù)據(jù)寫入時的鎖對查詢的影響
    • 由于單表數(shù)量下降,常見的查詢操作由于減少了需要掃描的記錄,使得單表單次查詢所需的檢索行數(shù)變少,減少了磁盤IO,時延變短
      但是它無法解決單表數(shù)據(jù)量太大的問題

    分庫分表后的難題分布式事務(wù)的問題數(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ù)制

  • 復(fù)制的基本原理
    • slave 會從 master 讀取 binlog 來進行數(shù)據(jù)同步
    • 三個步驟:

      1)master將改變記錄到二進制日志(binary log)。這些記錄過程叫做二進制日志事件,binary log events;
      2)salve 將 master 的 binary log events 拷貝到它的中繼日志(relay log);
      3)slave 重做中繼日志中的事件,將改變應(yīng)用到自己的數(shù)據(jù)庫中。MySQL 復(fù)制是異步且是串行化的。

    復(fù)制的基本原則

    • 每個 slave只有一個 master
    • 每個 salve只能有一個唯一的服務(wù)器 ID
    • 每個master可以有多個salve

    復(fù)制的最大問題:延時

    其他問題

  • 說一說三個范式
    • 第一范式(1NF):數(shù)據(jù)庫表中的字段都是單一屬性的,不可再分。這個單一屬性由基本類型構(gòu)成,包括整型、實數(shù)、字符型、邏輯型、日期型等。
    • 第二范式(2NF):數(shù)據(jù)庫表中不存在非關(guān)鍵字段對任一候選關(guān)鍵字段的部分函數(shù)依賴(部分函數(shù)依賴指的是存在組合關(guān)鍵字中的某些字段決定非關(guān)鍵字段的情況),也即所有非關(guān)鍵字段都完全依賴于任意一組候選關(guān)鍵字。
    • 第三范式(3NF):在第二范式的基礎(chǔ)上,數(shù)據(jù)表中如果不存在非關(guān)鍵字段對任一候選關(guān)鍵字段的傳遞函數(shù)依賴則符合第三范式。所謂傳遞函數(shù)依賴,指的是如 果存在"A → B → C"的決定關(guān)系,則C傳遞函數(shù)依賴于A。因此,滿足第三范式的數(shù)據(jù)庫表應(yīng)該不存在如下依賴關(guān)系:關(guān)鍵字段 → 非關(guān)鍵字段 x → 非關(guān)鍵字段y
  • 百萬級別或以上的數(shù)據(jù)如何刪除
    關(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é)

    以上是生活随笔為你收集整理的【面试】网易游戏面试题目整理及答案(3)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。