此文是根據楊尚剛在【QCON高可用架構羣】中,針對MySQL在單表海量記錄等場景下,業界普遍關注的MySQL問題的經驗分享整理而成,轉發請註明出處。mysql
楊尚剛,美圖公司數據庫高級DBA,負責美圖後端數據存儲平臺建設和架構設計。前新浪高級數據庫工程師,負責新浪微博核心數據庫架構改造優化,以及數據庫相關的服務器存儲選型設計。算法
MySQL數據庫你們應該都很熟悉,並且隨着前幾年的阿里的去IOE,MySQL逐漸引發更多人的重視。sql
上面這幾個因素也是大多數公司選擇考慮MySQL的緣由。不過MySQL自己存在的問題和限制也不少,有些問題點也常常被其餘數據庫吐槽或鄙視數據庫
看到了剛纔講的MySQL的優點和劣勢,能夠看到MySQL面臨的問題仍是遠大於它的優點的,不少問題也是咱們實際須要在運維中優化解決的,這也是 MySQL DBA的一方面價值所在。而且MySQL的不斷髮展也離不開社區支持,好比Google最先提交的半同步patch,後來也合併到官方主線。 Facebook Twitter等也都開源了內部使用MySQL分支版本,包含了他們內部使用的patch和特性。後端
數據庫開發規範定義:開發規範是針對內部開發的一系列建議或規則, 由DBA制定(若是有DBA的話)。緩存
開發規範自己也包含幾部分:基本命名和約束規範,字段設計規範,索引規範,使用規範。安全
想一想沒有開發規範,有的開發寫出各類全表掃描的SQL語句或者各類奇葩SQL語句,咱們以前就看過開發寫的SQL 能夠打印出好幾頁紙。這種形成業務自己不穩定,也會讓DBA每天忙於各類救火。性能優化
關於爲何定義不使用Null的緣由服務器
* 1.浪費存儲空間,由於InnoDB須要有額外一個字節存儲網絡
* 2.表內默認值Null過多會影響優化器選擇執行計劃
關於使用datatime和timestamp,如今在5.6.4以後又有了變化,使用兩者存儲在存儲空間上大差距愈來愈小 ,而且自己datatime存儲範圍就比timestamp大不少,timestamp只能存儲到2038年
關於索引規範,必定要記住索引這個東西是一把雙刃劍,在加速讀的同時也引入了不少額外的寫入和鎖,下降寫入能力,這也是爲何要控制索引數緣由。以前看到過很多人給表裏每一個字段都建了索引,其實對查詢可能起不到什麼做用。
冗餘索引例子
隱式轉換例子
字段:remark
varchar(50) NOT Null
MySQL>SELECT id
, gift_code
FROM gift WHERE deal_id
= 640 AND remark=115127; 1 row in set (0.14 sec)
MySQL>SELECT id
, gift_code
FROM pool_gift WHEREdeal_id
= 640 AND remark=‘115127’; 1 row in set (0.005 sec)
字段定義爲varchar,但傳入的值是個int,就會致使全表掃描,要求程序端要作好類型檢查
建議選擇優先級爲:MySQL社區版 > Percona Server > MariaDB > MySQL 企業版
不過如今若是你們使用RDS服務,基本還以社區版爲主
原生MySQL執行DDL時須要鎖表,且鎖表期間業務是沒法寫入數據的,對服務影響很大,MySQL對這方面的支持是比較差的。大表作DDL對DBA來講是很痛苦的,相信不少人經歷過。如何作到Online DDL呢,是否是就無解了呢?固然不是!
上面表格裏提到的 Facebook OSC和5.6 OSC也是目前兩種比較靠譜的方案
MySQL 5.6的OSC方案仍是解決不了DDL的時候到從庫延時的問題,因此如今建議使用Facebook OSC這種思路更優雅
下圖是Facebook OSC的思路
後來Percona公司根據Facebook OSC思路,用perl重寫了一版,就是咱們如今用得不少的pt-online-schema-change,軟件自己很是成熟,支持目前主流版本。
使用pt-online-schema-change的優勢有:
值得一提的是,騰訊互娛的DBA在內部分支上也實現了Online DDL,以前測試過確實不錯,速度快,原理是經過修改InnoDB存儲格式來實現。
使用pt-online-schema-change的限制有:
關於可用性,咱們今天分享一種無縫切主庫方案,能夠用於平常切換,使用思路也比較簡單
在正常條件下如何無縫去作主庫切換,核心思路是讓新主庫和從庫停在相同位置,主要依賴slave start until 語句,結合雙主結構,考慮自增問題。
MySQL集羣方案:
MySQL半同步複製:
如今也有一些理論上可用性更高的其它方案
紅框內是目前你們使用比較多的部署結構和方案。固然異常層面的HA也有不少第三方工具支持,好比MHA、MMM等,推薦使用MHA
曾經管理的單表最大60億+,單表數據文件大小1TB+,人有時候就要懶一些
上圖是水平拆分和垂直拆分的示意圖
首先要保證的,最核心的是數據庫數據安全性。數據安全都保障不了的狀況下談其餘的指標(如性能等),其實意義就不大了。
備份的意義是什麼呢?
目前備份方式的幾個緯度:
建議方式:
借用一下某大型互聯網公司作的備份系統數據:一年7000+次擴容,一年12+次數據恢復,日誌量天天3TB,數據總量2PB,天天備份數據量百TB級,整年備份36萬次,備份成功了99.9%。
主要作的幾點:
備份系統採用分佈式文件系統緣由:
使用分佈式文件系統優化點:
* Pbzip壓縮,下降多副本存儲帶來的存儲成本,下降網絡帶寬消耗
* 元數據節點HA,提升備份集羣的可用性
* erasure code方案調研
目前的MySQL數據恢復方案主要仍是基於備份來恢復,可見備份的重要性。好比我今天下午15點刪除了線上一張表,該如何恢復呢?首先確認刪除語 句,而後用備份擴容實例啓動,假設備份時間點是凌晨3點,就還須要把凌晨3點到如今關於這個表的binlog導出來,而後應用到新擴容的實例上,確認好恢 復的時間點,而後把刪除表的數據導出來應用到線上。
MySQL複製:
問題不少,可是能解決基本問題
上圖是MySQL複製原理圖,紅框內就是MySQL一直被人詬病的單線程問題
單線程問題也是MySQL主從延時的一個重要緣由,單線程解決方案:
上圖是MySQL5.6 目前實現的並行複製原理圖,是基於庫級別的複製,因此若是你只有一個庫,使用這個意義不大
固然MySQL也認識到5.6這種並行的瓶頸所在,因此在5.7引入了另一種並行複製方式,基於logical timestamp的並行複製,並行複製再也不受限於庫的個數,效率會大大提高
上圖是5.7的logical timestamp的複製原理圖
剛纔我也提到MySQL原來只支持異步複製,這種數據安全性是很是差的,因此後來引入了半同步複製,從5.5開始支持
上圖是原生異步複製和半同步複製的區別。能夠看到半同步經過從庫返回ACK這種方式確認從庫收到數據,數據安全性大大提升
在5.7以後,半同步也能夠配置你指定多個從庫參與半同步複製,以前版本都是默認一個從庫
對於半同步複製效率問題有一個小的優化,就是使用5.6+的mysqlbinlog以daemon方式做爲從庫,同步效率會好不少
關於更安全的複製,MySQL 5.7也是有方案的,方案名叫Group replication 官方多主方案,基於Corosync實現
緣由:通常都會作讀寫分離,其實從庫壓力反而比主庫大/從庫讀寫壓力大很是容易致使延時。
解決方案:
提到延時不得不提到很坑人的Seconds behind master,使用過MySQL的應該很熟悉
這個值的源碼裏算法
long time_diff= ((long)(time(0) – mi->rli.last_master_timestamp) – mi->clock_diff_with_master);
Secondsbehindmaster來判斷延時不可靠,在網絡抖動或者一些特殊參數配置狀況下,會形成這個值是0但其實延時很大了。經過heartbeat表插入時間戳這種機制判斷延時是更靠譜的
複製注意點:
主從數據一致性問題:
成熟開源事務存儲引擎,支持ACID,支持事務四個隔離級別,更好的數據安全性,高性能高併發,MVCC,細粒度鎖,支持O_DIRECT。
主要優化參數:
上圖是5.5 4G的redo log和5.6 設置大於4G redo log文件性能對比,能夠看到穩定性更好了。innodblogfile_size設置仍是頗有意義的
InnoDB比較好的特性:
InnoDB在SSD上的優化:
也要搞清楚InnoDB哪些文件是順序讀寫,哪些是隨機讀寫
隨機讀寫:
順序讀寫:
InnoDB VS MyISAM:
TokuDB:
目前主流使用TokuDB主要是看中了它的高壓縮比,Tokudb有三種壓縮方式:quicklz、zlib、lzma,壓縮比依次更高。如今不少使用zabbix的後端數據表都採用的TokuDB,寫入性能好,壓縮比高。
下圖是我以前作的測試對比和InnoDB
上圖是sysbench測試的和InnoDB性能對比圖,能夠看到TokuDB在測試過程當中寫入穩定性是很是好的。
tokudb存在的問題:
好比咱們以前遇到過一個問題:TokuDB的內部狀態顯示上一次完成的checkpoint時間是「Jul 17 12:04:11 2014」,距離當時發現如今都快5個月了,結果堆積了大量redo log不能刪除,後來只能重啓實例,結果重啓還花了七八個小時
Query cache,MySQL內置的查詢加速緩存,理念是好的,但設計不夠合理,有點out。
鎖的粒度很是大MySQL 5.6默認已經關閉
When the query cache helps, it can help a lot. When it hurts, it can hurt a lot.明顯前半句已經沒有太大用處,在高併發下很是容易遇到瓶頸。
關於事務隔離級別 ,InnoDB默認隔離級別是可重複讀級別,固然InnoDB雖然是設置的可重複讀,可是也是解決了幻讀的,建議改爲讀已提交級別,能夠知足大多數場景需求,有利於更高的併發,修改transaction-isolation。
上圖是一個比較經典的死鎖case,有興趣能夠測試下
關於SSD
關於SSD,仍是提一下吧。某知名大V說過「最近10年對數據庫性能影響最大的是閃存」,穩定性和性能可靠性已經獲得大規模驗證,多塊SATA SSD作Raid5,推薦使用。採用PCIe SSD,主流雲平臺都提供SSD雲硬盤支持。
最後說一下你們關注的單表60億記錄問題,表裏數據也是線上比較核心的。
先說下當時狀況,表結構比較簡單,都是bigint這種整型,索引比較多,應該有2-3個,單錶行數60億+,單表容量1.2TB左右,固然內部確定是有碎片的。
造成緣由:歷史遺留問題,按照咱們前面講的開發規範,這個應該早拆分了,固然不拆有幾個緣由:
咱們後續作的優化 ,採用了剛纔提到的TokuDB,單表容量在InnoDB下1TB+,使用Tokudb的lzma壓縮到80GB,壓縮效果很是好。這樣也解決了單表過大恢復時間問題,也支持online DDL,基本達到咱們預期。
今天講的主要針對MySQL自己優化和規範性質的東西,還有一些比較好的運維經驗,但願你們能有所收穫。今天這些內容是爲後續數據庫作平臺化的基礎。我今天分享就到這裏,謝謝你們。
Q1:use schema;select * from table; 和select * from schema.table;兩種寫法有什麼不同嗎?會對主從同步有影響嗎?
對於主從複製來講執行效率上差異不大,不過在使用replication filter時候這種狀況須要當心,應該要使用ReplicateWildIgnoreTable這種參數,若是不使用帶wildignore,第一種方式會有問題,過濾不起做用。
Q2:對於用於MySQL的ssd,測試方式和ssd的參數配置方面,有沒有好的建議?主要針對ssd的配置哈
關於SATA SSD配置參數,建議使用Raid5,想更保險使用Raid50,更土豪使用Raid 10
上圖是主要的參數優化,性能提高最大的是第一個修改調度算法的
Q3:數據庫規範已制定好,如何保證開發人員必須按照規範來開發?
關於數據庫規範實施問題,也是有兩個方面吧,第1、按期給開發培訓開發規範,讓開發能更瞭解。第2、仍是在流程上規範,好比把咱們平常通用的建表和字段策略固化到程序,作成自動化審覈系統。這兩方面結合 效果會比較好。
Q4:如何最大限度提升innodb的命中率?
這個問題前提是你的數據要有熱點,讀寫熱點要有交集,不然命中率很難提升。在有熱點的前提下,也要求你的你的內存要足夠大,可以存更多的熱點數據。儘可能不要作一些可能污染bufferpool的操做,好比全表掃描這種。
Q5:主從複製的狀況下,若是有CAS這樣的需求,是否是隻能強制連主庫?由於有延遲的存在,若是讀寫不在一塊兒的話,會有髒數據。
若是有CAS需求,確實仍是直接讀主庫好一些,由於異步複製仍是會有延遲的。只要SQL優化的比較好,讀寫都在主庫也是沒什麼問題的。
Q6:關於開發規範,是否有必要買國標?
這個國標是什麼東西,不太瞭解。不過從字面看,國標應該也是偏學術方面的,在具體工程實施時候未必能用好。
Q7:主從集羣能不能再細化一點那?不知道這樣問合適不?
看具體哪方面吧。主從集羣每一個小集羣通常都是採用一主多從方式,每一個小集羣對應特定的一組業務。而後監控備份和HA都是在每一個小集羣實現。
Q8:如何跟蹤數據庫table某個字段值發生變化?
追蹤字段值變化能夠經過分析row格式binlog好一些。好比之前同事就是經過本身開發的工具來解析row格式binlog,跟蹤數據行變化。
Q9:對超大表水平拆分,在使用MySQL中間件方面有什麼建議和經驗分享?
對於超大表水平拆分,在中間件上經驗不是不少,早期人肉搞過幾回。也使用過本身研發的數據庫中間件,不過線上應用的規模不大。關於目前衆多的開源中間件裏,360的atlas是目前還不錯的,他們公司內部應用的比較多。
Q10:咱們用的MySQL proxy作讀負載,可是少許數據壓力下並無負載,請問有這回事嗎?
少許數據壓力下,並無負載 ,這個沒測試過,很差評價
Q11:對於binlog格式,爲何只推薦row,而不用網上大部分文章推薦的Mix ?
這個主要是考慮數據複製的可靠性,row更好。mixed含義是指若是有一些容易致使主從不一致的SQL ,好比包含UUID函數的這種,轉換爲row。既然要革命,就搞的完全一些。這種mix的中間狀態最坑人了。
Q12: 讀寫分離,通常是在程序裏作,仍是用proxy ,用proxy的話通常用哪一個?
這個仍是獨立寫程序好一些,與程序解耦方便後期維護。proxy國內目前開源的比較多,選擇也要慎重。
Q13: 我想問一下關於mysql線程池相關的問題,什麼狀況下適合使用線程池,相關的參數應該如何配置,老師有這方面的最佳實踐沒有?
線程池這個我也沒測試過。從原理上來講,短連接更適合用線程池方式,減小創建鏈接的消耗。這個方面的最佳配置,我還沒測試過,後面測試有進展能夠再聊聊。
Q14: 誤刪數據這種,數據恢復流程是怎麼樣的(從庫也被同步刪除的狀況)?
看你刪除數據的狀況,若是隻是一張表,單表在幾GB或幾十GB。若是能有延時備份,對於數據恢復速度是頗有好處的。恢復流程能夠參考我剛纔分享的部 分。目前的MySQL數據恢復方案主要仍是基於備份來恢復 ,可見備份的重要性。好比我今天下午15點刪除了線上一張表,該如何恢復呢。首先確認刪除語句,而後用備份擴容實例啓動,假設備份時間點是凌晨3點。就還 須要把凌晨3點到如今關於這個表的binlog導出來,而後應用到新擴容的實例上。確認好恢復的時間點,而後把刪除表的數據導出來應用到線上。
Q15: 關於備份,binlog備份天然不用說了,物理備份有不少方式,有沒有推薦的一種,邏輯備份在量大的時候恢復速度比較慢,通常用在什麼場景?
物理備份採用xtrabackup熱備方案比較好。邏輯備份通常用在單表恢復效果會很是好。好比你刪了一個2G表,但你總數據量2T,用物理備份就會要慢了,邏輯備份就很是有用了。