數據庫優化(MySql)

1、數據庫相關概念的介紹sql

一、數據庫的訪問路徑數據庫

    在SQL語句可以被真正執行以前,優化器必須首先肯定如何訪問數據。這包括:應該使用哪個索引,索引的訪問方式如何,是否須要輔助式隨機讀,等等。從一條SQL,到優化器優化,再到引擎進行數據查詢,落地到數據的存儲頁面,這是一個訪問路徑肯定的過程。緩存

二、索引片微信

       索引片即表明謂詞表達式所肯定的值域範圍,而訪問路徑的成本很大程度上取決於索引片的厚度。網絡

索引片越厚,須要掃描的索引頁就越多,須要處理的索引記錄也越多,並且最大的開銷仍是來自於須要對標進行同步讀操做。(數據越大,維護的索引也越大,對增長和修改影響很大)相反,索引片比較窄,就會顯著減小索引訪問的那部分開銷,同時會有更少的表同步讀取上。同步讀是一個隨機IO操做,單次的讀取就要耗費10ms左右的時間。所以咱們須要經過謂詞來肯定索引片的厚度,過濾的值域範圍越少,索引片厚度就越窄。那麼謂詞必定就能匹配到索引麼,或者說匹配的規則是什麼?架構

三、肯定匹配列併發

    肯定匹配列以後咱們能夠知道當前的查詢會用到哪些索引,以及匹配到該索引的哪些列,最終能夠提早鎖定數據的訪問範圍,爲數據的讀取節省讀取壓力。相對於沒用匹配到索引的查詢,有匹配列的查詢,條件過濾是前置的,而沒有匹配到索引的查詢,條件過濾是後置的,即全表掃描以後,再過濾結果,如此磁盤IO壓力過大。另外 「最左匹配」原則也是基於匹配列規則而來,爲什麼是最左匹配,除了B樹的原理以外,還有一個重要的緣由,在覈對匹配列的時候,是從頭至尾依次檢查索引列。因此對因而否可以匹配到索引,where後面的謂詞順序不重要,重要的是索引列的順序框架

四、排序函數

    物化結果集意味着經過執行必要的數據庫訪問來構建結果集。最好狀況下,只須要返回一條記錄,而最壞的狀況下須要返回多條記錄,須要發起大量的磁盤讀取。而排序就是其中一種。在如下狀況中,一次fetch調用只須要物化一條記錄,不然對結果進行排序的時候就須要物化整個結果集。高併發

2、數據庫的架構優化(分庫分表)

1.熟悉業務,業務需求是咋樣的,要達到什麼樣的目標。

2.數據庫容器預估。

分庫分表最重要的是要先作容器預估,依據數據量和業務特性估算出容器/庫/表的數量及分庫分表規則。同時根據實際需求要根據壓測結果來決定;如壓測其餘一些指標是否知足需求,如QPS、響應時間等。

3.底層路由策略選擇及實現

咱們知道常見的分表策略有兩種:

hash路由

優勢:可實現數據分散,熱點分散;

不足:增長數據庫節點時,會影響路由策略,需作數據遷移;

分區路由(增量區間路由)

優勢:策略支持動態擴容,理論上可無限擴展;

不足:存在數據熱點問題,新產生的表,讀寫頻率較高;每次查詢須要通過路由策略表。

固然每種策略都不是完美的,只有最適合業務場景的策略纔是好的。固然項目能夠採用的是兩種方式的結合方式。

4.聚合查詢及聚合數據同步的實現

上圖是數據層改造後的架構圖,以前是單表主從模式,改造後爲多個分庫、基礎庫。聚合採用了elastic search (如下簡稱ES)。注:canal是阿里裏巴巴開源的數據庫中間件(它實際上是模擬了數據庫主從複製機制,假裝爲一個從庫,當數據庫(爲不影響主庫生產,咱們監聽的是從庫)binlog有變化時,canal監聽到,經過解析服務解析過濾binlog,把須要的日誌過濾出來。解析後,咱們經過發送MQ消息,消息體是表名和主鍵id,不是整條數據,消費端接到變化的表名和id,實時從庫中查詢最新數據,同步到ES、聚合表)。查詢時,一部分業務會先查詢緩存,不存在再查詢ES,若是降級,纔會查庫,正常的聚合查詢都不會查到庫。

5.歷史數據遷移

遷移步驟以下:


前半部分,從掃描到同步到分庫是新代碼,後面canal到同步ES、聚合表都是複用上面邏輯,這樣設計,下降咱們總體工做量,而且保證數據遷移完整。

6.系統關鍵節點降級

這一部分也很重要,咱們的降級主要有兩點,一是canal同步延遲降級,一是ES不可用降級。第一種以下:

若是canal同步延遲,或者從庫掛掉,開啓開關,掃描主庫數據(最近幾小時)直接同步到ES、聚合表;這樣,即便從庫掛掉,也不影響業務數據,這一點很重要。注意:就是若是服務不當心掛掉,怎麼處理,通常是降級處理。

ES降級,ES不可用時,關閉ES開關,直接查詢聚合表。

3、數據庫索引知識點

1.什麼是索引?

百度百科是這樣描述的:

索引是爲來加速對錶中數據行中的檢索而建立的一種分散的數據結果,時針對錶而創建的,它是由數據頁面之外的索引頁面組成,每一個索引頁中的行都含有邏輯指針,以便加速檢索物理數據。

索引的誤誤區:

  • 新建表時不須要建索引,後續才添加索引。續建立索引的成本也相對高不少。實際項目中,在百萬級的數據中添加索引須要半個小時,或者更長時間(坑的要死)。

  • where條件後的字段均建索引。(過多的索引,也會致使索引文件劇增,也還達不到指望中的效果。)

  • 簡單SQL不須要索引,聯合查詢才須要索引。

  • 聯合索引的順序是where條件後字段的前後順序。(聯合索引的順序,是根據最左前綴原則,以及區分度來區分的,和where條件後字段的前後順序無關。)

  • 對於區分度小的字段上也新建索引,如狀態,性別等字段等。(在區分度較小的字段上新建索引,基本無效,還會增長大量的索引文件。)

索引的區分度:

在說上述問題以前,咱們先來看看另外一個概念,就是區分度。

區分度: 指字段在數據庫中的不重複比

區分度在新建索引時有着很是重要的參考價值,在MySQL中,區分度的計算規則以下:

字段去重後的總數與全表總記錄數的商。

在實際項目中,根據省份進行查詢,同時是百萬級的數據,創建了一個普通索引,因爲省份的區分度並不高,致使至關於沒有創建索引。解決的方法能夠是:組合索引。

2.如何建索引

   (一)  : 區分度
我的強烈建議, 建索引時,必定要先計算該字段的區分度,緣由以下:

1. 單列索引
能夠查看該字段的區分度,根據區分度的大小,也能大概知道在該字段上的新建索引是否有效,以及效果如何。區分度越大,索引效果越明顯。

2.多列索引(聯合索引)
多列索引中其實還有一個字段的前後順序問題,通常是將區分度較高的放在前面,這樣聯合索引才更有效,例如:

select * from t_base_user where name="" and status=1;

像上述語句,若是建聯合索引的話,就應該是:

alter table t_base_user add index idx_name_status(name,status);

而不是:

alter table t_base_user add index idx_status_name(status,name);

(二) 最左前綴匹配原則
  MySQL會一直向右匹配直到遇到範圍查詢(>、<、between、like)就中止匹配,好比

select * from t_base_user where type="10" and created_at<"2017-11-03" and status=1, (該語句僅做爲演示)

在上述語句中,status就不會走索引,由於遇到<時,MySQL已經中止匹配,此時走的索引爲:(type,created_at),其前後順序是能夠調整的,而走不到status索引,此時須要修改語句爲:

select * from t_base_user where type=10 and status=1 and created_at<"2017-11-03"

便可走status索引。

(三) 函數運算
  不要在索引列上,進行函數運算,不然索引會失效。由於b+樹中存的都是數據表中的字段值,但進行檢索時,須要把全部元素都應用函數才能比較,顯然成本太大。在實際項目中的時候,作報表須要查詢月初到月末的數據,一開始咱們採用的是:DATE_FORMAT(o.create_date, "%Y-%m")=#{date},查詢很慢由於是全表掃描,函數的運算在「=」的左邊,後面進行優化以後採用:o.create_date between #{startDate} and #{endDate},速度明顯快起來。

(四) 擴展優先
  擴展優先,不要新建索引,儘可能在已有索引中修改。以下:

select * from t_base_user where name="andyqian" and email="andytohome"

在表t_base_user表中已經存在idx_name索引,若是須要加入idx_name_email的索引,應該是修改idx_name索引,而不是新建一個索引。

4、表設計規範

1.選擇優化的數據類型

儘可能避免null:若是查詢中包含可爲NULL 的列, 對MySQL來講更難優化, 由於可爲NULL 的列使得索引、 索引統計和值比較都更復雜。 可爲NULL的列會使用更多的存儲空間, 在MySQL裏也須要特殊處理。 當可爲NULL的列被索引時, 每一個索引記錄須要一個額外的字節, 在MyISAM裏甚至還可能致使固定大小的索引(例如只有一個整數列的索引)變成可變大小的索引。
禁止使用TEXT、BLOB類型
解讀:會浪費更多的磁盤和內存空間,非必要的大量的大字段查詢會淘汰掉熱數據,致使內存命中率急劇下降,影響數據庫性能
 
禁止使用小數存儲貨幣(能夠把單位算做分,顯示和計算時x100,...):
解讀:使用整數吧,小數容易致使錢對不上。實際項目中:微信使用的是百分制。

禁止使用ENUM,可以使用TINYINT代替:
解讀:
a)增長新的ENUM值要作DDL操做。
b)ENUM的內部實際存儲就是整數,你覺得本身定義的是字符串?

單實例表數目必須小於500。
單表列數目必須小於30。
表必須有主鍵,例如自增主鍵:

解讀:
a)主鍵遞增,數據行寫入能夠提升插入性能,能夠避免page分裂,減小表碎片提高空間和內存的使用。
b)主鍵要選擇較短的數據類型, Innodb引擎普通索引都會保存主鍵的值,較短的數據類型能夠有效的減小索引的磁盤空間,提升索引的緩存效率。
c) 無主鍵的表刪除,在row模式的主從架構,會致使備庫夯住。
禁止使用外鍵,若是有外鍵完整性約束,須要應用程序控制:
解讀:外鍵會致使表與表之間耦合,update與delete操做都會涉及相關聯的表,十分影響sql 的性能,甚至會形成死鎖。高併發狀況下容易形成數據庫性能,大數據高併發業務場景數據庫使用以性能優先。實際項目中有些數據使用了外鍵的原則致使刪除很麻煩,最後去掉了外鍵約束。

2.命名規則

表——「模塊名_表名」。表名最好不要用複數,緣由是在使用ORM框架開發時,代碼生成器根據DB生成類定義,表生成了某個實例的類型定義,而不是實例集合。表名不要太長。緣由之一,某些軟件對錶名最大長度有限制;緣由之二,使用代碼生成器每每會根據表名生產類型名稱,以後懶人會直接使用這一名稱,若是將太長的名稱跨網絡邊界顯然不是明智之舉。

字段——bool類型用「Is」、「Can」、「Has」等表示;日期類型命名必須包含「Date」;時間類型必須包含「Time」。

存儲過程——使用「proc_」前綴。

視圖——使用「view_」前綴。

觸發器——使用「trig_」前綴。

相關文章
相關標籤/搜索