https://www.zhihu.com/question/19719997
python
千萬級,MySQL實際上確實不是什麼壓力,InnoDB的存儲引擎,使用的是B+樹存儲結構,千萬級的數據量,基本也就是三到四層的搜索,若是有合適的索引,性能基本也不是問題。mysql
但常常出現的狀況是,業務上面的增加,致使數據量還會繼續增加,爲了應對這方面的問題而必需要作擴展了此時可能首先須要考慮的就是分表策略了。react
固然分表,可能還有其它幾個緣由,好比表變大了,千萬級的數據庫,爲了減小運維成本,下降風險,就想到了經過分表來解決問題,這都是比較合適的。redis
分表,還有另外一個方面的意思,就是在數據量更大的狀況下,爲了分擔業務壓力,將數據表分到不一樣的實例中去,這樣有兩方面的好處:1. 下降業務風險,若是一套數據庫集羣出問題了,那至少還有其它的能夠服務,這樣被影響的業務可能只是一部分。2. 下降運維成本,若是數據庫想要作遷移,或者正常維護等操做了,那涉及到的數據量小,下線時間短,操做快,從而對業務影響也就小了。這種方式,咱們稱之爲「分實例」。sql
分表的話,仍是要根據具體的業務邏輯等方面來作,這方面有更精彩的回答,我這裏貼一下:數據庫
========================================後端
分庫分表是MySQL永遠的話題,通常狀況下認爲MySQL是個簡單的數據庫,在數據量大到必定程度以後處理查詢的效率下降,若是須要繼續保持高性能運轉的話,必須分庫或者分表了。關於數據量達到多少大是個極限這個事兒,本文先不討論,研究源碼的同窗已經證明MySQL或者Innodb內部的鎖粒度太大的問題大大限制了MySQL提供QPS的能力或者處理大規模數據的能力。在這點上,通常的使用者只好坐等官方不斷推出的優化版本了。緩存
在通常運維的角度來看,咱們什麼狀況下須要考慮分庫分表? 安全
首先說明,這裏所說的分庫分表是指把數據庫數據的物理拆分到多個實例或者多臺機器上去,而不是相似分區表的原地切分。微信
原則零:能不分就不分。
是的,MySQL 是關係數據庫,數據庫表之間的關係從必定的角度上映射了業務邏輯。任何分庫分表的行爲都會在某種程度上提高業務邏輯的複雜度,數據庫除了承載數據的存儲和訪問外,協助業務更好的實現需求和邏輯也是其重要工做之一。分庫分表會帶來數據的合併,查詢或者更新條件的分離,事務的分離等等多種後果,業務實現的複雜程度每每會翻倍或者指數級上升。因此,在分庫分表以前,不要爲分而分,去作其餘力所能及的事情吧,例如升級硬件,升級,升級網絡,升級數據庫版本,讀寫分離,負載均衡等等。全部分庫分表的前提是,這些你已經盡力了。
原則一:數據量太大,正常的運維影響正常業務訪問。
這裏說的運維,例如:
(1)對數據庫的備份。若是單表或者單個實例太大,在作備份的時候須要大量的磁盤IO或者網絡IO資源。例如1T的數據,網絡傳輸佔用50MB的時候,須要20000秒才能傳輸完畢,在此整個過程當中的維護風險都是高於平時的。咱們在Qunar的作法是給全部的數據庫機器添加第二塊網卡,用來作備份,或者SST,Group Communication等等各類內部的數據傳輸。1T的數據的備份,也會佔用大量的磁盤IO,若是是SSD還好,固然這裏忽略某些廠商的產品在集中IO的時候會出一些BUG的問題。若是是普通的物理磁盤,則在不限流的狀況下去執行xtrabackup,該實例基本不可用。
(2)對數據表的修改。若是某個表過大,對此表作DDL的時候,MySQL會鎖住全表,這個時間可能很長,在這段時間業務不能訪問此表,影響甚大。解決的辦法有相似騰訊遊戲DBA本身改造的能夠在線秒改表,不過他們目前也只是能添加字段而已,對別的DDL仍是無效;或者使用pt-online-schema-change,固然在使用過程當中,它須要創建觸發器和影子表,同時也須要很長很長的時間,在此操做過程當中的全部時間,均可以看作是風險時間。把數據表切分,總量減少,有助於改善這種風險。
(3)整個表熱點,數據訪問和更新頻繁,常常有鎖等待,你又沒有能力去修改源碼,下降鎖的粒度,那麼只會把其中的數據物理拆開,用空間換時間,變相下降訪問壓力。
原則二:表設計不合理,須要對某些字段垂直拆分
這裏舉一個例子,若是你有一個用戶表,在最初設計的時候多是這樣:
table :users
id bigint 用戶的ID
name varchar 用戶的名字
last_login_time datetime 最近登陸時間
personal_info text 私人信息
xxxxx 其餘信息字段。
通常的users表會有不少字段,我就不列舉了。如上所示,在一個簡單的應用中,這種設計是很常見的。可是:
設想狀況一:你的業務中彩了,用戶數從100w飆升到10個億。你爲了統計活躍用戶,在每一個人登陸的時候都會記錄一下他的最近登陸時間。而且的用戶活躍得很,不斷的去更新這個login_time,搞的你的這個表不斷的被update,壓力很是大。那麼,在這個時候,只要考慮對它進行拆分,站在業務的角度,最好的辦法是先把last_login_time拆分出去,咱們叫它 user_time。這樣作,業務的代碼只有在用到這個字段的時候修改一下就好了。若是你不這麼作,直接把users表水平切分了,那麼,全部訪問users表的地方,都要修改。或許你會說,我有proxy,可以動態merge數據。到目前爲止我還從沒看到誰家的proxy不影響性能的。
設想狀況二:personal_info這個字段原本沒啥用,你就是讓用戶註冊的時候填一些我的愛好而已,基本不查詢。一開始的時候有它沒它無所謂。可是到後來發現兩個問題,一,這個字段佔用了大量的空間,由於是text嘛,有不少人喜歡長篇大論地介紹本身。更糟糕的是二,不知道哪天哪一個產品經理心血來潮,說容許我的信息公開吧,以方便讓你們更好的相互瞭解。那麼在全部人獵奇窺私心理的影響下,對此字段的訪問大幅度增長。數據庫壓力瞬間抗不住了,這個時候,只好考慮對這個表的垂直拆分了。
原則三:某些數據表出現了無窮增加
例子很好舉,各類的評論,消息,日誌記錄。這個增加不是跟人口成比例的,而是不可控的,例如微博的feed的廣播,我發一條消息,會擴散給不少不少人。雖然主體可能只存一份,但不排除一些索引或者路由有這種存儲需求。這個時候,增長存儲,提高機器配置已經蒼白無力了,水平切分是最佳實踐。拆分的標準不少,按用戶的,按時間的,按用途的,不在一一舉例。
原則四:安全性和可用性的考慮
這個很容易理解,雞蛋不要放在一個籃子裏,我不但願個人數據庫出問題,但我但願在出問題的時候不要影響到100%的用戶,這個影響的比例越少越好,那麼,水平切分能夠解決這個問題,把用戶,庫存,訂單等等原本同統一的資源切分掉,每一個小的數據庫實例承擔一小部分業務,這樣總體的可用性就會提高。這對Qunar這樣的業務仍是比較合適的,人與人之間,某些庫存與庫存之間,關聯不太大,能夠作一些這樣的切分。
原則五:業務耦合性考慮
這個跟上面有點相似,主要是站在業務的層面上,咱們的火車票業務和烤羊腿業務是徹底無關的業務,雖然每一個業務的數據量可能不太大,放在一個MySQL實例中徹底沒問題,可是極可能烤羊腿業務的DBA 或者開發人員水平不好,動不動給你出一些幺蛾子,直接把數據庫搞掛。這個時候,火車票業務的人員雖然技術很優秀,工做也很努力,照樣被老闆打屁股。解決的辦法很簡單:惹不起,躲得起。
《三國演義》第一回:「話說天下大勢,分久必合,合久必分。」其實在實踐中,有時候可能你本來要分,後來又發現分了還得合,分分合合,徹底是現實的需求,隨需而變纔是王道,而DBA的價值也能在此體現。或分或合的狀況太多,不能窮舉,歡迎繼續交流這個話題,若是以上有錯誤以後,也請批評指正。
給生活加點料。
================================
文章摘自微信公衆號formysql。
如何分表的方案,其實這個不能一律而論,與業務邏輯有關係,與數據性質有關係,好比訂單類型的,那就很是容易了,經過時間這個特性,能夠經過一個路由表,把數據分散到多個實例上面,或者多個表上面,擴展性很是強,可是若是是用戶關係等相似的表,他的惟一能夠作HASH的值就是用戶ID,作HASH時,涉及到不均勻、可擴展能力,遷移麻煩等問題,因此仍是不太容易的,因此只能是具體問題具體分析了。
上面說的是分表的優化方案,固然還有其它方案,那就是要儘量的寫好SQL語句,不要留坑,MySQL就是適合那種快進快出的語句,儘量的別把業務邏輯放到MySQL中去處理,要保持MySQL的高效運行纔是最正確的選擇。
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
優化順序:優化sql和索引 > 加緩存 memcached,redis > 主從複製或主主複製,讀寫分離 > mysql分區表 > 垂直分區 > 水平分區
不少人第一反應是各類切分;我給的順序是:
第一優化你的sql和索引;
第二加緩存,memcached,redis;
第三以上都作了後,仍是慢,就作主從複製或主主複製,讀寫分離,能夠在應用層作,效率高,也能夠用三方工具,第三方工具推薦360的atlas,其它的要麼效率不高,要麼沒人維護;
第四若是以上都作了仍是慢,不要想着去作切分,mysql自帶分區表,先試試這個,對你的應用是透明的,無需更改代碼,可是sql語句是須要針對分區表作優化的,sql條件中要帶上分區條件的列,從而使查詢定位到少許的分區上,不然就會掃描所有分區,另外分區表還有一些坑,在這裏就很少說了;
第五若是以上都作了,那就先作垂直拆分,其實就是根據你模塊的耦合度,將一個大的系統分爲多個小的系統,也就是分佈式系統;
第六纔是水平切分,針對數據量大的表,這一步最麻煩,最能考驗技術水平,要選擇一個合理的sharding key,爲了有好的查詢效率,表結構也要改動,作必定的冗餘,應用也要改,sql中儘可能帶sharding key,將數據定位到限定的表上去查,而不是掃描所有的表;
mysql數據庫通常都是按照這個步驟去演化的,成本也是由低到高;
有人也許要說第一步優化sql和索引這還用說嗎?的確,你們都知道,可是不少狀況下,這一步作的並不到位,甚至有的只作了根據sql去建索引,根本沒對sql優化(中槍了沒?),除了最簡單的增刪改查外,想實現一個查詢,能夠寫出不少種查詢語句,不一樣的語句,根據你選擇的引擎、表中數據的分佈狀況、索引狀況、數據庫優化策略、查詢中的鎖策略等因素,最終查詢的效率相差很大;優化要從總體去考慮,有時你優化一條語句後,其它查詢反而效率被下降了,因此要取一個平衡點;即便精通mysql的話,除了純技術面優化,還要根據業務面去優化sql語句,這樣才能達到最優效果;你敢說你的sql和索引已是最優了嗎?
再說一下不一樣引擎的優化,myisam讀的效果好,寫的效率差,這和它數據存儲格式,索引的指針和鎖的策略有關的,它的數據是順序存儲的(innodb數據存儲方式是聚簇索引),他的索引btree上的節點是一個指向數據物理位置的指針,因此查找起來很快,(innodb索引節點存的則是數據的主鍵,因此須要根據主鍵二次查找);myisam鎖是表鎖,只有讀讀之間是併發的,寫寫之間和讀寫之間(讀和插入之間是能夠併發的,去設置concurrent_insert參數,按期執行表優化操做,更新操做就沒有辦法了)是串行的,因此寫起來慢,而且默認的寫優先級比讀優先級高,高到寫操做來了後,能夠立刻插入到讀操做前面去,若是批量寫,會致使讀請求餓死,因此要設置讀寫優先級或設置多少寫操做後執行讀操做的策略;myisam不要使用查詢時間太長的sql,若是策略使用不當,也會致使寫餓死,因此儘可能去拆分查詢效率低的sql,
innodb通常都是行鎖,這個通常指的是sql用到索引的時候,行鎖是加在索引上的,不是加在數據記錄上的,若是sql沒有用到索引,仍然會鎖定表,mysql的讀寫之間是能夠併發的,普通的select是不須要鎖的,當查詢的記錄遇到鎖時,用的是一致性的非鎖定快照讀,也就是根據數據庫隔離級別策略,會去讀被鎖定行的快照,其它更新或加鎖讀語句用的是當前讀,讀取原始行;由於普通讀與寫不衝突,因此innodb不會出現讀寫餓死的狀況,又由於在使用索引的時候用的是行鎖,鎖的粒度小,競爭相同鎖的狀況就少,就增長了併發處理,因此併發讀寫的效率仍是很優秀的,問題在於索引查詢後的根據主鍵的二次查找致使效率低;
ps:很奇怪,爲什innodb的索引葉子節點存的是主鍵而不是像mysism同樣存數據的物理地址指針嗎?若是存的是物理地址指針不就不須要二次查找了嗎,這也是我開始的疑惑,根據mysism和innodb數據存儲方式的差別去想,你就會明白了,我就不費口舌了!
因此innodb爲了不二次查找可使用索引覆蓋技術,沒法使用索引覆蓋的,再延伸一下就是基於索引覆蓋實現延遲關聯;不知道什麼是索引覆蓋的,建議你不管如何都要弄清楚它是怎麼回事!
盡你所能去優化你的sql吧!說它成本低,卻又是一項費時費力的活,須要在技術與業務都熟悉的狀況下,用心去優化才能作到最優,優化後的效果也是立竿見影的!++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
主要從三個維度說:Why, How, When。
0. sql vs nosql
有些跑題,但也是很重要的一方面。
Why: nosql天生分佈,並且大多針對某種類型的數據、某種使用場景作過優化。
好比大批量的監控數據,用mysql存費時費力,能夠選擇mongo,甚至時間序列數據庫,存取會有量級提高。
How: 找對應解決方案。
When: 有足夠誘惑 - 針對使用場景,有成熟解決方案,效率得到大量提高。
1. 優化shema、sql語句+索引
Why: 再好的MySQL架構也扛不住一個頻繁的垃圾查詢。不合理的schema設計也會致使數據存取慢。索引的做用沒必要多說,但如innodb下,錯的索引帶來的可能不僅是查詢變慢而已。
How: 設計階段就須要預計QPS及數據規模,參考業務場景對數據的要求,合理設計表結構(參考mysql在線DDL問題),甚至違反設計範式作到適當冗餘。生產環境分析慢日誌,優化語句。索引的設計須要知道索引是怎麼用的,好比innodb的加鎖機制。
When: 這個不只僅是第一個要考慮的,而應該是須要持續去優化的。特別是要參考業務。但實際環境中若是是這個的問題,那通常比較幸運了,由於通常已經優化過不少了。實際中遇到的通常是更深的問題。
2. 緩存
緩存沒有那麼簡單。
緩存對於應用不是徹底透明的,除非你用Django這種成熟框架,並且緩存粒度很大,但實際。。。像python,最少也得加幾個裝飾器。
如何保證緩存裏面的數據是始終正確的?寫數據前失效緩存仍是寫數據後?
緩存掛了或者過冷,流量壓到後端mysql了怎麼辦?
緩存也不是萬能的。寫多讀少,命中率會很低。
How: memcache用作緩存,redis用於須要持久化的場景。(redis能不能徹底取代memcache?呵呵。。)
還可使用mysql自帶的query cache,對應用基本徹底透明。但會受限於本機。並且只緩存查詢結果,mc和redis能夠緩存一些加工後的數據。
並且數據量大、QPS大的狀況下,也須要考慮分片及HA的問題。若是有一個數據過熱,把一個節點壓垮了怎麼辦?
When: 基本上大多數讀多寫少的場景都能用,寫多的狀況下可能須要考慮考慮。
3. 複製及讀寫分離
Why: 這個實際上是大多數場景下都必須的。由於複製能夠實現備份、高可用、負載均衡。就算嫌麻煩不作負載均衡,那備份下老是要的吧?既然已經備份了,何不加個LVS+HAProxy作下HA?順便稍微修改下應用,讀寫分離也就成了。
How: 節點少的狀況下,主備。前面加Keepalived+HAProxy等組件,失效自動切換。讀寫分離可能須要修改下應用。
節點多的狀況下,一是考慮多級備份,減輕主的壓力。其次能夠引入第三方組件,接管主節點的備份工做。
主主不是很推薦。一是須要考慮數據衝突的狀況,好比錯開id,同時操做數據後衝突解決。其次若是強一致會致使延遲增長,若是有節點掛了,須要等到超時才返回。
When: 主備幾乎大多數場景。甚至不論數據大小。高可用對應用透明,爲啥不用?主主麻煩,建議先用切分。
4. 切分
包括垂直切分和水平切分,實現方式上又包括分庫、分表。
雖然有些難度,但仍是推薦經常使用的。
Why: 垂直切分保證業務的獨立性,防止不一樣業務爭搶資源,畢竟業務是有優先級的。
水平切分主要用於突破單機瓶頸。除了主主外,只有切分能真正作到將負載分配下去。
切分後也可對不一樣片數據進行不一樣優化。如按時間切分,超過必定時間數據不容許修改,就能夠引入壓縮了,數據傳輸及讀取減小不少。
How: 根據業務垂直切分。業務內部分庫、分表。通常都須要修改應用。除分表外,其他實現不是很複雜。有第三方組件可用,但通用高效又靈活的方式,仍是本身寫client。
When: 垂直切分通常都要作,只不過業務粒度大小而已。
分庫有是常常用的,就算當前壓力小,也儘可能分出幾個邏輯庫出來。等規模上去了,很方便就遷移擴展。
水平拆分有必定難度,但若是未來必定會到這個規模,又可能用到,建議越早作越好。由於對應用的改動較大,並且遷移成本高。