深刻淺出MySQL靈魂十連問,你真的有把握嗎?

深刻淺出MySQL靈魂十連問,你真的有把握嗎?

1、SQL語句執行流程

MySQL大致上可分爲Server層存儲引擎層兩部分。mysql

Server層算法

  • 鏈接器:TCP握手後服務器來驗證登錄用戶身份,A用戶建立鏈接後,管理員對A用戶權限修改了也不會影響到已經建立的連接權限,必須從新登錄。
  • 查詢緩存:查詢後的結果存儲位置,MySQL8.0版本之後已經取消,由於查詢緩存失效太頻繁,得不償失。
  • 分析器:根據語法規則,判斷你輸入的這個SQL語句是否知足MySQL語法。
  • 優化器:多種執行策略可實現目標,系統自動選擇最優進行執行。
  • 執行器:判斷是否有權限,將最終任務提交到存儲引擎。

存儲引擎層sql

負責數據的存儲和提取。其架構模式是插件式的,支持InnoDBMyISAMMemory等多個存儲引擎。如今最經常使用的存儲引擎是InnoDB,它從MySQL 5.5.5版本開始成爲了默認存儲引擎(常常用的也是這個)。數據庫

深刻淺出MySQL靈魂十連問,你真的有把握嗎?

SQL執行順序編程

深刻淺出MySQL靈魂十連問,你真的有把握嗎?

2、BinLog、RedoLog、UndoLog

BinLog數組

BinLog是記錄全部數據庫表結構變動(例如create、alter table)以及表數據修改(insert、update、delete)的二進制日誌,主從數據庫同步用到的都是BinLog文件。BinLog日誌文件有三種模式。緩存

STATEMENT 模式安全

內容:binlog 只會記錄可能引發數據變動的 sql 語句服務器

優點:該模式下,由於沒有記錄實際的數據,因此日誌量和 IO 都消耗很低,性能是最優的網絡

劣勢:但有些操做並非肯定的,好比 uuid() 函數會隨機產生惟一標識,當依賴 binlog 回放時,該操做生成的數據與原數據必然是不一樣的,此時可能形成沒法預料的後果。

ROW 模式

內容:在該模式下,binlog 會記錄每次操做的源數據與修改後的目標數據,StreamSets就要求該模式。

優點:能夠絕對精準的還原,從而保證了數據的安全與可靠,而且複製和數據恢復過程能夠是併發進行的

劣勢:缺點在於 binlog 體積會很是大,同時,對於修改記錄多、字段長度大的操做來講,記錄時性能消耗會很嚴重。閱讀的時候也須要特殊指令來進行讀取數據。

MIXED 模式

內容:是對上述STATEMENT 跟 ROW  兩種模式的混合使用。

細節:對於絕大部分操做,都使用 STATEMENT 來進行 binlog 的記錄,只有如下操做使用 ROW 來實現:表的存儲引擎爲 NDB,使用了uuid() 等不肯定函數,使用了 insert delay 語句,使用了臨時表

深刻淺出MySQL靈魂十連問,你真的有把握嗎?

主從同步流程

一、主節點必須啓用二進制日誌,記錄任何修改了數據庫數據的事件。

二、從節點開啓一個線程(I/O Thread)把本身扮演成 mysql 的客戶端,經過 mysql 協議,請求主節點的二進制日誌文件中的事件 。

三、主節點啓動一個線程(dump Thread),檢查本身二進制日誌中的事件,跟對方請求的位置對比,若是不帶請求位置參數,則主節點就會從第一個日誌文件中的第一個事件一個一個發送給從節點。

四、從節點接收到主節點發送過來的數據把它放置到中繼日誌(Relay log)文件中。並記錄該次請求到主節點的具體哪個二進制日誌文件內部的哪個位置(主節點中的二進制文件會有多個)。

五、從節點啓動另一個線程(sql Thread ),把 Relay log 中的事件讀取出來,並在本地再執行一次。

mysql默認的複製方式是異步的,而且複製的時候是有並行複製能力的。主庫把日誌發送給從庫後無論了,這樣會產生一個問題就是假設主庫掛了,從庫處理失敗了,這時候從庫升爲主庫後,日誌就丟失了。由此產生兩個概念。

1.全同步複製

主庫寫入binlog後強制同步日誌到從庫,全部的從庫都執行完成後才返回給客戶端,可是很顯然這個方式的話性能會受到嚴重影響。

2.半同步複製

半同步複製的邏輯是這樣,從庫寫入日誌成功後返回ACK確認給主庫,主庫收到至少一個從庫的確認就認爲寫操做完成。

還能夠延伸到因爲主從配置不同、主庫大事務、從庫壓力過大、網絡震盪等形成主備延遲,如何避免這個問題?主備切換的時候用可靠性優先原則仍是可用性優先原則?如何判斷主庫Crash了?互爲主備狀況下如何避免主備循環複製?被刪庫跑路瞭如何正確恢復?(⊙o⊙)… 感受愈來愈扯到DBA的活兒上去了。

深刻淺出MySQL靈魂十連問,你真的有把握嗎?

RedoLog

能夠先經過下面demo理解:

飯點記帳能夠把帳單寫在帳本上也能夠寫在粉板上。有人賒帳或者還帳的話,通常有兩種作法:

一、直接把帳本翻出來,把此次賒的帳加上去或者扣除掉。

二、先在粉板上記下此次的帳,等打烊之後再把帳本翻出來覈算。

生意忙時選後者,由於前者太麻煩了。得在密密麻麻的記錄中找到這我的的賒帳總額信息,找到以後再拿出算盤計算,最後再將結果寫回到帳本上。

一樣在MySQL中若是每一次的更新操做都須要寫進磁盤,而後磁盤也要找到對應的那條記錄,而後再更新,整個過程IO成本、查找成本都很高。而粉板和帳本配合的整個過程就是MySQL用到的是Write-Ahead Logging 技術,它的關鍵點就是先寫日誌,再寫磁盤。此時帳本 = BinLog,粉板 = RedoLog。

一、 記錄更新時,InnoDB引擎就會先把記錄寫到RedoLog(粉板)裏面,並更新內存。同時,InnoDB引擎會在空閒時將這個操做記錄更新到磁盤裏面。

二、 若是更新太多RedoLog處理不了的時候,需先將RedoLog部分數據寫到磁盤,而後擦除RedoLog部分數據。RedoLog相似轉盤。

RedoLog有write pos 跟checkpoint

write pos :是當前記錄的位置,一邊寫一邊後移,寫到第3號文件末尾後就回到0號文件開頭。

check point:是當前要擦除的位置,也是日後推移而且循環的,擦除記錄前要把記錄更新到數據文件。

write pos和check point之間的是粉板上還空着的部分,能夠用來記錄新的操做。若是write pos追上checkpoint,表示粉板滿了,這時候不能再執行新的更新,得停下來先擦掉一些記錄,把checkpoint推動一下。

有了redo log,InnoDB就能夠保證即便數據庫發生異常重啓,以前提交的記錄都不會丟失,這個能力稱爲crash-safe

深刻淺出MySQL靈魂十連問,你真的有把握嗎?

redolog兩階段提交:爲了讓binlog跟redolog兩份日誌之間的邏輯一致。提交流程大體以下:

1 prepare階段 -->  2 寫binlog  --> 3 commit

  1. 當在2以前崩潰時,重啓恢復後發現沒有commit,回滾。備份恢復:沒有binlog 。一致
  2. 當在3以前崩潰時,重啓恢復發現雖沒有commit,但知足prepare和binlog完整,因此重啓後會自動commit。備份:有binlog. 一致

binlog跟redolog區別

  1. redo log是InnoDB引擎特有的;binlog是MySQL的Server層實現的,全部引擎均可以使用。
  2. redo log是物理日誌,記錄的是在某個數據頁上作了什麼修改;binlog是邏輯日誌,記錄的是這個語句的原始邏輯,好比給ID=2這一行的c字段加1。
  3. redo log是循環寫的,空間固定會用完;binlog是能夠追加寫入的。追加寫是指binlog文件寫到必定大小後會切換到下一個,並不會覆蓋之前的日誌。

UndoLog

UndoLog 通常是邏輯日誌,主要分爲兩種:

  1. insert undo log

表明事務在insert新記錄時產生的undo log, 只在事務回滾時須要,而且在事務提交後能夠被當即丟棄

  1. update undo log

事務在進行update或delete時產生的undo log; 不只在事務回滾時須要,在快照讀時也須要;因此不能隨便刪除,只有在快速讀或事務回滾不涉及該日誌時,對應的日誌纔會被purge線程統一清除

3、MySQL中的索引

索引的常見模型有哈希表有序數組搜索樹

哈希表:一種以KV存儲數據的結構,只適合等值查詢,不適合範圍查詢。

有序數組:只適用於靜態存儲引擎,涉及到插入的時候比較麻煩。能夠參考Java中的ArrayList

搜索樹:按照數據結構中的二叉樹來存儲數據,不過此時是N叉樹(B+樹)。普遍應用在存儲引擎層中

深刻淺出MySQL靈魂十連問,你真的有把握嗎?

B+樹比B樹優點在於:

  1. B+ 樹非葉子節點存儲的只是索引,能夠存儲的更多。B+樹比B樹更加矮胖,IO次數更少。
  2. B+ 樹葉子節點先後管理,更加方便範圍查詢。同時結果都在葉子節點,查詢效率穩定。
  3. B+樹中更有利於對數據掃描,能夠避免B樹的回溯掃描。

索引的優勢:

一、惟一索引能夠保證每一行數據的惟一性 

二、提升查詢速度 

三、加速表與表的鏈接 

四、顯著的減小查詢中分組和排序的時間

五、經過使用索引,能夠在查詢的過程當中,使用優化隱藏器,提升系統的性能。

索引的缺點:

一、建立跟維護都須要耗時 

二、建立索引時,須要對錶加鎖,在鎖表的同時,可能會影響到其餘的數據操做 

三、 索引須要磁盤的空間進行存儲,磁盤佔用也很快。

四、當對錶中的數據進行CRUD的時,也會觸發索引的維護,而維護索引須要時間,可能會下降數據操做性能

索引設計的原則不該該:

一、索引不是越多越好。索引太多,維護索引須要時間跟空間。

二、 頻繁更新的數據,不宜建索引。

三、數據量小的表不必創建索引。

應該:

一、重複率小的列建議生成索引。由於重複數據少,索引樹查詢更有效率,等價基數越大越好。

二、數據具備惟一性,建議生成惟一性索引。在數據庫的層面,保證數據正確性 

三、頻繁group by、order by的列建議生成索引。能夠大幅提升分組和排序效率 

四、常常用於查詢條件的字段建議生成索引。經過索引查詢,速度更快

索引失效的場景

一、模糊搜索:左模糊或全模糊都會致使索引失效,好比'%a'和'%a%'。可是右模糊是能夠利用索引的,好比'a%' 。

二、隱式類型轉換:好比select from t where name = xxx , name是字符串類型,可是沒有加引號,因此是由MySQL隱式轉換的,因此會讓索引失效 三、當語句中帶有or的時候:好比select from t where name=‘sw’ or age=14

四、不符合聯合索引的最左前綴匹配:(A,B,C)的聯合索引,你只where了C或B或只有B,C

關於索引的知識點

主鍵索引:主鍵索引的葉子節點存的是整行數據信息。在InnoDB裏,主鍵索引也被稱爲聚簇索引(clustered index)。主鍵自增是沒法保證徹底自增的哦,遇到惟一鍵衝突、事務回滾等均可能致使不連續。

惟一索引:以惟一列生成的索引,該列不容許有重複值,但容許有空值(NULL)

普通索引跟惟一索引查詢性能:InnoDB的數據是按數據頁爲單位來讀寫的,默認每頁16KB,所以這兩種索引查詢數據性能差異微乎其微。

change buffer:普通索引用在更新過程的加速,更新的字段若是在緩存中,若是是普通索引則直接更新便可。若是是惟一索引須要將全部數據讀入內存來確保不違背惟一性,因此儘可能用普通索引。

非主鍵索引:非主鍵索引的葉子節點內容是主鍵的值。在InnoDB裏,非主鍵索引也被稱爲二級索引(secondary index)

回表:先經過數據庫索引掃描出數據所在的行,再經過行主鍵id取出索引中未提供的數據,即基於非主鍵索引的查詢須要多掃描一棵索引樹。

覆蓋索引:若是一個索引包含(或者說覆蓋)全部須要查詢的字段的值,咱們就稱之爲覆蓋索引。

聯合索引:相對單列索引,組合索引是用多個列組合構建的索引,一次性最多聯合16個。

最左前綴原則:對多個字段同時創建的組合索引(有順序,ABC,ACB是徹底不一樣的兩種聯合索引) 以聯合索引(a,b,c)爲例,創建這樣的索引至關於創建了索引a、ab、abc三個索引。另外組合索引實際仍是一個索引,並不是真的建立了多個索引,只是產生的效果等價於產生多個索引。

索引下推:MySQL 5.6引入了索引下推優化,能夠在索引遍歷過程當中,對索引中包含的字段先作判斷,過濾掉不符合條件的記錄,減小回表字數。

索引維護:B+樹爲了維護索引有序性涉及到頁分裂跟頁合併。增刪數據時需考慮頁空間利用率。

自增主鍵:通常會創建與業務無關的自增主鍵,不會觸發葉子節點分裂。

延遲關聯:經過使用覆蓋索引查詢返回須要的主鍵,再根據主鍵關聯原表得到須要的數據。

InnoDB存儲* .frm文件是一份定義文件,也就是定義數據庫表是一張怎麼樣的表。*.ibd文件則是該表的索引,數據存儲文件,既該表的全部索引樹,全部行記錄數據都存儲在該文件中。

MyISAM存儲* .frm文件是一份定義文件,也就是定義數據庫表是一張怎麼樣的表。* .MYD文件是MyISAM存儲引擎表的全部行數據的文件。* .MYI文件存放的是MyISAM存儲引擎表的索引相關數據的文件。MyISAM引擎下,表數據和表索引數據是分開存儲的。

MyISAM查詢:在MyISAM下,主鍵索引和輔助鍵索引都屬於非聚簇索引。查詢無論是走主鍵索引,仍是非主鍵索引,在葉子結點獲得的都是目的數據的地址,還須要經過該地址,才能在數據文件中找到目的數據。

PSInnoDB支持聚簇索引,MyISAM不支持聚簇索引

4、SQL事務隔離級別

ACID的四個特性

  1. 原子性(Atomicity):把多個操做放到一個事務中,保證這些操做要麼都成功,要麼都不成功
  2. 一致性(Consistency):理解成一串對數據進行操做的程序執行下來,不會對數據產生很差的影響,好比憑空產生,或消失
  3. 隔離性(Isolation,又稱獨立性):隔離性的意思就是多個事務之間互相不干擾,即便是併發事務的狀況下,他們只是兩個併發執行沒有交集,互不影響的東西;固然實現中,也不必定須要這麼完整隔離性,即不必定須要這麼的互不干擾,有時候仍是容許有部分干擾的。因此MySQL能夠支持4種事務隔離性
  4. 持久性(Durability):當某個操做操做完畢了,那麼結果就是這樣了,而且這個操做會持久化到日誌記錄中

PS:ACID中C與CAP定理中C的區別

ACID的C着重強調單數據庫事務操做時,要保證數據的完整和正確性,數據不會憑空消失跟增長。CAP
理論中的C指的是對一個數據多個備份的讀寫一致性

事務操做可能會出現的數據問題

一、髒讀(dirty read):B事務更改數據還未提交,A事務已經看到而且用了。B事務若是回滾,則A事務作錯了 

二、 不可重複讀(non-repeatable read):不可重複讀的重點是修改: 一樣的條件, 你讀取過的數據, 再次讀取出來發現值不同了,只須要鎖住知足條件的記錄 

三、 幻讀(phantom read):事務A先修改了某個表的全部紀錄的狀態字段爲已處理,未提交;事務B也在此時新增了一條未處理的記錄,並提交了;事務A隨後查詢記錄,卻發現有一條記錄是未處理的形成幻讀現象,幻讀僅專指新插入的行。幻讀會形成語義上的問題跟數據一致性問題。

四、 在可重複讀RR隔離級別下,普通查詢是快照讀,是不會看到別的事務插入的數據的。所以,幻讀在當前讀下才會出現。要用間隙鎖解決此問題。

在說隔離級別以前,你首先要知道,你隔離得越嚴實,效率就會越低。所以不少時候,咱們都要在兩者之間尋找一個平衡點。SQL標準的事務隔離級別由低到高以下:

深刻淺出MySQL靈魂十連問,你真的有把握嗎?

上圖從上到下的模式會致使系統的並行性能依次下降,安全性依次提升。

讀未提交:別人改數據的事務還沒有提交,我在個人事務中也能讀到。

讀已提交(Oracle默認):別人改數據的事務已經提交,我在個人事務中才能讀到。

可重複讀(MySQL默認):別人改數據的事務已經提交,我在個人事務中也不去讀,以此保證重複讀一致性。

串行:個人事務還沒有提交,別人就別想改數據。

標準跟實現:上面都是關於事務的標準,可是每一種數據庫都有不一樣的實現,好比MySQL InnDB 默認爲RR級別,可是不會出現幻讀。由於當事務A更新了全部記錄的某個字段,此時事務A會得到對這個表的表鎖,由於事務A尚未提交,因此事務A得到的鎖沒有釋放,此時事務B在該表插入新記錄,會由於沒法得到該表的鎖,則致使插入操做被阻塞。只有事務A提交了事務後,釋放了鎖,事務B才能進行接下去的操做。因此能夠說  MySQL的RR級別的隔離是已經實現解決了髒讀,不可重複讀和幻讀的

5、MySQL中的鎖

不管是Java的併發編程仍是數據庫的併發操做都會涉及到鎖,研發人員引入了悲觀鎖樂觀鎖這樣一種鎖的設計思想

悲觀鎖

優勢:適合在寫多讀少的併發環境中使用,雖然沒法維持很是高的性能,可是在樂觀鎖沒法提更好的性能前提下,能夠作到數據的安全性

缺點:加鎖會增長系統開銷,雖然能保證數據的安全,但數據處理吞吐量低,不適合在讀書寫少的場合下使用

樂觀鎖

優勢:在讀多寫少的併發場景下,能夠避免數據庫加鎖的開銷,提升DAO層的響應性能,不少狀況下ORM工具都有帶有樂觀鎖的實現,因此這些方法不必定須要咱們人爲的去實現。

缺點:在寫多讀少的併發場景下,即在寫操做競爭激烈的狀況下,會致使CAS屢次重試,衝突頻率太高,致使開銷比悲觀鎖更高。

實現:數據庫層面的樂觀鎖其實跟CAS思想相似, 通數據版本號或者時間戳也能夠實現。

數據庫併發場景主要有三種:

讀-讀:不存在任何問題,也不須要併發控制

讀-寫:有隔離性問題,可能遇到髒讀,幻讀,不可重複讀

寫-寫:可能存更新丟失問題,好比第一類更新丟失,第二類更新丟失

兩類更新丟失問題:

第一類更新丟失:事務A的事務回滾覆蓋了事務B已提交的結果 第二類更新丟失:事務A的提交覆蓋了事務B已提交的結果

爲了合理貫徹落實鎖的思想,MySQL中引入了雜七雜八的各類鎖:

深刻淺出MySQL靈魂十連問,你真的有把握嗎?

鎖分類

MySQL支持三種層級的鎖定,分別爲

1.表級鎖定

MySQL中鎖定粒度最大的一種鎖,最常使用的MYISAM與INNODB都支持表級鎖定。

2.頁級鎖定

是MySQL中鎖定粒度介於行級鎖和表級鎖中間的一種鎖,表級鎖速度快,但衝突多,行級衝突少,但速度慢。因此取了折衷的頁級,一次鎖定相鄰的一組記錄。

3.行級鎖定

Mysql中鎖定粒度最細的一種鎖,表示只針對當前操做的行進行加鎖。行級鎖能大大減小數據庫操做的衝突。其加鎖粒度最小,但加鎖的開銷也最大行級鎖不必定比表級鎖要好:鎖的粒度越細,代價越高,相比表級鎖在表的頭部直接加鎖,行級鎖還要掃描找到對應的行對其上鎖,這樣的代價實際上是比較高的,因此表鎖和行鎖各有所長。

MyISAM中的鎖

  1. 雖然MySQL支持表,頁,行三級鎖定,但MyISAM存儲引擎只支持表鎖。因此MyISAM的加鎖相對比較開銷低,但數據操做的併發性能相對就不高。但若是寫操做都是尾插入,那仍是能夠支持必定程度的讀寫併發

  2. 從MyISAM所支持的鎖中也能夠看出,MyISAM是一個支持讀讀併發,但不支持通用讀寫併發,寫寫併發的數據庫引擎,因此它更適合用於讀多寫少的應用場合,通常工程中也用的較少。

InnoDB中的鎖

該模式下支持的鎖實在是太多了,具體以下:

共享鎖和排他鎖 (Shared and Exclusive Locks)

意向鎖(Intention Locks) 

記錄鎖(Record Locks) 

間隙鎖(Gap Locks) 

臨鍵鎖 (Next-Key Locks) 

插入意向鎖(Insert Intention Locks) 

主鍵自增鎖 (AUTO-INC Locks) 

空間索引斷言鎖(Predicate Locks for Spatial Indexes)

舉個栗子,好比行鎖裏的共享鎖跟排它鎖:lock in share modle 共享讀鎖:

爲了確保本身查到的數據沒有被其餘的事務正在修改,也就是說確保查到的數據是最新的數據,而且不容許其餘人來修改數據。可是本身不必定可以修改數據,由於有可能其餘的事務也對這些數據使用了 in share mode 的方式上了S 鎖。若是不及時的commit 或者rollback 也可能會形成大量的事務等待

for update排它寫鎖:

爲了讓本身查到的數據確保是最新數據,而且查到後的數據只容許本身來修改的時候,須要用到for update。至關於一個 update 語句。在業務繁忙的狀況下,若是事務沒有及時的commit或者rollback 可能會形成其餘事務長時間的等待,從而影響數據庫的併發使用效率。

Gap Lock間隙鎖:

一、行鎖只能鎖住行,若是在記錄之間的間隙插入數據就沒法解決了,所以MySQL引入了間隙鎖(Gap Lock)。間隙鎖是左右開區間。間隙鎖之間不會衝突

二、間隙鎖和行鎖合稱NextKeyLock,每一個NextKeyLock前開後閉區間

間隙鎖加鎖原則(學完忘那種):

一、加鎖的基本單位是 NextKeyLock,是前開後閉區間。

二、查找過程當中訪問到的對象纔會加鎖。

三、索引上的等值查詢,給惟一索引加鎖的時候,NextKeyLock退化爲行鎖。

四、索引上的等值查詢,向右遍歷時且最後一個值不知足等值條件的時候,NextKeyLock退化爲間隙鎖。

五、惟一索引上的範圍查詢會訪問到不知足條件的第一個值爲止。

6、MVCC

MVCC:

一、全稱Multi-Version Concurrency Control,即多版本併發控制。MVCC是一種併發控制的理念,維持一個數據的多個版本,使得讀寫操做沒有衝突。

二、MVCC在MySQL InnoDB中實現目的主要是爲了提升數據庫併發性能,用更好的方式去處理讀-寫衝突,作到即便有讀寫衝突時,也能作到不加鎖,非阻塞併發讀。

MySQL InnoDB下的當前讀和快照讀

1.當前讀

一、像select lock in share mode(共享鎖)、select for update 、updateinsertdelete(排他鎖)這些操做都是一種當前讀,就是它讀取的是記錄的最新版本,讀取時還要保證其餘併發事務不能修改當前記錄,會對讀取的記錄進行加鎖

二、當前讀能夠認爲是悲觀鎖的具體功能實現

2.快照讀

一、不加鎖的select就是快照讀,即不加鎖的非阻塞讀;快照讀的前提是隔離級別不是串行級別,串行級別下的快照讀會退化成當前讀;之因此出現快照讀的狀況,是基於提升併發性能的考慮,快照讀的實現是基於多版本併發控制,即MVCC,能夠認爲MVCC是行鎖的一個變種,但它在不少狀況下,避免了加鎖操做,下降了開銷;既然是基於多版本,即快照讀可能讀到的並不必定是數據的最新版本,而有多是以前的歷史版本。

二、快照讀就是MVCC思想在MySQL的具體非阻塞讀功能實現,MVCC的目的就是爲了實現讀-寫衝突不加鎖,提升併發讀寫性能,而這個讀指的就是快照讀

三、快照讀就是MySQL爲咱們實現MVCC理想模型的其中一個具體非阻塞讀功能。

由於大佬不滿意只讓數據庫採用悲觀鎖這樣性能不佳的形式去解決讀-寫衝突問題,而提出了MVCC,因此咱們能夠造成兩個組合:

MVCC + 悲觀鎖:MVCC解決讀寫衝突,悲觀鎖解決寫寫衝突

MVCC + 樂觀鎖:MVCC解決讀寫衝突,樂觀鎖解決寫寫衝突

MVCC的實現原理

MVCC實現原理主要是依賴記錄中的 四個隱式字段undo日誌 、Consistent Read View來實現的。

四個隱式字段

1.DB_TRX_ID:

6byte,最近修改(修改/插入)事務ID:記錄建立這條記錄/最後一次修改該記錄的事務ID

2.DB_ROLL_PTR

7byte,回滾指針,指向這條記錄的上一個版本(存儲於rollback segment裏)

3.DB_ROW_ID

6byte,隱含的自增ID(隱藏主鍵),若是數據表沒有主鍵,InnoDB會自動以DB_ROW_ID產生一個聚簇索引

4.FLAG

一個刪除flag隱藏字段, 既記錄被更新或刪除並不表明真的刪除,而是刪除flag變了

事務對一條記錄的修改,會致使該記錄的undo log成爲一條記錄版本線性表(鏈表),undo log的鏈首就是最新的舊記錄,鏈尾就是最先的舊記錄。

undo日誌:此知識點上文已經說過了,對MVCC有幫助的實質是update undo log,undo log實際上就是存在rollback segment中舊記錄鏈。

一致讀視圖 Consistent Read View:Read View是事務進行快照讀操做的時候生產的讀視圖(Read View),在該事務執行的快照讀的那一刻,會生成數據庫系統當前的一個快照,記錄並維護系統當前活躍事務的ID(InnoDB裏面每一個事務有一個惟一的事務ID,叫做transaction id。它是在事務開始的時候向InnoDB的事務系統申請的,是按申請順序嚴格遞增的)。拿着這個ID跟記錄中ID對比進行選擇性展現,這裏說下大體的思惟

你能夠簡單的理解爲MVCC爲每一行增長了兩個隱藏字段,兩個字段分別保存了這個行的當前事務ID跟行的刪除事務ID

1.insert時:

InnoDB爲新插入的每一行保存當前系統版本號做爲版本號。

2.select時:

一、 InnoDB只會查找版本早於當前事務版本的數據行(也就是行的系統版本號<=事務的系統版本號),這樣能夠確保事務讀取的行,要麼是在事務開始前已經存在的,要麼是事務自身插入或者修改過的。

二、行的刪除版本要麼未定義,要麼大於當前事務版本號,這能夠確保事務讀取到的行在事務開始以前未被刪除。

三、只有1,2 同時知足的記錄,才能返回做爲查詢結果

3.delete時:

InnoDB會爲刪除的每一行保存當前系統的版本號(事務的ID)做爲刪除標識.

4.update時:

InnoDB執行update,其實是新插入了一行記錄,並保存其建立時間爲當前事務的ID,同時保存當前事務ID到要update的行的刪除時間。

上面只是一個淺顯的講解MVCC選擇標準流程,源碼層面應該是根據低水位高水位來截取的。具體實現可自行百度。

重點

一、事務中快照讀的結果是很是依賴該事務首次出現快照讀的地方,即某個事務中首次出現快照讀的地方很是關鍵,它有決定該事務後續快照讀結果的能力。

二、在RC隔離級別下,是每一個快照讀都會生成並獲取最新的Read View;而在RR隔離級別下,則是同一個事務中的第一個快照讀纔會建立Read View, 以後的快照讀獲取的都是同一個Read View。

7、緩衝池(buffer pool)

深刻淺出MySQL靈魂十連問,你真的有把握嗎?

應用系統分層架構,爲了加速數據訪問,會把最常訪問的數據,放在緩存(cache)裏,避免每次都去訪問數據庫。操做系統,會有緩衝池(buffer pool)機制,避免每次訪問磁盤,以加速數據的訪問。MySQL做爲一個存儲系統,一樣具備緩衝池(buffer pool)機制,以免每次查詢數據都進行磁盤IO,主要做用:

一、存在的意義是加速查詢 

二、緩衝池(buffer pool) 是一種常見的下降磁盤訪問 的機制;

三、緩衝池一般以頁(page 16K)爲單位緩存數據;

四、緩衝池的常見管理算法是LRU,memcache,OS,InnoDB都使用了這種算法;

五、InnoDB對普通LRU進行了優化:將緩衝池分爲老生代新生代,入緩衝池的頁,優先進入老生代,該頁被訪問,才進入新生代,以解決預讀失效的問題頁被訪問。且在老生代停留時間超過配置閾值的,才進入新生代,以解決批量數據訪問,大量熱數據淘汰的問題

預讀失效

因爲預讀(Read-Ahead),提早把頁放入了緩衝池,但最終MySQL並無從頁中讀取數據,稱爲預讀失效

深刻淺出MySQL靈魂十連問,你真的有把握嗎?

緩衝池污染

當某一個SQL語句,要批量掃描大量數據時,可能致使把緩衝池的全部頁都替換出去,致使大量熱數據被換出,MySQL性能急劇降低,這種狀況叫緩衝池污染。解決辦法:加入老生代停留時間窗口策略後,短期內被大量加載的頁,並不會馬上插入新生代頭部,而是優先淘汰那些,短時間內僅僅訪問了一次的頁。

8、table瘦身

空洞

MySQL執行delete命令其實只是把記錄的位置,或者數據頁標記爲了可複用,但磁盤文件的大小是不會變的。經過delete命令是不能回收表空間的。這些能夠複用,而沒有被使用的空間,看起來就像是空洞。插入時候引起分裂一樣會產生空洞。

重建表思路

一、新建一個跟A表結構相同的表B 

二、按照主鍵ID將A數據一行行讀取同步到表B 

三、用表B替換表A實現效果上的瘦身。

重建表指令

一、alter table A engine=InnoDB,慎重用,牛逼的DBA都用下面的開源工具。

二、推薦Github:gh-ost

9、SQL Joins、統計、 隨機查詢

7種join具體以下:

深刻淺出MySQL靈魂十連問,你真的有把握嗎?

統計

一、MyISAM模式下把一個表的總行數存在了磁盤上,直接拿來用便可 

二、InnoDB引擎因爲 MVCC的緣由,須要把數據讀出來而後累計求和 

三、性能來講 由壞到好:count(字段) < count(主鍵id) < count(1) ≈ count(),`儘可能用count()便可。`

隨機查詢

mysql> select word from words order by rand() limit 3;

直接使用order by rand()explain 這個語句發現須要 Using temporary和 Using filesort,查詢的執行代價每每是比較大的。因此在設計的時要避開這種寫法。

mysql> select count(*) into @C from t;
set @Y1 = floor(@C * rand());
set @Y2 = floor(@C * rand());
set @Y3 = floor(@C * rand());
select * from t limit @Y1,1; 
select * from t limit @Y2,1;
select * from t limit @Y3,1;

這樣能夠避免臨時表跟排序的產生,最終查詢行數 = C + (Y1+1) + (Y2+1) + (Y3+1)

exist 和 in 對比

一、in查詢時首先查詢子查詢的表,而後將內表和外表作一個笛卡爾積,而後按照條件進行篩選。

二、子查詢使用 exists,會先進行主查詢,將查詢到的每行數據循環帶入子查詢校驗是否存在,過濾出總體的返回數據。

三、兩表大小至關,in 和 exists 差異不大。內表大,用 exists 效率較高;內表小,用 in 效率較高

四、查詢用not in 那麼內外表都進行全表掃描,沒有用到索引;而not exists 的子查詢依然能用到表上的索引。not exists比not in要快

10、MySQL優化

SQL優化主要分4個方向:SQL語句跟索引表結構系統配置硬件

總優化思路就是最大化利用索引儘量避免全表掃描減小無效數據的查詢

一、減小數據訪問:設置合理的字段類型,啓用壓縮,經過索引訪問等減小磁盤 IO。

二、返回更少的數據:只返回須要的字段和數據分頁處理,減小磁盤 IO 及網絡 IO。

三、減小交互次數:批量 DML 操做,函數存儲等減小數據鏈接次數。

四、減小服務器 CPU 開銷:儘可能減小數據庫排序操做以及全表查詢,減小 CPU 內存佔用 。

五、分表分區:使用表分區,能夠增長並行操做,更大限度利用 CPU 資源。

SQL語句優化大體舉例

一、合理創建覆蓋索引:能夠有效減小回表。

二、union,or,in都能命中索引,建議使用in 

三、負向條件(!=、<>、not in、not exists、not like 等) 索引不會使用索引,建議用in。

四、在列上進行運算或使用函數會使索引失效,從而進行全表掃描 

五、當心隱式類型轉換,原字符串用整型會觸發CAST函數致使索引失效。原int用字符串則會走索引。

六、不建議使用%前綴模糊查詢。

七、多表關聯查詢時,小表在前,大表在後。在 MySQL 中,執行 from 後的表關聯查詢是從左往右執行的(Oracle 相反),第一張表會涉及到全表掃描。

八、調整 Where 字句中的鏈接順序,MySQL 採用從左往右,自上而下的順序解析 where 子句。根據這個原理,應將過濾數據多的條件往前放,最快速度縮小結果集。

SQL調優大體思路

一、先用慢查詢日誌定位具體須要優化的sql 

二、使用 explain 執行計劃查看索引使用狀況 

三、重點關注(通常狀況下根據這4列就能找到索引問題):

一、key(查看有沒有使用索引) 

二、key_len(查看索引使用是否充分)

三、type(查看索引類型) 

四、Extra(查看附加信息:排序、臨時表、where條件爲false等)

四、根據上1步找出的索引問題優化sql 五、再回到第2步

深刻淺出MySQL靈魂十連問,你真的有把握嗎?

表結構優化

一、儘可能使用TINYINT、SMALLINT、MEDIUM_INT做爲整數類型而非INT,若是非負則加上UNSIGNED 。

二、VARCHAR的長度只分配真正須要的空間 。

三、儘可能使用TIMESTAMP而非DATETIME 。

四、單表不要有太多字段,建議在20之內。

五、避免使用NULL字段,很難查詢優化且佔用額外索引空間。字符串默認爲''。

讀寫分離

只在主服務器上寫,只在從服務器上讀。對應到數據庫集羣通常都是一主一從、一主多從。業務服務器把須要寫的操做都寫到主數據庫中,讀的操做都去從庫查詢。主庫會同步數據到從庫保證數據的一致性。通常 讀寫分離 的實現方式有兩種:代碼封裝數據庫中間件

分庫分表:分庫分表 分爲垂直和水平兩個方式,通常是先垂直後水平

一、垂直分庫:將應用分爲若干模塊,好比訂單模塊、用戶模塊、商品模塊、支付模塊等等。其實就是微服務的理念。

二、垂直分表:通常將不經常使用字段跟數據較大的字段作拆分。

三、水平分表:根據場景選擇什麼字段做分表字段,好比淘寶日訂單1000萬,用userId做分表字段,數據查詢支持到最近6個月的訂單,超過6個月的作歸檔處理,那麼6個月的數據量就是18億,分1024張表,每一個表存200W數據,hash(userId)%100找到對應表格。

四、ID生成器:分佈式ID 須要跨庫全局惟一方便查詢存儲-檢索數據,確保惟一性跟數字遞增性。

目前主要流行的分庫分表工具 就是Mycatsharding-sphere

TiDB:開源分佈式數據庫,結合了傳統的 RDBMS 和NoSQL 的最佳特性。TiDB 兼容 MySQL,支持無限的水平擴展,具有強一致性和高可用性。TiDB 的目標是爲 OLTP(Online Transactional Processing) 和 OLAP (Online Analytical Processing) 場景提供一站式的解決方案。TiDB 具有以下核心特色

一、支持 MySQL 協議(開發接入成本低)。

二、100% 支持事務(數據一致性實現簡單、可靠)。

三、無限水平拓展(沒必要考慮分庫分表),不停服務。

四、TiDB 支持和 MySQL 的互備。

五、遵循jdbc原則,學習成本低,強關係型,強一致性,不用擔憂主從配置,不用考慮分庫分表,還能夠無縫動態擴展。

適合:

一、原業務的 MySQL 的業務遇到單機容量或者性能瓶頸時,能夠考慮使用 TiDB 無縫替換 MySQL。

二、大數據量下,MySQL 複雜查詢很慢。

三、大數據量下,數據增加很快,接近單機處理的極限,不想分庫分表或者使用數據庫中間件等對業務侵入性較大、對業務有約束的 Sharding 方案。

四、大數據量下,有高併發實時寫入、實時查詢、實時統計分析的需求。五、有分佈式事務、多數據中心的數據 100% 強一致性、auto-failover 的高可用的需求。

不適合:

一、單機 MySQL 能知足的場景也用不到 TiDB。

二、數據條數少於 5000w 的場景下一般用不到 TiDB,TiDB 是爲大規模的數據場景設計的。

三、若是你的應用數據量小(全部數據千萬級別行如下),且沒有高可用、強一致性或者多數據中心複製等要求,那麼就不適合使用 TiDB。

寫在最後

歡迎你們關注個人公衆號【風平浪靜如碼】,海量Java相關文章,學習資料都會在裏面更新,整理的資料也會放在裏面。

以爲寫的還不錯的就點個贊,加個關注唄!點關注,不迷路,持續更新!!!

相關文章
相關標籤/搜索