轉 Mysql性能優化教程

Mysql性能優化教程前端

背景及目標

  • 廈門遊家公司(4399.com)用於員工培訓和分享。node

  • 針對用戶羣爲已經使用過mysql環境,並有必定開發經驗的工程師mysql

  • 針對高併發,海量數據的互聯網環境。ios

  • 本文語言爲口語,非學術標準用語。git

  • 以實戰和解決具體問題爲主要目標,非應試,很是規教育。友情提醒,在校生學習本教程可能對成績提升有害無益。web

  • 非技術挑戰,非高端架構師培訓,請高手自動忽略。算法

  • 本文檔在20117-12月持續更新,增強了影響結果集分析的內容並增補優化實戰案例若干。sql

Mysql執行優化

認識數據索引

爲何使用數據索引能提升效率

    • 關係型數據庫的數據索引(Btree及常見索引結構)的存儲是有序的。數據庫

    • 在有序的狀況下,經過索引查詢一個數據是無需遍歷索引記錄的apache

    • 關係型數據庫數據索引的查詢效率趨近於二分法查詢效率,趨近於log2(N)

    • 極端狀況下(更新請求少,更新實時要求低,查詢請求頻繁),創建單向有序序列可替代數據索引。

    • HASH索引的查詢效率是尋址操做,趨近於1次查詢,比有序索引查詢效率更高,可是不支持比對查詢,區間查詢,排序等操做,僅支持key-value類型查詢。不是本文重點。

如何理解數據索引的結構

    • 數據索引一般默認採用btree索引,(內存表也使用了hash索引)。

    • 僅就有序前提而言,單向有序排序序列是查找效率最高的(二分查找,或者說折半查找),使用樹形索引的目的是爲了達到快速的更新和增刪操做。

    • 在極端狀況下(好比數據查詢需求量很是大,而數據更新需求極少,實時性要求不高,數據規模有限),直接使用單一排序序列,折半查找速度最快。

    • 在進行索引分析和SQL優化時,能夠將數據索引字段想象爲單一有序序列,並以此做爲分析的基礎。涉及到複合索引狀況,複合索引按照索引順序拼湊成一個字段,想象爲單一有序序列,並以此做爲分析的基礎。

    • 一條數據查詢只能使用一個索引,索引能夠是多個字段合併的複合索引。可是一條數據查詢不能使用多個索引。

優化實戰範例

  • 實戰範例1ip地址反查

    • 資源: Ip地址對應表,源數據格式爲 startip, endip, area

源數據條數爲 10萬條左右,呈很大的分散性

    • 目標: 須要經過任意ip查詢該ip所屬地區

性能要求達到每秒1000次以上的查詢效率

    • 挑戰: 如使用between startip and endip這樣的條件數據庫操做,由於涉及兩個字段的betweenand, 沒法有效使用索引。

若是每次查詢請求須要遍歷10萬條記錄,根本不行。

    • 方法: 一次性排序(只在數據準備中進行,數據可存儲在內存序列)

折半查找(每次請求以折半查找方式進行)

  • 實戰範例2:目標:查找與訪問者同一地區的異性,按照最後登陸時間逆序

    • 挑戰:高訪問量社區的高頻查詢,如何優化。

查詢SQL:select * from user where area=’$area’ and sex=’$sex’ order bylastlogin desc limit 0,30;

創建複合索引並不難,area+sex+lastlogin 三個字段的複合索引,如何理解?

    • 解讀:首先,忘掉btree,將索引字段理解爲一個排序序列。

另外,牢記數據查詢只能使用一個索引,每一個字段創建獨立索引的狀況下,也只能有一條索引被使用!

若是隻使用area會怎樣?搜索會把符合area的結果所有找出來,而後在這裏面遍歷,選擇命中sex的並排序。遍歷全部 area=’$area’數據!

若是使用了area+sex,略好,仍然要遍歷全部area=’$area’and sex=’$sex’數據,而後在這個基礎上排序!!

Area+sex+lastlogin複合索引時(切記lastlogin在最後),該索引基於area+sex+lastlogin三個字段合併的結果排序,該列表能夠想象以下。

廣州女$時間1

廣州女$時間2

廣州女$時間3

廣州男

.

深圳女

.

數據庫很容易命中到area+sex的邊界,而且基於下邊界向上追溯30條記錄,搞定!在索引中迅速命中全部結果,無需二次遍歷!

認識影響結果集

影響結果集的獲取

    • 經過Explain分析SQL,查看rows 列內容

    • 經過慢查詢日誌的Rows_examined:後面的數字

    • 影響結果集數字是查詢優化的重要中間數字,工程師在開發和調試過程當中,應隨時關注這一數字。

影響結果集的解讀

    • 查詢條件與索引的關係決定影響結果集。

      • 影響結果集不是輸出結果數,不是查詢返回的記錄數,而是索引所掃描的結果數。

      • 範例 select* from user where area=’廈門’ andsex=’女’

        • 假設 索引爲 area

        • 假設User表中area=’廈門’的有125000條,而搜索返回結果爲60233條。

        • 影響結果集是125000條,索引先命中125000條廈門用戶,再遍歷以sex=’女’進行篩選操做,獲得60233條結果。

        • 若是該SQL增長 limit0,30的後綴。查詢時,先命中 area=’廈門’,而後依順序執行sex=’女’篩選操做,直到知足能夠返回30條爲止,所涉及記錄數未知。除非知足條件的結果不足30條,不然不會遍歷125000條記錄。

        • 可是若是SQL中涉及了排序操做,好比order by lastlogin desc 再有limit0,30時,排序須要遍歷全部area=’廈門’的記錄,而不是知足即止。

    • 影響結果集越趨近於實際輸出或操做的目標結果集,索引效率越高。

    • 影響結果集與查詢開銷的關係能夠理解爲線性相關。減小一半影響結果集,便可提高一倍查詢效率!當一條搜索query能夠符合多個索引時,選擇影響結果集最少的索引。

    • SQL的優化,核心就是對結果集的優化,認識索引是加強對結果集的判斷,基於索引的認識,能夠在編寫SQL的時候,對該SQL可能的影響結果集有預判,並作出適當的優化和調整。

    • Limit的影響,須要斟酌對待

      • 若是索引與查詢條件和排序條件徹底命中,影響結果集就是limit後面的數字($start+ $end),好比 limit200,30 影響結果集是230.而不是30.

      • 若是索引只命中部分查詢條件,甚至無命中條件,在無排序條件狀況下,會在索引命中的結果集中遍歷到知足全部其餘條件爲止。好比 select* from user limit 10;雖然沒用到索引,可是由於不涉及二次篩選和排序,系統直接返回前10條結果,影響結果集依然只有10條,就不存在效率影響。

      • 若是搜索所包含的排序條件沒有被索引命中,則系統會遍歷是全部索引所命中的結果,而且排序。例如Select * from user order bytimeline desc limit 10;若是timeline不是索引,影響結果集是全表,就存在須要全表數據排序,這個效率影響就巨大。再好比Select * from user wherearea=’廈門’ order bytimeline desc limit 10; 若是area是索引,而area+timeline未創建索引,則影響結果集是全部命中area=’廈門’的用戶,而後在影響結果集內排序。

 

常見案例及優化思路

    • 毫秒級優化案例

      • 某遊戲用戶進入後顯示最新動態,SQLselect * from userfeed whereuid=$uid order by timeline desc limit 20; 主鍵爲$uid。 該SQL天天執行數百萬次之多,高峯時數據庫負載較高。經過 show processlist顯示大量進程處於Sendingdata狀態。沒有慢查詢記錄。 仔細分析發現,因存在較多高頻用戶訪問,命中uid=$uid的影響結果集一般在幾百到幾千,在上千條影響結果集狀況下,該SQL查詢開銷一般在0.01秒左右。創建uid+timeline複合索引,將排序引入到索引結構中,影響結果集就只有limit後面的數字,該SQL查詢開銷銳減至0.001秒,數據庫負載驟降。

    • Innodb鎖表案例

      • 某遊戲數據庫使用了innodbinnodb是行級鎖,理論上不多存在鎖表狀況。出現了一個SQL語句(deletefrom tabname wherexid=…),這個SQL很是用SQL,僅在特定狀況下出現,天天出現頻繁度不高(一天僅10次左右),數據表容量百萬級,可是這個xid未創建索引,因而悲慘的事情發生了,當執行這條delete的時候,真正刪除的記錄很是少,也許一到兩條,也許一條都沒有;可是!因爲這個xid未創建索引,delete操做時遍歷全表記錄,全表被delete操做鎖定,select操做所有被locked,因爲百萬條記錄遍歷時間較長,期間大量select被阻塞,數據庫鏈接過多崩潰。

這種非高發請求,操做目標不多的SQL,因未使用索引,連帶致使整個數據庫的查詢阻塞,須要極大提升警覺。

    • 實時排名策略優化

      • 背景: 用戶提交遊戲積分,顯示實時排名。

      • 原方案:

        • 提交積分是插入記錄,略,

        • selectcount(*) from jifen where gameid=$gameid and fenshu>$fenshu

      • 問題與挑戰

        • 即使索引是 gameid+fenshu複合索引,涉及count操做,當分數較低時,影響結果集巨大,查詢效率緩慢,高峯期會致使鏈接過多。

      • 優化思路

        • 減小影響結果集,又要取得實時數據,單純從SQL上考慮,不太有方法。

        • 將遊戲積分預約義分紅數個積分斷點,而後分紅積分區間,原始狀態,每一個區間設置一個統計數字項,初始爲0

        • 每次積分提交時,先肯定該分數屬於哪兩個區間之間,這個操做很是簡單,由於區間是預約義的,並且數量不多,只需遍歷便可,找到最該分數符合的區間,該區間的統計數字項(獨立字段,可用內存處理,異步回寫數據庫或文件)+1。記錄該區間上邊界數字爲$duandian

        • SQL: select count(*) from jifen where gameid=$gameid andfenshu>$fenshu andfenshu<$duandian,若是處於第一區間,則無需$duandian,這樣由於第一區間自己也是最好的成績,影響結果集不會不少。經過該SQL得到其在該區間的名次。

        • 獲取前面區間的總數總和。(該數字是直接從上述提到的區間統計數字獲取,不須要進行count操做)將區間內名次+前區間的統計數字和,得到總名次。

        • 該方法關鍵在於,積分區間須要合理定義,保證積分提交成績能平均散落在不一樣區間。

        • 如涉及較多其餘條件,如日排行,總排行,以及其餘獨立用戶去重等,請按照影響結果集思路自行發揮。

      • Redis方案

        • Redis數據結構包括String,list,dictZset四種,在本案例中是很是好的替代數據庫的方案,本文檔只作簡介,不作額外擴展。

        • String哈希索引,key-value結構,主鍵查詢效率極高,不支持排序,比較查詢。

        • List隊列結構,在數據異步寫入處理中能夠替代memcache

        • Dict數組結構,存儲結構化,序列化內容,能夠針對數組中的特定列進行操做。

        • Zset有序數組結構,分兩個子結構,第一是多層樹形的存儲結構,第二是每一個樹形節點的計數器,這樣相似於前面的分段方式,能夠理解爲多層分段方式,因此查詢效率更高,缺點是更新效率有所增長。

    • 論壇翻頁優化

      • 背景,常見論壇帖子頁SQL: select * from post wheretagid=$tagid order by lastpost limit $start, $end 翻頁。索引爲 tagid+lastpost 複合索引

      • 挑戰,超級熱帖,幾萬回帖,用戶頻頻翻到末頁,limit25770,30 一個操做下來,影響結果集巨大(25770+30),查詢緩慢。

      • 解決方法:

        • 只涉及上下翻頁狀況

          • 每次查詢的時候將該頁查詢結果中最大的$lastpost和最小的分別記錄爲$minlastpost $maxlastpost ,上翻頁查詢爲select * from post wheretagid=$tagid and lastpost<$minlastpost order by lastpost desclimit 30; 下翻頁爲 select* from post where tagid=$tagid and lastpost>$maxlastpostorder by lastpost limit 30; 使用這種方式,影響結果集只有30條,效率極大提高。

        • 涉及跳轉到任意頁

          • 互聯網上常見的一個優化方案能夠這樣表述,select* from post where tagid=$tagid and lastpost>=(select lastpostfrom post where tagid=$tagid order by lastpost limit $start,1)order by lastpost limit 30; 或者 select* from post where pid in (select pid from post wheretagid=$tagid order by lastpost limit $start,30);(2S語法在新的mysql版本已經不支持,新版本mysqlin的子語句再也不支持limit條件,但能夠分解爲兩條SQL實現,原理不變,不作贅述)

          • 以上思路在於,子查詢的影響結果集仍然是$start+30,可是數據獲取的過程(Sendingdata狀態)發生在索引文件中,而不是數據表文件,這樣所須要的系統開銷就比前一種普通的查詢低一個數量級,而主查詢的影響結果集只有30條,幾乎無開銷。可是切記,這裏仍然涉及了太多的影響結果集操做。

      • 延伸問題:

        • 來自於uchome典型查詢SELECT * FROM uchome_threadWHERE tagid='73820' ORDER BY displayorder DESC, lastpost DESCLIMIT $start,30;

        • 若是換用 如上方法,上翻頁代碼SELECT * FROM uchome_threadWHERE tagid='73820' and lastpost<$minlastpost ORDER BYdisplayorder DESC,lastpost DESC LIMIT 0,30; 下翻頁代碼SELECT* FROM uchome_thread WHERE tagid='73820' andlastpost>$maxlastpost ORDER BY displayorder DESC, lastpost ASCLIMIT 0,30;

        • 這裏涉及一個orderby 索引可用性問題,當orderby中 複合索引的字段,一個是ASC,一個是DESC時,其排序沒法在索引中完成。因此只有上翻頁能夠正確使用索引,影響結果集爲30。下翻頁沒法在排序中正確使用索引,會命中全部索引內容而後排序,效率低下。

  • 總結:

    • 基於影響結果集的理解去優化,不論從數據結構,代碼,仍是涉及產品策略上,都須要貫徹下去。

    • 涉及 limit$start,$num的搜索,若是$start巨大,則影響結果集巨大,搜索效率會很是難太低,儘可能用其餘方式改寫爲limit 0,$num;確係沒法改寫的狀況下,先從索引結構中得到limit$start,$num limit$start,1 ;再用in操做或基於索引序的limit 0,$num 二次搜索。

    • 請注意,我這裏永遠不會講關於外鍵和join的優化,由於在咱們的體系裏,這是根本不容許的!架構優化部分會解釋爲何。

理解執行狀態

常見關注重點

  • 慢查詢日誌,關注重點以下

    • 是否鎖定,及鎖定時間

      • 如存在鎖定,則該慢查詢一般是因鎖定因素致使,自己無需優化,需解決鎖定問題。

    • 影響結果集

      • 如影響結果集較大,顯然是索引項命中存在問題,須要認真對待。

  • Explain操做

    • 索引項使用

      • 不建議用usingindex作強制索引,如未如預期使用索引,建議從新斟酌表結構和索引設置。

    • 影響結果集

      • 這裏顯示的數字不必定準確,結合以前提到對數據索引的理解來看,還記得嘛?就把索引看成有序序列來理解,反思SQL

  • Setprofiling , show profiles for query操做

    • 執行開銷

      • 注意,有問題的SQL若是重複執行,可能在緩存裏,這時要注意避免緩存影響。經過這裏能夠看到。

      • 執行時間超過0.005秒的頻繁操做SQL建議都分析一下。

      • 深刻理解數據庫執行的過程和開銷的分佈

  • Showprocesslist 執行狀態監控

    • 這是在數據庫負載波動時常常進行的一項操做

    • 具體參見以下

執行狀態分析

  • Sleep狀態

    • 一般表明資源未釋放,若是是經過鏈接池,sleep狀態應該恆定在必定數量範圍內

    • 實戰範例:因前端數據輸出時(特別是輸出到用戶終端)未及時關閉數據庫鏈接,致使因網絡鏈接速度產生大量sleep鏈接,在網速出現異常時,數據庫too many connections 掛死。

    • 簡單解讀,數據查詢和執行一般只須要不到0.01秒,而網絡輸出一般須要1秒左右甚至更長,本來數據鏈接在0.01秒便可釋放,可是由於前端程序未執行close操做,直接輸出結果,那麼在結果未展示在用戶桌面前,該數據庫鏈接一直維持在sleep狀態!

  • Waitingfor net, reading from net, writing to net

    • 偶爾出現無妨

    • 如大量出現,迅速檢查數據庫到前端的網絡鏈接狀態和流量

    • 案例:因外掛程序,內網數據庫大量讀取,內網使用的百兆交換迅速爆滿,致使大量鏈接阻塞在waitingfor net,數據庫鏈接過多崩潰

  • Locked狀態

    • 有更新操做鎖定

    • 一般使用innodb能夠很好的減小locked狀態的產生,可是切記,更新操做要正確使用索引,即使是低頻次更新操做也不能疏忽。如上影響結果集範例所示。

    • myisam的時代,locked是不少高併發應用的噩夢。因此mysql官方也開始傾向於推薦innodb

  • Copyto tmp table

    • 索引及現有結構沒法涵蓋查詢條件,纔會創建一個臨時表來知足查詢要求,產生巨大的恐怖的i/o壓力。

    • 很可怕的搜索語句會致使這樣的狀況,若是是數據分析,或者半夜的週期數據清理任務,偶爾出現,能夠容許。頻繁出現務必優化之。

    • Copyto tmp table 一般與連表查詢有關,建議逐漸習慣不使用連表查詢。

    • 實戰範例:

      • 某社區數據庫阻塞,求救,經查,其服務器存在多個數據庫應用和網站,其中一個不經常使用的小網站數據庫產生了一個恐怖的copyto tmp table 操做,致使整個硬盤i/ocpu壓力超載。Kill掉該操做一切恢復。

  • Sendingdata

    • Sendingdata並非發送數據,別被這個名字所欺騙,這是從物理磁盤獲取數據的進程,若是你的影響結果集較多,那麼就須要從不一樣的磁盤碎片去抽取數據,

    • 偶爾出現該狀態鏈接無礙。

    • 回到上面影響結果集的問題,通常而言,若是sendingdata鏈接過多,一般是某查詢的影響結果集過大,也就是查詢的索引項不夠優化。

    • 前文提到影響結果集對SQL查詢效率線性相關,主要就是針對這個狀態的系統開銷。

    • 若是出現大量類似的SQL語句出如今showproesslist列表中,而且都處於sendingdata狀態,優化查詢索引,記住用影響結果集的思路去思考。

  • Storingresult to query cache

    • 出現這種狀態,若是頻繁出現,使用setprofiling分析,若是存在資源開銷在SQL總體開銷的比例過大(即使是很是小的開銷,看比例),則說明querycache碎片較多

    • 使用flushquery cache 可即時清理,也能夠作成定時任務

    • Querycache參數可適當酌情設置。

  • Freeingitems

    • 理論上這玩意不會出現不少。偶爾出現無礙

    • 若是大量出現,內存,硬盤可能已經出現問題。好比硬盤滿或損壞。

    • i/o壓力過大時,也可能出現Freeitems執行時間較長的狀況。

  • Sortingfor …

    • Sendingdata相似,結果集過大,排序條件沒有索引化,須要在內存裏排序,甚至須要建立臨時結構排序。

  • 其餘

    • 還有不少狀態,遇到了,去查查資料。基本上咱們遇到其餘狀態的阻塞較少,因此不關心。

分析流程

  • 基本流程

    • 詳細瞭解問題情況

      • Toomany connections 是常見表象,有不少種緣由。

      • 索引損壞的狀況在innodb狀況下不多出現。

      • 如出現其餘狀況應追溯日誌和錯誤信息。

    • 瞭解基本負載情況和運營情況

      • 基本運營情況

        • 當前每秒讀請求

        • 當前每秒寫請求

        • 當前在線用戶

        • 當前數據容量

      • 基本負載狀況

        • 學會使用這些指令

          • Top

          • Vmstat

          • uptime

          • iostat

          • df

        • Cpu負載構成

          • 特別關注i/o壓力(wa%)

          • 多核負載分配

        • 內存佔用

          • Swap分區是否被侵佔

          • Swap分區被侵佔,物理內存是否較多空閒

        • 磁盤狀態

          • 硬盤滿和inode節點滿的狀況要迅速定位和迅速處理

    • 瞭解具體鏈接情況

      • 當前鏈接數

        • Netstat–an|grep 3306|wc –l

        • Showprocesslist

      • 當前鏈接分佈 showprocesslist

        • 前端應用請求數據庫不要使用root賬號!

          • Root賬號比其餘普通賬號多一個鏈接數許可。

          • 前端使用普通賬號,在toomany connections的時候root賬號仍能夠登陸數據庫查詢show processlist!

          • 記住,前端應用程序不要設置一個不叫rootroot賬號來糊弄!非root帳戶是骨子裏的,而不是名義上的。

        • 狀態分佈

          • 不一樣狀態表明不一樣的問題,有不一樣的優化目標。

          • 參見如上範例。

        • 雷同SQL的分佈

          • 是否較多雷同SQL出如今同一狀態

      • 當前是否有較多慢查詢日誌

        • 是否鎖定

        • 影響結果集

    • 頻繁度分析

      • 寫頻繁度

        • 若是i/o壓力高,優先分析寫入頻繁度

        • Mysqlbinlog輸出最新binlog文件,編寫腳本拆分

        • 最多寫入的數據表是哪一個

        • 最多寫入的數據SQL是什麼

        • 是否存在基於同一主鍵的數據內容高頻重複寫入?

          • 涉及架構優化部分,參見架構優化-緩存異步更新

      • 讀取頻繁度

        • 若是cpu資源較高,而i/o壓力不高,優先分析讀取頻繁度

        • 程序中在封裝的db類增長抽樣日誌便可,抽樣比例酌情考慮,以不顯著影響系統負載壓力爲底線。

        • 最多讀取的數據表是哪一個

        • 最多讀取的數據SQL是什麼

          • SQL進行explainset profiling斷定

          • 注意斷定時須要避免querycache影響

            • 好比,在這個SQL末尾增長一個條件子句and 1=1 就能夠避免從querycache中獲取數據,而獲得真實的執行狀態分析。

        • 是否存在同一個查詢短時間內頻繁出現的狀況

          • 涉及前端緩存優化

    • 抓大放小,解決顯著問題

      • 不苛求解決全部優化問題,可是應以保證線上服務穩定可靠爲目標。

      • 解決與評估要同時進行,新的策略或解決方案務必通過評估後上線。

常見案例解析

  • 現象:服務器出現toomany connections 阻塞

    • 入手點:

      • 查看服務器狀態,cpu佔用,內存佔用,硬盤佔用,硬盤i/o壓力

      • 查看網絡流量狀態,mysql與應用服務器的輸入輸出情況

      • 經過Showprocesslist查看當前運行清單

        • 注意事項,平常應用程序鏈接數據庫不要使用root帳戶,保證故障時能夠經過root進入數據庫查看 showprocesslist

    • 狀態分析:

      • 參見如上執行狀態清單,根據鏈接狀態的分佈去肯定緣由。

    • 緊急恢復

      • 在肯定故障緣由後,應經過kill掉阻塞進程的方式當即恢復數據庫。

    • 善後處理

      • 如下針對常見問題簡單解讀

      • Sleep鏈接過多致使,應用端及時釋放鏈接,排查關聯因素。

      • Locked鏈接過多,如源於myisam表級鎖,更innodb引擎;如源於更新操做使用了不恰當的索引或未使用索引,改寫更新操做SQL或創建恰當索引。

      • Sendingdata鏈接過多,用影響結果集的思路優化SQL查詢,優化表索引結構。

      • Freeitems鏈接過多,i/o壓力過大或硬盤故障

      • Waitingfor net , writing to net 鏈接過多, mysql與應用服務器鏈接阻塞。

      • 其餘仍參見如上執行狀態清單所示分析。

      • 如涉及不十分嚴格安全要求的數據內容,可用按期腳本跟蹤請求進程,並kill掉僵死進程。如數據安全要求較嚴格,則不能如此進行。

  • 現象:數據庫負載太高,響應緩慢。

    • 入手點:

      • 查看cpu狀態,服務器負載構成

    • 分支1i/o佔用太高。

      • 步驟1:檢查內存是否佔用swap分區,排除因內存不足致使的i/o開銷。

      • 步驟2:經過iostat指令分析i/o是否集中於數據庫硬盤,是不是寫入度較高。

      • 步驟3:若是壓力來自於寫,使用mysqlbinlog解開最新的binlog文件。

      • 步驟4:編寫日誌分析腳本或grep指令,分析每秒寫入頻度和寫入內容。

        • 寫入頻度不高,則說明i/o壓力另有緣由或數據庫配置不合理。

      • 步驟5:編寫日誌分析腳本或grep指令,分析寫入的數據表構成,和寫入的目標構成。

      • 步驟6:編寫日誌分析腳本,分析是否存在同一主鍵的重複寫入。好比出現大量 update postset views=views+1 wheretagid=****的操做,假設在一段時間內出現了2萬次,而其中不一樣的tagid1萬次,那麼就是有50%的請求是重複update請求,有能夠經過異步更新合併的空間。

      • 提示一下,以上所說起的日誌分析腳本編寫,正常狀況下不該超過1個小時,而對系統負載分析所提供的數據支持價值是巨大的,對性能優化方案的選擇是很是有意義的,若是您認爲這項工做是繁冗並且複雜的工做,那麼必定是在分析思路和目標把握上出現了誤差。

    • 分支2i/o佔用不高,CPU佔用太高

      • 步驟1:查看慢查詢日誌

      • 步驟2:不斷刷新查看Showprocesslist清單,並把握可能頻繁出現的處於Sendingdata狀態的SQL

      • 步驟3:記錄前端執行SQL

        • 於前端應用程序執行查詢的封裝對象內,設置隨機採樣,記錄前端執行的SQL,保證有必定的樣本規模,而且不會帶來前端i/o負載的激增。

        • 基於採樣率和記錄頻率,得到每秒讀請求次數數據指標。

        • 編寫日誌分析腳本,分析採樣的SQL構成,所操做的數據表,所操做的主鍵。

        • 對頻繁重複讀取的SQL(徹底一致的SQL)進行斷定,是否數據存在頻繁變更,是否須要實時展示最新數據,若有可能,緩存化,並預估緩存命中率。

        • 對頻繁讀取但不重複的(SQL結構一致,但條件中的數據不一致)SQL進行斷定,是否索引足夠優化,影響結果集與輸出結果是否足夠接近。

      • 步驟4:將致使慢查詢的SQL或頻繁出現於showprocesslist狀態的SQL,或採樣記錄的頻繁度SQL進行分析,按照影響結果集的思路和索引理解來優化。

      • 步驟5:對如上難以界定問題的SQL進行set profiling 分析。

      • 步驟6:優化後分析繼續採樣跟蹤分析。並跟蹤比對結果。

    • 善後處理

      • 平常跟蹤腳本,不斷記錄一些狀態信息。保證每一個時間節點都能回溯。

      • 確保隨時能瞭解服務器的請求頻次,讀寫請求的分佈。

      • 記錄一些未形成致命影響的隱患點,可暫不解決,但須要記錄。

      • 如確係服務器請求頻次太高,可基於負載分佈決定硬件擴容方案,好比i/o壓力太高可考慮固態硬盤;內存佔用swap可考慮增長內容容量等。用盡量少的投入實現最好的負載支撐能力,而不是簡單的買更多服務器。

總結

  • 要學會怎樣分析問題,而不是單純拍腦殼優化

  • 慢查詢只是最基礎的東西,要學會優化0.01秒的查詢請求。

  • 當發生鏈接阻塞時,不一樣狀態的阻塞有不一樣的緣由,要找到緣由,若是不對症下藥,就會南轅北轍

    • 範例:若是自己系統內存已經超載,已經使用到了swap,而還在考慮加大緩存來優化查詢,那就是自尋死路了。

  • 影響結果集是很是重要的中間數據和優化指標,學會理解這一律念,理論上影響結果集與查詢效率呈現很是緊密的線性相關。

  • 監測與跟蹤要常常作,而不是出問題才作

    • 讀取頻繁度抽樣監測

      • 全監測不要搞,i/o嚇死人。

      • 按照一個抽樣比例抽樣便可。

      • 針對抽樣中發現的問題,能夠按照特定SQL在特定時間內監測一段全查詢記錄,但仍要考慮i/o影響。

    • 寫入頻繁度監測

      • 基於binlog解開便可,可定時或不定時分析。

    • 微慢查詢抽樣監測

      • 高併發狀況下,查詢請求時間超過0.01秒甚至0.005秒的,建議酌情抽樣記錄。

    • 鏈接數預警監測

      • 鏈接數超過特定閾值的狀況下,雖然數據庫沒有崩潰,建議記錄相關鏈接狀態。

  • 學會經過數據和監控發現問題,分析問題,然後解決問題瓜熟蒂落。特別是要學會在平常監控中發現隱患,而不是問題爆發了纔去處理和解決。

Mysql 運維優化

存儲引擎類型

  • Myisam速度快,響應快。表級鎖是致命問題。

  • Innodb目前主流存儲引擎

    • 行級鎖

      • 務必注意影響結果集的定義是什麼

      • 行級鎖會帶來更新的額外開銷,可是一般狀況下是值得的。

    • 事務提交

      • i/o效率提高的考慮

      • 對安全性的考慮

  • HEAP內存引擎

    • 頻繁更新和海量讀取狀況下仍會存在鎖定情況

內存使用考量

  • 理論上,內存越大,越多數據讀取發生在內存,效率越高

  • Querycache的使用

    • 若是前端請求重複度不高,或者應用層已經充分緩存重複請求,querycache沒必要設置很大,甚至能夠不設置。

    • 若是前端請求重複度較高,無應用層緩存,querycache是一個很好的偷懶選擇

      • 對於中等如下規模數據庫應用,偷懶不是一個壞選擇。

      • 若是確認使用querycache,記得定時清理碎片,flushquery cache.

  • 要考慮到現實的硬件資源和瓶頸分佈

  • 學會理解熱點數據,並將熱點數據儘量內存化

    • 所謂熱點數據,就是最多被訪問的數據。

    • 一般數據庫訪問是不平均的,少數數據被頻繁讀寫,而更多數據鮮有讀寫。

    • 學會制定不一樣的熱點數據規則,並測算指標。

      • 熱點數據規模,理論上,熱點數據越少越好,這樣能夠更好的知足業務的增加趨勢。

      • 響應知足度,對響應的知足率越高越好。

      • 好比依據最後更新時間,總訪問量,回訪次數等指標定義熱點數據,並測算不一樣定義模式下的熱點數據規模

性能與安全性考量

  • 數據提交方式

    • innodb_flush_log_at_trx_commit= 1 每次自動提交,安全性高,i/o壓力大

    • innodb_flush_log_at_trx_commit= 2 每秒自動提交,安全性略有影響,i/o承載強。

  • 日誌同步

    • Sync-binlog =1每條自動更新,安全性高,i/o壓力大

    • Sync-binlog= 0 根據緩存設置狀況自動更新,存在丟失數據和同步延遲風險,i/o承載力強。

    • 我的建議保存binlog日誌文件,便於追溯更新操做和系統恢復。

    • 如對日誌文件的i/o壓力有擔憂,在內存寬裕的狀況下,可考慮將binlog寫入到諸如 /dev/shm這樣的內存映射分區,並定時將舊有的binlog轉移到物理硬盤。

  • 性能與安全自己存在相悖的狀況,須要在業務訴求層面決定取捨

    • 學會區分什麼場合側重性能,什麼場合側重安全

    • 學會將不一樣安全等級的數據庫用不一樣策略管理

存儲/寫入壓力優化

  • 順序讀寫性能遠高於隨機讀寫

  • 將順序寫數據和隨機讀寫數據分紅不一樣的物理磁盤進行,有助於i/o壓力的疏解

    • 數據庫文件涉及索引等內容,寫入是隨即寫

    • binlog文件是順序寫

    • 淘寶數據庫存儲優化是這樣處理的

  • 部分安全要求不高的寫入操做能夠用/dev/shm分區存儲,簡單變成內存寫。

  • 多塊物理硬盤作raid10,能夠提高寫入能力

  • 關鍵存儲設備優化,善於比對不一樣存儲介質的壓力測試數據。

    • 例如fusion-io在新浪和淘寶都有較多使用。

  • 涉及必須存儲較爲龐大的數據量時

    • 壓縮存儲,能夠經過增長cpu開銷(壓縮算法)減小i/o壓力。前提是你確認cpu相對空閒而i/o壓力很大。新浪微博就是壓縮存儲的典範。

    • 經過md5去重存儲,案例是QQ的文件共享,以及dropbox這樣的共享服務,若是你上傳的是一個別人已有的文件,計算md5後,直接經過md5定位到原有文件,這樣能夠極大減小存儲量。涉及文件共享,頭像共享,相冊等應用,經過這種方法能夠減小超過70%的存儲規模,對硬件資源的節省是至關巨大的。缺點是,刪除文件須要甄別該md5是否有其餘人使用。去重存儲,用戶量越多,上傳文件越多,效率越高!

    • 文件儘可能不要存儲到數據庫內。儘可能使用獨立的文件系統存儲,該話題不展開。

運維監控體系

  • 系統監控

    • 服務器資源監控

      • Cpu,內存,硬盤空間,i/o壓力

      • 設置閾值報警

    • 服務器流量監控

      • 外網流量,內網流量

      • 設置閾值報警

    • 鏈接狀態監控

      • Showprocesslist 設置閾值,每分鐘監測,超過閾值記錄

  • 應用監控

    • 慢查詢監控

      • 慢查詢日誌

      • 若是存在多臺數據庫服務器,應有彙總查閱機制。

    • 請求錯誤監控

      • 高頻繁應用中,會出現偶發性數據庫鏈接錯誤或執行錯誤,將錯誤信息記錄到日誌,查看每日的比例變化。

      • 偶發性錯誤,若是數量極少,能夠不用處理,可是需時常監控其趨勢。

      • 會存在惡意輸入內容,輸入邊界限定缺少致使執行出錯,需基於此防止惡意入侵探測行爲。

    • 微慢查詢監控

      • 高併發環境裏,超過0.01秒的查詢請求都應該關注一下。

    • 頻繁度監控

      • 寫操做,基於binlog,按期分析。

      • 讀操做,在前端db封裝代碼中增長抽樣日誌,並輸出執行時間。

      • 分析請求頻繁度是開發架構進一步優化的基礎

      • 最好的優化就是減小請求次數!

  • 總結:

    • 監控與數據分析是一切優化的基礎。

    • 沒有運營數據監測就不要妄談優化!

    • 監控要注意不要產生太多額外的負載,不要因監控帶來太多額外系統開銷

Mysql 架構優化

架構優化目標

防止單點隱患

  • 所謂單點隱患,就是某臺設備出現故障,會致使總體系統的不可用,這個設備就是單點隱患。

  • 理解連帶效應,所謂連帶效應,就是一種問題會引起另外一種故障,舉例而言,memcache+mysql是一種常見緩存組合,在前端壓力很大時,若是memcache崩潰,理論上數據會經過mysql讀取,不存在系統不可用狀況,可是mysql沒法對抗如此大的壓力衝擊,會所以連帶崩潰。因A系統問題致使B系統崩潰的連帶問題,在運維過程當中會頻繁出現。

    • 實戰範例:在mysql鏈接不及時釋放的應用環境裏,當網絡環境異常(同機房友鄰服務器遭受拒絕服務攻擊,出口阻塞),網絡延遲加重,空鏈接數急劇增長,致使數據庫鏈接過多崩潰。

    • 實戰範例2:前端代碼一般咱們封裝mysql_connectmemcache_connect,兩者的順序不一樣,會產生不一樣的連帶效應。若是mysql_connect在前,那麼一旦memcache鏈接阻塞,會連帶mysql空鏈接過多崩潰。

    • 連帶效應是常見的系統崩潰,平常分析崩潰緣由的時候須要認真考慮連帶效應的影響,頭疼醫頭,腳疼醫腳是不行的。

方便系統擴容

  • 數據容量增長後,要考慮可以將數據分佈到不一樣的服務器上。

  • 請求壓力增長時,要考慮將請求壓力分佈到不一樣服務器上。

  • 擴容設計時須要考慮防止單點隱患。

安全可控,成本可控

  • 數據安全,業務安全

  • 人力資源成本>帶寬流量成本>硬件成本

    • 成本與流量的關係曲線應低於線性增加(流量爲橫軸,成本爲縱軸)。

    • 規模優點

  • 本教程僅就與數據庫有關部分討論,與數據庫無關部門請自行參閱其餘學習資料。

 

分佈式方案

分庫&拆表方案

  • 基本認識

    • 用分庫&拆表是解決數據庫容量問題的惟一途徑。

    • 分庫&拆表也是解決性能壓力的最優選擇。

    • 分庫 –不一樣的數據表放到不一樣的數據庫服務器中(也多是虛擬服務器)

    • 拆表 –一張數據表拆成多張數據表,可能位於同一臺服務器,也可能位於多臺服務器(含虛擬服務器)。

  • 去關聯化原則

    • 摘除數據表之間的關聯,是分庫的基礎工做。

    • 摘除關聯的目的是,當數據表分佈到不一樣服務器時,查詢請求容易分發和處理。

    • 學會理解反範式數據結構設計,所謂反範式,第一要點是不用外鍵,不容許Join操做,不容許任何須要跨越兩個表的查詢請求。第二要點是適度冗餘減小查詢請求,好比說,信息表,fromuid,touid,message字段外,還須要一個fromuname字段記錄用戶名,這樣查詢者經過touid查詢後,可以當即獲得發信人的用戶名,而無需進行另外一個數據表的查詢。

    • 去關聯化處理會帶來額外的考慮,好比說,某一個數據表內容的修改,對另外一個數據表的影響。這一點須要在程序或其餘途徑去考慮。

  • 分庫方案

    • 安全性拆分

      • 將高安全性數據與低安全性數據分庫,這樣的好處第一是便於維護,第二是高安全性數據的數據庫參數配置能夠以安全優先,而低安全性數據的參數配置以性能優先。參見運維優化相關部分。

    • 基於業務邏輯拆分

      • 根據數據表的內容構成,業務邏輯拆分,便於平常維護和前端調用。

      • 基於業務邏輯拆分,能夠減小前端應用請求發送到不一樣數據庫服務器的頻次,從而減小連接開銷。

      • 基於業務邏輯拆分,可保留部分數據關聯,前端web工程師可在限度範圍內執行關聯查詢。

    • 基於負載壓力拆分

      • 基於負載壓力對數據結構拆分,便於直接將負載分擔給不一樣的服務器。

      • 基於負載壓力拆分,可能拆分後的數據庫包含不一樣業務類型的數據表,平常維護會有必定的煩惱。

    • 混合拆分組合

      • 基於安全與業務拆分爲數據庫實例,可是可使用不一樣端口放在同一個服務器上。

      • 基於負載能夠拆分爲更多數據庫實例分佈在不一樣數據庫上

      • 例如,

        • 基於安全拆分出A數據庫實例,

        • 基於業務拆分出B,C數據庫實例,

        • C數據庫存在較高負載,基於負載拆分爲C1,C2,C3,C4等實例。

        • 數據庫服務器徹底能夠作到A+B+C1 爲一臺,C2,C3,C4各單獨一臺。

 

  • 分表方案

    • 數據量過大或者訪問壓力過大的數據表須要切分

    • 縱向分表(常見爲忙閒分表)

      • 單數據表字段過多,可將頻繁更新的整數數據與非頻繁更新的字符串數據切分

      • 範例 user表,我的簡介,地址,QQ號,聯繫方式,頭像這些字段爲字符串類型,更新請求少;最後登陸時間,在線時常,訪問次數,信件數這些字段爲整數型字段,更新頻繁,能夠將後面這些更新頻繁的字段獨立拆出一張數據表,表內容變少,索引結構變少,讀寫請求變快。

    • 橫向切表

      • 等分切表,如哈希切表或其餘基於對某數字取餘的切表。等分切表的優勢是負載很方便的分佈到不一樣服務器;缺點是當容量繼續增長時沒法方便的擴容,須要從新進行數據的切分或轉表。並且一些關鍵主鍵不易處理。

      • 遞增切表,好比每1kw用戶開一個新表,優勢是能夠適應數據的自增趨勢;缺點是每每新數據負載高,壓力分配不平均。

      • 日期切表,適用於日誌記錄式數據,優缺點等同於遞增切表。

      • 我的傾向於遞增切表,具體根據應用場景決定。

    • 熱點數據分表

      • 將數據量較大的數據表中將讀寫頻繁的數據抽取出來,造成熱點數據表。一般一個龐大數據表常常被讀寫的內容每每具備必定的集中性,若是這些集中數據單獨處理,就會極大減小總體系統的負載。

      • 熱點數據表與舊有數據關係

        • 能夠是一張冗餘表,即該表數據丟失不會妨礙使用,因源數據仍存在於舊有結構中。優勢是安全性高,維護方便,缺點是寫壓力不能分擔,仍須要同步寫回原系統。

        • 能夠是非冗餘表,即熱點數據的內容原有結構再也不保存,優勢是讀寫效率所有優化;缺點是當熱點數據發生變化時,維護量較大。

        • 具體方案選擇須要根據讀寫比例決定,在讀頻率遠高於寫頻率狀況下,優先考慮冗餘表方案。

      • 熱點數據表能夠用單獨的優化的硬件存儲,好比昂貴的閃存卡或大內存系統。

      • 熱點數據表的重要指標

        • 熱點數據的定義須要根據業務模式自行制定策略,常見策略爲,按照最新的操做時間;按照內容豐富度等等。

        • 數據規模,好比從1000萬條數據,抽取出100萬條熱點數據。

        • 熱點命中率,好比查詢10次,多少次命中在熱點數據內。

        • 理論上,數據規模越小,熱點命中率越高,說明效果越好。須要根據業務自行評估。

      • 熱點數據表的動態維護

        • 加載熱點數據方案選擇

          • 定時從舊有數據結構中按照新的策略獲取

          • 在從舊有數據結構讀取時動態加載到熱點數據

        • 剔除熱點數據方案選擇

          • 基於特定策略,定時將熱點數據中訪問頻次較少的數據剔除

          • 如熱點數據是冗餘表,則直接刪除便可,如不是冗餘表,須要回寫給舊有數據結構。

      • 一般,熱點數據每每是基於緩存或者key-value 方案冗餘存儲,因此這裏提到的熱點數據表,其實更可能是理解思路,用到的場合可能並很少….

反範式設計(冗餘結構設計)

  • 反範式設計的概念

    • 無外鍵,無連表查詢。

    • 便於分佈式設計,容許適度冗餘,爲了容量擴展容許適度開銷。

    • 基於業務自由優化,基於i/o或查詢設計,無須遵循範式結構設計。

  • 冗餘結構設計所面臨的典型場景

    • 原有展示程序涉及多個表的查詢,但願精簡查詢程序

    • 數據表拆分每每基於主鍵,而原有數據表每每存在非基於主鍵的關鍵查詢,沒法在分表結構中完成。

    • 存在較多數據統計需求(count,sum等),效率低下。

  • 冗餘設計方案

    • 基於展示的冗餘設計

      • 爲了簡化展示程序,在一些數據表中每每存在冗餘字段

      • 舉例,信息表  message,存在字段fromuid,touid,msg,sendtime 四個字段,其中touid+sendtime是複合索引。存在查詢爲select * frommessage where touid=$uid order by sendtime desc limit 0,30;

      • 展現程序須要顯示發送者姓名,此時一般會在message表中增長字段fromusername,甚至有的會增長fromusersex,從而無需連表查詢直接輸出信息的發送者姓名和性別。這就是一種簡單的,爲了不連表查詢而使用的冗餘字段設計。

    • 基於查詢的冗餘設計

      • 涉及分表操做後,一些常見的索引查詢可能須要跨表,帶來沒必要要的麻煩。確認查詢請求遠大於寫入請求時,應設置便於查詢項的冗餘表。

      • 冗餘表要點

        • 數據一致性,簡單說,同增,同刪,同更新。

        • 能夠作全冗餘,或者只作主鍵關聯的冗餘,好比經過用戶名查詢uid,再基於uid查詢源表。

      • 實戰範例1

        • 用戶分表,將用戶庫分紅若干數據表

        • 基於用戶名的查詢和基於uid的查詢都是高併發請求。

        • 用戶分表基於uid分紅數據表,同時基於用戶名作對應冗餘表。

        • 若是容許多方式登錄,能夠有以下設計方法

          • uid,passwd,用戶信息等等,主數據表,基於uid 分表

          • ukey,ukeytype,uid基於ukey分表,便於用戶登錄的查詢。分解成以下兩個SQL

            • selectuid from ulist_key_13 where ukey=’$username’and ukeytype=‘login’;

            • select *from ulist_uid_23 where uid=$uid and passwd=’$passwd’;

          • ukeytype定義用戶的登錄依據,好比用戶名,手機號,郵件地址,網站暱稱等。Ukey+ukeytype必須惟一。

          • 此種方式須要登錄密碼統一,對於第三方connect接入模式,能夠經過引伸額外字段完成。

      • 實戰範例2:用戶遊戲積分排名

        • 表結構 uid,gameid,score參見前文實時積分排行。表內容巨大,須要拆表。

        • 需求1:基於遊戲id查詢積分排行

        • 需求2:基於用戶id查詢遊戲積分記錄

        • 解決方案:創建徹底相同的兩套表結構,其一以uid爲拆表主鍵,其二以gameid爲拆表主鍵,用戶提交積分時,向兩個數據結構同時提交。

      • 實戰範例3:全冗餘查詢結構

        • 主信息表僅包括主鍵及備註memo 字段(text類型),只支持主鍵查詢,能夠基於主鍵拆表。因此須要展示和存儲的內容均在memo字段重體現。

        • 對每個查詢條件,創建查詢冗餘表,以查詢條件字段爲主鍵,以主信息表主鍵id爲內容。

        • 平常查詢只基於查詢冗餘表,而後經過in的方式從主信息表得到內容。

        • 優勢是結構擴展很是方便,只須要擴展新的查詢信息表便可,核心思路是,只有查詢才須要獨立的索引結構,展示無需獨立字段。

        • 缺點是隻適合於相對固定的查詢架構,對於更加靈活的組合查詢一籌莫展。

    • 基於統計的冗餘結構

      • 爲了減小會涉及大規模影響結果集的表數據操做,好比countsum操做。應將一些統計類數據經過冗餘數據結構保存。

      • 冗餘數據結構可能以字段方式存在,也可能以獨立數據表結構存在,可是都應能經過源數據表恢復。

      • 實戰範例:

        • 論壇板塊的發帖量,回帖量,每日新增數據等。

        • 網站每日新增用戶數等。

        • 參見Discuz論壇系統數據結構,有較多相關結構。

        • 參見前文分段積分結構,是典型用於統計的冗餘結構。

        • 後臺能夠經過源數據表更新該數字。

        • RedisZset類型能夠理解爲存在一種冗餘統計結構。

    • 歷史數據表

      • 歷史數據表對應於熱點數據表,將需求較少又不能丟棄的數據存入,僅在少數狀況下被訪問。

主從架構

  • 基本認識

    • 讀寫分離對負載的減輕遠遠不如分庫分表來的直接。

    • 寫壓力會傳遞給從表,只讀從庫同樣有寫壓力,同樣會產生讀寫鎖!

    • 一主多從結構下,主庫是單點隱患,很難解決(如主庫當機,從庫能夠響應讀寫,可是沒法自動擔當主庫的分發功能)

    • 主從延遲也是重大問題。一旦有較大寫入問題,如表結構更新,主從會產生巨大延遲。

  • 應用場景

    • 在線熱備

    • 異地分佈

      • 寫分佈,讀統一。

      • 仍然困難重重,受限於網絡環境問題巨多!

    • 自動障礙轉移

      • 主崩潰,從自動接管

    • 我的建議,負載均衡主要使用分庫方案,主從主要用於熱備和障礙轉移。

  • 潛在優化點

    • 爲了減小寫壓力,有些人建議主不建索引提高i/o性能,從創建索引知足查詢要求。我的認爲這樣維護較爲麻煩。並且從自己會繼承主的i/o壓力,所以優化價值有限。該思路特此分享,不作推薦。

故障轉移處理

  • 要點

    • 程序與數據庫的鏈接,基於虛地址而非真實ip,由負載均衡系統監控。

    • 保持主從結構的簡單化,不然很難作到故障點摘除。

  • 思考方式

    • 遍歷對服務器集羣的任何一臺服務器,前端web,中間件,監控,緩存,db等等,假設該服務器出現故障,系統是否會出現異常?用戶訪問是否會出現異常。

    • 目標:任意一臺服務器崩潰,負載和數據操做均會很短期內自動轉移到其餘服務器,不會影響業務的正常進行。不會形成惡性的數據丟失。(哪些是能夠丟失的,哪些是不能丟失的)

緩存方案

緩存結合數據庫的讀取

  • Memcached是最經常使用的緩存系統

  • Mysql最新版本已經開始支持memcache插件,但據牛人分析,尚不成熟,暫不推薦。

  • 數據讀取

    • 並非全部數據都適合被緩存,也並非進入了緩存就意味着效率提高。

    • 命中率是第一要評估的數據。

    • 如何評估進入緩存的數據規模,以及命中率優化,是很是須要細心分析的。

      • 實景分析:前端請求先鏈接緩存,緩存未命中鏈接數據庫,進行查詢,未命中狀態比單純鏈接數據庫查詢多了一次鏈接和查詢的操做;若是緩存命中率很低,則這個額外的操做非但不能提升查詢效率,反而爲系統帶來了額外的負載和複雜性,得不償失。

    • 相關評估相似於熱點數據表的介紹。

    • 善於利用內存,請注意數據存儲的格式及壓縮算法。

  • Key-value方案繁多,本培訓文檔暫不展開。

緩存結合數據庫的寫入

  • 利用緩存不但能夠減小數據讀取請求,還能夠減小數據庫寫入i/o壓力

  • 緩存實時更新,數據庫異步更新

    • 緩存實時更新數據,並將更新記錄寫入隊列

    • 可使用相似mq的隊列產品,自行創建隊列請注意使用increment來維持隊列序號。

    • 不建議使用 get後處理數據再set的方式維護隊列

      • 測試範例:

        • 範例1

$var=Memcache_get($memcon,」var」);

$var++;

memcache_set($memcon,」var」,$var);

這樣一個腳本,使用apacheab去跑,100個併發,跑10000次,而後輸出緩存存取的數據,很遺憾,並非1000,而是5000多,6000多這樣的數字,中間的數字全在get & set的過程當中丟掉了。

緣由,讀寫間隔中其餘併發寫入,致使數據丟失。

    • 範例2

memcache_increment來作這個操做,一樣跑測試

會獲得完整的10000,一條數據不會丟。

      • 結論:用increment存儲隊列編號,用標記+編號做爲key存儲隊列內容。

    • 後臺基於緩存隊列讀取更新數據並更新數據庫

      • 基於隊列讀取後能夠合併更新

      • 更新合併率是重要指標

        • 實戰範例:

某論壇熱門貼,前端不斷有views=views+1數據更新請求。

緩存實時更新該狀態

後臺任務對數據庫作異步更新時,假設執行週期是5分鐘,那麼五分鐘可能會接收到這樣的請求多達數十次乃至數百次,合併更新後只執行一次update便可。

相似操做還包括遊戲打怪,生命和經驗的變化;我的主頁訪問次數的變化等。

    • 異步更新風險

      • 先後端同時寫,可能致使覆蓋風險。

        • 使用後端異步更新,則前端應用程序就不要寫數據庫,不然可能形成寫入衝突。一種兼容的解決方案是,前端和後端不要寫相同的字段。

        • 實戰範例:

用戶在線上時,後臺異步更新用戶狀態。

管理員後臺屏蔽用戶是直接更新數據庫。

結果管理員屏蔽某用戶操做完成後,因該用戶在線有操做,後臺異步更新程序再次基於緩存更新用戶狀態,用戶狀態被複活,屏蔽失效。

      • 緩存數據丟失或服務崩潰可能致使數據丟失風險。

        • 如緩存中間出現故障,則緩存隊列數據不會回寫到數據庫,而用戶會認爲已經完成,此時會帶來比較明顯的用戶體驗問題。

        • 一個不完全的解決方案是,確保高安全性,高重要性數據實時數據更新,而低安全性數據經過緩存異步回寫方式完成。此外,使用相對數值操做而不是絕對數值操做更安全。

          • 範例:支付信息,道具的購買與得到,一旦丟失會對用戶形成極大的傷害。而經驗值,訪問數字,若是隻丟失了不多時間的內容,用戶仍是能夠容忍的。

          • 範例:若是使用Views=Views+…的操做,一旦出現數據格式錯誤,從binlog中反推是能夠進行數據還原,可是若是使用Views=特定值的操做,一旦緩存中數據有錯誤,則直接被賦予了一個錯誤數據,沒法回溯!

      • 異步更新如出現隊列阻塞可能致使數據丟失風險。

        • 異步更新一般是使用緩存隊列後,在後臺由cron或其餘守護進程寫入數據庫。

        • 若是隊列生成的速度>後臺更新寫入數據庫的速度,就會產生阻塞,致使數據越累計越多,數據庫響應遲緩,而緩存隊列沒法迅速執行,致使溢出或者過時失效。

    • 建議使用內存隊列產品而不使用memcache 來進行緩存異步更新。

總結

  • 第一步,完成數據庫查詢的優化,須要理解索引結構,才能學會判斷影響結果集。而影響結果集對查詢效率線性相關,掌握這一點,編寫數據查詢語句就很容易判斷系統開銷,瞭解業務壓力趨勢。

  • 第二步,在SQL語句已經足夠優化的基礎上,學會對數據庫總體情況的分析,可以對異常和負載的波動有正確的認識和解讀;可以對系統資源的分配和瓶頸有正確的認識。

  • 學會經過監控和數據來進行系統的評估和優化方案設計,杜絕拍腦殼,學會抓大放小,把握要點的處理方法。

  • 第三步,在完全掌握數據庫語句優化和運維優化的基礎上,學會分佈式架構設計,掌握複雜,大容量數據庫系統的搭建方法。

  • 最後,分享一句話,學會把問題簡單化,正如Caoz 常說的,你若是認爲這個問題很複雜,你必定想錯了。

  • 感謝您的閱讀,如對您有幫助,請在百度文庫給本文五分好評,並推薦給您的朋友,多謝。

相關文章
相關標籤/搜索