-
無(wú)論何時(shí)殉疼,只要有多個(gè)查詢(xún)需要在同一個(gè)時(shí)刻修改數(shù)據(jù)時(shí)才漆,就會(huì)有并發(fā)問(wèn)題牛曹。MySql主要在
服務(wù)器層
與存儲(chǔ)引擎層
進(jìn)行并發(fā)控制。假設(shè)數(shù)據(jù)庫(kù)中國(guó)一張郵箱表醇滥,每個(gè)郵件都是一條記錄黎比。如果某個(gè)客戶(hù)正在讀取郵箱,同時(shí)其他客戶(hù)試圖在刪除郵箱表中的某一條數(shù)據(jù)鸳玩。這個(gè)時(shí)候阅虫,讀取的結(jié)構(gòu)就是不確定的了。在MySql中會(huì)通過(guò)鎖定防止其它用戶(hù)讀取同一數(shù)據(jù)不跟。大多數(shù)時(shí)候颓帝,MySQL鎖的內(nèi)部管理都是透明的。
MySQL鎖的粒度
- 每種MySql引擎都可以實(shí)現(xiàn)自己的鎖策略和鎖粒度窝革,將鎖粒度固定在某個(gè)級(jí)別购城,可以為某些特定的場(chǎng)景提供更好的性能。
表鎖(table lock)
- 表鎖是mysql中最基本的鎖略虐译,并且是開(kāi)銷(xiāo)最小的策略瘪板。它會(huì)鎖定整個(gè)表,一個(gè)用戶(hù)在對(duì)表進(jìn)行寫(xiě)操作(插入菱蔬、刪除篷帅、更新等)前史侣,需要先獲得寫(xiě)鎖拴泌,這會(huì)阻塞其他用戶(hù)對(duì)該表的所有讀寫(xiě)操作。只有沒(méi)有寫(xiě)鎖時(shí),其他讀取的用戶(hù)才能獲得讀鎖惊橱,讀鎖之間是不相互阻塞的蚪腐。
- 在特定的場(chǎng)景中,表鎖也可能有良好的性能税朴。例如回季,
READ L0CAL
表鎖支持某些類(lèi)型的并發(fā)寫(xiě)操作。另外正林,寫(xiě)鎖也比讀鎖有更高的優(yōu)先級(jí)泡一,因此-一個(gè)寫(xiě)鎖請(qǐng)求可能會(huì)被插入到讀鎖隊(duì)列的前面(寫(xiě)鎖可以插入到鎖隊(duì)列中讀鎖的前面,反之讀鎖則不能插入到寫(xiě)鎖的前面)觅廓。 - 盡管存儲(chǔ)引擎可以管理自己的鎖鼻忠,MySQL本身還是會(huì)使用各種有效的表鎖來(lái)實(shí)現(xiàn)不同的目的。例如杈绸,服務(wù)器會(huì)為諸如
ALTER TABLE
之類(lèi)的語(yǔ)句使用表鎖帖蔓,而忽略存儲(chǔ)引擎的鎖機(jī)制矮瘟。
行級(jí)鎖(row lock)
- 行級(jí)鎖可以最大程度地支持并發(fā)處理(同時(shí)也帶來(lái)了最大的鎖開(kāi)銷(xiāo))。
- 在
InnoDB
和XtraDB
,以及其他一些存儲(chǔ)引擎中實(shí)現(xiàn)了行級(jí)鎖塑娇。 - 行級(jí)鎖只在存儲(chǔ)引擎層實(shí)現(xiàn)澈侠,而MySQL服務(wù)器層沒(méi)有實(shí)現(xiàn)。服務(wù)器層完全不了解存儲(chǔ)引擎中的鎖實(shí)現(xiàn)埋酬。
MySQL的事務(wù)
事務(wù)特性
- A(原子性)事務(wù)的各步操作是不可分的哨啃,保證一系列的操作要么都完成,要么都不完成写妥;
- C(一致性)事務(wù)完成棘催,數(shù)據(jù)必須處于一致的狀態(tài);
- I(隔離性)對(duì)數(shù)據(jù)進(jìn)行修改的所有并發(fā)事務(wù)彼此之間是相互隔離耳标,這表明事務(wù)必須是獨(dú)立的醇坝,不應(yīng)以任何方式依賴(lài)或影響其他事務(wù);
- D(持久性)表示事務(wù)對(duì)數(shù)據(jù)處理結(jié)束后次坡,對(duì)數(shù)據(jù)更改必須持久化呼猪,不管是事務(wù)成功還是回滾。事務(wù)日志都能夠保持事務(wù)的永久性砸琅。
事務(wù)的隔離級(jí)別
- SQL標(biāo)準(zhǔn)的事務(wù)隔離級(jí)別包括:讀未提交(read uncommitted)宋距、讀提交(read committed)、可重復(fù)讀(repeatable read)和串行化(serializable )
- 讀未提交是指症脂,一個(gè)事務(wù)還沒(méi)提交時(shí)谚赎,它做的變更就能被別的事務(wù)看到。
- 讀提交是指诱篷,一個(gè)事務(wù)提交之后壶唤,它做的變更才會(huì)被其他事務(wù)看到。
- 可重復(fù)讀是指棕所,一個(gè)事務(wù)執(zhí)行過(guò)程中看到的數(shù)據(jù)闸盔,總是跟這個(gè)事務(wù)在啟動(dòng)時(shí)看到的數(shù)據(jù)是一致的。未提交的更改對(duì)其他事務(wù)是不可見(jiàn)的
- 串行化:對(duì)應(yīng)一個(gè)記錄會(huì)加讀寫(xiě)鎖琳省,出現(xiàn)沖突的時(shí)候迎吵,后訪(fǎng)問(wèn)的事務(wù)必須等前一個(gè)事務(wù)執(zhí)行完成才能繼續(xù)執(zhí)行。最高的隔離級(jí)別
MySQL中的事務(wù)
- MySQL提供了兩種事務(wù)型的存儲(chǔ)引擎:
InnoDB
和NDB Cluster
针贬。另外還有一些第三方存儲(chǔ)引擎也支持事 - MySQL默認(rèn)采用自動(dòng)提交(AUTOCOMIT) 模式击费。如果不是顯式地開(kāi)始-一個(gè)個(gè)事務(wù),則每個(gè)查詢(xún)都被當(dāng)作一事務(wù)執(zhí)行提交操作桦他。在當(dāng)前連接中蔫巩,可以通過(guò)設(shè)置AUTOCOMMIT變量來(lái)啟用或者禁用自動(dòng)提交模式:
- InnoDB采用的是兩階段鎖定協(xié)議(two-phase locking protocol)。在事務(wù)執(zhí)行過(guò)程中,隨時(shí)都可以執(zhí)行鎖定批幌,鎖只有在執(zhí)行
COMMIT
或者ROLLBACK
的時(shí)候才會(huì)釋放础锐,并且所有的鎖是在同一時(shí)刻被釋放。 - InnoDB也支持通過(guò)特定的語(yǔ)句進(jìn)行顯式鎖定
SELECT ... LOCK IN SHARE MODE
和SELECT FOR UPDATE
些語(yǔ)句不屬于SQL規(guī)范
多版本并發(fā)控制MVCC
-
MVCC
是行級(jí)鎖的一個(gè)變種荧缘,但是它在很多情況下避免了加鎖操作皆警,因此開(kāi)銷(xiāo)更低。雖然實(shí)現(xiàn)機(jī)制有所不同截粗,但大都實(shí)現(xiàn)了非阻塞的讀操作信姓,寫(xiě)操作也只鎖定必要的行。 - MVCC的實(shí)現(xiàn)绸罗,是通過(guò)保存數(shù)據(jù)在某個(gè)時(shí)間點(diǎn)的快照來(lái)實(shí)現(xiàn)的意推。也就是說(shuō),不管需要執(zhí)行多長(zhǎng)時(shí)間珊蟀,每個(gè)事務(wù)看到的數(shù)據(jù)都是一致的菊值。根據(jù)事務(wù)開(kāi)始的時(shí)間不同,每個(gè)事務(wù)對(duì)同張表育灸,同一時(shí)刻看到的數(shù)據(jù)可能是不一樣的腻窒。
-
InnoDB
的MVCC,是通過(guò)在每行記錄后面保存兩個(gè)隱藏的列來(lái)實(shí)現(xiàn)的。這兩個(gè)列磅崭,一個(gè)保存了行的創(chuàng)建時(shí)間儿子,一個(gè)保存行的過(guò)期時(shí)間(或刪除時(shí)間)。當(dāng)然存儲(chǔ)的并不是實(shí)際的時(shí)間值砸喻,而是系統(tǒng)版本號(hào)柔逼。每開(kāi)始一個(gè)新的事務(wù),系統(tǒng)版本號(hào)都會(huì)自動(dòng)遞增割岛。事務(wù)開(kāi)始時(shí)刻的系統(tǒng)版本號(hào)會(huì)作為事務(wù)的版本號(hào)愉适,用來(lái)和查詢(xún)到的每行記錄的版本號(hào)進(jìn)行比較。 - MVCC只在可重復(fù)讀和讀提交的隔離級(jí)別生效蜂桶。其它兩個(gè)級(jí)別都不兼容
在可重復(fù)讀(
REPEATABLE READ
)隔離級(jí)別下儡毕,MVCC具體是如何操作的也切。
SELECT查詢(xún)操作時(shí)
InnoDB會(huì)根據(jù)以下兩個(gè)條件檢查每行記錄:
- InnoDB只查找版本早于當(dāng)前事務(wù)版本的數(shù)據(jù)行(也就是,行的系統(tǒng)版本號(hào)小于或等于事務(wù)的系統(tǒng)版本號(hào)),這樣可以確保事務(wù)讀取的行扑媚,要么是在事務(wù)開(kāi)始前已經(jīng)存在的,要么是事務(wù)自身插入或者修改過(guò)的雷恃。
- 行的刪除版本要么未定義疆股,要么大于當(dāng)前事務(wù)版本號(hào)。這可以確保事務(wù)讀取到的行倒槐,在事務(wù)開(kāi)始之前未被刪除旬痹。
INSERT
- InnoDB為新播入的每-一行保存當(dāng)前系統(tǒng)版本號(hào)作為行版本號(hào)。
DELETE
- InnoDB為刪除的每-*行保存當(dāng)前系統(tǒng)版本號(hào)作為行刪除標(biāo)識(shí)。
UPDATE
- InnoDB為插入-行新記錄两残,保存當(dāng)前系統(tǒng)版本號(hào)作為行版本號(hào)永毅,同時(shí)保存當(dāng)前系統(tǒng)版本號(hào)到原來(lái)的行作為行刪除標(biāo)識(shí)。
數(shù)據(jù)庫(kù)存儲(chǔ)引擎
InnDB存儲(chǔ)引擎
- InnDB是Mysql默認(rèn)的事務(wù)型存儲(chǔ)引擎人弓。它被設(shè)計(jì)用來(lái)處理大量的短期(short-lived) 事務(wù)沼死,短期事務(wù)大部分情況是正常提交的,很少會(huì)被回滾崔赌。InnoDB的性能和自動(dòng)崩潰恢復(fù)特性意蛀,使得它在非事務(wù)型存儲(chǔ)的需求中也很流行
- InnoDB的數(shù)據(jù)存儲(chǔ)在表空間(tablespace) 中,表空間是由InnoDB管理的-個(gè)黑盒子健芭,由一系列的數(shù)據(jù)文件組成县钥。
- InnoDB采用
MVCC
來(lái)支持高并發(fā),并且實(shí)現(xiàn)了四個(gè)標(biāo)準(zhǔn)的隔離級(jí)別慈迈。其默認(rèn)級(jí)別是REPEATABLE READ (可重復(fù)讀
) ,并且通過(guò)間隙鎖
(next-key locking)策略防止幻讀
的出現(xiàn)若贮。間隙鎖使得InnoDB不僅僅鎖定查詢(xún)涉及的行,還會(huì)對(duì)索引中的間隙進(jìn)行鎖定痒留,以防止幻影行的插入兜看。 - InnoDB表是基于聚簇索引建立的。聚簇素引對(duì)主鍵查詢(xún)有很高的性能狭瞎,不過(guò)它的二級(jí)索引(secondary index细移,非主鍵索引)中必須包含主鍵列,所以如果主鍵列很大的話(huà)熊锭,其他的所有索引都會(huì)很大弧轧。因此,若表上的索引較多的話(huà)碗殷,主鍵應(yīng)當(dāng)盡可能的小精绎。
- InnoDB內(nèi)部做了很多優(yōu)化,包括從磁盤(pán)讀取數(shù)據(jù)時(shí)采用的可預(yù)測(cè)性預(yù)讀锌妻,能夠自動(dòng)在內(nèi)存中創(chuàng)建hash索引以加速讀操作的自適應(yīng)哈希索引(adaptive hash index),以及能夠加速插入操作的插入緩沖區(qū)(insert buffer)等
MyISAM引擎
-
MyISAM不支持事務(wù)和行鎖
代乃,在MySQL5.1之前的版本是默認(rèn)的存儲(chǔ)引擎,有一個(gè)缺陷是崩潰后無(wú)法恢復(fù)仿粹。 - 優(yōu)點(diǎn)是對(duì)于只讀的數(shù)據(jù)搁吓,或者表比較小,可以忍受修復(fù)操作吭历,可以繼續(xù)使用
- MyISAM會(huì)將表存儲(chǔ)在兩個(gè)文件中:數(shù)據(jù)文件和索引文件堕仔,分別以.MYD和.MYI為擴(kuò)展名
- MyISAM表可以包含動(dòng)態(tài)或者靜態(tài)(長(zhǎng)度固定)行。MySQL會(huì)根據(jù)表的定義來(lái)決定采用何種行格式晌区。MyISAM表可以存儲(chǔ)的行記錄數(shù)摩骨,一般受限于可用的磁盤(pán)空間,或者操作系統(tǒng)中單個(gè)文件的最大尺寸通贞。
-
MyISAM對(duì)整張表加鎖,而不是針對(duì)行
恼五。讀取時(shí)會(huì)對(duì)需要讀到的所有表加共享鎖,寫(xiě)入時(shí)則對(duì)表加排他鎖昌罩。但是在表有讀取查詢(xún)的同時(shí),也可以往表中插入新的記錄(這被稱(chēng)為并發(fā)插入)
Archive引擎
- Archive引擎會(huì)緩存所有的寫(xiě)并利用zlib對(duì)插人的行進(jìn)行壓縮灾馒,所以比MyISAM表的磁盤(pán)I/O更少客蹋。但是每次SELECT查詢(xún)都需要執(zhí)行全表掃描晶衷。所以Archive表適合日志和數(shù)據(jù)采集類(lèi)應(yīng)用乳蛾,這類(lèi)應(yīng)用做數(shù)據(jù)分析時(shí)往往需要全表掃描遣钳。或者在一- 些需要更快速的INSERT操作的場(chǎng)合下也可以使用傅物。
-
Archive引擎
支持行級(jí)鎖和專(zhuān)用的緩沖區(qū)
夯辖,所可以實(shí)現(xiàn)高并發(fā)的插人。在一個(gè)查詢(xún)開(kāi)始直到返回表中存在的所有行數(shù)之前董饰,Archive引擎會(huì)阻止其他的SELECT執(zhí)行蒿褂,以實(shí)現(xiàn)一致性讀。另外卒暂,也實(shí)現(xiàn)了批量插入在完成之前對(duì)讀操作是不可見(jiàn)的啄栓。這種機(jī)制模仿了事務(wù)和MVCC的一些特性,但Archive引擎不是一個(gè)事務(wù)型的引擎也祠,而是-一個(gè)針對(duì)高速插人和壓縮做了優(yōu)化的簡(jiǎn)單引擎昙楚。
Blackhole引擎
- Blackhole引擎沒(méi)有實(shí)現(xiàn)任何的存儲(chǔ)機(jī)制,它會(huì)丟棄所有插入的數(shù)據(jù)诈嘿,不做任何保存堪旧。但是服務(wù)器會(huì)記錄Blackhole表的日志,所以可以用于復(fù)制數(shù)據(jù)到備庫(kù)奖亚,或者只是簡(jiǎn)單地記錄到日志淳梦。這種特殊的存儲(chǔ)引擎可以在--些特殊的復(fù)制架構(gòu)和8志審核時(shí)發(fā)揮作用。但這種應(yīng)用方式我們碰到過(guò)很多問(wèn)題昔字,因此并不推薦爆袍。
CSV引擎
- CSV引擎可以將普通的CSV文件(逗號(hào)分割值的文件)作為MySQL的表來(lái)處理,但這種表不支持索引作郭。CSV引擎可以在數(shù)據(jù)庫(kù)運(yùn)行時(shí)拷入或者拷出文件陨囊。可以將Excel等電子表格軟件中的數(shù)據(jù)存儲(chǔ)為CSV文件所坯,然后復(fù)制到MySQL數(shù)據(jù)目錄下谆扎,就能在MySQL中打開(kāi)使用。同樣芹助,如果將數(shù)據(jù)寫(xiě)人到一個(gè)CSV引擎表堂湖,其他的外部程序也能立即從表的數(shù)據(jù)文件中讀取CSV格式的數(shù)據(jù)。因此CSV引擎可以作為- -種數(shù)據(jù)交換的機(jī)制状土,非常有用无蜂。
Federated引擎
- Federated引擎是訪(fǎng)問(wèn)其他MySQL服務(wù)器的-一個(gè)代理,它會(huì)創(chuàng)建-一個(gè)到遠(yuǎn)程MySQL服務(wù)器的客戶(hù)端連接蒙谓,并將查詢(xún)傳輸?shù)竭h(yuǎn)程服務(wù)器執(zhí)行斥季,然后提取或者發(fā)送需要的數(shù)據(jù)。最初設(shè)計(jì)該存儲(chǔ)引擎是為了和企業(yè)級(jí)數(shù)據(jù)庫(kù)如Microsoft SQL Server和Oracle的類(lèi)似特性競(jìng)爭(zhēng)的累驮,可以說(shuō)更多的是一種市場(chǎng)行為酣倾。盡管該引擎看起來(lái)提供了一種很好的跨服務(wù)器的靈活性,但也經(jīng)常帶來(lái)問(wèn)題谤专,因此默認(rèn)是禁用的躁锡。
Memory引擎
- 如果需要快速地訪(fǎng)問(wèn)數(shù)據(jù),并且這些數(shù)據(jù)不會(huì)被修改置侍,重啟以后丟失也沒(méi)有關(guān)系映之,那么使用Memory表(以前也叫做HEAP表)是非常有用的。Memory 表至少比MyISAM表要快一個(gè)數(shù)量級(jí)蜡坊,因?yàn)樗械臄?shù)據(jù)都保存在內(nèi)存中,不需要進(jìn)行磁盤(pán)I/O杠输。Memory 表的結(jié)構(gòu)在重啟以后還會(huì)保留,但數(shù)據(jù)會(huì)丟失。
Schema(數(shù)據(jù)庫(kù)的組織和結(jié)構(gòu))與數(shù)據(jù)類(lèi)型優(yōu)化
更小的通常好
- 盡量使用可以正確存儲(chǔ)數(shù)據(jù)的最下數(shù)據(jù)類(lèi)型秕衙,更小的數(shù)據(jù)類(lèi)型通常更快蠢甲,占用更小的磁盤(pán),內(nèi)存和cpu緩存,并且處理時(shí)需要的cpu周期更少
- 但是要確保沒(méi)有低估需要存儲(chǔ)的值的范圍,因?yàn)樵趕chema中的多個(gè)地方增加數(shù)據(jù)類(lèi)型的范圍是一個(gè)非常耗時(shí)和痛苦的操作据忘。如果無(wú)法確定哪個(gè)數(shù)據(jù)類(lèi)型是最好的峡钓,就選擇你認(rèn)為不會(huì)超過(guò)范圍的最小類(lèi)型
簡(jiǎn)單就好
- 簡(jiǎn)單數(shù)據(jù)類(lèi)型的操作通常需要更少的CPU周期。例如若河,整型比字符操作代價(jià)更低能岩,
- 整型比字符操作代價(jià)更低,因?yàn)樽址托?duì)規(guī)則(排序規(guī)則)使字符比較比整型比較更復(fù)雜
- 使用MySQL內(nèi)建的類(lèi)型而不是字符串來(lái)存儲(chǔ)日期和時(shí)間
- 用整型存儲(chǔ)IP地址萧福。int類(lèi)型占4個(gè)字節(jié)拉鹃,tinyint占1個(gè)字節(jié)。
盡量避免NUll
- 如果查詢(xún)中包含null的列鲫忍,會(huì)使得索引膏燕,索引統(tǒng)計(jì)和值比較更復(fù)雜
- 當(dāng)可以NULL的列被索引時(shí),每個(gè)索引記錄需要一個(gè)額外的字節(jié)
datetime和timesamp
- datetime和timesamp都可以存儲(chǔ)相同的數(shù)據(jù)類(lèi)型,時(shí)間和日期,精確到秒悟民。然而timesamp只使用datetime一半的存儲(chǔ)空間
- timesamp 會(huì)根據(jù)時(shí)區(qū)變化坝辫,具有特殊的自動(dòng)更新能力,允許的時(shí)間范圍要小很多射亏,有時(shí)它的特許能力會(huì)成為障礙
字段列的類(lèi)型選擇
- 在滿(mǎn)足值的范圍情況下近忙,盡量選擇最小的數(shù)據(jù)類(lèi)型竭业。列如tinyint比int少三個(gè)字節(jié)。tinyint 1字節(jié) (-128及舍,127) (0未辆,255) 小整數(shù)值
- 字段固定長(zhǎng)度如手機(jī)號(hào),身份證號(hào)用
char
,可變長(zhǎng)度使用varchar
因?yàn)槠溟L(zhǎng)度固定锯玛,方便程序的存儲(chǔ)與查找,付出的是空間的代價(jià)咐柜。varchar是以空間效率為首位的 - 整數(shù)類(lèi)型通常是標(biāo)識(shí)列最好的數(shù)據(jù)類(lèi)型,因?yàn)樗麄兒芸觳⑶铱梢允?code>AUTO_INCREMENT
- 如果可能攘残,盡量批量使用字符串類(lèi)型拙友,因?yàn)樗鼈兿母嗟目臻g,比數(shù)字類(lèi)型慢
- 如果存儲(chǔ)UUID值歼郭,可以移除-符號(hào)遗契。使用
UNHEX()
函數(shù)轉(zhuǎn)換為UUID的16字節(jié)的的數(shù)字,并存儲(chǔ)在一個(gè)binary(16)列中实撒。取值時(shí)可以使用HEX()
格式化為16進(jìn)制格式
使用inet_aton和inet_ntoa處理ip地址數(shù)據(jù)
- 插入數(shù)據(jù)前姊途,先用
inet_aton
把ip地址轉(zhuǎn)為整型,可以節(jié)省空間知态,因?yàn)?code>char(15) 占16字節(jié)捷兰。 - 顯示數(shù)據(jù)時(shí),使用
inet_ntoa
把整型的ip地址轉(zhuǎn)為電地址顯示即可负敏。
總結(jié)
- 盡量避免過(guò)度設(shè)計(jì)贡茅,例如會(huì)導(dǎo)致極其復(fù)雜査詢(xún)的schema設(shè)計(jì),或者有很多列的表設(shè)計(jì)
- 使用小而簡(jiǎn)單的合適數(shù)據(jù)類(lèi)型其做,除非真實(shí)數(shù)據(jù)模型中有確切的需要顶考,否則應(yīng)該盡可能地避免使用NULL值。
- 盡量使用相同的數(shù)據(jù)類(lèi)型存儲(chǔ)相似或相關(guān)的值妖泄,尤其是要在關(guān)聯(lián)條件中使用的列驹沿。
- 注意可變長(zhǎng)字符串,其在臨時(shí)表和排序時(shí)可能導(dǎo)致悲觀的按最大長(zhǎng)度分配內(nèi)存蹈胡。
- 盡量使用整型定義標(biāo)識(shí)列渊季。
- 避免使用MySQL已經(jīng)遺棄的特性,例如指定浮點(diǎn)數(shù)的精度罚渐,或者整數(shù)的顯示寬度却汉。
- 小心使用ENUM和SET。雖然它們用起來(lái)很方便荷并,但是不要濫用合砂,否則有時(shí)候會(huì)變成 陷阱。最好避免使用BIT源织。
- ALTER TABLE是讓人痛苦的操作翩伪,因?yàn)樵诖蟛糠智闆r下微猖,它都會(huì)鎖表并且重建整張表。例如在備機(jī)執(zhí)行ALTER并在完成后把它切換為主庫(kù)幻工。
創(chuàng)建高性能的索引
索引基礎(chǔ)
- 索引有很多類(lèi)型,Mysql是使用
B+tree
樹(shù)索引,索引是在引擎層實(shí)現(xiàn)而不是服務(wù)層励两。不同引擎的索引工作方式不一樣黎茎。 -
MyISAM
使用前綴壓縮技術(shù)使得索引更小囊颅,InnoDB
按照原數(shù)據(jù)根式進(jìn)行存儲(chǔ)。MyISAM
索引通過(guò)數(shù)據(jù)到物理位置引用被索引的行傅瞻,InnoDB
根據(jù)主鍵引用被索引的行 - 全值匹配:指的是和索引中所有列進(jìn)行匹配踢代。比如
EXPLAIN
中的type=index - 最左匹配原則: mysql索引規(guī)則中要求復(fù)合索引要想使用第二個(gè)索引,必須先使用第一個(gè)索引的原因嗅骄。(而且第一個(gè)索引必須是等值匹配)胳挎。也就是如果使用復(fù)合索引查詢(xún)時(shí)優(yōu)先按照索引創(chuàng)建的順序進(jìn)行條件查詢(xún)
索引的選擇策略
- 索引字段必須是獨(dú)立的列,不嗯呢該是表達(dá)式和函數(shù)運(yùn)算
- 計(jì)算合適的前綴索引長(zhǎng)度
- 善用復(fù)合索引溺森,而不是為每個(gè)字段都建立索引慕爬,選擇合適的索引列順序
*當(dāng)不需要排序和分組時(shí)使用頻率較高的放在復(fù)合索引前列,這時(shí)候的索引優(yōu)化用于where條件 - 盡可能將左范圍查詢(xún)的列放在索引的后面,以便優(yōu)化器使用盡可能多的索引列。
- 對(duì)于范圍條件查詢(xún),mysql范圍列后面的其它的索引列,對(duì)于多個(gè)等值條件查詢(xún)則沒(méi)有這種限制
Inndb主鍵索引和非主鍵索引的區(qū)別
- 主鍵索引即存儲(chǔ)了索引值屏积,又在葉子中存儲(chǔ)了行的數(shù)據(jù)医窿。所以通過(guò)主鍵查詢(xún)時(shí)效率高,一次查詢(xún)即可炊林,不需要回表操作姥卢。這種即存儲(chǔ)索引值又存儲(chǔ)行的所有數(shù)據(jù)的結(jié)構(gòu)叫做(聚簇索引)
- 非主鍵索引存儲(chǔ)索引值,但葉子中存儲(chǔ)的是主鍵ID渣聚,所以查詢(xún)時(shí)需要進(jìn)行一次回表操作才可以取到所有的行數(shù)據(jù)
索引覆蓋
- 索引覆蓋是指如果查詢(xún)的列恰好是索引的一部分,那么查詢(xún)只需要在索引文件上進(jìn)行,不需要回行到磁盤(pán)再找數(shù)據(jù).這種查詢(xún)速度非扯懒瘢快,稱(chēng)為”索引覆蓋”
理想的索引
- 理想的索引。1:查詢(xún)頻繁 2:區(qū)分度高 3:長(zhǎng)度小 4:盡量能覆蓋常用查詢(xún)字段.
- 索引長(zhǎng)度直接影響索引文件的大小,影響增刪改的速度,并間接影響查詢(xún)速度(占用內(nèi)存多).
- 針對(duì)列中的值,從左往右截取部分,來(lái)建索引
- 截的越短,重復(fù)度越高,區(qū)分度越小, 索引效果越不好
-截的越長(zhǎng),重復(fù)度越低,區(qū)分度越高,索引效果越好,但帶來(lái)的影響也越大--增刪改變慢,并間影響查詢(xún)速度.所以,我們要在 區(qū)分度+長(zhǎng)度奕枝,兩者上,取得一個(gè)平衡. - 慣用手法:截取不同長(zhǎng)度,并測(cè)試其區(qū)分度
select count(distinct left(word,6))/count(*) from dict;
使用索引掃描來(lái)做排序
- mysql有兩種方式可以生成有序的結(jié)果:通過(guò)排序操作或者按索引順序掃描棺榔。如果explan出來(lái)的type值為
index
則說(shuō)明使用了索引掃描來(lái)做排序。 - 只有索引列的順序和
order by
字段的順序完全一致隘道,并且所有列的排序方向都一樣時(shí),mysql才能使用索引對(duì)結(jié)果進(jìn)行排序症歇。 - 如果查詢(xún)需要關(guān)聯(lián)多表,則只有當(dāng)order by中使用的字段完全是第一個(gè)表時(shí)薄声,才能使用索引做排序当船。order by和查找型的限制一樣,都要滿(mǎn)足最左前綴要求默辨。
- 如果索引的第一個(gè)字段為常量時(shí)德频,where或join中指對(duì)這個(gè)列制定了固定的常量值,就可以不滿(mǎn)足最左前綴要求缩幸。比如有個(gè)復(fù)合索引
(c1,c2,c3)
, 執(zhí)行select * from where c1=1 order c2,c3
就可以使用到索引排序
重復(fù)索引與冗余索引
- 重復(fù)索引: 是指 在同1個(gè)列(如age), 或者 順序相同的幾個(gè)列(age,school), 建立了多個(gè)索引,稱(chēng)為重復(fù)索引,重復(fù)索引沒(méi)有任何幫助,只會(huì)增大索引文件,拖慢更新速度, 去掉.
- 冗余索引:是指2個(gè)索引所覆蓋的列有重疊, 稱(chēng)為冗余索引比如x,m,列,加索引
index x(x),index xm(x,m)x,xm
索引,兩者的x列重疊了,這種情況,稱(chēng)為冗余索引. - 甚至可以把
index mx(m,x)
索引也建立,mx,xm也不是重復(fù)的,因?yàn)榱械捻樞虿灰粯?
索引和鎖
- 索引可以讓查詢(xún)鎖定更少的行,Innodb在訪(fǎng)問(wèn)行的時(shí)候?qū)ζ浼渔i壹置,而索引可以減少I(mǎi)nnodb訪(fǎng)問(wèn)的行數(shù),從而減少鎖的數(shù)量
查詢(xún)性能優(yōu)化
優(yōu)化數(shù)據(jù)庫(kù)訪(fǎng)問(wèn)
- 確認(rèn)程序是否在檢索大量超過(guò)需要的數(shù)據(jù)
- 確認(rèn)MySQL服務(wù)器層是否在分析大量超過(guò)需要的數(shù)據(jù)行
- 是否查詢(xún)不需要的記錄竞思,常見(jiàn)的錯(cuò)誤是誤認(rèn)為MySQL只會(huì)返回需要的數(shù)據(jù),實(shí)際上mysql先是返回全部數(shù)據(jù)钞护,在進(jìn)行計(jì)算盖喷。一般使用limit區(qū)分
- 多表關(guān)聯(lián)的時(shí)候返回了全部的列,只需要取出所用的列即可
- 優(yōu)化select *操作,以及重復(fù)查詢(xún)同一條數(shù)據(jù)行做好緩存
- 執(zhí)行語(yǔ)句分析难咕,檢查MySQL是否掃描了額外的記錄
重構(gòu)查詢(xún)的方式
- 設(shè)計(jì)查詢(xún)語(yǔ)句時(shí)课梳,是否要將一個(gè)復(fù)雜的查詢(xún)拆分成多個(gè)簡(jiǎn)單的查詢(xún)
- 切分查詢(xún):將大查詢(xún)切分成小查詢(xún),每個(gè)查詢(xún)功能完全一樣余佃,但是只完成一部分?jǐn)?shù)據(jù)的操作暮刃,每次返回一小部分的結(jié)果。比如在做定時(shí)清除線(xiàn)日志表大表數(shù)據(jù)刪除時(shí)爆土,分批次刪除比較高效椭懊,可以大大減少刪除時(shí)鎖的持有時(shí)間
- 分解關(guān)聯(lián)查詢(xún):可以對(duì)每一個(gè)表進(jìn)行一次單表查詢(xún),然后將結(jié)果在程序中就行關(guān)聯(lián)匯總步势。查詢(xún)拆分后,執(zhí)行單個(gè)查詢(xún)可以減少鎖的競(jìng)爭(zhēng)氧猬。可以讓緩存的效率更高坏瘩,許多應(yīng)用可以方便緩存單表查詢(xún)對(duì)應(yīng)的結(jié)果對(duì)象盅抚。
Mysql中驅(qū)動(dòng)表的概念
- mysql中指定了連接條件時(shí),滿(mǎn)足查詢(xún)條件的記錄行數(shù)少的表為驅(qū)動(dòng)表桑腮;如未指定查詢(xún)條件泉哈,則掃描行數(shù)少的為驅(qū)動(dòng)表。mysql優(yōu)化器就是這么粗暴以小表驅(qū)動(dòng)大表的方式來(lái)決定執(zhí)行順序的破讨。
- 有時(shí)這種優(yōu)化方式反而使表的查詢(xún)效率變低丛晦,這個(gè)時(shí)候
STRAIGHT_JOIN
就排上用場(chǎng)了
比如如下測(cè)試的sql,Table1表的FilterID字段建了索引
select t1.*
from Table1 t1
inner join Table2 t2
on t1.CommonID = t2.CommonID
where t1.FilterID = 1
STRAIGHT_JOIN 方式改寫(xiě)驅(qū)動(dòng)表
select t1.*
from Table1 t1
STRAIGHT_JOIN Table2 t2
on t1.CommonID = t2.CommonID
where t1.FilterID = 1
排序優(yōu)化
- 當(dāng)MySQL不能通過(guò)索引生成排序結(jié)果時(shí),MySQL就需要自己排序提陶,如果數(shù)據(jù)量少則在內(nèi)存中進(jìn)行烫沙,如果數(shù)據(jù)量大則使用磁盤(pán),這個(gè)過(guò)程統(tǒng)一稱(chēng)為文件排序
- 排序的數(shù)據(jù)量小于排序緩沖區(qū)隙笆,則使用內(nèi)存進(jìn)行快速排序操作锌蓄。如果內(nèi)存不夠,會(huì)先將數(shù)據(jù)分塊撑柔,對(duì)每個(gè)獨(dú)立的塊進(jìn)行排序,并將各個(gè)塊的排序結(jié)果放在磁盤(pán)上瘸爽,最后將各個(gè)排序好的塊進(jìn)行合并返回結(jié)果
- mysql在新版本中使用單次排序算法,先讀取查詢(xún)所需要的所有列铅忿,然后在根據(jù)給定的列進(jìn)行排序剪决,最后直接返回排序結(jié)果。
優(yōu)化COUNT查詢(xún)
# 改查詢(xún)需要掃描474條行數(shù)
EXPLAIN select count(*) from system_log20190401000001 where id >5;
# 將條件反轉(zhuǎn)一下,先查詢(xún)id<5的數(shù)據(jù)柑潦,然后利用總數(shù)去做減法也能得到結(jié)果
EXPLAIN select (select count(*) from system_log20190401000001)-count(*) from system_log20190401000001 where id <=5;
優(yōu)化limit分頁(yè)
- 在做limit分頁(yè)時(shí)通常使用偏移量加排序的方式實(shí)現(xiàn)享言,但是當(dāng)偏移量非常大的時(shí)候性能非常低下,比如取10020條只返回最后20條渗鬼,前10000條都拋棄了览露,一般這種方面優(yōu)化,要嗎在頁(yè)面上做大分頁(yè)數(shù)限制譬胎,要嗎優(yōu)化大偏移量的性能差牛。
- 優(yōu)化此類(lèi)分頁(yè)查詢(xún)的最簡(jiǎn)單辦法就是利用覆蓋索返回需要的列,防止回表操作银择。然后利用返回的數(shù)據(jù)做一次關(guān)聯(lián)返回所需要的其余列多糠。
- 有時(shí)也可以將limit查詢(xún)轉(zhuǎn)為為已知的位置進(jìn)行查詢(xún)累舷,比如betwen 10000 and 10020
- 另一種做法就是緩存1000條數(shù)據(jù)浩考,每次分頁(yè)從緩存中取,大于1000就在頁(yè)面額外設(shè)計(jì)按鈕找到更多數(shù)據(jù)被盈。
優(yōu)化UNION查詢(xún)
- UNION 操作符用于合并兩個(gè)或多個(gè)SELECT語(yǔ)句的結(jié)果集
- 默認(rèn)地析孽,UNION操作符選取不同的值。如果允許重復(fù)的值只怎,請(qǐng)使用 UNION ALL
SELECT column_name(s) FROM table_name1
UNION
SELECT column_name(s) FROM table_name2
# UNION ALL
SELECT column_name(s) FROM table_name1
UNION ALL
SELECT column_name(s) FROM table_name2