如何從設計和規範上規避RDS效能問題?

tianxiaoxu發表於2018-06-22

【本文轉自雲棲社群 作者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)當組合索引和單欄位索引同時存在時,建議刪掉單欄位索引,避免最佳化器做“無用功”;

場景四:線上資料和線下資料沒有隔離

現象

這裡說的線上資料,指的是直接面對廣大使用者的資料;線下資料,是面向公司內部客服、運營的後臺系統用到的資料。後臺系統由於工作職責的不同,會有各種各樣的查詢需求,有的可能會很大、很複雜,比如匯出一整個月、一整個季度的資料。會直接導致資料的壓力非常大,進而影響了整個資料庫例項,導致線上系統發生故障。

建議

  • 線上資料的特點是:

    1. 訪問量大;
    2. 每個使用者只查自己的資料,可以命中索引;
    3. 查詢條件簡單;
    4. 返回條數少;
    5. 對響應時間要求極高;
    6. 對資料的時效性要求高;
  • 線下資料的特點是:

    1. 訪問量小;
    2. 後臺同事會查詢整個平臺的資料,不容易命中索引;
    3. 查詢條件複雜;
    4. 返回條數大;
    5. 對響應時間要求不高;
    6. 對資料的時效性要求不是特別高;

綜上,兩類資料從各個方面都是完全不同的。要把線上資料和線下資料隔離開來。更新時,統一更新線上資料;查詢時,線上查線上,線下查線下。線上資料透過DTS等實時資料同步的方式更新到線下。

場景五:過於依賴MYSQL,沒有考慮其他的儲存

現象

某些列表查詢類場景,可能涉及到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)根據場景,考慮用其他資料儲存工具、或其他業務上的邏輯來解決大表的問題;

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/31137683/viewspace-2156533/,如需轉載,請註明出處,否則將追究法律責任。

相關文章