mysql知識概括彙總

類型

  1. mysql的類型定義中,整形類型括號裏的數字,指的是顯示位數,與存儲大小無關
  2. 對於varchar和char,前者是可變長,後者固定,其中的數字都表明字符長度,char的長度在0~255,varchar存儲字節數在0~65535.若是某個數據表裏的數據行的長度是可變的,那麼,爲了節約存儲空間,MySQL會把這個數據表裏的固定長度類型的數據列轉換爲相應的可變長度類型.mysql

    例外:長度小於4個字符的char數據列不會被轉換爲varchar類型

explain的使用

  1. id:查詢順序是從大到小依次查詢,id相同時從上到下查詢,id能夠爲空,當且僅當 select_type爲 UNION_RESULT
  2. type:const和eq_ref都上用了主鍵或者惟一索引,它們的區別是const是隻查一次就獲得結果,而eq_ref要匹配屢次,由於有多條記錄。
  3. possible_keys可能爲null,可是key不爲null,緣由就是使用了全索引掃描。
  4. key_len:表示使用了索引的長度。長度由三個因素決定:算法

    1. 字符集
    2. 長度
    3. 是否爲空
    字段類型 佔用字節數
    char(n) n
    varchar(n) n+2
    tinyint 1
    smallint 2
    int 4
    bigint 8
    date 3
    timestamp 4
    datetime 8

    若是字段容許空則還要加一個字節。
    若是有一個索引是聯合索引 (a,b) a、b都是varchar(30)非null,編碼是UTF8 那麼若是索引都用上 長度爲30*3+2+30*3+2=184 3是utf8佔用3個字節sql

innodb存儲引擎

  1. 結構:分爲聚簇索引和非聚簇索引,其中,除了主鍵以外,其餘索引都是非聚簇索引。用的是B+樹。
    爲何用B+樹,而不是其餘結構?先看看通常有什麼結構:
    哈希表數據庫

    • 一種以key-value鍵值對存儲數據的結構,經過散列運算,將hash結果做爲文件指針,能夠從索引文件中得到數據的文件指針,再到數據文件中獲取到數據,按照這種結構,咱們很快能定位出來某一條數據的位置,查詢的效率很是高,那麼它的問題點在哪呢,那就是不支持範圍查詢,分頁或者大於、小於某個範圍的查詢都是沒法支持的,只能支持固定的字段名 = 目標值的場景,一樣也不適合Like這種模糊查詢,因此這種算法確定是不適合做爲數據庫的索引的。
    • 不一樣的key值經過哈希函數運算,可能會出現相同的值,這個時候咱們稱爲哈希衝突。解決哈西衝突,咱們能夠用鏈地址法,即把全部相同的值放到一個鏈表裏,這樣不管有多少衝突,只是增長了鏈表的長度而已。

    有序列表安全

    • 查找和更新很是快,可是插入、刪除代價很高,須要移動後面的所有數據
    • 適合作靜態存儲的索引,即不會修改的數據。

    併發

    1. 二叉樹
      二叉樹的優勢是查找速度很是快,時間複雜度是O(log2(n)),可是會出現左傾或者右傾的問題而退化成鏈表;其次,因爲一個節點只能有兩個子節點,致使樹的高度會變得很是高,在查詢的時候須要屢次遍歷,這樣磁盤掃描會很是多,致使查詢所需時間增長。
    2. 平衡二叉樹:紅黑樹mvc

      • 紅黑樹是一種平衡二叉樹,它繼承了二叉樹的優勢,由解決了二叉樹遇到的自增數據索引失效的問題,由於紅黑樹的會對樹的結構進行調整,進行左旋或者右旋及顏色變換等操做,始終保證 左子節點數<父節點數<右子節點數
      • 因爲一個節點只能有兩個子節點,在數據量大的時候致使樹的高度會變得很是高,在查詢的時候須要屢次遍歷,在磁盤尋址的時候很是不利,也是至關耗時的。
      • 查詢效率不穩定,查詢在根節點和在子節點相差很大。
      • 節點存儲的數據太少,不能很好的利用操做系統和磁盤數據交換的特性,也沒有很好的利用磁盤IO預讀能力。操做系統和磁盤之間一次數據交換是以頁爲單位的,一頁等於4k,也就是每次IO交互操做系統會將4K的數據加載到內存中,可是在二叉樹的每一個節點的結構中只保存了一個關鍵字,一個數據區,兩個子節點的引用,並不可以填滿4K的數據量,也就是辛辛苦苦作了一次IO操做,卻只加載了一個關鍵字,在數的高度很高,搜索的數據又是在葉子節點,取一個關鍵字須要作不少次的IO
    3. B樹(多路平衡查找樹(Balance Tree))函數

      • B Tree是一個絕對平衡樹,全部的葉子節點在同一個高度
        image.png
        上面的這個樹是一個 2-3樹(每一個節點存儲2個關鍵字,有3路),每一個節點保存的關鍵字個數和路數關係爲:關鍵字個數=路數-1
      • Mysql爲了能更好的利用磁盤的預讀能力,將頁的大小設置爲16K,就是將一個節點(磁盤塊)的大小設置爲16K,一次IO將一個節點(16K)內容加載到內存中,假設關鍵字的類型是int,4個字節,每一個關鍵字對應的數據區也爲4個字節,不考慮子節點應用的狀況下,上圖中每一個節點大約可以存儲(16*1000)/8 = 2000個關鍵字,那麼對應的就是2001個路數,對於這種有2001個路數的B樹,三層的高度可以搜索到的關鍵字的個數是遠遠大於普通的二叉樹的。
      • 在B樹保持樹的平衡的過程當中,每次關鍵字的變化都會致使結構發生很大的變化,這個過程是特別浪費時間的。因此建立索引必定要建立合適的索引,而不是把全部的字段都創建索引。
    4. B+樹高併發

      • B+樹是B樹的一個變種,它再也不遵循 關鍵字個數=路數-1 這個規則,數據的檢索規則是採用的左閉合取件,路數和關鍵字的個數關係爲1:1

      image.png

      • B+樹中的根結點和支節點中沒有數據區,關鍵字對應的數據只保存在葉子節點中,因此只有葉子節點中的關鍵字數據區纔會保存真正的數據內容或者數據對應的地址,可是在B樹中,若是根結點命中,是直接返回的,B+樹中,葉子節點不會保存子節點的引用
      • B+樹的葉子節點是順序排列的,而且相鄰節點之間是順序引用的關係,葉子節點之間經過指針相連
      • B樹能解決的問題,B+樹都能解決,且可以更好的解決,下降了樹的高度,增長節點的數據存儲量。
      • B+樹的掃庫和掃表能力更強,若是根據索引去對數據表掃描,B樹須要整顆樹遍歷,B+樹只須要遍歷全部的葉子節點
      • B+樹的磁盤讀寫能力更強,根結點和支節點不保存數據區,全部的根結點和支節點在一樣大小的狀況下,保存的關鍵字更多,葉子結點不存子節點的引用,因此,B+樹讀寫一次磁盤加載的關鍵字更多
      • B+樹具備自然排序功能,並且查詢效率更加穩定,查詢IO的次數是穩定的

      以上就是選擇B+樹的緣由。
      而聚簇索引和非聚簇索引的區別在於,聚簇索引會把整行數據都保存,非聚簇索引只保存索引相關字段數據,還有主鍵的地址。所以通常狀況下,若是非聚簇索引不是覆蓋索引,須要回表查找,才能獲得須要的結果。工具

  2. 事務
    事務隔離級別與鎖
    一共有如下四種隔離級別

    • 未提交讀:會出現髒讀問題,即讀到別的事務沒有提交的數據。
    • 提交讀:解決了髒讀問題,可是會出現不可重複讀,就是在事務查詢先後,若是別的事務提交了數據,會獲得不同結果。
    • 可重複讀:解決了不可重複讀,可是會出現幻讀,即查詢先後,若是別的事務插入了數據,會致使插入相同數據的時候報錯,就像查詢是假的,出現了幻覺同樣。
    • 串行化:徹底串行化的讀,每次讀都須要得到表級共享鎖,讀寫相互都會阻塞

    不可重複讀和幻讀的區別:前者重點在於update和delete,後者重點在於insert。
    鎖的類型有不少,分類範疇也有不少,下面根據不一樣的分類簡單描述一下。

    1. 樂觀鎖和悲觀鎖

      • 悲觀鎖
        正如其名,它指的是對數據被外界(包括本系統當前的其餘事務,以及來自外部系統的事務處理)修改持保守態度,所以,在整個數據處理過程當中,將數據處於鎖定狀態。
        在悲觀鎖的狀況下,爲了保證事務的隔離性,就須要一致性鎖定讀。讀取數據時給加鎖,其它事務沒法修改這些數據。修改刪除數據時也要加鎖,其它事務沒法讀取這些數據。
      • 樂觀鎖
        相對悲觀鎖而言,樂觀鎖機制採起了更加寬鬆的加鎖機制。悲觀鎖大多數狀況下依靠數據庫的鎖機制實現,以保證操做最大程度的獨佔性。但隨之而來的就是數據庫性能的大量開銷,特別是對長事務而言,這樣的開銷每每沒法承受。
        樂觀鎖,大可能是基於數據版本( Version )記錄機制實現。何謂數據版本?即爲數據增長一個版本標識,在基於數據庫表的版本解決方案中,通常是經過爲數據庫表增長一個 「version」 字段來實現。讀取出數據時,將此版本號一同讀出,以後更新時,對此版本號加一。此時,將提交數據的版本數據與數據庫表對應記錄的當前版本信息進行比對,若是提交的數據版本號大於數據庫表當前版本號,則予以更新,不然認爲是過時數據。
    2. 共享鎖與排它鎖

      • 共享鎖
        也叫讀鎖,簡稱S鎖,原理:一個事務獲取了一個數據行的共享鎖,其餘事務能得到該行對應的共享鎖,但不能得到排他鎖,即一個事務在讀取一個數據行的時候,其餘事務也能夠讀,但不能對該數據行進行增刪改
        設置共享鎖:SELECT ... LOCK IN SHARE MODE
      • 排它鎖
        也叫寫鎖,簡稱x鎖,原理:一個事務獲取了一個數據行的排他鎖,其餘事務就不能再獲取該行的其餘鎖(排他鎖或者共享鎖),即一個事務在讀取一個數據行的時候,其餘事務不能對該數據行進行增刪改查
        設置排他鎖:SELECT ... FOR UPDATE
      • 意向共享鎖和意向排它鎖
        意向共享鎖,簡稱IS,其做用在於:通知數據庫接下來須要施加什麼鎖並對錶加鎖。若是須要對記錄A加共享鎖,那麼此時innodb會先找到這張表,對該表加意向共享鎖以後,再對記錄A添加共享鎖。
        意向排他鎖,簡稱IX,其做用在於:通知數據庫接下來須要施加什麼鎖並對錶加鎖。若是須要對記錄A加排他鎖,那麼此時innodb會先找到這張表,對該表加意向排他鎖以後,再對記錄A添加排他鎖。
        意向共享鎖和意向排他鎖都是系統自動添加和自動釋放的,整個過程無需人工干預。
        多個意向鎖能夠和行級鎖共存,由於意向鎖的做用是爲了對須要給表加鎖的時候,更高效的處理可否加鎖的處理,對行鎖沒有影響。
      • 自增鎖(AUTO-INC Locks)
        特殊表鎖,自增加計數器經過該「鎖」來得到子增加計數器最大的計數值。在insert結束後當即釋放。咱們能夠執行show engine innodb statusG來查看自增鎖的狀態信息。
      • 在自增鎖的使用過程當中,有一個核心參數,須要關注一下,即innodb_autoinc_lock_mode,它有0、一、2三個值。保持默認就行。具體的含義能夠參考官方文檔。
        image.png
        InnoDB鎖關係矩陣以下,其中:+ 表示兼容,- 表示不兼容。
        image.png
    3. 兩段鎖協議
      將事務分紅兩個階段,加鎖階段和解鎖階段(因此叫兩段鎖)

      • 加鎖階段:在該階段能夠進行加鎖操做。在對任何數據進行讀操做以前要申請並得到S鎖(共享鎖,其它事務能夠繼續加共享鎖,但不能加排它鎖),在進行寫操做以前要申請並得到X鎖(排它鎖,其它事務不能再得到任何鎖)。加鎖不成功,則事務進入等待狀態,直到加鎖成功才繼續執行。
      • 解鎖階段:當事務釋放了一個封鎖之後,事務進入解鎖階段,在該階段只能進行解鎖操做不能再進行加鎖操做。
      • 上面咱們講到,若是查詢時用到了索引,是會給數據加上行鎖,可是若是查詢沒有用到事務,這時候就是加上表鎖。可是在實際操做中,mysql作了改進,當查詢時,發現不屬於過濾條件時,就會調用unlock_row方法,將不知足條件的記錄釋放鎖 (違背了二段鎖協議的約束)。
        這樣作,保證了最後只會持有知足條件記錄上的鎖,可是每條記錄的加鎖操做仍是不能省略的。可見即便是MySQL,爲了效率也是會違反規範的。(參見《高性能MySQL》中文第三版p181)
    4. 行鎖、gap鎖和next-key鎖

      • 行鎖(record lock)
        單條索引記錄上加鎖,record lock鎖住的永遠是索引,而非記錄自己,即便該表上沒有任何索引,那麼innodb會在後臺建立一個隱藏的彙集主鍵索引,那麼鎖住的就是這個隱藏的聚簇主鍵索引。因此說當一條sql沒有走任何索引時,那麼將會在每一條聚簇索引後面加X鎖
      • gap鎖
        在索引記錄之間的間隙中加鎖,或者是在某一條索引記錄以前或者以後加鎖,並不包括該索引記錄自己。gap lock的機制主要是解決可重複讀模式下的幻讀問題。
        gap lock的前置條件:

        1. 事務隔離級別爲REPEATABLE-READ,innodb_locks_unsafe_for_binlog參數爲0,且sql走的索引爲非惟一索引
        2. 事務隔離級別爲REPEATABLE-READ,innodb_locks_unsafe_for_binlog參數爲0,且sql是一個範圍的當前讀操做,這時即便不是非惟一索引也會加gap lock
      • next-key lock
        即gap lock與record lock的結合,即除了鎖住記錄自己,還要再鎖住索引之間的間隙
        如下幾種場景來分析在RR級別下鎖的執行


        概括在一個表以下:

      • 死鎖
        產生死鎖的條件:

        1. 互斥條件:一個資源每次只能被一個進程使用;
        2. 請求與保持條件:一個進程因請求資源而阻塞時,對已得到的資源保持不放;
        3. 不剝奪條件:進程已得到的資源,在沒使用完以前,不能強行剝奪;
        4. 循環等待條件:多個進程之間造成一種互相循環等待資源的關係。

        避免死鎖的產生的一些建議:

        1. 加鎖順序一致
        2. 儘可能基於primary或unique key更新數據
        3. 單次操做數據量不宜過多,涉及表儘可能少
        4. 減小表上索引,減小鎖定資源
        5. 相關工具:pt-deadlock-logger
    5. mvcc

      • 當前讀與快照讀

        1. 當前讀
          像select lock in share mode(共享鎖), select for update ; update, insert ,delete(排他鎖)這些操做都是一種當前讀,爲何叫當前讀?就是它讀取的是記錄的最新版本,讀取時還要保證其餘併發事務不能修改當前記錄,會對讀取的記錄進行加鎖
        2. 快照讀(snapshot)
          像不加鎖的select操做就是快照讀,即不加鎖的非阻塞讀;快照讀的前提是隔離級別不是串行級別,串行級別下的快照讀會退化成當前讀;之因此出現快照讀的狀況,是基於提升併發性能的考慮,快照讀的實現是基於多版本併發控制,即MVCC,能夠認爲MVCC是行鎖的一個變種,但它在不少狀況下,避免了加鎖操做,下降了開銷;既然是基於多版本,即快照讀可能讀到的並不必定是數據的最新版本,而有多是以前的歷史版本。
          而像在RR和RC隔離級別,它們的產生的快照時機不一樣致使了隔離結果的區別:RR事務在begin/start transaction以後的第一條select讀操做後, 會建立一個快照(read view), 將當前系統中活躍的其餘事務記錄記錄起來,而RC事務中每條select語句都會建立一個快照(read view)。
      • 隱式字段
        InnoDB存儲引擎在數據庫每行數據的後面添加了三個字段:

        • 6字節的事務ID(DB_TRX_ID)字段: 用來標識最近一次對本行記錄作修改(insert|update)的事務的標識符, 即最後一次修改(insert|update)本行記錄的事務id。至於delete操做,在innodb看來也不過是一次update操做,更新行中的一個特殊位將行表示爲deleted, 並不是真正刪除
        • 7字節的回滾指針(DB_ROLL_PTR)字段: 指寫入回滾段(rollback segment)的 undo log record (撤銷日誌記錄的記錄)。若是一行記錄被更新, 則 undo log record 包含 '重建該行記錄被更新以前的內容' 所必須的信息。
        • 6字節的DB_ROW_ID字段: 包含一個隨着新行插入而單調遞增的行ID, 當由innodb自動產生彙集索引時,彙集索引會包括這個行ID的值,不然這個行ID不會出現在任何索引中。
      • undo log

        1. Undo log是InnoDB MVCC事務特性的重要組成部分。當咱們對記錄作了變動操做時就會產生undo記錄,Undo記錄默認被記錄到系統表空間(ibdata)中,但從5.6開始,也可使用獨立的Undo 表空間
        2. Undo記錄中存儲的是老版本數據,當一箇舊的事務須要讀取數據時,爲了能讀取到老版本的數據,須要順着undo鏈找到知足其可見性的記錄。當版本鏈很長時,一般能夠認爲這是個比較耗時的操做(例如bug#69812)。
        3. 大多數對數據的變動操做包括INSERT/DELETE/UPDATE,其中INSERT操做在事務提交前只對當前事務可見,所以產生的Undo日誌能夠在事務提交後直接刪除,而對於UPDATE/DELETE則須要維護多版本信息,在InnoDB裏,UPDATE和DELETE操做產生的Undo日誌被歸成一類,即update_undo log
        4. 在回滾段中的undo logs分爲: insert undo log 和 update undo log
          insert undo log : 事務對insert新記錄時產生的undolog, 只在事務回滾時須要, 而且在事務提交後就能夠當即丟棄。
          update undo log : 事務對記錄進行delete和update操做時產生的undo log, 不只在事務回滾時須要, 一致性讀也須要,因此不能隨便刪除,只有當數據庫所使用的快照中不涉及該日誌記錄,對應的回滾日誌纔會被purge線程刪除

          purge

          從前面的分析能夠看出,爲了實現InnoDB的MVCC機制,更新或者刪除操做都只是設置一下老記錄的deleted_bit,並不真正將過期的記錄刪除。
          
            爲了節省磁盤空間,InnoDB有專門的purge線程來清理deleted_bit爲true的記錄。爲了避免影響MVCC的正常工做,purge線程本身也維護了一個read view(這個read view至關於系統中最老活躍事務的read view);若是某個記錄的deleted_bit爲true,而且DB_TRX_ID相對於purge線程的read view可見,那麼這條記錄必定是能夠被安全清除的。
      • read view(讀視圖)

        • Read View就是事務進行快照讀操做的時候生產的讀視圖(Read View),在該事務執行的快照讀的那一刻,會生成數據庫系統當前的一個快照,記錄並維護系統當前活躍事務的ID(當每一個事務開啓時,都會被分配一個ID, 這個ID是遞增的,因此最新的事務,ID值越大)
        • 因此咱們知道 Read View主要是用來作可見性判斷的, 即當咱們某個事務執行快照讀的時候,對該記錄建立一個Read View讀視圖,把它比做條件用來判斷當前事務可以看到哪一個版本的數據,既多是當前最新的數據,也有多是該行記錄的undo log裏面的某個版本的數據。
        • Read View遵循一個可見性算法,主要是將要被修改的數據的最新記錄中的DB_TRX_ID(即當前事務ID)取出來,與系統當前其餘活躍事務的ID去對比(由Read View維護),若是DB_TRX_ID跟Read View的屬性作了某些比較,不符合可見性,那就經過DB_ROLL_PTR回滾指針去取出Undo Log中的DB_TRX_ID再比較,即遍歷鏈表的DB_TRX_ID(從鏈首到鏈尾,即從最近的一次修改查起),直到找到知足特定條件的DB_TRX_ID, 那麼這個DB_TRX_ID所在的舊記錄就是當前事務能看見最新老版本
      • 可見性比較算法
        設當前新開事務id爲 new_id
        當前新開事務建立的快照read view 中最先的事務id爲up_limit_id, 最遲的事務id爲low_limit_id(注意這個low_limit_id=未開啓的事務id=當前最大事務id+1)

        1. DB_TRX_ID < up_limit_id, 這種狀況比較好理解, 表示, 新事務在讀取該行記錄時, 該行記錄的穩定事務ID是小於系統當前全部活躍的事務, 因此當前行穩定數據對新事務可見, 跳到步驟5
        2. DB_TRX_ID=m_creator_trx_id 表示若是當前行事務DB_TRX_ID等於開啓事務時的事務id。簡單來講,在同一個事務中insert,update的記錄將可見。
        3. DB_TRX_ID >= low_limit_id,表明是當前行記錄是在該事務生成read view後產生的(執行第二個select前就commit),因此確定對當前事務不可見,跳到步驟4
        4. DB_TRX_ID < low_limit_id,表明是當前行記錄是在該事務生成read view時還活躍的事務,若是遍歷read view中的ids(up_limit_id到low_limit_id),存在ids之中證實事務已經提交,因此不屬於該事務可見,跳到步驟4,不然可見(意味着,是當前事務開始的時候,該行記錄對應事務還沒提交,但在建立read view前提交了,建立read view時活躍的事務最晚的又在該行事務後)
        5. 從該行記錄的 DB_ROLL_PTR 指針所指向的回滾段中取出最新的undo-log的版本號, 將它賦值該 DB_TRX_ID ,而後跳到步驟1從新開始判斷
        6. 將該可見行的值返回。
      • 可見性算法案例分析

        1. 下面是一個很是簡版的演示事務對某行記錄的更新過程, 固然, InnoDB引擎在內部要作的工做很是多
          image.png
        2. 下面是一套比較算法的應用過程
          image.png
      • innodb的mvcc與理想mvcc區別

        1. 通常咱們認爲MVCC有下面幾個特色:
          每行數據都存在一個版本,每次數據更新時都更新該版本
          修改時Copy出當前版本, 而後隨意修改,各個事務之間無干擾
          保存時比較版本號,若是成功(commit),則覆蓋原記錄, 失敗則放棄copy(rollback)
          就是每行都有版本號,保存時根據版本號決定是否成功,聽起來含有樂觀鎖的味道, 由於這看起來正是,在提交的時候才能知道到底可否提交成功
        2. 而InnoDB實現MVCC的方式是:
          事務以排他鎖的形式修改原始數據
          把修改前的數據存放於undo log,經過回滾指針與主數據關聯
          修改爲功(commit)啥都不作,失敗則恢復undo log中的數據(rollback)
        3. 兩者最本質的區別是: 當修改數據時是否要排他鎖定

        innodb算不上真正的mvcc,由於沒有實現核心的多版本共存。其緣由是理想mvcc對多行數據無能爲力。
        譬如,若是事務A執行理想的MVCC, 修改Row1成功, 而修改Row2失敗, 此時須要回滾Row1, 但由於Row1沒有被鎖定, 其數據可能又被事務B所修改, 若是此時回滾Row1的內容,則會破壞事務B的修改結果,致使事務B違反ACID。 這也正是所謂的 第一類更新丟失的狀況

  3. 特別的查詢優化策略(待續)
相關文章
相關標籤/搜索