摘要:mysql的表級鎖有兩種:元數據鎖和表鎖。mysql的行級鎖是有存儲引擎實現的,mysql現在默認的數據引擎爲Innodb。

MySql鎖與InnoDB引擎

mysql的鎖是面試中很高頻問題,也是我們在日常開發中經常會遇到但是我們並沒有注意到的地方。我把我自己理解的鎖通過本篇博文分享出來,由於鎖需要結合事務來理解,本文只介紹鎖的基本概念,同樣爲了理解事務會更加深刻,先介紹了InnoDB的一些基礎概念,也是記錄自己的學習,歡迎大家一起探討交流。

下一篇:mysql的事務與mvcc

鎖的分類:

  • 按照鎖的粒度來分

    • 全局鎖: 鎖的是整個database,類比一個庫爲一棟大樓,那此時就是鎖的整棟樓的大門
    • 表級鎖: 鎖的是某個table,類比一個表爲大樓的某一層,此時鎖的就是某一層整層
    • 行級鎖:鎖的是某一行數據,類比每行鎖的是某一層的某一間房間,此時鎖的是某一個房間
  • 表鎖和行鎖的區別
    • 表級鎖,開銷大,加鎖快,不會出現死鎖。鎖的粒度大。併發度低。
    • 行級鎖,開銷小,加鎖慢,會出現死鎖,鎖的粒度小,併發度高。

表級鎖:

mysql的表級鎖有兩種:元數據鎖和表鎖。

表鎖的兩種形式:

  • 表共享讀鎖

  • 表排它寫鎖

    • 手動加表鎖

      lock table tableName read;
    • 查看錶鎖情況

      show open tables;
    • 刪除表鎖

      unlock tables;

元數據鎖:

  • 5.5版本中引入了MDL,對一個表數據進行增刪改的時候,加MDL讀鎖;要對錶結構進行修改的時候,加MDL寫鎖。

行級鎖

mysql的行級鎖是有存儲引擎實現的,mysql現在默認的數據引擎爲Innodb。本文主要介紹InnoDB的行鎖;
InnoDB的行鎖是給索引項加鎖實現的,也就意味着只有使用索引檢索的數據才能使用行鎖,否則將使用表鎖。

按照範圍來說

  • 行鎖:鎖定表中的某一條記錄。
  • 間隙鎖:
    • 鎖住索引記錄中間的值
    • 鎖住第一個索引記錄前面的值或者最後一個索引後面的值

按照功能來說

  • 共享鎖,也叫做S鎖:允許一個事務去讀一行數據,阻止其他事務添加排它鎖,允許繼續添加共享鎖讀
  • 排它鎖,也叫做X鎖:允許獲得排它鎖的事務更新數據,阻止其他事務添加讀共享鎖和添加排它鎖寫

對於InnoDB來說,會自動給增刪改語句添加排它鎖,X鎖。對於普通的查詢語句不會添加任何鎖。

意向鎖

InnoDB同樣也實現了表級鎖,也就是意向鎖。意向鎖是mysql內部使用的,不需要用戶去幹預。

  • 意向共享鎖,IS鎖:事務打算給數據行加共享鎖,事務在給一個數據行添加共享鎖前必須獲取該表的IS鎖。
  • 意向排它鎖,IX鎖:事務打算給數據航加排它鎖,事務在給一個數據行添加排它鎖前必須獲取該表的IX鎖。

意向和行鎖可以共存,意向鎖的作用是爲了提升全表更新數據時的性能提升,否則在更新全表時要檢索哪些數據行上有行鎖。

間隙鎖

顧名思義,主要是在記錄之間添加鎖,不允許往間隙插入數據。比如id爲 2 4,那此時使用間隙鎖就會鎖2 3 4 這三個,稍後在介紹事務的時候也會再次介紹間隙鎖,間隙鎖的主要作用就是爲了解決幻讀問題。此處先了解一下。

死鎖

mysql的死鎖和我們代碼中死鎖理論是一樣的,不同的是,mysql指的是兩個不同的連接互相等待對方釋放鎖,才能釋放自己持有的資源,所以造成了死鎖。mysql中也有對死鎖的優化。我們稍後再具體說。

接下來我們開始介紹事務,上面只是簡單介紹了一下鎖的基本概念,鎖還有一部分內容需要結合事務來理解,所以稍後還有鎖的介紹。

在我們介紹事務之前,我們先聊一下InnoDB的架構,事務中的一些部分會涉及到這部分的內容。

InnoDB的磁盤文件

InnoDB的磁盤文件

  • 系統表空間
    • 系統表空是一個共享的表空間
    • 系統表空間包含數據字典、doule write buffer、change buffer、undo log的存儲區域,包含用戶在系統表創建的表結構和索引數據
  • 用戶表空間
    • 設置參數 innodb_file_per_table ,用戶就可以爲每個基於InnoDB引擎的表創建一個獨立的用戶表空間,也就是.ibd文件。
    • 存儲該表的數據、索引等信息。

InnoDB內存結構

  • buff pool 緩衝池
    • 數據是存儲於磁盤的,由於cpu速度和磁盤速度的差別,所以使用緩衝池提高整體性能。
    • 通過innodb_buffer_pool_size可以設置緩衝池的大小,緩衝池的大小對性能也是有影響的。
    • 緩衝池中緩衝的數據類型:
      • 索引頁
      • 數據頁
      • 存儲引擎工作時,需要以頁爲單位將磁盤數據加載到內存中,數據頁和索引頁是頁類型中最重要的兩種類型
      • undo頁:實現了mysql多版本的快照,可以理解爲版本鏈。mvcc和回滾操作都涉及到了undo日誌。
      • insert buffer:提高了對於非聚簇索引的插入性能
      • 自適應哈希索引
      • InnoDB存儲的鎖信息
      • 數據字典信息

內存數據落盤

InnoDB數據落盤有圖可以看出來是通過兩種方式來實現的

  • 髒頁數據落盤
  • 預寫redo日誌

通過兩種方式來落盤,也可以理解爲持久化到磁盤上。是爲了保證數庫發生突然宕機,造成數據丟失。

髒頁落盤會產生IO並且是隨機寫入,耗時比較長。頻繁進行磁盤IO對性能損耗是非常大。並且數據的安全性得不到保障。如果在髒頁數據還沒來得及落盤或者落盤過程出現宕機,那麼數據就會丟失。

鑑於以上情況,mysql用雙保險完成數據的安全性,髒頁落盤是一種,另一種就是預寫redo 日誌,首先我們要知道redo 持久化到磁盤是順序寫入,順序寫入的速度要比隨機寫入要快,此時有朋友就會問,那髒頁落盤爲什麼不採用順序寫入呢但?

順序寫入速度快的同時是會產生磁盤碎片的,磁盤碎片會大大浪費磁盤資源。

redo 日誌持久化的時機是在事務提交時寫入到磁盤的redo file中,此時髒頁數據並不一定完成了落盤,髒頁落盤是由checkPoint檢查點機制控制的,我們這裏不展開多說。

數據庫發生宕機的情況:

  • 髒頁數據未落盤,事務未提交,此時產生了數據丟失,我們都知道如果事務未提交換個角度來講這些數據丟失是正常的。
  • 髒頁數據未落盤,事務已提交,此時redo log file已經有了數據,那麼重啓的時候mysql就會從redo log file中進行數據恢復。

有的朋友還會說,那redo log file的數據豈不是無限大?

ib_logfile0,ib_logfile1 這是rodo log 在我們磁盤上的命名,可以看到有兩個文件,採用的循環寫入的方式,如果1滿了就寫入2,2滿了寫入1,這樣循環。

redo 日誌持久化到磁盤也是可以配置的,通過InnoDB的innodb_push_log_at_trx_commit來設置

  1. 屬性值爲0時,事務提交,不會對redo進行寫入操作,等待主線程按時寫入;
  2. 屬性值爲1時,事務提交,將數據寫入磁盤,確保不會出現數據丟失;
  3. 屬性值爲2時,事務提交,將數據寫入系統緩存,讓文件系統自己判斷什麼時候寫入磁盤。

默認值爲1,一般也建議設置爲1,會保證數據的安全性,並且只有爲1的時候纔會保證事務的一致性。

以上就是本篇博文的全部內容,感謝各位看官。歡迎提出問題一起交流探討。

下一篇:mysql的事務和mvcc

相關文章