MySQL數據庫學習(二)------MySQL中的日誌+事務+鎖機制

MySQL數據庫中的鎖

數據庫鎖的分類

  • 按鎖的粒度劃分,可以劃分爲表級鎖、行級鎖、頁級鎖
  • 按鎖的級別劃分,可分爲共享鎖、排它鎖
  • 按加鎖的方式劃分,可分爲自動鎖、顯示鎖
  • 按操作方式劃分,可分爲DML鎖、DDL鎖(其中增刪該查這一類的語句對應的爲DML鎖,而變動表結構這一類的操作爲DDL鎖)
  • 按使用方式劃分,可分爲樂觀鎖,悲觀鎖

樂觀鎖:總是假設最好的情況,每次去拿數據的時候都認爲別人不會修改,所以不會上鎖,但是在更新的時候會判斷一下在此期間別人有沒有去更新這個數據,可以使用版本號機制和CAS算法實現。適用於多讀場景。
悲觀鎖:總是假設最壞的情況,每次去拿數據的時候都認爲別人會修改,所以每次在拿數據的時候都會上鎖,這樣別人想拿這個數據就會阻塞直到它拿到鎖(共享資源每次只給一個線程使用,其它線程阻塞,用完後再把資源轉讓給其它線程)。傳統的關係型數據庫裏邊就用到了很多這種鎖機制,比如行鎖,表鎖等,讀鎖,寫鎖等,都是在做操作之前先上鎖。Java中synchronized和ReentrantLock等獨佔鎖就是悲觀鎖思想的實現。適用於多寫場景。

MyISAM與InnoDB關於鎖方面的區別

MyISAM默認用的是表級鎖,不支持行級鎖
InnoDB默認用的是行級鎖,也支持表級鎖

MyISAM中

  1. 當進行SELECT時,會生成一個表級的讀鎖,此時若有UPDATE,INSERT,DELETE均會被BLOCK,陷入阻塞,直到讀鎖釋放。
  2. 同理,當進行UPDATE,INSERT,DELETE時,會生成一個表級的寫鎖,SELECT會被BLOCK,陷入阻塞直到寫鎖釋放。

手動爲表增加讀/寫鎖:

LOCK TABLES table_name read|write;

讀鎖:即共享鎖 ,因爲當已經有一個SELECT操作正在進行中時,此時對於同一張表再進行一個SELECT操作,第二個SELECT操作並不會被阻塞,而是正常執行,這個鎖可以被兩個SELECT操作所共享,所以也叫共享鎖。但是,當在第一個SELECT語句後面加上FOR UPDATE,即將其變成一個排他鎖,那麼此時若還有一個SELECT操作,則該操作會陷入阻塞
寫鎖:當已經有一個寫鎖時,此時若再設置一個寫鎖或者讀,都會陷入阻塞狀態。

InnoDB

InnoDB使用的是二段鎖,即加鎖和解鎖是分成兩個步驟:即先對同一個事務裏的一批操作進行加鎖,然後在commit時,再對加上的鎖進行統一的解鎖

由於在MySQL的InnoDB,事務默認是自動提交的,關閉事務的自動提交,可以用

SET autocommit = 0;

在InooDB中,對SELECT進行了優化,顯式得加上讀鎖應該在SELECT語句的後面加上 lock in share mode;InnoDB中鎖是默認支持行級的

在InnoDB中,sql沒有用到索引的時候使用的是表級鎖,用到索引的時候使用的是行級鎖

在這裏插入圖片描述

MySQL 數據庫中的死鎖

MyISAM不支持行級鎖,所以MySQL中的死鎖主要是在說InnoDB存儲引擎的死鎖。


MySQL中解決死鎖的兩種方式:通過業務和通過數據庫的設置

通過業務邏輯來解決死鎖問題

  • 指定鎖的獲取順序
  • 將大事務拆分成各個小事務
  • 在同一個事務中,一次鎖定儘量多的資源,減少死鎖概率
  • 給表建立合適的索引以及降低事務的隔離級別等

通過數據庫的設置來解決死鎖問題

  • 通過參數 innodb_lock_wait_timeout 根據實際業務場景來設置超時時間,InnoDB引擎默認值是50s。
  • 發起死鎖檢測,發現死鎖後,主動回滾死鎖鏈條中的某一個事務,讓其他事務得以繼續執行。將參數 innodb_deadlock_detect 設置爲 on,表示開啓這個邏輯(默認是開啓狀態)。

“行級鎖什麼時候會鎖住整個表?“
InnoDB行級鎖是通過鎖索引記錄實現的,如果更新的列沒建索引是會鎖住整個表的。
此處感謝一下牛客網,收穫很大

MyISAM和InnoDB各自的適用場景

  1. MyISAM:
  • 頻繁執行全表count語句,MyISAM中有一個變量保存了表的行數,所以很快
  • 對數據進行增刪查的效率不高,查詢非常頻繁
  • 沒有事務
  1. InnoDB:
  • 數據的增刪改查都相當頻繁(通過行級鎖與表級鎖就能理解)
  • 可靠性要求比較高,要求支持事務

MySQL數據庫中的事務

數據庫事務的四大特性

ACID

  • 原子性(Automic):事務包含的所有操作要麼全部執行,要麼全部失敗回滾
  • 一致性(Consistency):事務應確保數據庫的狀態從一個一致狀態轉變爲另外一個一致狀態。
  • 隔離性(Isolation):多個事務併發執行時,一個事務的執行不該影響其他事務的執行
  • 持久性(Durability):一個事務一旦提交,他對數據庫的提交應該永久保存在數據庫中。

事務併發訪問的問題以及事務隔離機制

事務的隔離級別

  • 讀未提交(Read Uncommitted):

允許髒讀取。如果一個事務已經開始寫數據,則另外一個數據則不允許同時進行寫操作,但允許其他事務讀此行數據。

  • 讀已提交(Read Committed):

允許不可重複讀取,但不允許髒讀取。讀取數據的事務允許其他事務繼續訪問該行數據,但是未提交的寫事務將會禁止其他事務訪問該行。

  • 可重複讀(Repeatable Read):

禁止不可重複讀取和髒讀取,但是有時可能出現幻讀。讀取數據的事務將會禁止寫事務(但允許讀事務),寫事務則禁止任何其他事務。

  • 序列化(Serializable):

提供嚴格的事務隔離。它要求事務序列化執行,事務只能一個接着一個地執行,但不能併發執行。

事務的隔離級別越高,對數據的完整性和一致性保證越佳,但是對併發操作的影響也越大。MySQL事務默認隔離級別是可重複讀。

事務併發訪問引起的問題以及如何避免

  1. 更新丟失:兩個不同事務同時獲得相同數據,然後在各自事務中同時修改了該數據,那麼先提交的事務更新會被後提交事務的更新給覆蓋掉,這種情況先提交的事務所做的更新就被覆蓋,導致數據更新丟失。(MySQL所有事務隔離級別在數據庫層面上均可避免)
  2. 髒讀:事務A讀取了事務B未提交的數據,由於事務B回滾,導致了事務A的數據不一致,結果事務A出現了髒讀(READ-COMMITED事務隔離級別以上可以避免,MySQL的InnoBD的默認隔離級別爲REPEATABLE_READ

設置隔離級別:

SET SESSION TRANSACTION ISOLATION LEVEL read uncommited; 
  1. 不可重複讀:一個事務在自己沒有更新數據庫數據的情況,同一個查詢操作執行兩次或多次得到的結果數值不同,因爲別的事務更新了該數據,並且提交了事務(REPEATABLE_READ事務隔離級別以上可以避免)
  2. 幻讀:事務A讀的時候讀出了N條記錄,事務B在事務A執行的過程中增加 了1條,事務A再讀的時候就變成了N+1條,這種情況就叫做幻讀。(設置爲SERIALIZABLE隔離級別即可避免,導致事務A看起來像出現幻覺一樣,這是最高隔離級別)

不可重複讀與幻讀的區別:幻讀是指一種結構上的改變,比如說條數發生了改變;不可重複讀是指讀出的數值發生了改變。


數據庫層面規避上述問題的具體總結見下表:

事務隔離級別 更新丟失 髒讀 不可重複讀 幻讀
未提交讀 避免 發生 發生 發生
已提交讀 避免 避免 發生 發生
可重複讀 避免 避免 避免 發生
串行化 避免 避免 避免 避免

出於性能考慮,事務隔離級別越高,安全性越高,串行化執行越嚴重,這樣會降低數據庫的併發度,因此需要根據業務去設置事務隔離級別

MySQL數據庫中兩大重要的日誌模塊

在MySQL的使用中,更新操作是很頻繁的,如果每一次更新操作都根據條件找到對應的記錄,然後將記錄更新,再寫回磁盤,那麼IO成本以及查找記錄的成本都會很高。所以,就出現了日誌模塊,也就是說,我們的update更新操作是先寫日誌,在合適的時間纔會去寫磁盤,日誌更新完畢就將執行結果返回給了客戶端。

binlog(歸檔日誌)

  • bin log是Server層的日誌,所有引擎都可以使用
  • binlog是邏輯日誌,記錄語句的原始邏輯,如 給uid=1這一行的數據賦新值"Bob",其中的statement形式,說白了本質也就是sql語句
  • binlog是追加寫,一個日誌文件寫到一定大小後會切換到下一個,並不會覆蓋以前的日誌。

binlog日誌文件的格式(statement,row,mixed)

  1. statement格式的binlog記錄的是完整的SQL語句,優點是日誌文件小,性能較好,缺點也很明顯,那就是準確性差,遇到SQL語句中有now()等函數會導致不準確
  2. row格式的binlog中記錄的是數據行的實際數據的變更,優點就是數據記錄準確,缺點就是日誌文件較大。
  3. mixed格式的binlog是前面兩者的混合模式

目前大多數使用的是 row 模式,因爲很多情況下對準確性的要求是排在第一位的。

redo log(重做日誌)

  • redo log是InnoDB引擎所特有的日誌模塊,是物理日誌,記錄了某個數據頁上做了哪些修改
  • InnoDB的redo log是固定大小的,比如可以配置爲一組4個文件,每個文件的大小是1GB,那麼redo log總共就可以記錄 4GB的操作。從頭開始寫,寫到末尾就又回到開頭循環寫。
  • InnoDB的redo log保證了數據庫發生異常重啓之後,之前提交的記錄不會丟失,這個能力稱爲crash-safe。

在下一篇MySQL的總結中會總結一下SQL語句中一些技巧,還有就是一個高頻面試點:慢查詢的sql調優以及一些其它瑣碎知識

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