關於資料庫優化的一些想法
優化table結構
#1 列資料型別儘量使用數字型別,避免使用字元型別,後者不僅會佔用較多儲存空間而且會降低查詢效率(逐字元比較);
#2 優先使用VARCHAR,變長欄位儲存空間小,還可以提升查詢效率;
#3 對需要經常作為where條件出現的column新增索引,通過新增constraint設定為unique key(自動新增索引);
#4 對於單DB而言,primary key使用AUTO_INCREMENT的BIGINT是最合適的,自增型別的ID便於分頁和索引(UUID的key具有無序性,而且字元型別耗費資源);對於DB叢集而言,以DB自身的演算法生成ID不能保證唯一性,所以需要引入一個全域性的ID generator保證唯一性的同時提供較好的效能,Redis和ZooKeeper是比較好的選擇;另外通過給不同的DB或者App加入一個唯一的SN也能最大程度保證唯一性。
優化SQL語句
#1 對於查詢語句儘量新增NO_LOCK標誌,提升DB併發量;
#2 篩選條件放置位置按優先順序從高到低依次為on, where, having,越到後面需要儲存的資料和進行的計算就越多:
#3 在where子句中避免使用[!=, <>, or, in, not in, %, like, is null, is not null],原因是會導致DB引擎放棄使用index而進行全文掃描,推薦使用exist或者not exist;
#4 如果某列的重複資料較多,則index不能發揮較好的效能,反而過多的索引還會降低insert和update的效率;
#5 對於涉及多張表的聯合查詢,依次將資料量最小的表按從右到左的順序放置,保證中間虛擬表只包含最少的資料;
#6 DB引擎解析一條SQL語句的流程
1 [8] SELECT [9] DISTINCT [11] TOP # <column list> 2 [1] FROM <left table> [3] <join type> JOIN <right table> [2] ON <join condition> 3 [4] WHERE <where condition> 4 [5] GROUP BY <group by list> 5 [6] WITH <CUBE | ROLLUP>6 [7] HAVING <having condition> 7 [10] ORDER BY <order by list>;
對於sql中每一個子句都會生成一張virtual table用於儲存中間資料,因此需要儘可能的減少每一步中VT的資料,上述SQL語句的執行順序如下:
【1-3】:FROM子句中如果只有一張表,則不做任何處理;如果有兩張表,則對其做笛卡爾積處理生成VT1;然後根據join condition執行ON子句進行篩選並生成VT2;接著根據join type(LEFT, RIGHT, INNER, OUTTER)執行JOIN子句生成VT3;如果有超過兩張表,則依次處理完前兩張表之後在VT3的基礎上繼續處理之後的表,直到所有的表都處理完最終生成VT4;SQL直譯器一般按照從右到左的順序處理表,因此需要保證越是靠右的表的資料量是越少的。
【4】:根據where condition執行WHERE子句,對中間表資料進行篩選並生成VT5。
【5】:根據group by list執行GROUP子句,對中間表進行分組並生成VT6。
【6】:根據CUBE還是ROLLUP執行WITH子句,對分組結果進行彙總並生成VT7。
【7】:根據having condition執行HAVING子句,對分組結果進行篩選並生成VT8。
【8】:根據column list執行SELECT子句,對中間表進行資料對映並生成VT9。
【9】:執行DISTINCT子句,將重複的行從表中去除並生成VT10。
【10】:根據order by list執行ORDER BY子句,對中間表進行排序並生成VT11。
【11】:根據#(數量或者比例)執行TOP子句,從中間表中去除相應資料量結果並生成VT12。
定期檢視SQL執行status
SQL server profiler,需要定期檢視DB中超過某個執行時間限制的, 或者是資源佔用率超過某些限制的SQL語句;
讀寫分離
對於單庫DB設計而言,如果讀寫請求同時操作一個庫,勢必極大的降低DB效能;而一般情況讀操作遠遠多於寫操作,這時候就可以通過master-slave的設計,將同一個庫的資料做多個備份,寫操作僅針對master進行,讀操作在master和salve都可以進行;master定期將自身的資料更新到slave上,保證資料的最終一致性同時提升DB的QPS。
分庫分表
對於單庫多表而言,業務相關的資料儘量放置於同一個庫1裡面(垂直切分,業務拆分),比如使用者A支付B元購買了C商品寄送到D地址,這樣可以保證較好的讀取效能,這樣也能將事務限制在一個庫的範圍內;但是當庫1的某張表的資料記錄超過某個限制(單庫單表資料量在800萬條以內具有比較好的讀寫效能),需要另起一個庫2放置新增加的各個表的資料(水平切分),庫1和庫2具有相同的表組成,資料量增長的時候也以此類推,之後新的業務資料需要訪問和操作某個庫時,需要根據某種對映演算法(key % n)選擇對應的庫進行資料讀寫,這個key必須是滿足分散式ID的全域性唯一性;
在異地多活中心的架構中,分庫的設計可以解決高併發訪問的問題,比如有100個手機庫存,如果放到一個庫裡,則所有併發都需要競爭同一把鎖,但如果將100個手機庫存平均分配到4個庫裡,每個庫有25個庫存,則併發訪問可以同時競爭四把鎖,極大提升了併發效率;另外一種設計是將100個手機庫存維護在一個DB庫中,但使用redis等快取基於視窗機制一次性獲取多個庫存到快取中,比如redis-01預獲取0-24號庫存,則更新DB庫存為75,redis-02預獲取25-49庫存,則更新DB庫存為50,這樣的設計可以減少較慢的DB鎖的競爭,而使用較快的cache鎖的競爭替代。
使用cache替代DB檢索
使用redis或者memcached作為資料快取