點贊多大膽,就有多大產!有支持纔有動力!將技術分享給每一個技術使用者和愛好者!
乾貨滿滿,擺好姿勢,點贊發車!
前言
數據庫優化是一個老生常談的問題,剛入門的小白或者工作N年的光頭對這個問題應該都不陌生,你要面試一箇中高級工程師那麼他就想"哥倆好"一樣那麼粘,面試官肯定會問這個問題,這篇文章我們就和它哥倆好!而且這個問題就是一個送分題,數據庫的優化方案基本就是那些,答案也都是固定的,大家只要好好準備這個問題就不會住你,可以在面試中安排面試官,不然就被面試官安排!話不多說下邊就針對數據庫優化展開講!
相關文章
面試開始
小夥子看你簡歷上寫了Mysql,數據庫優化了解嗎?
摸摸頭之後笑着說數據庫優化不是很瞭解嘿嘿~~~,這時和藹的面試官頭上出現了一抹紅!
如果這時你正好想到了我這篇文章,那麼你就會說數據庫優化方面我還是很有研究的,請您聽我慢慢道來......
首先
面試官我想解釋一下爲什麼做數據庫優化(這個你心裏知道就好了,面試的時候就不要說了)
- 系統的數據都從數據庫上來,數據庫的吞吐量和速度一定程度決定系統的併發和響應速度
- 系統運行與數據量成正比,數據讀處理尤其是查詢自然就慢
- Mysql數據庫的數據最終在磁盤上持久化存儲,讀寫不如Redis等這些內存數據庫
其次
面試官大人我想說一下數據庫優化一般從以下幾個方面來:
- 數據庫設計:數據表設計遵循三範式,使用合適的數據類型,使用合適的存儲引擎
- 適當創建索引
- 數據庫擴展:數據庫的分表分庫,讀寫分離等
- SQL語句優化等
接下來我們一一說明解釋
數據庫設計
數據庫設計3範式
數據庫設計範式如果要滿足N範式必須要先滿足N-1範式
第一範式1NF:字段原子性
第一範式簡單的說就是表中的字段是最小不可再分的,我們下邊舉個例子,我們看到以下一張用戶表。裏邊的字段是不可再分的,這樣就符合第一範式的原子性,可能有些朋友會疑問,這個地址還可以分成省份、城市、區/縣三個字段,是的!如果是一個電商項目它需要再分,那麼就不符合第一範式,所以具體還是看項目的需求,沒有固定標準,在項目需求中它的設計已不可再分那麼就符合第一範式!
第二範式2NF: 消除對主鍵的部分依賴
2NF的使用是需要滿足1NF爲前提,在表中添加一個業務字段,而主鍵不用來做業務處理,比如我們的商品表有商品id,商品id爲商品的主鍵,但是需要創建一個商品編號列來專門處理業務,因爲id太敏感,我們處理業務都是用商品編號來處理,比如展示商品時展示編號等等!
第三範式3NF:在2NF的基礎上添加外鍵
3NF的使用必須滿足2NF,要求表中的每一列只與主鍵直接相關而不是間接相關,(表中的每一列只能依賴於主鍵),比如下面的例子,訂單表中有客戶相關信息,在分離出客戶表之後,訂單表中只需要有一個用戶id即可(外鍵),而不能有其他的客戶信息。因爲其他的客戶信息直接關聯於用戶id,而不是直接與訂單id直接相關。如下圖所示:
分離之後:
三大範式只是一般設計數據庫的基本理念,可以建立冗餘較小、結構合理的數據庫。如果有特殊情況,當然要特殊對待,數據庫設計最重要的是看需求跟性能,需求>性能>表結構。所以不能一味的去追求範式建立數據庫!需求才是粑粑
數據類型
儘量使用可以正確存儲數據的最小數據類型
更小的數據類型意味着更快,佔用更少的磁盤,內存、緩存和處理時間
儘量使用整型表示字符串
因爲字符集和校對規則,使處理字符比整型更復雜,比如:我們使用數據庫內置的datetime類型存儲時間而不是字符類型,我們使用整型存儲ip而不是直接將ip字符串存到數據庫中
儘可能使用not null
這個值是很煩人的,建字段時請儘量指定是否非空,NULL使得索引,統計,比較都變得更復雜,而且索引儘量不要創建到可以爲null的字段上
字符串類型
VARCHAR是可變長字符串
比定長字符串(CHAR)更節省空間,僅使用必要的空間另外VARCHAR需要額外字節記錄字符串長度(不同情況需要字節數不同)
CHAR類型是定長字符串
開發中基本很少用(一些公司甚至基本上不考慮這種類型了),注意:字符串長度定義不是字節數,是字符數
日期和時間類型
datetime
使用8字節存儲空間,保存從1001年到9999年的秒數。與時區無關,默認情況下,Mysql以一種可排序的格式顯示它的值,例如:"2018-10-14 22:30:08"
timestamp
只使用4字節存儲,保存1970年1月1日午夜以來的秒數,依賴於系統時區,和UNIX時間戳相同,轉換函數分別爲FROM_UNIXTIME()和UNIX_TIMESTAMP(),可以設置根據當前時間戳更新,比如我們熟悉的update_time字段
整數類型
UNSIGNED
屬性表示不允許負值,可以使得正數的上限提高一倍,比如tinyint+unsigned可以使原本的-128~127的範圍變爲0~255
tinyint
我們一般用它存儲狀態值而不要用int,如果是Boolean類型,那麼tinyint(1)當值爲1和0時,查詢結果自動轉爲true和false,條件參數相應的也可以直接傳入true和false即可
INT(11)
不會限制值的範圍,只是規定了一些客戶端工具用來顯示的字符的個數,所以對於存儲和計算來說INT(11)和INT(1)相同
IP地址
實際上是32位無符號整數,用INT存儲,Mysql提供轉換函數爲INET_ATON()和INET_NTOA()
小數
decimal不會損失精度,存儲空間會隨數據的增大而增大。double佔用固定空間,較大數的存儲會損失精度,通常存金額用decimal(11,2),這表示整數部分和小數部分分別爲9位和2位注意!,當然可以根據具體的金額大小選擇長度,注意這時候對應的java中用BigDecimal類來處理運算時要仔細,因爲加減法和比較跟平常不一樣
存儲引擎
介紹
數據庫存儲引擎是數據庫底層組件,數據庫管理系統使用數據引擎進行創建、查詢、更新和刪除數據操作。不同的存儲引擎提供不同的存儲機制、索引技巧、鎖定水平等功能,使用不同的存儲引擎還可以獲得特定的功能。我們可以通過SHOW ENGINES;
InnoDB存儲引擎
InnoDB越做越好從MySQL5.5版本之後,MySQL的默認內置存儲引擎已經是InnoDB,主要特點有
- 容災恢復性比較好
- 支持事務,默認事務隔離界別爲可重複讀
- 使用的鎖粒度爲行鎖,可以支持更高的併發
- 支持外鍵
- 配合一些熱備工具可以支持在線熱備份
- 在InnoDB中存在着緩衝管理,通過緩衝池,將索引和數據全部緩存起來,加快查詢的速度
- 對於InnoDB類型的表,其數據的物理組織形式是聚簇表。所有的數據按照主鍵來組織。根據主鍵進行排序,數據和索引放在一塊,都位於B+數的葉子節點上
MyISAM存儲引擎
在5.5版本之前,MyISAM是MySQL的默認存儲引擎,該存儲引擎併發性差,不支持事務,所以使用場景比較少,主要特點有
- 不支持事務
- 不支持外鍵,如果強行增加外鍵,不會提示錯誤,只是外鍵不其作用
- 對數據的查詢緩存只會緩存索引,不會像InnoDB一樣緩存數據,而且是利用操作系統本身的緩存
- 默認的鎖粒度爲表級鎖,所以併發度很差,加鎖快,鎖衝突較少,所以不太容易發生死鎖
- 支持全文索引(MySQL5.6之後,InnoDB存儲引擎也對全文索引做了支持),但是MySQL的全文索引基本不會使用,對於全文索引,現在有其他成熟的解決方案,比如:ElasticSearch,Solr,Sphinx等
- 數據庫所在主機如果宕機,MyISAM的數據文件容易損壞,而且難恢復
MEMORY存儲引擎
將數據存在內存中,和市場上的Redis,memcached等思想類似,爲了提高數據的訪問速度,主要特點有
- 支持的數據類型有限制,不支持TEXT和BLOB類型,對於字符串類型的數據,只支持固定長度的行,VARCHAR會被自動存儲爲CHAR類型
- 支持的鎖粒度爲表級鎖。所以,在訪問量比較大時,表級鎖會成爲MEMORY存儲引擎的瓶頸
- 由於數據是存放在內存中,所以在服務器重啓之後,所有數據都會丟失
- 查詢的時候,如果有用到臨時表,而且臨時表中有BLOB,TEXT類型的字段,那麼這個臨時表就會轉化爲MyISAM類型的表,性能會急劇降低
ARCHIVE存儲引擎
ARCHIVE存儲引擎適合的場景有限,由於其支持壓縮,故主要是用來做日誌,流水等數據的歸檔,主要特點有
- 支持Zlib壓縮,數據在插入表之前,會先被壓縮
- 僅支持SELECT和INSERT操作,存入的數據就只能查詢,不能做修改和刪除;
- 只支持自增鍵上的索引,不支持其他索引
CSV存儲引擎
數據中轉試用,主要特點有
- 其數據格式爲.csv格式的文本,可以直接編輯保存
- 導入導出比較方便,可以將某個表中的數據直接導出爲csv,試用Excel辦公軟件打開
選擇依據
如果沒有特殊需求默認使用InnoDB引擎即可
MyISAM:以讀寫插入爲主的應用程序,比如博客系統、新聞門戶網站。
Innodb:更新(刪除)操作頻率也高,或者要保證數據的完整性;併發量高,支持事務和外鍵保證數據完整性。比如OA自動化辦公系統
索引
已爲客官備好,輕點哦《這小夥子把MySQL索引使用講的真明白,真好,快來戳他》
索引數據結構在這在這《搞懂MySQL數據庫索引數據結構這一篇足夠從此不再萌萌噠》
MySQL讀寫分離
MySQL分表分庫
一樣點一下就成《手把手基於Mycat實現MySQL數據拆分》
SQL優化
這裏列舉出來一些用過的,看到的歡迎大家評論區補充討論
1、查詢儘量避免全表掃描,首先考慮在where、order by字段上添加索引
2、避免在where字段上使用NULL值,所以在設計表時儘量使用NOT NULL約束,有些數據會默認爲NULL,可以設置默認值爲0或者-1
3、避免在where子句中使用!=或<>操作符,Mysql只對<,<=,=,>,>=,BETWEEN,IN,以及某些時候的LIKE使用索引
4、避免在where中使用OR來連接條件,否則可能導致引擎放棄索引來執行全表掃描,可以使用UNION進行合併查詢
select id from t where num = 30 union select id from t where num = 40;
5、儘量避免在where子句中進行函數或者表達式操作
6、最好不要使用select * from t,用具體的字段列表代替"*",不要返回用不到的任何字段
7、in 和 not in 也要慎用,否則會導致全表掃描,如
select id from t where num IN(1,2,3)如果是連續的值建議使用between and,select id from t where between 1 and 3;
8、select id from t where col like %a%;模糊查詢左側有%會導致全表檢索,如果需要全文檢索可以使用全文搜索引擎比如es,slor
9、limit offset rows關於分頁查詢,儘量保證不要出現大的offset,比如limit 10000,10相當於對已查詢出來的行數棄掉前10000行後再取10行,完全可以加一些條件過濾一下(完成篩選),而不應該使用limit跳過已查詢到的數據。這是一個==offset做無用功==的問題。對應實際工程中,要避免出現大頁碼的情況,儘量引導用戶做條件過濾
關注本系列文章的朋友應該發現,這裏的未完待續已經消失,我們的MySQL優化就告一段落,主要從數據庫設計、索引、數據庫拆分和SQL語句上進行優化,更多優化方案希望大家通過評論區讓曉添和讀者朋友們都學習到,謝謝閱讀,點贊收藏和關注!