關於前面講過的知識點我就再也不贅述了,還沒看過的朋友能夠進入個人首頁進行查閱(前言部分附贈飛機票)。這篇文章將是整個專題的總結,並且也是面試官會問到的最高頻率的一個問題——「你對 MySQL 的性能優化有什麼想法?」php
不少出去面試的朋友應該基本上都會被問到這個問題,可是可能可以回答得盡善盡美的比較少,看過我專題且可以消化成本身肚子裏的東西的朋友應該能夠吊打面試官了哈哈哈哈(針對中高級),但願今天這篇文章以後你們可以對本身腦海中零散知識點進行整合整理,我盼着大家能回來給我報喜(固然吐苦水也能夠),也盼着能跟你們一塊兒不斷進步。html
回到正題,關於此次的 MySQL 性能優化的知識點,我會分紅兩篇幅的文章來輸出,關於 SQL 語句的性能優化我會以單獨的篇幅來進行編寫,語句優化在實操中是屬於性能優化的最高級別的優化點,但願你們也能好好消化。mysql
這個 MySQL 專題是我從年前就一直在準備的,恰好過年在家也沒事就一直在思考着要怎麼去發表這部分的文章,讓你們可以看的時候思路比較清晰,記憶可以更加深入,最後我是經過先發布腦圖,而後再根據腦圖的方向進行專題知識點的發表,以後應該也會是這種形式,畢竟,這樣我寫文章思路清晰,你們看文章的時候思路也清晰嘛,複習知識點的時候也能夠根據腦圖來。git
博文是我從去年開始寫的,以前是本身在雲筆記上作筆記比較多。我以爲作筆記寫總結對自我提高有很大的幫助,而分享出來,也是但願你們可以從中學到新的知識,同時也能幫助我一塊兒不斷改進,給我提一些建議,讓我在給你們分享總結的東西的同時本身也能查缺補漏(再次感謝一直以來支持個人朋友們 Thanks♪(・ω・)ノ)程序員
關於下一個專題我還沒想好要寫什麼,你們若是有什麼想法的話能夠在公衆號給我留言。github
老規矩,先上飛機票:面試
前面提到的腦圖以下,想要完整高清圖片能夠到微信個人公衆號下【6曦軒】下回復 MySQL 腦圖獲取: sql
做爲架構師或者開發人員,說到數據庫性能優化,你的思路是什麼樣的?shell
或者具體一點,若是在面試的時候遇到這個問題:你會從哪些維度來優化數據庫,你會怎麼回答?數據庫
經過前面的篇章,相信你們已經慢慢創建了數據庫的知識體系,和正確的調優的思路。
咱們說到性能調優,大部分時候想要實現的目標是讓咱們的查詢更快。一個查詢的動做又是由不少個環節組成的,每一個環節都會消耗時間,第一篇章裏講 SQL 語句的執行流程的時候已經分析過了。
咱們要減小查詢所消耗的時間,就要從每個環節入手(先上你們比較熟悉的一張圖)。
第一個環節是客戶端鏈接到服務端,鏈接這一塊有可能會出現什麼樣的性能問題?
有多是服務端鏈接數不夠致使應用程序獲取不到鏈接。好比報了一個 Mysql: error 1040: Too many connections
的錯誤。
咱們能夠從兩個方面來解決鏈接數不夠的問題:
若是有多個應用或者不少請求同時訪問數據庫,鏈接數不夠的時候,咱們能夠: (1)修改配置參數增長可用鏈接數,修改 max_connections 的大小:
show variables like 'max_connections';
-- 修改最大鏈接數,當有多個應用鏈接的時候
(2)或者,或者及時釋放不活動的鏈接。交互式和非交互式的客戶端的默認超時時間都是 28800 秒,8 小時,咱們能夠把這個值調小。
show global variables like 'wait_timeout';
--及時釋放不活動的鏈接,注意不要釋放鏈接池還在使用的鏈接
這個時候咱們能夠引入鏈接池,實現鏈接的重用。
咱們能夠在哪些層面使用鏈接池?ORM 層面(MyBatis 自帶了一個鏈接池);或者使用專用的鏈接池工具(阿里的 Druid、Spring Boot 2.x 版本默認的鏈接池 Hikari、老牌的 DBCP 和 C3P0)。
當客戶端改爲從鏈接池獲取鏈接以後,鏈接池的大小應該怎麼設置呢?你們可能會有一個誤解,以爲鏈接池的最大鏈接數越大越好,這樣在高併發的狀況下客戶端能夠獲取的鏈接數更多,不須要排隊。
實際狀況並非這樣。鏈接池並非越大越好,只要維護必定數量大小的鏈接池,其餘的客戶端排隊等待獲取鏈接就能夠了。有的時候鏈接池越大,效率反而越低。
在 Hikari 的 github 文檔中,給出了一個 PostgreSQL 數據庫建議的設置鏈接池大小的公式: github.com/brettwooldr…
它的建議是機器核數乘以 2 加 1。也就是說,4 核的機器,鏈接池維護 9 個鏈接就夠了。這個公式從必定程度上來講對其餘數據庫也是適用的。這裏面還有一個減小鏈接池大小實現提高併發度和吞吐量的案例。
每個鏈接,服務端都須要建立一個線程去處理它。鏈接數越多,服務端建立的線程數就會越多。
CPU 是怎麼同時執行遠遠超過它的核數大小的任務的?時間片。上下文切換。
而 CPU 的核數是有限的,頻繁的上下文切換會形成比較大的性能開銷。
咱們這裏說到了從數據庫配置的層面去優化數據庫。無論是數據庫自己的配置,仍是安裝這個數據庫服務的操做系統的配置,對於配置進行優化,最終的目標都是爲了更好地發揮硬件自己的性能,包括 CPU、內存、磁盤、網絡。
在不一樣的硬件環境下,操做系統和 MySQL 的參數的配置是不一樣的,沒有標準的配置。前面的篇章中咱們也接觸了不少的 MySQL 和 InnoDB 的配置參數,包括各類開關和數值的配置,大多數參數都提供了一個默認值,好比默認的 buffer_pool_size,默認的頁大小,InnoDB 併發線程數等等。
這些默認配置能夠知足大部分狀況的需求,除非有特殊的需求,在清楚參數的含義的狀況下再去修改它。修改配置的工做通常由專業的 DBA 完成。
至於硬件自己的選擇,好比使用固態硬盤,搭建磁盤陣列,選擇特定的 CPU 型號這些,更不是咱們開發人員關注的重點,這個咱們就不作過多的介紹了。
若是想要了解一些特定的參數的含義,官網有一份系統的參數列表能夠參考:
咱們能夠引入緩存。
在應用系統的併發數很是大的狀況下,若是沒有緩存,會形成兩個問題:一方面是會給數據庫帶來很大的壓力。另外一方面,從應用的層面來講,操做數據的速度也會受到影響。
咱們能夠用第三方的緩存服務來解決這個問題,例如 Redis。
運行獨立的緩存服務,屬於架構層面的優化。爲了減小單臺數據庫服務器的讀寫壓力,在架構層面咱們還能夠作其餘哪些優化措施?
若是單臺數據庫服務知足不了訪問需求,那咱們能夠作數據庫的集羣方案。
集羣的話必然會面臨一個問題,就是不一樣的節點之間數據一致性的問題。若是同時讀寫多臺數據庫節點,怎麼讓全部的節點數據保持一致?
這個時候咱們須要用到複製技術(replication),被複制的節點稱爲 master,複製的節點稱爲 slave。slave 自己也能夠做爲其餘節點的數據來源,這個叫作級聯複製。
主從複製是怎麼實現的呢?更新語句會記錄 binlog,它是一種邏輯日誌。
有了這個 binlog,從服務器會獲取主服務器的 binlog 文件,而後解析裏面的 SQL 語句,在從服務器上面執行一遍,保持主從的數據一致。
這裏面涉及到三個線程,鏈接到 master 獲取 binlog,而且解析 binlog 寫入中繼日誌,這個線程叫作 I/O 線程。
Master 節點上有一個 log dump 線程,是用來發送 binlog 給 slave 的。
從庫的 SQL 線程,是用來讀取 relay log,把數據寫入到數據庫的。
作了主從複製的方案以後,咱們只把數據寫入 master 節點,而讀的請求能夠分擔到 slave 節點。咱們把這種方案叫作讀寫分離。
讀寫分離能夠必定程度低減輕數據庫服務器的訪問壓力,可是須要特別注意主從數據一致性的問題。若是咱們在 master 寫入了,立刻到 slave 查詢,而這個時候 slave 的數據尚未同步過來,怎麼辦?
因此,基於主從複製的原理,咱們須要弄明白,主從複製到底慢在哪裏?
在早期的 MySQL 中,slave 的 SQL 線程是單線程。master 能夠支持 SQL 語句的並行執行,配置了多少的最大鏈接數就是最多同時多少個 SQL 並行執行。
而 slave 的 SQL 卻只能單線程排隊執行,在主庫併發量很大的狀況下,同步數據確定會出現延遲。
爲何從庫上的 SQL Thread 不能並行執行呢?舉個例子,主庫執行了多條 SQL 語句,首先用戶發表了一條評論,而後修改了內容,最後把這條評論刪除了。這三條語句在從庫上的執行順序確定是不能顛倒的。
insert into user_comments (10000009,'nice');
update user_comments set content ='very good' where id =10000009;
delete from user_comments where id =10000009;
複製代碼
那麼怎麼解決這個問題呢?怎麼減小主從複製的延遲?
首先咱們須要知道,在主從複製的過程當中,MySQL 默認是異步複製的。也就是說,對於主節點來講,寫入 binlog,事務結束,就返回給客戶端了。對於 slave 來講,接收到 binlog,就完事兒了,master 不關心 slave 的數據有沒有寫入成功。
若是要減小延遲,是否是能夠等待所有從庫的事務執行完畢,才返回給客戶端呢?這樣的方式叫作全同步複製。從庫寫完數據,主庫才返回給客戶端。這種方式雖然能夠保證在讀以前,數據已經同步成功了,可是帶來的反作用你們應該能想到,事務執行的時間會變長,它會致使 master 節點性能降低。
有沒有更好的辦法呢?能夠既減小 slave 寫入的延遲,又不會明顯增長 master 返回給客戶端的時間?
介於異步複製和全同步複製之間,還有一種半同步複製的方式。
半同步複製是什麼樣的呢?
主庫在執行完客戶端提交的事務後不是馬上返回給客戶端,而是等待至少一個從庫接收到 binlog 並寫到 relay log 中才返回給客戶端。master 不會等待很長的時間,可是返回給客戶端的時候,數據就即將寫入成功了,由於它只剩最後一步了:就是讀取 relay log,寫入從庫。
若是咱們要在數據庫裏面用半同步複製,必須安裝一個插件,這個是谷歌的一位工程師貢獻的。這個插件在 mysql 的插件目錄下已經有提供:cd /usr/lib64/mysql/plugin/
主庫和從庫是不一樣的插件,安裝以後須要啓用:
--主庫執行
INSTALL PLUGIN rpl_semi_sync_master SONAME 'semisync_master.so';
set global rpl_semi_sync_master_enabled=1;
show variables like '%semi_sync%';
複製代碼
--從庫執行
INSTALL PLUGIN rpl_semi_sync_slave SONAME 'semisync_slave.so';
set global rpl_semi_sync_slave_enabled=1;
show global variables like '%semi%';
複製代碼
相對於異步複製,半同步複製提升了數據的安全性,同時它也形成了必定程度的延遲,它須要等待一個 slave 寫入中繼日誌,這裏多了一個網絡交互的過程。因此,半同步複製最好在低延時的網絡中使用。
這個是從主庫和從庫鏈接的角度,來保證 slave 數據的寫入。
另外一個思路,若是要減小主從同步的延遲,減小 SQL 執行形成的等待的時間,那有沒有辦法在從庫上,讓多個 SQL 語句能夠並行執行,而不是排隊執行呢?
怎麼實現並行複製呢?設想一下,若是 3 條語句是在三個數據庫執行,操做各自的數據庫,是否是確定不會產生併發的問題呢?執行的順序也沒有要求。固然是,因此若是是操做三個數據庫,這三個數據庫的從庫的 SQL 線程能夠併發執行。這是 MySQL 5.6 版本里面支持的多庫並行複製。
可是在大部分的狀況下,咱們都是單庫多表的狀況,在一個數據庫裏面怎麼實現並行複製呢?或者說,咱們知道,數據庫自己就是支持多個事務同時操做的;爲何這些事務在主庫上面能夠並行執行,卻不會出現問題呢?
由於他們自己就是互相不干擾的,好比這些事務是操做不一樣的表,或者操做不一樣的行,不存在資源的競爭和數據的干擾。那在主庫上並行執行的事務,在從庫上確定也是能夠並行執行,是否是?好比在 master 上有三個事務同時分別操做三張表,這三個事務是否是在 slave 上面也能夠並行執行呢?
因此,咱們能夠把那些在主庫上並行執行的事務,分爲一個組,而且給他們編號,這一個組的事務在從庫上面也能夠並行執行。這個編號,咱們把它叫作 GTID(Global Transaction Identifiers),這種主從複製的方式,咱們把它叫作基於 GTID 的複製。
若是咱們要使用 GTID 複製,咱們能夠經過修改配置參數打開它,默認是關閉的:
show global variables like 'gtid_mode';
複製代碼
不管是優化 master 和 slave 的鏈接方式,仍是讓從庫能夠並行執行 SQL,都是從數據庫的層面去解決主從複製延遲的問題。
除了數據庫自己的層面以外,在應用層面,咱們也有一些減小主從同步延遲的方法。
咱們在作了主從複製以後,若是單個 master 節點或者單張表存儲的數據過大的時候,好比一張表有上億的數據,單表的查詢性能仍是會降低,咱們要進一步對單臺數據庫節點的數據分型拆分,這個就是分庫分表。
垂直分庫,減小併發壓力。水平分表,解決存儲瓶頸。
垂直分庫的作法,把一個數據庫按照業務拆分紅不一樣的數據庫:
水平分庫分表的作法,把單張表的數據按照必定的規則分佈到多個數據庫。
經過主從或者分庫分表能夠減小單個數據庫節點的訪問壓力和存儲壓力,達到提高數據庫性能的目的,可是若是 master 節點掛了,怎麼辦?
因此,高可用(High Available)也是高性能的基礎。
傳統的 HAProxy + keepalived 的方案,基於主從複製。
基於 NDB 集羣存儲引擎的 MySQL Cluster。
一種多主同步複製的集羣方案。
MMM(Master-Master replication manager for MySQL),一種多主的高可用架構,是一個日本人開發的,像美團這樣的公司早期也有大量使用 MMM。
MHA(MySQL Master High Available)。 BM和 MHA 都是對外提供一個虛擬 IP,而且監控主節點和從節點,當主節點發生故障的時候,須要把一個從節點提高爲主節點,而且把從節點裏面比主節點缺乏的數據補上,把 VIP 指向新的主節點。
dev.mysql.com/doc/refman/… dev.mysql.com/doc/refman/…
MySQL 5.7.17 版本推出的 InnoDB Cluster ,也叫 MySQL Group Replicatioin (MGR),這個套件裏面包括了 mysql shell 和 mysql-route。
總結一下:高可用 HA 方案須要解決的問題都是當一個 master 節點宕機的時候,如何提高一個數據最新的 slave 成爲 master。若是同時運行多個 master,又必需要解決 master 之間數據複製,以及對於客戶端來講鏈接路由的問題。
不一樣的方案,實施難度不同,運維管理的成本也不同。
以上是架構層面的優化,能夠用緩存,主從,分庫分表。
第三個環節: 解析器,詞法和語法分析,主要保證語句的正確性,語句不出錯就沒問題。由 Sever 本身處理,跳過。
第四步:優化器 這一節內容我將經過新的篇章來說述,主要是 SQL 語句性能優化的方面。
除了對於代碼、SQL 語句、表定義、架構、配置優化以外,業務層面的優化也不能忽視。舉幾個例子:
1)在某一年的雙十一,爲何會作一個充值到餘額寶和餘額有獎金的活動(充 300 送 50)?
由於使用餘額或者餘額寶付款是記錄本地或者內部數據庫,而使用銀行卡付款,須要調用接口,操做內部數據庫確定更快。
2)在去年的雙十一,爲何在凌晨禁止查詢今天以外的帳單?
這是一種降級措施,用來保證當前最核心的業務。
3)最近幾年的雙十一,爲何提早一個多星期就已經有雙十一當天的價格了?預售分流。
在應用層面一樣有不少其餘的方案來優化,達到儘可能減輕數據庫的壓力的目的,好比限流,或者引入 MQ 削峯,等等等等。
爲何一樣用 MySQL,有的公司能夠扛住百萬千萬級別的併發,而有的公司幾百個併發都扛不住,關鍵在於怎麼用。因此,用數據庫慢,不表明數據庫自己慢,有的時候還要往上層去優化。
MySQL 專題到這個篇章就正式結束了,基本上是按照腦圖的方向來,因此你們能夠對着腦圖以及個人博文來進行 MySQL 的複習,基本上面試的問題都有涉及,編寫一個專題確實是費腦又費精力費時間的事情,我仍是須要你們的關注和點贊來支撐一下哈哈哈~
若是你們以爲寫得還有點東西的話幫忙關注一下個人公衆號,而且在後臺給我留言但願我寫哪一個專題的東西(現學現賣的若是有什麼不對的地方也請幫忙指正,萬分感謝),人多的話立刻安排上~
仍是那樣,修整一段時間後會先在公衆號推送腦圖,而後根據腦圖來擬專題的提綱,這樣我寫得不會雲裏霧裏,你們也會比較有方向地看個人博文,再次感謝你們的支持~
有問題?能夠給我留言或私聊 有收穫?那就順手點個讚唄~
固然,也能夠到個人公衆號下「6曦軒」,
回覆「學習」,便可領取一份 【Java工程師進階架構師的視頻教程】~
回覆「面試」,能夠得到: 【本人嘔心瀝血整理的 Java 面試題】
回覆「MySQL腦圖」,能夠得到 【MySQL 知識點梳理高清腦圖】
因爲我咧,科班出身的程序員,php,Android以及硬件方面都作過,不過最後仍是選擇專一於作 Java,因此有啥問題能夠到公衆號提問討論(技術情感傾訴均可以哈哈哈),看到的話會盡快回復,但願能夠跟你們共同窗習進步,關於服務端架構,Java 核心知識解析,職業生涯,面試總結等文章會不按期堅持推送輸出,歡迎你們關注~~~