leecode-143.重排列表(連結串列,指標)
資料庫做為當今系統的資料中心,不僅給程式提供了方便,也給開發提供了統一的選擇。資料庫是一個獨立的資料系統,主要是讀寫資料並儲存到指定的檔案中,對外提供了一個很靈活的命令操作端囗。而如今的資料訪問量不但的增加(多的每天過億獨立訪問者),資料記錄量也在增加(多的過億條),而我們的硬體資料傳輸在一定的程度上沒有跟上,導致資料在讀取時存在著延時。資料庫中的資料是一條一條的儲存,而讀取時一般是一條一條的判斷達到篩選的條件,就好比如說是程式中的while一樣,如果我們在程式種用while迴圈個一萬次就可以看到延時(當然資料庫內肯定使用特殊的方式迴圈記錄,但還是跑不了迴圈判斷的過程,只是迴圈的方式要快)。如今MYSQL資料庫一般在高檔伺服器中十萬條內記錄可不特意建立索引來優化。如果資料過百萬時我們還不建立索引,那麼伺服器讀取速度會大大降低,不斷會增加客戶瀏覽頁面等待時間,訪問量大時還會使伺服器崩潰。網上也有很多的統計資料說明如果一個頁面的等待時間超12秒99%的瀏覽者會關閉這個頁面,從而流失客戶。
當然如果你夠有錢可以購買高階的伺服器來支援你的資料庫操作能力,當然即使再好的硬體如果在軟體上沒在做好優化,它的效能也不會好到哪裡去,相反再好的軟體優化如果沒有硬體的支援,也等於白搭。
對於資料庫來說軟體優化主要是減少內部不必要的迴圈判斷來篩選,硬體的優化主要是提高整機的硬體訪問執行速度減少迴圈判斷時間。當然很大的程度上講只能在軟體上進行優化,理想化中希望整個系統達到最佳執行狀態才是一個合格的系統,不過這也只是理想化的,實際中沒有絕對的最佳狀態,系統在執行中會有很多的因素影響到它的執行速度,我們也可以通過獲取伺服器端開始執行程式到返回結果的執行時間,可以看到每次的時間基本不一樣,所以在軟體上的優化只是在最大程式上減少執行時間,只有做這樣才能讓硬體可以執行更多的請求。
這裡只說下軟體方面的優化,當然不能做到所有的優化方式,後期也會在工作中新增,如果你看完後有更好的想法可以給我留言,我會盡快修正。
軟體優化一般有兩類:一類是隻修改程式的執行方式減少不必要的重複操作,一類是以犧牲硬體資源來減少程式執行時間。當然在資料庫中這兩類都可以使用到。比如建立索引就是犧牲硬體資源來換取程式的執行時間,查詢取一條記錄時加一個limit 1就是隻修改程式的執行方式,等。
下面是我收集的一些優化方法。如果有不當之外還請不吝指正。
一,建表時要根據表的用途選擇好儲存引擎,字符集,欄位資料型別。
一般常用的儲存引擎有:
InnoBD 這類引擎支援事務,行級鎖機制,外來鍵約束特殊功能,但查詢速度比MyISAM稍慢,尤其在使用count(*)時,在實際應用中不在這類引擎中使用全表count(*)尤其記錄上十萬條時,其會進行全表查詢,很慢。這類表主要是針對使用它的特殊功能時才會用到,比如要使用到事務。
MyISAM 這類引擎不支援事務,表級鎖機制,外來鍵約束功能。查詢速度比InnoDB稍快,在count(*)更是快到InnoDB沒有辦法比。這類引擎又分三種類型:靜態,動態,壓縮。當建立的表中沒有變長欄位(VARCHAR,xxxTEXT,xxxBLOB等)時系統自動選擇靜態型別,這種型別欄位長度不變所有的記錄儲存空間一樣大,所以查詢速度高,資料受損時,恢復工作相對好做。當建立的表中包含了變長欄位(VARCHAR,xxxTEXT,xxxBLOB等)時系統自動選擇為動態型別,這種型別欄位長度不一所有的記錄儲存空間大小也不一,而且多次修改後會產生碎片,相對於靜態型別來說,這種型別的查詢速度會低,而且長時間修改記錄會新增很多的碎片,浪費空間和降低查詢速度,必需定時使用(optimize table 表名)或優化工具進行碎片整理來優化表。壓縮型別是要藉助myisamchk工具把前兩種型別進行壓縮,壓縮後不能修改資料,只是減少了資料佔用空間,每次讀取時還得進行解壓,所以查詢速度相對要低,只是針對一些舊資料儲存用。
MyISAM Merge 這類引擎是MyISAM的變種,可以合併幾個相同MyISAM的表為一個虛表。常用於日誌和資料倉庫。
MEMORY 這種引擎資料表只存在於記憶體中,它使用雜湊索引,所以資料的讀取速度非常的快,因為資料儲存在記憶體中,所以一般用於臨時表(如儲存session資料)
archive 這種表只支援select,insert語句,而且不支援索引,常應用於日誌記錄和聚合分析方面。
在建立一張表時,我們就得開始考慮優化,根據不同的要求來建立表選擇不同的引擎(當然引擎有很多種,實際中根據不同的需求選擇合適的儲存引擎),引擎會影響到後期的查詢,修改,刪除,插入,和恢復。欄位資料型別選用是體現你對這個表是否負責。要做到,能使用定長資料型別不用變長,能用小的資料型別不用大的資料型別,能使用數字型別不使用字串型,有三個到五個狀態的使用列舉型別。當然在選用這些型別完全看你對這些資料的敏感度,往往選擇不當會造成資料被遺漏或轉換,比如說你選擇了char(10)的長度,但後期插入的記錄超了10個長度,那麼10個後面的字元就會被遺漏。如果你使用了tinyint的短整型,插入了一個400會導致資料過大而轉換為最大值255 等,還有最好給欄位新增一個預設值和not null因為欄位為null時影響到索引。字符集一般選用UTF8或gbk,當然如果沒有中文內容可以選用其它字符集。
二,建立索引,改善查詢速度。
索引只是為了提高資料庫查詢速度,如果索引建立不當不僅會影響插入修改甚至影響刪除的速度(因為索引在建立後會產生索引資料並寫檔案,當對資料增刪改操作時也會去修改對應索引資料)。一般在建表時就會建立部分索引,索引主要是針對SQL查詢中的篩選條件欄位,索引可以說是給指定的欄位列內容進行一個分類,然後類分的越小所篩選時查詢的時間越短,當然這些分類記錄會儲存到硬碟中,從而增加了硬碟佔用空間,這種做法主要是針對大資料量才會起到明顯的作用。
程式不像人可以自主的分類查詢,程式所有的執行過程都是人為設定好的,也只有這樣才能夠了解程式的執行過程,從而分析優化。在MYSQL中索引使用了二叉樹的結構,如上圖,左邊的是沒有使用索引,那麼在取記錄時,程式只能一個一個的去找,100000多的記錄找到位置在55000的記錄,那最少程式要迴圈判斷55000次,明顯的程式執行效率不高;而右邊是建立了索引的表,如果查詢位置在55000的記錄時程式就可以先判斷55000在1~50000或50000~100000哪個範圍內,很明顯是在50000~100000內,那麼程式又可以再判斷到55000在50000~75000之間,程式就可以在50000到75000之間進行迴圈判斷,只要5000次就可以找到,加上前面的幾次也就5002次,執行效率可想而知(當然這只是簡單模擬,實際資料庫的索引不只是這樣簡單的分類)。
索引在大資料量的資料庫中是必須瞭解的優化知識。
MYSQL的索引分為:主鍵索引,唯一索引,普通索引,全文索引;前面的三個索引是針對定長空間,全文索引是針對大文字變長空間。在MYSQL中並不是看到篩選條件就建立索引就可以進行優化,內部查詢機制只採用滿足一定條件的索引,所以索引建立不當,直接影響到增刪改查的速度。
由於要實際應用中SQL查詢大部分不是簡單的查詢,大部分用到了連線查詢,子查詢,聯合查詢等,所以在建立索引時要知道哪些篩選條件欄位建立索引後不會被採用。下面是收集部分索引建立要素:
索引列儘可能不包含NULL值。只要列中包含NULL值是不會在索引資料中;複合索引中某一列包含NULL的值,那該列在複合索引中是無效的。所以在建立索引的欄位中必須要新增not null來明確說明這個列裡的值不能為NULL,當然這個值如果可以不填時可以設定預設defaule 預設值。
字串類列儘可能的使用短索引。如果要建立索引的列是字串類(不論定長還是變長的),當前面的幾個字元在整列中將近唯一時,就可以使用短索引,這種索引可以減少索引資料量,同時可以提高索引速度。這類索引只要在指定的欄位後面加上一個()中間寫上指定的前面字元長度就可以。
排序索引儘可能不要與WHERE索引同時分開建立。如果SQL中有WHERE並且建立了索引,那麼就不給order建立索引,因為MYSQL一般只使用一個索引進行查詢,當然如果不用order可以完成查詢要求的最好不要排序;儘量不要使用多個列排序,如果需要把這些多個排序欄位建立一個複合索引。
like語句索引。like是一種模糊查詢,很多的地方會使用到,如果採用"%string%" 的形式是不會使用到索引,只有在"string%"時才會使用到索引,所以查詢中儘可能使用後面的查詢方式,否則這個欄位沒有必要建立索引(如果有其它查詢篩選另論)。
儘可能縮小篩選後的範圍。MYSQL資料庫往往有時在篩選後範圍過大就放棄採用索引,所以在篩選前儘量縮小篩選範圍。所以在篩選是儘量不要使用not in,<>,!=等條件,除非這類的範圍很小。
雖然通過explain分析在changes大於27時沒有使用索引,而在大於30時使用了range索引樣式,說明大於30時篩選範圍要小於大於27,但到底多少範圍才能採用索引,個人評估最好在1/3以內(需要的記錄或不需要的記錄勻算)。但後面的兩個圖片中說明了兩種count()獲取總數,一個時間差很大,一個相差卻相反。當然使用count(*)時使用的時間比count(id)快很多,這也是使用count()的一些技巧,當有where時count(*)是最快的,而如果沒有where那count(*)與count(id)時間是相等的,這個問題在使用中一定要注意。最好全部使用count(*)(當然這個不要在大資料量的InnoDB引擎類的表中使用)。
過多篩選條件建立複合索引。MYSQL索引機制在每個表中只會使用一個索引,所以當條件欄位加排序欄位或多個篩選欄位時,如果分別建立索引,最後只會採用最後建立索引的欄位。所以當沒有特殊要求下可以給這些條件欄位加排序欄位建立一個複合索引,不僅提高速度還能減少索引數量。但在複合索引中的條件欄位儘量不要出現> ,>=,<,<=類的篩選,否則複合索引可能不被採用,主要是因為這些判斷會擴大篩選範圍,當有的欄位出現了類似篩選條件可以不把該欄位新增到複合索引中。複合索引建立的欄位順序一定要同SQL中的條件欄位加排序欄位相同。
常用EXPLAIN來分析建立的索引。索引建立後不等於萬事大吉,往往在後期的運營中會隨著資料的增加,系統更新等變動後,使原來建立的索引沒有用,有的甚至影響到系統一正常執行。所以在後期的維護中,定期的瞭解你的索引狀態是必要的,一般在的這類變動較大時或系統變慢時就得找到對應的頁面取出裡面使用的SQL進行explain分析,修正不合理的索引。如果可以在程式操作每個查詢SQL時用PHP程式或其它操作MYSQL的程式中新增一個explain分析語句並且判斷結果,把不合理的SQL記錄下來,這樣一來後期的維護會更加快捷,不過這個分析週期一定要注意,如今就有部分PHP的框架使用了這一思想(如:TYPO3內容管理系統CMS)。當然可以開啟資料庫的慢查詢日誌,這個相對方便,只要定時去檢視下日誌內容即可。
三,SQL語句的優化。
很多時候資料庫的優化不能光靠索引,有好多的時候我們可以使用SQL語句本身來優化。
如果取的記錄條數有限制可以新增一個limit,這樣可以大大的減少篩選範圍。
group by分組時預設MYSQL會自動進行排序,當不需要排序時可以使用order by null,禁止排序影響查詢速度,當然如果分組項數過少是也沒有什麼影響。
簡化where條件。有的時候where條件複雜,但最好去簡化它。
儘量不要在SQL中使用一些動態函式,如時間函式now(),sysdate(),curdate(),curtime(),current_time(),current_date(),隨機函式rand()等;這類函式會影響到資料庫使用查詢快取。
轉載於:https://blog.51cto.com/php2012web/1289823