您好,登錄后才能下訂單哦!
【本文轉自云棲社區 作者neo.Wang】
在初創型互聯網公司中,開發們整天想的唯一一件事,就是“把功能做出來”。而當公司業務量逐漸上漲、甚至翻了好幾番之后,最開始的程序上的問題,就一個接一個地暴露了出來。
其中,最明顯的,就是數據庫的壓力問題。下文提到的數據庫,都指RDS for MYSQL。
開發在設計表結構的時候,很大程度上是在參考產品原型的設計。通常會把產品原型中,需要一起查詢的條件、一起展示的字段,都放在同一張表中。而互聯網公司的產品迭代又是非常快的,新功能層出不窮,開發沒有時間重新梳理數據庫結構,就盲目地在原來的表上增加冗余字段,為了使功能盡快開發完成。這就使得某些“老古董”的表,越來越寬,七八十個字段以上的大寬表越來越多。
(1)單個InnoDB表的字段數,建議少于50個;
(2)大字段,例如:text、blob類型,考慮單獨存放;
在后臺或者部分前端的功能中,常常會出現類似MIS系統的列表查詢功能。一般這種功能,都是按照選定的查詢條件,先查出行數,再按照分頁規則查詢出第一頁的數據。于是,就出現了這樣的幾種情況:
(1)查詢列表明細,是需要使用多表關聯查詢的。開發為了方便,直接把這個查詢明細的“多表關聯”的SQL拿過來,把select后邊的字段改成count(*),就直接作為查詢行數的SQL,甚至連order by都不去掉。而從業務上來說,查詢行數的時候,只需要查詢其中一張表就可以了;
(2)代碼邏輯的設計不合理,導致某些開發,直接套用“首頁”邏輯,查詢后續每一頁的時候,都重新查詢了一遍行數;
(1)增加SQL審核機制,不合規范的SQL不允許上線;
(2)增加代碼審核機制;
很常見的情況是,某些表最開始的數據量很小,后來由于產品功能重心的調整,變成了大表。之前做好的程序、SQL、表結構卻沒有跟著調整。就出現了很多大表查詢,沒走索引,導致的慢查詢。而慢查詢堆積多了,整個數據庫就癱瘓了,于是就出現了“次要業務拖累主要業務”的現象。
(1)根據查詢場景,設置合理的索引,組合索引優先;
(2)當組合索引和單字段索引同時存在時,建議刪掉單字段索引,避免優化器做“無用功”;
這里說的線上數據,指的是直接面對廣大用戶的數據;線下數據,是面向公司內部客服、運營的后臺系統用到的數據。后臺系統由于工作職責的不同,會有各種各樣的查詢需求,有的可能會很大、很復雜,比如導出一整個月、一整個季度的數據。會直接導致數據的壓力非常大,進而影響了整個數據庫實例,導致線上系統發生故障。
線上數據的特點是:
線下數據的特點是:
綜上,兩類數據從各個方面都是完全不同的。要把線上數據和線下數據隔離開來。更新時,統一更新線上數據;查詢時,線上查線上,線下查線下。線上數據通過DTS等實時數據同步的方式更新到線下。
某些列表查詢類場景,可能涉及到10~20個查詢條件,而且檢索數據量一般也很大。此時再使用MYSQL就比較吃力了,索引幾乎無法覆蓋。
除了關系型數據庫之外,我們有很多不同的數據存儲的選擇,比如:搜索引擎類、NOSQL類、時序類、緩存類,等等。應當根據不同的查詢場景,選擇最適合的數據存儲方式。企圖用MYSQL解決一切問題,是不明智的。
比如,子查詢。開發站在人類的角度思考問題,就會出現形如:
SELECT * FROM table1 WHERE id IN ( SELECT id FROM table2 );
這種子查詢。而MYSQL在處理子查詢的時候,是拿外層的每一條數據,去內層掃描,結果就是掃描了table1的行數 × table2的行數次。
避免使用子查詢,改為通過索引做表關聯等方式;
部分開發會在SQL中寫例如case when、group by + count/sum等的計算。MYSQL擅長的是,數據的查詢與存儲,并不擅長做計算——雖然它可以做。導致出現了很多慢SQL。MYSQL只對查詢做了優化,并沒有對計算做優化。
(1)group by + count/sum可以考慮進行預計算;
(2)case when可以在業務端或者前端進行;
(3)要有效利用每個工具最擅長做的事。
部分表在bigint類型的、存放時間戳的字段上做了索引,而查詢的條件是精確到天的。某些開發就會把SQL寫成:
WHERE from_unixtime(create_timestamp) >= '2018-01-01' AND from_unixtime(create_timestamp) < '2018-02-01'
這樣。在索引字段上使用函數,索引就起不到作用,掃描數據的時候依然是全表掃描,并對每一行數據的create_timestamp做from_unixtime運算。
如:
WHERE from_unixtime(create_timestamp) >= '2018-01-01' AND from_unixtime(create_timestamp) < '2018-02-01'
這種場景,可以改為:
WHERE create_timestamp >= unix_timestamp('2018-01-01') AND create_timestamp < unix_timestamp('2018-02-01')
這樣,只會計算一次,然后直接去匹配索引。避免了全表掃描。
部分對數據實時性要求不高的場景。會有相同條件的查詢頻繁執行的情況,甚至于并發執行多個相同查詢條件的查詢。這時候如果每次都查詢數據庫,勢必造成了資源的浪費。
把這部分查詢結果,緩存到redis中。把大部分請求量引到redis去。
由于對MYSQL依賴嚴重,導致很多更適合存在NOSQL數據庫的數據,也被存到了MYSQL中,而且行數非常多。這樣的表,無論是查詢、還是更新、或是DDL操作,都需要停服之后、花大量時間去做。
(1)單表不要超過1千萬行,大小不要超過5G;如果超過,可以考慮分庫分表;
(2)根據場景,考慮用其他數據存儲工具、或其他業務上的邏輯來解決大表的問題;
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。