數據庫優化有不少能夠講,按照支撐的數據量來分能夠分爲兩個階段:單機數據庫和分庫分表,前者通常能夠支撐500W或者10G之內的數據,超過這個值則須要考慮分庫分表。另外,通常大企業面試每每會從單機數據庫問起,一步一步問到分庫分表,中間會穿插不少數據庫優化的問題。本文試圖描述單機數據庫優化的一些實踐,數據庫基於mysql,若有不合理的地方,歡迎指正。
一、表結構優化
在開始作一個應用的時候,數據庫的表結構設計每每會影響應用後期的性能,特別是用戶量上來了之後的性能。所以,表結構優化是一個很重要的步驟。
1.一、字符集
通常來講儘可能選擇UTF-8,雖然在存中午的時候GBK比UTF-8使用的存儲空間少,可是UTF-8兼容各國語言,其實咱們沒必要爲了這點存儲空間而犧牲了擴展性。事實上,後期若是要從GBK轉爲UTF-8所要付出的代價是很高的,須要進行數據遷移,而存儲空間徹底能夠用花錢擴充硬盤來解決。
1.二、主鍵
在使用mysql的innodb的時候,innodb的底層存儲模型是B+樹,它使用主鍵做爲聚簇索引,使用插入的數據做爲葉子節點,經過主鍵能夠很快找到葉子節點,從而快速獲取記錄。所以在設計表的時候須要增長一個主鍵,並且最好要自增。由於自增主鍵可讓插入的數據按主鍵順序插入到底層的B+樹的葉子節點中,因爲是按序的,這種插入幾乎不須要去移動已有的其它數據,因此插入效率很高。若是主鍵不是自增的,那麼每次主鍵的值近似隨機,這時候就有可能須要移動大量數據來保證B+樹的特性,增長了沒必要要的開銷。
1.三、字段
1.3.一、建了索引的字段必須加上not null約束,而且設置default值
1.3.二、不建議使用float、double來存小數,防止精度損失,建議使用decimal
1.3.三、不建議使用Text/blob來保存大量數據,由於對大文本的讀寫會形成比較大的I/O開銷,同時佔用mysql的緩存,高併發下會極大的下降數據庫的吞吐量,建議將大文本數據保存在專門的文件存儲系統中,mysql中只保存這個文件的訪問地址,好比博客文章能夠保存在文件中,mysql中只保存文件的相對地址。
1.3.四、varchar類型長度建議不要超過8K。
1.3.五、時間類型建議使用Datetime,不要使用timestamp,雖然Datetime佔用8個字節,而timestamp只佔用4個字節,可是後者要保證非空,並且後者是對時區敏感的。
1.3.六、建議表中增長gmt_create和gmt_modified兩個字段,用來記錄數據建立的修改時間。這兩個字段創建的緣由是方便查問題。
1.四、索引建立
1.4.一、這個階段因爲對業務並不瞭解,因此儘可能不要盲目加索引,只爲一些必定會用到索引的字段加普通索引。
1.4.二、建立innodb單列索引的長度不要超過767bytes,若是超過會用前255bytes做爲前綴索引
1.4.三、建立innodb組合索引的各列索引長度不要超過767bytes,一共加起來不要超過3072bytes
二、SQL優化
通常來講sql就那麼幾種:基本的增刪改查,分頁查詢,範圍查詢,模糊搜索,多表鏈接
2.一、基本查詢
通常查詢須要走索引,若是沒有索引建議修改查詢,把有索引的那個字段加上,若是因爲業務場景無法使用這個字段,那麼須要看這個查詢調用量大不大,若是大,好比天天調用10W+,這就須要新增索引,若是不大,好比天天調用100+,則能夠考慮保持原樣。另外,select * 儘可能少用,用到什麼字段就在sql語句中加什麼,沒必要要的字段就別查了,浪費I/O和內存空間。
2.二、高效分頁
limit m,n其實質就是先執行limit m+n,而後從第m行取n行,這樣當limit翻頁越日後翻m越大,性能越低。好比
select * from A limit 100000,10,這種sql語句的性能是不好的,建議改爲下面的版本:
selec id,name,age from A where id >=(select id from A limit 100000,1) limit 10
2.三、範圍查詢
範圍查詢包括between、大於、小於以及in。Mysql中的in查詢的條件有數量的限制,若數量較小能夠走索引查詢,若數量較大,就成了全表掃描了。而between、大於、小於等,這些查詢不會走索引,因此儘可能放在走索引的查詢條件以後。
2.四、模糊查詢like
使用 like %name%這樣的語句是不會走索引的,至關於全表掃描,數據量小的時候不會有太大的問題,數據量大了之後性能會降低的很厲害,建議數據量大了之後使用搜索引擎來代替這種模糊搜索,實在不行也要在模糊查詢前加個能走索引的條件。
2.五、多表鏈接
子查詢和join均可以實如今多張表之間取數據,可是子查詢性能較差,建議將子查詢改爲join。對於mysql的join,它用的是Nested Loop Join算法,也就是經過前一個表查詢的結果集去後一個表中查詢,好比前一個表的結果集是100條數據,後一個表有10W數據,那麼就須要在100*10W的數據集合中去過濾獲得最終的結果集。所以,儘可能用小結果集的表去和大表作join,同時在join的字段上創建索引,若是建不了索引,就須要設置足夠大的join buffer size。若是以上的技巧都沒法解決join所帶來的性能降低的問題,那乾脆就別用join了,將一次join查詢拆分紅兩次簡單查詢。另外,多表鏈接儘可能不要超過三張表,超過三張表通常來講性能會不好,建議拆分sql。
三、數據庫鏈接池優化
數據庫鏈接池本質上是一種緩存,它是一種抗高併發的手段。數據庫鏈接池優化主要是對參數進行優化,通常咱們使用DBCP鏈接池,它的具體參數以下:
3.1 initialSize
初始鏈接數,這裏的初始指的是第一次getConnection的時候,而不是應用啓動的時候。初始值能夠設置爲併發量的歷史平均值
3.二、minIdle
最小保留的空閒鏈接數。DBCP會在後臺開啓一個回收空閒鏈接的線程,當該線程進行空閒鏈接回收的時候,會保留minIdle個鏈接數。通常設置爲5,併發量實在很小能夠設置爲1.
3.三、maxIdle
最大保留的空閒鏈接數,按照業務併發高峯設置。好比並發高峯爲20,那麼當高峯過去後,這些鏈接不會立刻被回收,若是過一小段時間又來一個高峯,那麼鏈接池就能夠複用這些空閒鏈接而不須要頻繁建立和關閉鏈接。
3.四、maxActive
最大活躍鏈接數,按照能夠接受的併發極值設置。好比單機併發量可接受的極值是100,那麼這個maxActive設置成100後,就只能同時爲100個請求服務,多餘的請求會在最大等待時間以後被拋棄。這個值必須設置,能夠防止惡意的併發攻擊,保護數據庫。
3.五、maxWait
獲取鏈接的最大等待時間,建議設置的短一點,好比3s,這樣可讓請求快速失敗,由於一個請求在等待獲取鏈接的時候,線程是不能夠被釋放的,而單機的線程併發量是有限的,若是這個時間設置的過長,好比網上建議的60s,那麼這個線程在這60s內是沒法被釋放的,只要這種請求一多,應用的可用線程就少了,服務就變得不可用了。
3.六、minEvictableIdleTimeMillis
鏈接保持空閒而不被回收的時間,默認30分鐘。
3.七、validationQuery
用於檢測鏈接是否有效的sql語句,通常是一條簡單的sql,建議設置
3.八、testOnBorrow
申請鏈接的時候對鏈接進行檢測,不建議開啓,嚴重影響性能
3.九、testOnReturn
歸還鏈接的時候對鏈接進行檢測,不建議開啓,嚴重影響性能
3.十、testWhileIdle
開啓了之後,後臺清理鏈接的線程會沒隔一段時間對空閒鏈接進行validateObject,若是鏈接失效則會進行清除,不影響性能,建議開啓
3.十一、numTestsPerEvictionRun
表明每次檢查連接的數量,建議設置和maxActive同樣大,這樣每次能夠有效檢查全部的連接。
3.十二、預熱鏈接池
對於鏈接池,建議在啓動應用的時候進行預熱,在還未對外提供訪問以前進行簡單的sql查詢,讓鏈接池充滿必要的鏈接數。
四、索引優化
當數據量增長到必定程度後,靠sql優化已經沒法提高性能了,這時候就須要祭出大招:索引。索引有三級,通常來講掌握這三級就足夠了,另外,對於創建索引的字段,須要考慮其選擇性。
4.一、一級索引
在where後面的條件上創建索引,單列能夠創建普通索引,多列則創建組合索引。組合索引須要注意最左前綴原則。
4.二、二級索引
若是有被order by或者group by用到的字段,則能夠考慮在這個字段上建索引,這樣一來,因爲索引自然有序,能夠避免order by以及group by所帶來的排序,從而提升性能。
4.三、三級索引
若是上面兩招還不行,那麼就把所查詢的字段也加上索引,這時候就造成了所謂的索引覆蓋,這樣作能夠減小一次I/O操做,由於mysql在查詢數據的時候,是先查主鍵索引,而後根據主鍵索引去查普通索引,而後根據普通索引去查相對應的記錄。若是咱們所須要的記錄在普通索引裏都有,那就不須要第三步了。固然,這種建索引的方式比較極端,不適合通常場景。
4.四、索引的選擇性
在創建索引的時候,儘可能在選擇性高的字段上創建。什麼是選擇性高呢?所謂選擇性高就是經過這個字段查出來的數據量少,好比按照名字查一我的的信息,查出來的數據量通常會不多,而按照性別查則可能會把數據庫一半的數據都查出來,因此,名字是一個選擇性高的字段,而性別是個選擇性低的字段。
五、歷史數據歸檔
當數據量到了一年增長500W條的時候,索引也無能爲力,這時候通常的思路都是考慮分庫分表。若是業務沒有爆發式增加,可是數據的確在緩慢增長,則能夠不考慮分庫分表這種複雜的技術手段,而是進行歷史數據歸檔。咱們針對生命週期已經完結的歷史數據,好比6個月以前的數據,進行歸檔。咱們可使用quartz的調度任務在凌晨定時將6個月以前的數據查出來,而後存入遠程的hbase服務器。固然,咱們也須要提供歷史數據的查詢接口,以備不時之需。mysql