1. 程式人生 > 實用技巧 >資料庫知識(2)

資料庫知識(2)

mysql的MVCC機制

MVCC的維基百科解釋:多版本併發控制(Multiversion concurrency control, MCCMVCC),是資料庫管理系統常用的一種併發控制,也用於程式設計語言實現事務記憶體

MVCC是一種多版本併發控制機制,是MySQL的InnoDB儲存引擎實現隔離級別的一種具體方式,用於實現提交讀和可重複讀這兩種隔離級別

MVCC作用

MVCC意圖解決讀寫鎖造成的多個、長時間的讀操作餓死寫操作問題。所以MVCC通過儲存某個時間點的快照來實現該機制,每個事務讀到的資料項都是一個歷史快照(snapshot)並依賴於實現的隔離級別。寫操作不覆蓋已有資料項,而是建立一個新的版本,直至所在操作提交時才變為可見。

快照隔離使得事物看到它啟動時的資料狀態。

MVCC可以無鎖實現。

常見的資料庫優化方法

或者索引相關的點你全部都知道麼?聚簇索引,非聚簇索引,普通索引,唯一索引,change buffer,表鎖、行鎖、間隙鎖以及行鎖併發情況下的最大TPS是多少?還有索引為啥會選擇錯誤?這些大家知道嘛?

資料庫的組成結構圖:

我們所謂的調優也就是在,執行器執行之前的分析器,優化器階段完成的

  1. 先跑sql explain

    MySQL 提供了一個 EXPLAIN 命令, 它可以對 SELECT 語句進行分析, 並輸出 SELECT 執行的詳細資訊, 以供開發人員針對性優化.
    EXPLAIN 命令用法十分簡單, 在 SELECT 語句前加上 Explain 就可以了, 例如:

    EXPLAIN SELECT * from user_info WHERE id < 300;
    

    EXPLAIN 命令的輸出內容大致如下:

    mysql> explain select * from user_info where id = 2\G
    *************************** 1. row ***************************
               id: 1
      select_type: SIMPLE
            table: user_info
       partitions: NULL
             type: const
    possible_keys: PRIMARY
              key: PRIMARY
          key_len: 8
              ref: const
             rows: 1
         filtered: 100.00
            Extra: NULL
    1 row in set, 1 warning (0.00 sec)
    

    各列的含義如下:

    • id: SELECT 查詢的識別符號. 每個 SELECT 都會自動分配一個唯一的識別符號.

    • select_type: SELECT 查詢的型別.

      它的常用取值有:

      • SIMPLE, 表示此查詢不包含 UNION 查詢或子查詢
      • PRIMARY, 表示此查詢是最外層的查詢
      • UNION, 表示此查詢是 UNION 的第二或隨後的查詢
      • DEPENDENT UNION, UNION 中的第二個或後面的查詢語句, 取決於外面的查詢
      • UNION RESULT, UNION 的結果
      • SUBQUERY, 子查詢中的第一個 SELECT
      • DEPENDENT SUBQUERY: 子查詢中的第一個 SELECT, 取決於外面的查詢. 即子查詢依賴於外層查詢的結果.
    • table: 查詢的是哪個表

    • partitions: 匹配的分割槽

    • type: join 型別

      type 欄位比較重要, 它提供了判斷查詢是否高效的重要依據依據. 通過 type 欄位, 我們判斷此次查詢是 全表掃描 還是 索引掃描 等.

      type 常用的取值有:

      • system: 表中只有一條資料. 這個型別是特殊的 const 型別.
      • const: 針對主鍵或唯一索引的等值查詢掃描, 最多隻返回一行資料. const 查詢速度非常快, 因為它僅僅讀取一次即可.
    • possible_keys: 此次查詢中可能選用的索引

      possible_keys 表示 MySQL 在查詢時, 能夠使用到的索引. 注意, 即使有些索引在 possible_keys 中出現, 但是並不表示此索引會真正地被 MySQL 使用到. MySQL 在查詢時具體使用了哪些索引, 由 key 欄位決定.

    • key: 此次查詢中確切使用到的索引.

    • ref: 哪個欄位或常數與 key 一起被使用

    • rows: 顯示此查詢一共掃描了多少行. 這個是一個估計值.

      rows 也是一個重要的欄位. MySQL 查詢優化器根據統計資訊, 估算 SQL 要查詢到結果集需要掃描讀取的資料行數.
      這個值非常直觀顯示 SQL 的效率好壞, 原則上 rows 越少越好.

    • filtered: 表示此查詢條件所過濾的資料的百分比

    • extra: 額外的資訊

  2. 排除快取干擾(Mysql8.0之前需要需要排除)

    我們在執行SQL的時候,記得加上SQL NoCache去跑SQL,這樣跑出來的時間就是真實的查詢時間了。

    為什麼快取會失效,而且是經常失效?

    快取失效比較頻繁的原因就是,只要我們一對錶進行更新,那這個表所有的快取都會被清空,其實我們很少存在不更新的表。

  3. 覆蓋索引

    我們在資料庫查詢的操作中,可能有回表的操作,我們希望不進行回表操作,在自己的索引上就查到自己想要的,不進行主鍵索引查詢。

    如果在我們建立的索引上就已經有我們需要的欄位,就不需要回表了,在電商裡面也是很常見的,我們需要去商品表通過各種資訊查詢到商品id,id一般都是主鍵,可能sql類似這樣:

    select itemId from itemCenter where size between 1 and 6
    

    因為商品id itemId一般都是主鍵,在size索引上肯定會有我們這個值,這個時候就不需要回主鍵表去查詢id資訊了。

    由於覆蓋索引可以減少樹的搜尋次數,顯著提升查詢效能,所以使用覆蓋索引是一個常用的效能優化手段。

  4. 聯合索引——構建兩個屬性之間的快速查詢

    還是商品表舉例,我們需要根據他的名稱,去查他的庫存,假設這是一個很高頻的查詢請求,你會怎麼建立索引呢?

    思考回表的消耗對SQL進行優化。

    建立一個,名稱和庫存的聯合索引,這樣名稱查出來就可以看到庫存了,不需要查出id之後去回表再查詢庫存了,聯合索引在我們開發過程中也是常見的,但是並不是可以一直建立的,大家要思考索引佔據的空間。

  5. 最左匹配原則

    如果利用一個模糊查詢 itemname like ’敖丙%‘,這樣還是能利用到這個索引的,而且如果有這樣的聯合索引,大家也沒必要去新建一個商品名稱單獨的索引了。

    很多時候我們索引可能沒建對,那你調整一下順序,可能就可以優化到整個SQL了。

  6. 索引下推

    知道了字首索引規則,那我就說一個官方幫我們優化的東西,索引下推。

    select * from itemcenter where name like '敖%' and size=22 and age = 20;
    

    所以這個語句在搜尋索引樹的時候,只能用 “敖”,找到第一個滿足條件的記錄ID1,當然,這還不錯,總比全表掃描要好。

    然後我們還可以使用索引下推進行優化:

    在MySQL 5.6之前,只能從ID1開始一個個回表,到主鍵索引上找出資料行,再對比欄位值。

    而MySQL 5.6 引入的索引下推優化(index condition pushdown), 可以在索引遍歷過程中,對索引中包含的欄位先做判斷,直接過濾掉不滿足條件的記錄,減少回表次數。

  7. 唯一索引普通索引選擇

    當需要更新一個數據頁時,如果資料頁在記憶體中就直接更新,而如果這個資料頁還沒有在記憶體中的話,在不影響資料一致性的前提下,InooDB會將這些更新操作快取在change buffer中,這樣就不需要從磁碟中讀入這個資料頁了。

    在下次查詢需要訪問這個資料頁的時候,將資料頁讀入記憶體,然後執行change buffer中與這個頁有關的操作,通過這種方式就能保證這個資料邏輯的正確性。

    將change buffer中的操作應用到原資料頁,得到最新結果的過程稱為merge。除了訪問這個資料頁會觸發merge外,系統有後臺執行緒會定期merge。在資料庫正常關閉(shutdown)的過程中,也會執行merge操作。

    那麼,什麼條件下可以使用change buffer呢?

    對於唯一索引來說,所有的更新操作都要先判斷這個操作是否違反唯一性約束。

    要判斷表中是否存在這個資料,而這必須要將資料頁讀入記憶體才能判斷,如果都已經讀入到記憶體了,那直接更新記憶體會更快,就沒必要使用change buffer了。

    因此,唯一索引的更新就不能使用change buffer,實際上也只有普通索引可以使用。

    change buffer用的是buffer pool裡的記憶體,因此不能無限增大,change buffer的大小,可以通過引數innodb_change_buffer_max_size來動態設定,這個引數設定為50的時候,表示change buffer的大小最多隻能佔用buffer pool的50%。

    將資料從磁碟讀入記憶體涉及隨機IO的訪問,是資料庫裡面成本最高的操作之一,change buffer因為減少了隨機磁碟訪問,所以對更新效能的提升是會很明顯的。

  8. 字首索引

    我們存在郵箱作為使用者名稱的情況,每個人的郵箱都是不一樣的,那我們是不是可以在郵箱上建立索引,但是郵箱這麼長,我們怎麼去建立索引呢?

    MySQL是支援字首索引的,也就是說,你可以定義字串的一部分作為索引。預設地,如果你建立索引的語句不指定字首長度,那麼索引就會包含整個字串。

Mysql各種引擎和區別

MySQL中的資料用各種不同的技術儲存在檔案(或者記憶體)中。這些技術中的每一種技術都使用不同的儲存機制、索引技巧、鎖定水平並且最終提供廣泛的不同的功能和能力。通過選擇不同的技術,你能夠獲得額外的速度或者功能,從而改善你的應用的整體功能。

MySQL儲存引擎主要有: MyIsam、InnoDB、Memory、Blackhole、CSV、Performance_Schema、Archive、Federated、Mrg_Myisam。

但是最常用的是InnoDB和Mylsam。

InnoDB

InnoDB是一個事務型的儲存引擎,有行級鎖定和外來鍵約束。

Innodb引擎提供了對資料庫ACID事務的支援,並且實現了SQL標準的四種隔離級別,關於資料庫事務與其隔離級別的內容請見資料庫事務與其隔離級別這型別的文章。該引擎還提供了行級鎖和外來鍵約束,它的設計目標是處理大容量資料庫系統,它本身其實就是基於MySQL後臺的完整資料庫系統,MySQL執行時Innodb會在記憶體中建立緩衝池,用於緩衝資料和索引。但是該引擎不支援FULLTEXT型別的索引,而且它沒有儲存表的行數,當SELECT COUNT(*) FROM TABLE時需要掃描全表。當需要使用資料庫事務時,該引擎當然是首選。由於鎖的粒度更小,寫操作不會鎖定全表,所以在併發較高時,使用Innodb引擎會提升效率。但是使用行級鎖也不是絕對的,如果在執行一個SQL語句時MySQL不能確定要掃描的範圍,InnoDB表同樣會鎖全表。

適用場景:

經常更新的表,適合處理多重併發的更新請求。

索引結構:InnoDB是B+Treee索引結構。並且Innodb的索引檔案本身就是資料檔案,即B+Tree的資料域儲存的就是實際的資料,這種索引就是聚集索引

InnoDB的輔助索引資料域儲存的也是相應記錄主鍵的值而不是地址,所以當以輔助索引查詢時,會先根據輔助索引找到主鍵,再根據主鍵索引找到實際的資料。所以Innodb不建議使用過長的主鍵,否則會使輔助索引變得過大。建議使用自增的欄位作為主鍵,這樣B+Tree的每一個結點都會被順序的填滿,而不會頻繁的分裂調整,會有效的提升插入資料的效率。

Mylsam

MyIASM是MySQL預設的引擎,但是它沒有提供對資料庫事務的支援,也不支援行級鎖和外來鍵,因此當INSERT或UPDATE資料時即寫操作需要鎖定整個表,效率便會低一些。MyIsam 儲存引擎獨立於作業系統,也就是可以在windows上使用,也可以比較簡單的將資料轉移到linux作業系統上去。

適用場景:

不支援事務的設計,但是並不代表著有事務操作的專案不能用MyIsam儲存引擎,可以在service層進行根據自己的業務需求進行相應的控制。

不支援外來鍵的表設計。

查詢速度很快,如果資料庫insert和update的操作比較多的話比較適用。

整天對錶進行加鎖的場景。

MyISAM極度強調快速讀取操作。

索引結構:MyISAM索引用的B+ tree來儲存資料,MyISAM索引的指標指向的是鍵值的地址,地址儲存的是資料。B+Tree的資料域儲存的內容為實際資料的地址,也就是說它的索引和實際的資料是分開的,只不過是用索引指向了實際的資料,這種索引就是所謂的非聚集索引。

InnoDB和MyISAM的區別

  1. 事務:MyISAM型別不支援事務處理等高階處理,而InnoDB型別支援,提供事務支援已經外部鍵等高階資料庫功能

  2. 效能:MyISAM型別的表強調的是效能,其執行數度比InnoDB型別更快。

  3. 行數儲存:InnoDB 中不儲存表的具體行數,也就是說,執行select count() fromtable時,InnoDB要掃描一遍整個表來計算有多少行,但是MyISAM只要簡單的讀出儲存好的行數即可。注意的是,當count()語句包含where條件時,兩種表的操作是一樣的。

  4. 索引儲存:對於AUTO_INCREMENT型別的欄位,InnoDB中必須包含只有該欄位的索引,但是在MyISAM表中,可以和其他欄位一起建立聯合索引。MyISAM支援全文索引(FULLTEXT)、壓縮索引,InnoDB不支援。

    MyISAM的索引和資料是分開的,並且索引是有壓縮的,記憶體使用率就對應提高了不少。能載入更多索引,而Innodb是索引和資料是緊密捆綁的,沒有使用壓縮從而會造成Innodb比MyISAM體積龐大不小。

    InnoDB儲存引擎被完全與MySQL伺服器整合,InnoDB儲存引擎為在主記憶體中快取資料和索引而維持它自己的緩衝池。InnoDB儲存它的表&索引在一個表空間中,表空間可以包含數個檔案(或原始磁碟分割槽)。這與MyISAM表不同,比如在MyISAM表中每個表被存在分離的檔案中。InnoDB 表可以是任何尺寸,即使在檔案尺寸被限制為2GB的作業系統上。

  5. 伺服器資料備份:InnoDB必須匯出SQL來備份,LOAD TABLE FROM MASTER操作對InnoDB是不起作用的,解決方法是首先把InnoDB表改成MyISAM表,匯入資料後再改成InnoDB表,但是對於使用的額外的InnoDB特性(例如外來鍵)的表不適用。

    MyISAM應對錯誤編碼導致的資料恢復速度快。MyISAM的資料是以檔案的形式儲存,所以在跨平臺的資料轉移中會很方便。在備份和恢復時可單獨針對某個表進行操作。

    InnoDB是拷貝資料檔案、備份 binlog,或者用 mysqldump,在資料量達到幾十G的時候就相對痛苦了。

  6. 鎖的支援:MyISAM只支援表鎖。InnoDB支援表鎖、行鎖 行鎖大幅度提高了多使用者併發操作的新能。但是InnoDB的行鎖,只是在WHERE的主鍵是有效的,非主鍵的WHERE都會鎖全表的。


參考連結:

  1. 有哪些常見的資料庫優化方法?
  2. MySQL 效能優化神器 Explain 使用分析