溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

實(shí)踐中應(yīng)該如何優(yōu)化MySQL

發(fā)布時(shí)間:2020-04-22 14:08:42 來源:億速云 閱讀:246 作者:三月 欄目:編程語(yǔ)言

本文主要給大家介紹實(shí)踐中應(yīng)該如何優(yōu)化MySQL,其所涉及的東西,從理論知識(shí)來獲悉,有很多書籍、文獻(xiàn)可供大家參考,從現(xiàn)實(shí)意義來講,億速云累計(jì)多年的實(shí)踐經(jīng)驗(yàn)可分享給大家。

實(shí)踐中如何優(yōu)化MySQL

實(shí)踐中,MySQL的優(yōu)化主要涉及SQL語(yǔ)句及索引的優(yōu)化、數(shù)據(jù)表結(jié)構(gòu)的優(yōu)化、系統(tǒng)配置的優(yōu)化和硬件的優(yōu)化四個(gè)方面,如下圖所示:

實(shí)踐中應(yīng)該如何優(yōu)化MySQL

SQL語(yǔ)句及索引的優(yōu)化

SQL語(yǔ)句的優(yōu)化

SQL語(yǔ)句的優(yōu)化主要包括三個(gè)問題,即如何發(fā)現(xiàn)有問題的SQL、如何分析SQL的執(zhí)行計(jì)劃以及如何優(yōu)化SQL,下面將逐一解釋。

  1. 怎么發(fā)現(xiàn)有問題的SQL?(通過MySQL慢查詢?nèi)罩緦?duì)有效率問題的SQL進(jìn)行監(jiān)控)

MySQL的慢查詢?nèi)罩臼荕ySQL提供的一種日志記錄,它用來記錄在MySQL中響應(yīng)時(shí)間超過閥值的語(yǔ)句,具體指運(yùn)行時(shí)間超過long_query_time值的SQL,則會(huì)被記錄到慢查詢?nèi)罩局小?/p>

實(shí)踐中應(yīng)該如何優(yōu)化MySQL

long_query_time的默認(rèn)值為10,意思是運(yùn)行10s以上的語(yǔ)句。慢查詢?nèi)罩镜南嚓P(guān)參數(shù)如下所示:

實(shí)踐中應(yīng)該如何優(yōu)化MySQL

通過MySQL的慢查詢?nèi)罩?,我們可以查詢出?zhí)行的次數(shù)多占用的時(shí)間長(zhǎng)的SQL、可以通過pt_query_disgest(一種mysql慢日志分析工具)分析Rows examine(MySQL執(zhí)行器需要檢查的行數(shù))項(xiàng)去找出IO大的SQL以及發(fā)現(xiàn)未命中索引的SQL,對(duì)于這些SQL,都是我們優(yōu)化的對(duì)象。

通過explain查詢和分析SQL的執(zhí)行計(jì)劃

使用 EXPLAIN 關(guān)鍵字可以知道MySQL是如何處理你的SQL語(yǔ)句的,以便分析查詢語(yǔ)句或是表結(jié)構(gòu)的性能瓶頸。通過explain命令可以得到表的讀取順序、數(shù)據(jù)讀取操作的操作類型、哪些索引可以使用、哪些索引被實(shí)際使用、表之間的引用以及每張表有多少行被優(yōu)化器查詢等問題。當(dāng)擴(kuò)展列extra出現(xiàn)Using filesort和Using temporay,則往往表示SQL需要優(yōu)化了。

優(yōu)化SQL語(yǔ)句

  • 優(yōu)化insert語(yǔ)句:一次插入多值;

  • 應(yīng)盡量避免在 where 子句中使用!=或<>操作符,否則將引擎放棄使用索引而進(jìn)行全表掃描;

  • 應(yīng)盡量避免在 where 子句中對(duì)字段進(jìn)行null值判斷,否則將導(dǎo)致引擎放棄使用索引而進(jìn)行全表掃描;

  • 優(yōu)化嵌套查詢:子查詢可以被更有效率的連接(Join)替代;

  • 很多時(shí)候用 exists 代替 in 是一個(gè)好的選擇。

索引優(yōu)化

建議在經(jīng)常作查詢選擇的字段、經(jīng)常作表連接的字段以及經(jīng)常出現(xiàn)在order by、group by、distinct 后面的字段中建立索引。但必須注意以下幾種可能會(huì)引起索引失效的情形:

  • 以“%(表示任意0個(gè)或多個(gè)字符)”開頭的LIKE語(yǔ)句,模糊匹配;

  • OR語(yǔ)句前后沒有同時(shí)使用索引;

  • 數(shù)據(jù)類型出現(xiàn)隱式轉(zhuǎn)化(如varchar不加單引號(hào)的話可能會(huì)自動(dòng)轉(zhuǎn)換為int型);

  • 對(duì)于多列索引,必須滿足最左匹配原則(eg,多列索引col1、col2和col3,則 索引生效的情形包括col1或col1,col2或col1,col2,col3)。

數(shù)據(jù)庫(kù)表結(jié)構(gòu)的優(yōu)化

數(shù)據(jù)庫(kù)表結(jié)構(gòu)的優(yōu)化包括選擇合適數(shù)據(jù)類型、表的范式的優(yōu)化、表的垂直拆分和表的水平拆分等手段。

選擇合適數(shù)據(jù)類型
  • 使用較小的數(shù)據(jù)類型解決問題;

  • 使用簡(jiǎn)單的數(shù)據(jù)類型(mysql處理int要比varchar容易);

  • 盡可能的使用not null 定義字段;

  • 盡量避免使用text類型,非用不可時(shí)最好考慮分表;

表的范式的優(yōu)化

一般情況下,表的設(shè)計(jì)應(yīng)該遵循三大范式。

表的垂直拆分
  • 把含有多個(gè)列的表拆分成多個(gè)表,解決表寬度問題,具體包括以下幾種拆分手段:

  • 把不常用的字段單獨(dú)放在同一個(gè)表中;

  • 把大字段獨(dú)立放入一個(gè)表中;

  • 把經(jīng)常使用的字段放在一起;

  • 這樣做的好處是非常明顯的,具體包括:拆分后業(yè)務(wù)清晰,拆分規(guī)則明確、系統(tǒng)之間整合或擴(kuò)展容易、數(shù)據(jù)維護(hù)簡(jiǎn)單。

表的水平拆分

表的水平拆分用于解決數(shù)據(jù)表中數(shù)據(jù)過大的問題,水平拆分每一個(gè)表的結(jié)構(gòu)都是完全一致的。一般地,將數(shù)據(jù)平分到N張表中的常用方法包括以下兩種:

  • 對(duì)ID進(jìn)行hash運(yùn)算,如果要拆分成5個(gè)表,mod(id,5)取出0~4個(gè)值;

  • 針對(duì)不同的hashID將數(shù)據(jù)存入不同的表中;

  • 表的水平拆分會(huì)帶來一些問題和挑戰(zhàn),包括跨分區(qū)表的數(shù)據(jù)查詢、統(tǒng)計(jì)及后臺(tái)報(bào)表的操作等問題,但也帶來了一些切實(shí)的好處:

    • 表分割后可以降低在查詢時(shí)需要讀的數(shù)據(jù)和索引的頁(yè)數(shù),同時(shí)也降低了索引的層數(shù),提高查詢速度;

    • 表中的數(shù)據(jù)本來就有獨(dú)立性,例如表中分別記錄各個(gè)地區(qū)的數(shù)據(jù)或不同時(shí)期的數(shù)據(jù),特別是有些數(shù)據(jù)常用,而另外一些數(shù)據(jù)不常用。

  • 需要把數(shù)據(jù)存放到多個(gè)數(shù)據(jù)庫(kù)中,提高系統(tǒng)的總體可用性(分庫(kù),雞蛋不能放在同一個(gè)籃子里)。

系統(tǒng)配置的優(yōu)化

操作系統(tǒng)配置的優(yōu)化:增加TCP支持的隊(duì)列數(shù)

mysql配置文件優(yōu)化:Innodb緩存池設(shè)置(innodb_buffer_pool_size,推薦總內(nèi)存的75%)和緩存池的個(gè)數(shù)(innodb_buffer_pool_instances)

硬件的優(yōu)化

CPU:核心數(shù)多并且主頻高的 內(nèi)存:增大內(nèi)存 磁盤配置和選擇:磁盤性能

MySQL中的悲觀鎖與樂觀鎖的實(shí)現(xiàn)

悲觀鎖與樂觀鎖是兩種常見的資源并發(fā)鎖設(shè)計(jì)思路,也是并發(fā)編程中一個(gè)非?;A(chǔ)的概念。

悲觀鎖

悲觀鎖的特點(diǎn)是先獲取鎖,再進(jìn)行業(yè)務(wù)操作,即“悲觀”的認(rèn)為所有的操作均會(huì)導(dǎo)致并發(fā)安全問題,因此要先確保獲取鎖成功再進(jìn)行業(yè)務(wù)操作。通常來講,在數(shù)據(jù)庫(kù)上的悲觀鎖需要數(shù)據(jù)庫(kù)本身提供支持,即通過常用的select … for update操作來實(shí)現(xiàn)悲觀鎖。當(dāng)數(shù)據(jù)庫(kù)執(zhí)行select … for update時(shí)會(huì)獲取被select中的數(shù)據(jù)行的行鎖,因此其他并發(fā)執(zhí)行的select … for update如果試圖選中同一行則會(huì)發(fā)生排斥(需要等待行鎖被釋放),因此達(dá)到鎖的效果。select for update獲取的行鎖會(huì)在當(dāng)前事務(wù)結(jié)束時(shí)自動(dòng)釋放,因此必須在事務(wù)中使用。    這里需要特別注意的是,不同的數(shù)據(jù)庫(kù)對(duì)select… for update的實(shí)現(xiàn)和支持都是有所區(qū)別的,例如oracle支持select for update no wait,表示如果拿不到鎖立刻報(bào)錯(cuò),而不是等待,mysql就沒有no wait這個(gè)選項(xiàng)。另外,mysql還有個(gè)問題是: select… for update語(yǔ)句執(zhí)行中所有掃描過的行都會(huì)被鎖上,這一點(diǎn)很容易造成問題。因此,如果在mysql中用悲觀鎖務(wù)必要確定使用了索引,而不是全表掃描。

樂觀鎖

樂觀鎖的特點(diǎn)先進(jìn)行業(yè)務(wù)操作,只在最后實(shí)際更新數(shù)據(jù)時(shí)進(jìn)行檢查數(shù)據(jù)是否被更新過,若未被更新過,則更新成功;否則,失敗重試。樂觀鎖在數(shù)據(jù)庫(kù)上的實(shí)現(xiàn)完全是邏輯的,不需要數(shù)據(jù)庫(kù)提供特殊的支持。一般的做法是在需要鎖的數(shù)據(jù)上增加一個(gè)版本號(hào)或者時(shí)間戳,然后按照如下方式實(shí)現(xiàn):

SELECT data AS old_data, version AS old_version FROM …;
//根據(jù)獲取的數(shù)據(jù)進(jìn)行業(yè)務(wù)操作,得到new_data和new_version
UPDATE SET data = new_data, version = new_version WHERE version = old_version
if (updated row > 0) {
// 樂觀鎖獲取成功,操作完成
} else {
// 樂觀鎖獲取失敗,回滾并重試
}

樂觀鎖是否在事務(wù)中其實(shí)都是無所謂的,其底層機(jī)制是這樣:在數(shù)據(jù)庫(kù)內(nèi)部update同一行的時(shí)候是不允許并發(fā)的,即數(shù)據(jù)庫(kù)每次執(zhí)行一條update語(yǔ)句時(shí)會(huì)獲取被update行的寫鎖,直到這一行被成功更新后才釋放。因此在業(yè)務(wù)操作進(jìn)行前獲取需要鎖的數(shù)據(jù)的當(dāng)前版本號(hào),然后實(shí)際更新數(shù)據(jù)時(shí)再次對(duì)比版本號(hào)確認(rèn)與之前獲取的相同,并更新版本號(hào),即可確認(rèn)這其間沒有發(fā)生并發(fā)的修改。如果更新失敗,即可認(rèn)為老版本的數(shù)據(jù)已經(jīng)被并發(fā)修改掉而不存在了,此時(shí)認(rèn)為獲取鎖失敗,需要回滾整個(gè)業(yè)務(wù)操作并可根據(jù)需要重試整個(gè)過程。

悲觀鎖與樂觀鎖的應(yīng)用場(chǎng)景

一般情況下,讀多寫少更適合用樂觀鎖,讀少寫多更適合用悲觀鎖。樂觀鎖在不發(fā)生取鎖失敗的情況下開銷比悲觀鎖小,但是一旦發(fā)生失敗回滾開銷則比較大,因此適合用在取鎖失敗概率比較小的場(chǎng)景,可以提升系統(tǒng)并發(fā)性能。

MySQL存儲(chǔ)引擎中的MyISAM和InnoDB區(qū)別詳解

在MySQL 5.5之前,MyISAM是mysql的默認(rèn)數(shù)據(jù)庫(kù)引擎,其由早期的ISAM(Indexed Sequential Access Method:有索引的順序訪問方法)所改良。雖然MyISAM性能極佳,但卻有一個(gè)顯著的缺點(diǎn): 不支持事務(wù)處理。不過,MySQL也導(dǎo)入了另一種數(shù)據(jù)庫(kù)引擎InnoDB,以強(qiáng)化參考完整性與并發(fā)違規(guī)處理機(jī)制,后來就逐漸取代MyISAM。

InnoDB是MySQL的數(shù)據(jù)庫(kù)引擎之一,其由Innobase oy公司所開發(fā),2006年五月由甲骨文公司并購(gòu)。與傳統(tǒng)的ISAM、MyISAM相比,InnoDB的最大特色就是支持ACID兼容的事務(wù)功能,類似于PostgreSQL。目前InnoDB采用雙軌制授權(quán),一是GPL授權(quán),另一是專有軟件授權(quán)。具體地,MyISAM與InnoDB作為MySQL的兩大存儲(chǔ)引擎的差異主要包括:

存儲(chǔ)結(jié)構(gòu):每個(gè)MyISAM在磁盤上存儲(chǔ)成三個(gè)文件:第一個(gè)文件的名字以表的名字開始,擴(kuò)展名指出文件類型。.frm文件存儲(chǔ)表定義,數(shù)據(jù)文件的擴(kuò)展名為.MYD (MYData),索引文件的擴(kuò)展名是.MYI (MYIndex)。InnoDB所有的表都保存在同一個(gè)數(shù)據(jù)文件中(也可能是多個(gè)文件,或者是獨(dú)立的表空間文件),InnoDB表的大小只受限于操作系統(tǒng)文件的大小,一般為2GB。

存儲(chǔ)空間:MyISAM可被壓縮,占據(jù)的存儲(chǔ)空間較小,支持靜態(tài)表、動(dòng)態(tài)表、壓縮表三種不同的存儲(chǔ)格式。InnoDB需要更多的內(nèi)存和存儲(chǔ),它會(huì)在主內(nèi)存中建立其專用的緩沖池用于高速緩沖數(shù)據(jù)和索引。

可移植性、備份及恢復(fù):MyISAM的數(shù)據(jù)是以文件的形式存儲(chǔ),所以在跨平臺(tái)的數(shù)據(jù)轉(zhuǎn)移中會(huì)很方便,同時(shí)在備份和恢復(fù)時(shí)也可單獨(dú)針對(duì)某個(gè)表進(jìn)行操作。InnoDB免費(fèi)的方案可以是拷貝數(shù)據(jù)文件、備份 binlog,或者用 mysqldump,在數(shù)據(jù)量達(dá)到幾十G的時(shí)候就相對(duì)痛苦了。

事務(wù)支持:MyISAM強(qiáng)調(diào)的是性能,每次查詢具有原子性,其執(zhí)行數(shù)度比InnoDB類型更快,但是不提供事務(wù)支持。InnoDB提供事務(wù)、外鍵等高級(jí)數(shù)據(jù)庫(kù)功能,具有事務(wù)提交、回滾和崩潰修復(fù)能力。

AUTO_INCREMENT:在MyISAM中,可以和其他字段一起建立聯(lián)合索引。引擎的自動(dòng)增長(zhǎng)列必須是索引,如果是組合索引,自動(dòng)增長(zhǎng)可以不是第一列,它可以根據(jù)前面幾列進(jìn)行排序后遞增。InnoDB中必須包含只有該字段的索引,并且引擎的自動(dòng)增長(zhǎng)列必須是索引,如果是組合索引也必須是組合索引的第一列。

表鎖差異:MyISAM只支持表級(jí)鎖,用戶在操作MyISAM表時(shí),select、update、delete和insert語(yǔ)句都會(huì)給表自動(dòng)加鎖,如果加鎖以后的表滿足insert并發(fā)的情況下,可以在表的尾部插入新的數(shù)據(jù)。InnoDB支持事務(wù)和行級(jí)鎖。行鎖大幅度提高了多用戶并發(fā)操作的新能,但是InnoDB的行鎖,只是在WHERE的主鍵是有效的,非主鍵的WHERE都會(huì)鎖全表的。

全文索引:MyISAM支持 FULLTEXT類型的全文索引;InnoDB不支持FULLTEXT類型的全文索引,但是innodb可以使用sphinx插件支持全文索引,并且效果更好。

表主鍵:MyISAM允許沒有任何索引和主鍵的表存在,索引都是保存行的地址。對(duì)于InnoDB,如果沒有設(shè)定主鍵或者非空唯一索引,就會(huì)自動(dòng)生成一個(gè)6字節(jié)的主鍵(用戶不可見),數(shù)據(jù)是主索引的一部分,附加索引保存的是主索引的值。

表的具體行數(shù):MyISAM保存表的總行數(shù),select count() from table;會(huì)直接取出出該值;而InnoDB沒有保存表的總行數(shù),如果使用select count() from table;就會(huì)遍歷整個(gè)表,消耗相當(dāng)大,但是在加了wehre條件后,myisam和innodb處理的方式都一樣。

CURD操作:在MyISAM中,如果執(zhí)行大量的SELECT,MyISAM是更好的選擇。對(duì)于InnoDB,如果你的數(shù)據(jù)執(zhí)行大量的INSERT或UPDATE,出于性能方面的考慮,應(yīng)該使用InnoDB表。DELETE從性能上InnoDB更優(yōu),但DELETE FROM table時(shí),InnoDB不會(huì)重新建立表,而是一行一行的刪除,在innodb上如果要清空保存有大量數(shù)據(jù)的表,最好使用truncate table這個(gè)命令。

外鍵:MyISAM不支持外鍵,而InnoDB支持外鍵。

通過上述的分析,基本上可以考慮使用InnoDB來替代MyISAM引擎了,原因是InnoDB自身很多良好的特點(diǎn),比如事務(wù)支持、存儲(chǔ)過程、視圖、行級(jí)鎖、外鍵等等。尤其在并發(fā)很多的情況下,相信InnoDB的表現(xiàn)肯定要比MyISAM強(qiáng)很多。另外,必須需要注意的是,任何一種表都不是萬(wàn)能的,合適的才是最好的,才能最大的發(fā)揮MySQL的性能優(yōu)勢(shì)。如果是不復(fù)雜的、非關(guān)鍵的Web應(yīng)用,還是可以繼續(xù)考慮MyISAM的,這個(gè)具體情況具體考慮。

MyISAM:不支持事務(wù),不支持外鍵,表鎖;插入數(shù)據(jù)時(shí)鎖定整個(gè)表,查行數(shù)時(shí)無需整表掃描。主索引數(shù)據(jù)文件和索引文件分離;與主索引無區(qū)別;

InnoDB:支持事務(wù),外鍵,行鎖,查表總行數(shù)時(shí),全表掃描;主索引的數(shù)據(jù)文件本身就是索引文件;輔助索引記錄主鍵的值;

MySQL鎖類型  

根據(jù)鎖的類型分,可以分為共享鎖,排他鎖,意向共享鎖和意向排他鎖。

根據(jù)鎖的粒度分,又可以分為行鎖,表鎖。

對(duì)于mysql而言,事務(wù)機(jī)制更多是靠底層的存儲(chǔ)引擎來實(shí)現(xiàn),因此,mysql層面只有表鎖,而支持事務(wù)的innodb存 儲(chǔ)引擎則實(shí)現(xiàn)了行鎖(記錄鎖(在行相應(yīng)的索引記錄上的鎖)),gap鎖(是在索引記錄間歇上的鎖),next-key鎖(是記錄鎖和在此索引記錄之前的gap上的鎖的結(jié)合)。Mysql的記錄鎖實(shí)質(zhì)是索引記錄的鎖,因?yàn)閕nnodb是索引組織表;gap鎖是索引記錄間隙的鎖,這種鎖只在RR隔離級(jí)別下有效;next-key鎖是記錄鎖加上記錄之前gap鎖的組合。mysql通過gap鎖和next-key鎖實(shí)現(xiàn)RR隔離級(jí)別。

說明:對(duì)于更新操作(讀不上鎖),只有走索引才可能上行鎖;否則會(huì)對(duì)聚簇索引的每一行上寫鎖,實(shí)際等同于對(duì)表上寫鎖。    若多個(gè)物理記錄對(duì)應(yīng)同一個(gè)索引,若同時(shí)訪問,也會(huì)出現(xiàn)鎖沖突;

當(dāng)表有多個(gè)索引時(shí),不同事務(wù)可以用不同的索引鎖住不同的行,另外innodb會(huì)同時(shí)用行鎖對(duì)數(shù)據(jù)記錄(聚簇索引)加鎖。

MVCC(多版本并發(fā)控制)并發(fā)控制機(jī)制下,任何操作都不會(huì)阻塞讀操作,讀操作也不會(huì)阻塞任何操作,只因?yàn)樽x不上鎖。    共享鎖:由讀表操作加上的鎖,加鎖后其他用戶只能獲取該表或行的共享鎖,不能獲取排它鎖,也就是說只能讀不能寫

排它鎖:由寫表操作加上的鎖,加鎖后其他用戶不能獲取該表或行的任何鎖,典型是mysql事務(wù)中的更新操作。

意向共享鎖(IS):事務(wù)打算給數(shù)據(jù)行加行共享鎖,事務(wù)在給一個(gè)數(shù)據(jù)行加共享鎖前必須先取得該表的IS鎖。

意向排他鎖(IX):事務(wù)打算給數(shù)據(jù)行加行排他鎖,事務(wù)在給一個(gè)數(shù)據(jù)行加排他鎖前必須先取得該表的IX鎖。

數(shù)據(jù)庫(kù)死鎖概念

多數(shù)情況下,可以認(rèn)為如果一個(gè)資源被鎖定,它總會(huì)在以后某個(gè)時(shí)間被釋放。而死鎖發(fā)生在當(dāng)多個(gè)進(jìn)程訪問同一數(shù)據(jù)庫(kù)時(shí),其中每個(gè)進(jìn)程擁有的鎖都是其他進(jìn)程所需的,由此造成每個(gè)進(jìn)程都無法繼續(xù)下去。簡(jiǎn)單的說,進(jìn)程A等待進(jìn)程B釋放他的資源,B又等待A釋放他的資源,這樣就互相等待就形成死鎖。

雖然進(jìn)程在運(yùn)行過程中,可能發(fā)生死鎖,但死鎖的發(fā)生也必須具備一定的條件,死鎖的發(fā)生必須具備以下四個(gè)必要條件:

1)互斥條件:指進(jìn)程對(duì)所分配到的資源進(jìn)行排它性使用,即在一段時(shí)間內(nèi)某資源只由一個(gè)進(jìn)程占用。如果此時(shí)還有其它進(jìn)程請(qǐng)求資源,則請(qǐng)求者只能等待,直至占有資源的進(jìn)程用畢釋放。    2)請(qǐng)求和保持條件:指進(jìn)程已經(jīng)保持至少一個(gè)資源,但又提出了新的資源請(qǐng)求,而該資源已被其它進(jìn)程占有,此時(shí)請(qǐng)求進(jìn)程阻塞,但又對(duì)自己已獲得的其它資源保持不放。    3)不剝奪條件:指進(jìn)程已獲得的資源,在未使用完之前,不能被剝奪,只能在使用完時(shí)由自己釋放。    4)環(huán)路等待條件:指在發(fā)生死鎖時(shí),必然存在一個(gè)進(jìn)程——資源的環(huán)形鏈,即進(jìn)程集合{P0,P1,P2,???,Pn}中的P0正在等待一個(gè)P1占用的資源;P1正在等待P2占用的資源,……,Pn正在等待已被P0占用的資源。    下列方法有助于最大限度地降低死鎖:

  • 按同一順序訪問對(duì)象。

  • 避免事務(wù)中的用戶交互。

  • 保持事務(wù)簡(jiǎn)短并在一個(gè)批處理中。

  • 使用低隔離級(jí)別。

  • 使用綁定連接。

千萬(wàn)級(jí)MySQL數(shù)據(jù)庫(kù)建立索引的事項(xiàng)及提高性能的手段

  1. 對(duì)查詢進(jìn)行優(yōu)化,應(yīng)盡量避免全表掃描,首先應(yīng)考慮在 where 及 order by 涉及的列上建立索引。

  2. 應(yīng)盡量避免在 where 子句中對(duì)字段進(jìn)行 null 值判斷,否則將導(dǎo)致引擎放棄使用索引而進(jìn)行全表掃描,如:select id from t where num is null可以在num上設(shè)置默認(rèn)值0,確保表中num列沒有null值,然后這樣查詢:select id from t where num=0

  3. 應(yīng)盡量避免在 where 子句中使用!=或<>操作符,否則引擎將放棄使用索引而進(jìn)行全表掃描。

  4. 應(yīng)盡量避免在 where 子句中使用or 來連接條件,否則將導(dǎo)致引擎放棄使用索引而進(jìn)行全表掃描,如:select id from t where num=10 or num=20可以這樣查詢:select id from t where num=10 union all select id from t where num=20

  5. in 和 not in 也要慎用,否則會(huì)導(dǎo)致全表掃描,如:select id from t where num in(1,2,3) 對(duì)于連續(xù)的數(shù)值,能用 between 就不要用 in 了:select id from t where num between 1 and 3

  6. 避免使用通配符。下面的查詢也將導(dǎo)致全表掃描:select id from t where name like ‘李%’若要提高效率,可以考慮全文檢索。

  7. 如果在 where 子句中使用參數(shù),也會(huì)導(dǎo)致全表掃描。因?yàn)镾QL只有在運(yùn)行時(shí)才會(huì)解析局部變量,但優(yōu)化程序不能將訪問計(jì)劃的選擇推遲到運(yùn)行時(shí);它必須在編譯時(shí)進(jìn)行選擇。然而,如果在編譯時(shí)建立訪問計(jì)劃,變量的值還是未知的,因而無法作為索引選擇的輸入項(xiàng)。如下面語(yǔ)句將進(jìn)行全表掃描:select id from t where num=@num可以改為強(qiáng)制查詢使用索引:select id from t with(index(索引名)) where num=@num

  8. 應(yīng)盡量避免在 where 子句中對(duì)字段進(jìn)行表達(dá)式操作,這將導(dǎo)致引擎放棄使用索引而進(jìn)行全表掃描。如:select id from t where num/2=100應(yīng)改為:select id from t where num=100*2

  9. 應(yīng)盡量避免在where子句中對(duì)字段進(jìn)行函數(shù)操作,這將導(dǎo)致引擎放棄使用索引而進(jìn)行全表掃描。如:select id from t where substring(name,1,3)=’abc’ ,name以abc開頭的id應(yīng)改為:select id from t where name like ‘a(chǎn)bc%’

  10. 不要在 where 子句中的“=”左邊進(jìn)行函數(shù)、算術(shù)運(yùn)算或其他表達(dá)式運(yùn)算,否則系統(tǒng)將可能無法正確使用索引。

  11. 在使用索引字段作為條件時(shí),如果該索引是復(fù)合索引,那么必須使用到該索引中的第一個(gè)字段作為條件時(shí)才能保證系統(tǒng)使用該索引,否則該索引將不會(huì)被使用,并且應(yīng)盡可能的讓字段順序與索引順序相一致。

  12. 不要寫一些沒有意義的查詢,如需要生成一個(gè)空表結(jié)構(gòu):select col1,col2 into #t from t where 1=0 這類代碼不會(huì)返回任何結(jié)果集,但是會(huì)消耗系統(tǒng)資源的,應(yīng)改成這樣:create table #t(…)

  13. 很多時(shí)候用 exists 代替 in 是一個(gè)好的選擇:select num from a where num in(select num from b)用下面的語(yǔ)句替換:select num from a where exists(select 1 from b where num=a.num)

  14. 并不是所有索引對(duì)查詢都有效,SQL是根據(jù)表中數(shù)據(jù)來進(jìn)行查詢優(yōu)化的,當(dāng)索引列有大量數(shù)據(jù)重復(fù)時(shí),SQL查詢可能不會(huì)去利用索引,如一表中有字段sex,male、female幾乎各一半,那么即使在sex上建了索引也對(duì)查詢效率起不了作用。

  15. 索引并不是越多越好,索引固然可以提高相應(yīng)的 select 的效率,但同時(shí)也降低了insert 及 update 的 效率,因?yàn)?insert 或 update 時(shí)有可能會(huì)重建索引,所以怎樣建索引需要慎重考慮,視具體情況而定。一個(gè)表的索引數(shù)最好不要超過6個(gè),若太多則應(yīng)考慮一些不常使用到的列上建的索引是否有 必要。

  16. 應(yīng)盡可能的避免更新 clustered 索引數(shù)據(jù)列,因?yàn)?clustered 索引數(shù)據(jù)列的順序就是表記錄的物理存儲(chǔ) 順序,一旦該列值改變將導(dǎo)致整個(gè)表記錄的順序的調(diào)整,會(huì)耗費(fèi)相當(dāng)大的資源。若應(yīng)用系統(tǒng)需要頻繁更新 clustered 索引數(shù)據(jù)列,那么需要考慮是否應(yīng)將該索引建為 clustered 索引。

  17. 盡量使用數(shù)字型字段,若只含數(shù)值信息的字段盡量不要設(shè)計(jì)為字符型,這會(huì)降低查詢和連接的性能,并會(huì)增加存儲(chǔ)開銷。這是因?yàn)橐嬖谔幚聿樵兒瓦B接時(shí)會(huì)逐個(gè)比較字符串中每一個(gè)字符,而對(duì)于數(shù)字型而言只需要比較一次就夠了。

  18. 盡可能的使用 varchar/nvarchar 代替 char/nchar ,因?yàn)槭紫茸冮L(zhǎng)字段存儲(chǔ)空間小,可以節(jié)省存儲(chǔ)空間,其次對(duì)于查詢來說,在一個(gè)相對(duì)較小的字段內(nèi)搜索效率顯然要高些。

  19. 任何地方都不要使用 select * from t ,用具體的字段列表代替“*”,不要返回用不到的任何字段。

  20. 盡量使用表變量來代替臨時(shí)表。如果表變量包含大量數(shù)據(jù),請(qǐng)注意索引非常有限(只有主鍵索引)。

  21. 避免頻繁創(chuàng)建和刪除臨時(shí)表,以減少系統(tǒng)表資源的消耗。

  22. 臨時(shí)表并不是不可使用,適當(dāng)?shù)厥褂盟鼈兛梢允鼓承├谈行?,例如,?dāng)需要重復(fù)引用大型表或常用表中的某個(gè)數(shù)據(jù)集時(shí)。但是,對(duì)于一次性事件,最好使用導(dǎo)出表。

  23. 在新建臨時(shí)表時(shí),如果一次性插入數(shù)據(jù)量很大,那么可以使用 select into 代替 create table,避免造成大量 log ,以提高速度;如果數(shù)據(jù)量不大,為了緩和系統(tǒng)表的資源,應(yīng)先create table,然后insert。

  24. 如果使用到了臨時(shí)表,在存儲(chǔ)過程的最后務(wù)必將所有的臨時(shí)表顯式刪除,先 truncate table ,然后 drop table ,這樣可以避免系統(tǒng)表的較長(zhǎng)時(shí)間鎖定。

  25. 盡量避免使用游標(biāo),因?yàn)橛螛?biāo)的效率較差,如果游標(biāo)操作的數(shù)據(jù)超過1萬(wàn)行,那么就應(yīng)該考慮改寫。

  26. 使用基于游標(biāo)的方法或臨時(shí)表方法之前,應(yīng)先尋找基于集的解決方案來解決問題,基于集的方法通常更有效。

  27. 與臨時(shí)表一樣,游標(biāo)并不是不可使用。對(duì)小型數(shù)據(jù)集使用 FAST_FORWARD 游標(biāo)通常要優(yōu)于其他逐行處理方法,尤其是在必須引用幾個(gè)表才能獲得所需的數(shù)據(jù)時(shí)。在結(jié)果集中包括“合計(jì)”的例程通常要比使用游標(biāo)執(zhí)行的速度快。如果開發(fā)時(shí)間允許,基于游標(biāo)的方法和基于集的方法都可以嘗試一下,看哪一種方法的效果更好。

  28. 在所有的存儲(chǔ)過程和觸發(fā)器的開始處設(shè)置 SET NOCOUNT ON ,在結(jié)束時(shí)設(shè)置 SET NOCOUNT OFF。無需在執(zhí)行存儲(chǔ)過程和觸發(fā)器的每個(gè)語(yǔ)句后向客戶端發(fā)送DONE_IN_PROC 消息。

  29. 盡量避免大事務(wù)操作,提高系統(tǒng)并發(fā)能力。

  30. 盡量避免向客戶端返回大數(shù)據(jù)量,若數(shù)據(jù)量過大,應(yīng)該考慮相應(yīng)需求是否合理。

看了以上介紹實(shí)踐中應(yīng)該如何優(yōu)化MySQL,希望能給大家在實(shí)際運(yùn)用中帶來一定的幫助。本文由于篇幅有限,難免會(huì)有不足和需要補(bǔ)充的地方,大家可以繼續(xù)關(guān)注億速云行業(yè)資訊板塊,會(huì)定期給大家更新行業(yè)新聞和知識(shí),如有需要更加專業(yè)的解答,可在官網(wǎng)聯(lián)系我們的24小時(shí)售前售后,隨時(shí)幫您解答問題的。

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI