MySQL實戰(持續更新)

01 | 基礎架構:一條SQL查詢語句是如何執行的?

下面我給出的是 MySQL 的基本架構示意圖,從中你能夠清楚地看到 SQL 語句在 MySQL 的各個功能模塊中的執行過程。html

img

從圖中能夠看出,不一樣的存儲引擎公用一個server層。mysql

鏈接器

  • 鏈接命令算法

    mysql -h$ip -P$port -u$user -psql

  • 鏈接保持時間shell

    • 鏈接完成以後,若是沒有後續的動做,這個鏈接就會處於空閒狀態,能夠在show processlist命令中看到。數據庫

    • 客戶端若是太長時間沒有動靜,鏈接器會主動將其斷連。這個參數是wait_timeout控制的,默認時間是8小時。數組

    • 斷開以後再發送請求的話,就會收到一個錯誤提醒:Lost connection to MySQL server during query。須要客戶端從新鏈接,而後再發送請求。緩存

  • 長鏈接佔用內存問題bash

    • 在執行過程當中,鏈接中使用的內存是管理在鏈接對象裏面的。這些資源只有在斷連的時候纔會釋放。這些長鏈接累積下來,可能會致使內存佔用愈來愈大,最終被系統OOM掉。
    • 解決:
      • 按期斷開鏈接。使用一段時間之後,或者在程序裏面判斷執行過一個比較大的操做以後,從新鏈接。
      • 或者能夠在執行過一個較大的操做以後,執行mysql_reset_connection來從新初始化鏈接資源。

查詢緩存

創建鏈接以後,進行select語句查詢,就會進入查詢緩存階段。每次的查詢會把結果放入緩存,下次有相同的查詢會直接在緩存中獲取便可,無需進入存儲引擎進行查詢。架構

可是,查詢緩存這個事情是弊大於利的,因此不會建議使用。mysql8.0以後直接將該模塊刪除了。

爲啥有弊呢?由於每次存在update操做的狀況下,就會把整個緩存都清除掉,致使緩存命中率特別低。

在8.0以前的版本,能夠直接將query_cache_type設置爲DEMAND,這樣的話,查詢語句默認不使用緩存。在須要使用查詢緩存的地方能夠顯示調用。mysql> select SQL_CACHE * from T where ID=10;

分析器

分析器先作「詞法分析」,根據語法規則,判斷輸入的sql語句是否知足mysql語法規則。

優化器

優化器是在表中存在多個索引的狀況下,決定用哪一個索引;或者在一個語句有多表關聯(join)的時候,決定各個表的鏈接順序。

執行器

MySQL經過分析器知道了你要作什麼,經過優化器知道了該怎麼作,因而就進入了執行器階段,開始執行語句。

02 | 日誌系統:一條SQL更新語句是如何執行的?

與查詢流程不同的是,更新流程還涉及兩個重要的日誌模塊,它們正是咱們今天要討論的主角:redo log(重作日誌)和 binlog(歸檔日誌)

重要的日誌模塊:redo log

若是每一次的更新操做都須要寫進磁盤,而後磁盤也要找到對應的那條記錄,而後再更新,整個過程 IO 成本、查找成本都很高。爲了解決這個問題,MySQL 的設計者就用了相似酒店掌櫃粉板的思路來提高更新效率。

而粉板和帳本配合的整個過程,其實就是 MySQL 裏常常說到的 WAL 技術,WAL 的全稱是 Write-Ahead Logging,它的關鍵點就是先寫日誌,再寫磁盤,也就是先寫粉板,等不忙的時候再寫帳本。

具體來講,當有一條記錄須要更新的時候,InnoDB 引擎就會先把記錄寫到 redo log(按我理解這個其實仍是要寫文件,只不過是順序寫,效率高)裏面,並更新內存,這個時候更新就算完成了。同時,InnoDB 引擎會在適當的時候,將這個操做記錄更新到磁盤裏面,而這個更新每每是在系統比較空閒的時候作。

InnoDB 的 redo log 是固定大小的,好比能夠配置爲一組 4 個文件,每一個文件的大小是 1GB,那麼這塊「粉板」總共就能夠記錄 4GB 的操做。從頭開始寫,寫到末尾就又回到開頭循環寫,以下面這個圖所示。

img

這種機制保證了即便數據庫異常宕機,以前的數據也不會丟失,這個能力成爲crash-safe

補充

做用

  • 確保事務的持久性,防止在發生故障的時間點,尚有髒頁未寫入磁盤,在重啓mysql服務的時候,根據redo log進行重作,從而達到事務的持久性這一特性。
    • 這個事情我是這樣想的,redolog自己是爲了彌補binlog的固有的缺點才作的。binlog不會實時寫磁盤,特別是在事務的狀況下,不commit binlog不會寫入磁盤。
    • redolog是實時順序寫磁盤的(固然也不是實時,可是最遲1s寫入)

內容

  • 物理格式的日誌,記錄的是物理數據頁面的修改的信息。對比binlog的格式是具體的sql操做語句。

何時產生

  • 事務或者操做開始的時候就會產生

是麼時候釋放

  • 當對應事務或操做的髒頁寫入到磁盤以後,redo log的使命也就完成了,重作日誌佔用的空間就能夠重用(被覆蓋)。

重要的日誌模塊:binlog

redo log是innodb引擎特有的日誌,而引擎層上面的server層(若是忘了mysql的架構,記得看上一篇的圖示)也有本身的日誌,成爲binlog(歸檔日誌)。

  • 爲啥會有兩份日誌嘛?

    由於最開始 MySQL 裏並無 InnoDB 引擎。MySQL 自帶的引擎是 MyISAM,可是 MyISAM 沒有 crash-safe 的能力,binlog 日誌只能用於歸檔。而 InnoDB 是另外一個公司以插件形式引入 MySQL 的,既然只依靠 binlog 是沒有 crash-safe 能力的,因此 InnoDB 使用另一套日誌系統——也就是 redo log 來實現 crash-safe 能力。

補充

做用

  • 用於複製,在主從複製中,從庫利用主庫上的binlog進行重播,實現主從同步。
  • 用於數據庫的基於時間點的還原。(在異常重啓的狀況下,會用到redolog

內容

  • 簡單認爲就是執行過的sql語句(查詢啥的就不須要了)

何時產生

  • 事務提交的時候,一次性將事務中的sql語句(一個事物可能對應多個sql語句)按照必定的格式記錄到binlog中。

何時釋放

  • binlog的默認是保持時間由參數expire_logs_days配置,也就是說對於非活動的日誌文件,在生成時間超過expire_logs_days配置的天數以後,會被自動刪除。

redolog和binlog的不一樣

  • 做用不一樣:redo log是保證事務的持久性的,是事務層面的,binlog做爲還原的功能,是數據庫層面的(固然也能夠精確到事務層面的),雖然都有還原的意思,可是其保護數據的層次是不同的。
  • 內容不一樣:redo log是物理日誌,是數據頁面的修改以後的物理記錄,binlog是邏輯日誌,能夠簡單認爲記錄的就是sql語句
  • 另外,二者日誌產生的時間,能夠釋放的時間,在可釋放的狀況下清理機制,都是徹底不一樣的。
  • 恢復數據時候的效率,基於物理日誌的redo log恢復數據的效率要高於語句邏輯日誌的binlog

update的流程

img

這裏面涉及到了兩階段提交。 MySQL經過兩階段提交過程來完成事務的一致性的,也即redo log和binlog的一致性的,理論上是先寫redo log,再寫binlog,兩個日誌都提交成功(刷入磁盤),事務纔算真正的完成。

如何讓數據庫恢復到半個月內的任何一秒的狀態?

若是你的 DBA 承諾說半個月內能夠恢復,那麼備份系統中必定會保存最近半個月的全部 binlog,同時系統會按期作整庫備份。這裏的「按期」取決於系統的重要性,能夠是一天一備,也能夠是一週一備。

當須要恢復到指定的某一秒時,好比某天下午兩點發現中午十二點有一次誤刪表,須要找回數據,那你能夠這麼作:

  • 首先,找到最近的一次全量備份,若是你運氣好,可能就是昨天晚上的一個備份,從這個備份恢復到臨時庫;
  • 而後,從備份的時間點開始,將備份的 binlog 依次取出來,重放到中午誤刪表以前的那個時刻。

03 | 事務隔離

事務就是要保證數據庫一組操做,要麼所有成功,要麼所有失敗。在mysql中,事務是引擎層支實現的,MyISAM不支持事務,InnoDB支持事務。這也是MyISAM被InnoDB取代的重要緣由之一。

隔離性與隔離級別

  • 讀未提交。一個事務還沒提交,他作的變動就能被別的事務看到。
  • 讀已提交。一個事務提交以後,才能被其餘事務看到。
  • 可重複讀。一個事務執行過程當中,老是跟這個事務啓動時看到的數據是一致的。
  • 串行化。當出現讀寫衝突的時候,後訪問的事務必須等待前一個事務執行完成以後,才能繼續執行。

在實現上,數據庫會建立一個視圖,訪問的時候以視圖的邏輯結果爲準。

在「可重複讀」級別下,這個視圖是在事務啓動的時候建立的,整個事務存在期間都用這個視圖。

在「讀提交」級別下,這個視圖是在每一個SQL語句開始執行的時候建立的。

在「讀未提交」級別下,直接返回記錄上的最新值。

在「串行化」級別下,直接用加鎖的方式來避免並行訪問。

Oracle的默認級別是「讀提交」。

Mysql的默認級別是「可重複讀」。

查看當前數據庫默認的事務隔離級別:

show variables like 'transaction_isolation';
複製代碼

事務隔離的實現

每條update語句執行的同時,都會同時記錄一條回滾日誌。記錄上的最新值,經過回滾操做,均可以獲得前一個狀態的值。

假設一個值從1按順序改爲了2/3/4,在回滾日誌裏會有相似下面的記錄。

img

當前該值爲4,不一樣時刻啓動的事務會有不一樣的read-view。同一條記錄在系統中能夠存在多個版本,就是數據庫的多版本併發控制(MVCC)。對於read-view A,要獲得1,就必須將當前值依次執行圖中全部的回滾操做獲得。

當沒有事務再須要用到這些回滾日誌的時候,相應的回滾日誌會被刪除。

事務的啓動方式

  • 顯式啓動:begin或者start transaction。配套的提交語句是commit,回滾語句是rollback。
  • set autocommit=0,將線程的自動提交關掉。意味着執行一個語句,事務就啓動了,並且不會自動提交。這個事務會一直存在,直到主動執行commit或者rollback。

查詢長事務

能夠在 information_schema 庫的 innodb_trx 這個表中查詢長事務,好比下面這個語句,用於查找持續時間超過 60s 的事務。

select * from information_schema.innodb_trx where TIME_TO_SEC(timediff(now(),trx_started))>60
複製代碼

?如何避免長事務對業務的影響

從應用開發端和數據庫端兩個方面來看問題。

  • 應用開發端

    1. 肯定auto_commit是否爲1.
    2. 避免出現沒必要要的事務,如都是select語句,徹底不必使用事務。
    3. 設置合理的SET MAX_EXECUTION_TIME來控制每一個語句的最長執行時間。
  • 數據庫端

    1. 監控information_schema.Innodb_trx表,設置長事務閾值,超過閾值就告警或kill。

      mysql -N -uroot -p'密碼' -e "select now(),(UNIX_TIMESTAMP(now()) - UNIX_TIMESTAMP(a.trx_started)) diff_sec,b.id,b.user,b.host,b.db,d.SQL_TEXT from information_schema.innodb_trx a inner join 
      information_schema.PROCESSLIST b 
      on a.TRX_MYSQL_THREAD_ID=b.id and b.command = 'Sleep' 
      inner join performance_schema.threads c ON b.id = c.PROCESSLIST_ID
      inner join performance_schema.events_statements_current d ON d.THREAD_ID = c.THREAD_ID;" | while read A B C D E F G H
      do 
      #echo $C 
      if [ "$C" -gt 5 ] 
      then 
      echo date "+%Y-%m-%d %H:%M:%S" 
      echo "processid[$D] $E@$F in db[$G] hold transaction time $C SQL:$H" 
      fi 
      done >> /tmp/longtransaction.txt
      複製代碼
    2. 使用pt-kill工具,kill長時間的異常事務。參考:www.cnblogs.com/bjx2020/p/9…

    3. 在業務開發測試階段,輸出全部的general_log,分析日誌行爲,提前發現。

      mysql>set global general_log_file='/tmp/general.log'; #設置路徑
      mysql>set global general_log=on; # 開啓general log模式
      複製代碼

04 | 深刻淺出索引(上)

索引就是爲了提升數據查詢的效率,就像書的目錄同樣。

索引的常見模型

  • 哈希表
    • 只適用於等值查詢的場景,對於範圍查詢只能走全表掃描。
  • 有序數組
    • 等值查詢和範圍查詢場景中的性能都很是好。
    • 插入性能太差。因此適合靜態存儲引擎,如2017年城市人口信息。
  • 二叉樹
    • 查詢複雜度是O(log(N))
    • 二叉樹的搜索效率是很高的,可是大多數的數據庫存儲不會用二叉樹。由於索引不止存在內存中,還要寫到磁盤上。想象一棵100萬節點的平衡二叉樹,樹高20,一次查詢可能須要訪問20個數據塊。從磁盤隨機讀一個數據塊就須要10ms左右的尋址時間,那麼20個數據塊的訪問時間就會達到200ms。

InnoDB的索引

在InnoDB中,表都是根據主鍵順序以索引的形式存放的,這種存儲方式的表稱爲索引組織表。

在InnoDB中,索引使用B+樹的索引模式。B+ 樹可以很好地配合磁盤的讀寫特性,減小單次查詢的磁盤訪問次數。

img

左邊是主鍵索引,右邊是普通索引。主鍵索引的葉子節點存的是整行數據,普通索引的葉子節點存的是主鍵的值。

  • 若是語句是 select * from T where ID=500,即主鍵查詢方式,則只須要搜索 ID 這棵 B+ 樹;
  • 若是語句是 select * from T where k=5,即普通索引查詢方式,則須要先搜索 k 索引樹,獲得 ID 的值爲 500,再到 ID 索引樹搜索一次。這個過程稱爲回表。

也就是說,基於非主鍵索引的查詢須要多掃描一棵索引樹。所以,咱們在應用中應該儘可能使用主鍵查詢。

索引維護

B+ 樹爲了維護索引有序性,在插入新值的時候須要作必要的維護。以上面這個圖爲例,若是插入新的行 ID 值爲 700,則只須要在 R5 的記錄後面插入一個新記錄。若是新插入的 ID 值爲 400,就相對麻煩了,須要邏輯上挪動後面的數據,空出位置。

而更糟的狀況是,若是 R5 所在的數據頁已經滿了,根據 B+ 樹的算法,這時候須要申請一個新的數據頁,而後挪動部分數據過去。這個過程稱爲頁分裂。在這種狀況下,性能天然會受影響。

因此使用自增主鍵的重要性就體如今這裏了。每次插入一條新記錄,都是追加操做,都不涉及到挪動其餘記錄,也不會觸發葉子節點的分裂。

顯然,主鍵長度越小,普通索引的葉子節點就越小,普通索引佔用的空間也就越小。

  • 適合用業務字段直接作主鍵的場景
    1. 只有一個索引
    2. 該索引爲惟一索引

總結

  • InnoDB使用B+樹做爲索引模型。由於B+ 樹可以很好地配合磁盤的讀寫特性,減小單次查詢的磁盤訪問次數。
  • InnoDB是主鍵索引組織表
  • 推薦使用自增主鍵

05 | 深刻淺出索引(下)

  • 以下所示的表:

    mysql> create table T (
    ID int primary key,
    k int NOT NULL DEFAULT 0, 
    s varchar(16) NOT NULL DEFAULT '',
    index k(k))
    engine=InnoDB;
    
    insert into T values(100,1, 'aa'),(200,2,'bb'),(300,3,'cc'),(500,5,'ee'),(600,6,'ff'),(700,7,'gg');
    複製代碼

    img

  • 以下所示的查詢,須要執行幾回樹的操做,會掃描多少行:

    select * from T where k between 3 and 5
    複製代碼
  • 執行流程以下:

    1. 在 k 索引樹上找到 k=3 的記錄,取得 ID = 300;
    2. 再到 ID 索引樹查到 ID=300 對應的 R3;
    3. 在 k 索引樹取下一個值 k=5,取得 ID=500;
    4. 再回到 ID 索引樹查到 ID=500 對應的 R4;
    5. 在 k 索引樹取下一個值 k=6,不知足條件,循環結束。

    在這個例子中,因爲查詢結果所須要的數據只在主鍵索引上有,因此不得不回表。那麼,有沒有可能通過索引優化,避免回表過程呢?

覆蓋索引

若是執行的語句是select ID from T where k between 3 and 5,這時只須要查詢ID的值,而ID的值自己已經在k索引樹上,不須要回表。

也就是說,在這個查詢裏面,索引k已經覆蓋了咱們的查詢需求,咱們稱爲覆蓋索引

又有一個問題:獲取字段和檢索字段都不是主鍵,怎麼使用覆蓋索引。

答案:創建兩個字段的聯合索引便可。

最左前綴原則

這邊有一個疑問,若是爲每一種查詢都設計一個索引,索引會不會太多了。這個時候能夠藉助最左前綴原則來複用索引。

當已經有(a,b)索引以後,就不須要單獨的a索引了。

06 | 全局鎖和表鎖 :給表加個字段怎麼有這麼多阻礙?

根據加鎖範圍,MySQL裏面的鎖大體能夠分爲全局鎖、表級鎖和行級鎖。

全局鎖

顧名思義,全局鎖就是對整個數據庫加鎖。

  • 加鎖方式

    Flush tables with read lock,使得整個數據庫處於只讀狀態。任何更新類的語句所有阻塞。

  • 使用場景

    **作全庫邏輯備份。**也就是把整庫每一個表都select出來存成文本。

  • 風險

    • 若是在主庫備份,在備份期間不能更新,業務停擺
    • 若是在從庫備份,備份期間不能執行主庫同步的binlog,致使主從延遲
  • 具體案例

    • 官方自帶的邏輯備份工具mysqldump,當mysqldump使用參數--single-transaction的時候,會啓動一個事務,確保拿到一致性視圖。而因爲MVCC的支持,這個過程當中數據是能夠正常更新的。固然,MVCC只是支持InnoDB。
  • 爲何不使用set global readonly=true的方式?

    • 在有些系統中,readonly的值會被用來作其餘邏輯,好比判斷主備庫。因此修改global變量的方式影響太大。
    • 在異常處理機制上有差別。若是執行FTWRL命令以後因爲客戶端發生異常斷開,那麼MySQL會自動釋放這個全局鎖,整個庫回到能夠正常更新的狀態。而將整個庫設置爲readonly以後,若是客戶端發生異常,則數據庫就會一直保持readonly狀態,這樣會致使整個庫長時間處於不可寫狀態,風險較高。

表級鎖

MySQL中表級鎖有兩種:表鎖和元數據鎖MDL(meta data lock)

  • 表鎖

    • 表鎖的語法是 lock tables … read/write。

      舉個例子, 若是在某個線程 A 中執行 lock tables t1 read, t2 write; 這個語句,則其餘線程寫 t一、讀寫 t2 的語句都會被阻塞。同時,線程 A 在執行 unlock tables 以前,也只能執行讀 t一、讀寫 t2 的操做。連寫 t1 都不容許,天然也不能訪問其餘表。

    • InnoDB支持行鎖,因此通常不會用到表鎖。

  • MDL

    • MDL不須要顯式的使用,在對一個表操做的時候,會自動被加上。
    • 做用
      • 保證讀寫的正確性
      • 在 MySQL 5.5 版本中引入了 MDL,當對一個表作增刪改查操做的時候,加 MDL 讀鎖;當要對錶作結構變動操做的時候,加 MDL 寫鎖。
    • 注意
      • MDL 會直到事務提交纔會釋放,在作表結構變動的時候,必定要當心不要致使鎖住線上查詢和更新。

07 | 行鎖功過:怎麼減小行鎖對性能的影響?

InnoDB支持行鎖,MyISAM不支持行鎖,這個也是被InnoDB替代的緣由之一。

  • 做用
    • 行鎖就是針對數據表中行記錄的鎖。好比事務A更新了一行,而這個時候事務B也要更新同一行,則必須等事務A更新完成以後才能進行更新。

兩階段鎖

img

事務 B 的 update 語句會被阻塞,直到事務 A 執行 commit 以後,事務 B 才能繼續執行。

在 InnoDB 事務中,行鎖是在須要的時候才加上的,但並不是不須要了就馬上釋放, 而是要等到事務結束時才釋放。

  • 建議:

    若是你的事務中須要鎖多個行,要把最可能形成鎖衝突、最可能影響併發度的鎖儘可能日後放。

  • 死鎖:

    當併發系統中不同線程出現循環資源依賴,涉及的線程都在等待別的線程釋放資源時,就會致使這幾個線程都進入無限等待的狀態。

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

  • 如何解決熱點行更新致使的性能問題? 一、若是你能確保這個業務必定不會出現死鎖,能夠臨時把死鎖檢測關閉掉。通常不建議採用 二、控制併發度,對應相同行的更新,在進入引擎以前排隊。這樣在InnoDB內部就不會有大量的死鎖檢測工做了。 三、將熱更新的行數據拆分紅邏輯上的多行來減小鎖衝突,可是業務複雜度可能會大大提升。

innodb行級鎖是經過鎖索引記錄實現的,若是更新的列沒建索引是會鎖住整個表的。

08 | 事務究竟是隔離的仍是不隔離的?

一致性視圖(MVCC)

  • innodb支持RC和RR隔離級別實現是用的一致性視圖(consistent read view)

  • 事務在啓動時會拍一個快照,這個快照是基於整個庫的. 基於整個庫的意思就是說一個事務內,整個庫的修改對於該事務都是不可見的(對於快照讀的狀況) 若是在事務內select t表,另外的事務執行了DDL t表,根據發生時間,要嘛鎖住要嘛報錯(參考第六章)

  • 事務是如何實現的MVCC呢?

    img

    1. 每一個事務都有一個事務ID,叫作transaction id(嚴格遞增)
    2. 事務在啓動時,找到已提交的最大事務ID記爲up_limit_id。
    3. 事務在更新一條語句時,好比id=1改成了id=2.會把id=1和該行以前的row trx_id寫到undo log裏, 而且在數據頁上把id的值改成2,而且把修改這條語句的transaction id記在該行行頭
    4. 再定一個規矩,一個事務要查看一條數據時,必須先用該事務的up_limit_id與該行的transaction id作比對, 若是up_limit_id>=transaction id,那麼能夠看.若是up_limit_id<transaction id,則只能去undo log裏去取。去undo log查找數據的時候,也須要作比對,必須up_limit_id>transaction id,才返回數據

什麼是當前讀

因爲當前讀都是先讀後寫,只能讀當前的值,因此爲當前讀.會更新事務內的up_limit_id爲該事務的transaction id

  • 爲何rr能實現可重複讀而rc不能,分兩種狀況
    • 快照讀的狀況下,rr不能更新事務內的up_limit_id,而rc每次會把up_limit_id更新爲快照讀以前最新已提交事務的transaction id,則rc不能可重複讀
    • 當前讀的狀況下,rr是利用record lock+gap lock來實現的,而rc沒有gap,因此rc不能可重複讀

09 | 普通索引和惟一索引,應該怎麼選擇?

change buffer

  • 概念

    當須要更新一個數據頁,若是數據頁在內存中就直接更新,若是不在內存中,在不影響數據一致性的前提下,InnoDB會將這些更新操做緩存在change buffer中。下次查詢須要訪問這個數據頁的時候,將數據頁讀入內存,而後執行change buffer中的與這個頁有關的操做。

  • 優點

    將數據從磁盤讀入內存涉及隨機IO的訪問,是數據庫裏面成本最高的操做之一。 change buffer 由於減小了隨機磁盤訪問,因此對更新性能的提高很明顯。

  • merge

    將change buffer中的操做應用到原數據頁上,獲得最新結果的過程,稱爲merge 訪問這個數據頁會觸發merge,系統有後臺線程按期merge,在數據庫正常關閉的過程當中,也會執行merge

  • 持久化

    change buffer是能夠持久化的數據。在內存中有拷貝,也會被寫入到磁盤上,順序寫,沒有性能問題。

  • 大小配置

    change buffer用的是buffer pool裏的內存,change buffer的大小,能夠經過參數innodb_change_buffer_max_size來動態設置。這個參數設置爲50的時候,表示change buffer的大小最多隻能佔用buffer pool的50%。

  • 使用場景

    • 在一個數據頁作merge以前,change buffer記錄的變動越多,收益就越大。
    • 對於寫多讀少的業務來講,頁面在寫完之後立刻被訪問到的機率比較小,此時change buffer的使用效果最好。這種業務模型常見的就是帳單類、日誌類的系統。
    • 反過來,假設一個業務的更新模式是寫入以後立刻會作查詢,那麼即便知足了條件,將更新先記錄在change buffer,但以後因爲立刻要訪問這個數據頁,會當即觸發merge過程。 這樣隨機訪問IO的次數不會減小,反而增長了change buffer的維護代價。因此,對於這種業務模式來講,change buffer反而起到了反作用。

change buffer、redo log和WAL的比較

redo log是WAL機制的實現方式,同change buffer的目的相同吧,都是爲了減小隨機讀寫。

  • 例子:經過一個場景來描述二者的區別

    img

    • mysql> insert into t(id,k) values(id1,k1),(id2,k2);其中k是索引。

      當前的狀態是k1所在的數據頁在內存(InnoDB buffer pool)中;k2所在的數據頁不在內存中。

    • insert過程

      • page1在內存中,直接更新內存便可。
      • page2沒在內存中,就在內存的change buffer區域,記錄下「我要往page2中插入一行(id2,k2)」
      • 同時,將這兩個操做記錄到redo log中。(圖中的3和4)
      • 後臺線程還會存在sync數據到磁盤的操做,不在本次的操做過程當中。

      從這邊能夠看出,在change buffer的參與下,執行的insert語句成本很低。(固然update和delete也是同樣的)

    • select過程mysql> select * from t where k in (k1,k2)

      • 讀page1的時候,直接從內存返回。
      • 讀page2的時候,須要把page2從磁盤讀入內存中,而後應用change buffer裏面的操做日誌,生成一個正確的結果進行返回。

選擇普通索引仍是惟一索引?

  • 對於查詢過程來講:

    • 普通索引,查到知足條件的第一個記錄後,繼續查找下一個記錄,知道第一個不知足條件的記錄
    • 惟一索引,因爲索引惟一性,查到第一個知足條件的記錄後,中止檢索 可是,二者的性能差距微乎其微。由於InnoDB根據數據頁來讀寫的。
  • 對於更新過程來講:

    惟一索引的更新不能使用change buffer

索引的選擇和實踐

  • 儘量使用普通索引。

10 | MySQL爲何有時候會選錯索引?

mysql是怎麼選擇索引的?

選擇索引是優化器的工做,而優化器選擇索引的目的,是找到一個最優的執行方案,並用最小的代價去執行語句。

在數據庫裏,掃描行數是影響執行代價的因素之一,掃描行數越少,意味着訪問磁盤數據的次數越少,消耗的cpu資源越少。

mysql選錯索引的狀況下,確定就是判斷掃描行數的時候出現了問題。

mysql如何判斷一個查詢的掃描行數?

mysql在真正開始執行語句以前,並不能精確的知道知足這個條件的記錄有多少條,而只能根據統計信息來估算記錄數。

這個統計信息就是索引的「區分度」。顯然,一個索引上不一樣的值越多,這個索引的區分度就越好。而一個索引上不一樣的值的個數,咱們稱之爲「基數」(cardinality)。也就是說,這個基數越大,索引的區分度越好。

show index from t就能夠看到每一個索引的基數(cardinality)。

索引基數如何計算? 經過哪一個參數能夠設置索引統計的存儲方式?

mysql使用的是採樣統計的方法。

採樣統計的時候,InnoDB 默認會選擇 N 個數據頁,統計這些頁面上的不一樣值,獲得一個平均值,而後乘以這個索引的頁面數,就獲得了這個索引的基數。

能夠從新統計索引信息的命令是什麼?

analyze tabel t

如何定位索引選擇異常這樣的問題?

  1. 使用explain進行分析,是否存在索引選擇異常的狀況。

索引選擇異常的問題能夠有哪幾種處理方式?

  1. 肯定是索引選擇錯誤的問題,可使用analyze table t從新統計。
  2. 或者可使用force index強行選擇一個正確的索引。

11 | 怎麼給字符串字段加索引?

字符串通常佔用空間較大,**在對空間比較敏感的系統作操做的時候,而且不會存在範圍查詢的時候。**能夠考慮如下方案

  • 使用字符串中足夠有區分度的部分做爲索引。
  • hash索引
相關文章
相關標籤/搜索