mysql 运行模式_MySQL的运行模式及一些特性,引擎、事务、并发控制、优化总结...
一 MySQL總體架構(gòu)
上圖是《高性能MySQL》中對MySQL總體架構(gòu)的描述,客戶端對服務(wù)端的連接有很多條,有一個專門的處理組件,類似tomcat使用線程池處理請求。解析器負(fù)責(zé)解析sql語句,在這同時會訪問緩存如果緩存有目標(biāo)數(shù)據(jù)就直接返回。如果需要執(zhí)行sql語句,還會先經(jīng)過優(yōu)化器重新編排執(zhí)行過程(重寫查詢,重排查詢表的順序,選擇合適的索引、優(yōu)化min()max() in()、重排where的順序以適應(yīng)左前綴原則等),優(yōu)化的原則根本上只有一個:從磁盤讀取的數(shù)據(jù)頁(一頁16K,IO的基本單位)越少越好。例如:
使用where語句想走索引查詢,但是如果優(yōu)化器認(rèn)為查到的數(shù)據(jù)基本是全表就會直接走全表掃描,不走索引減少數(shù)據(jù)頁的讀取,也無需回表,雖然這也是性能上的優(yōu)化,但是會讓MySQL執(zhí)行的操作在我們的意料之外,例如不走索引的查詢不會對受影響的行加鎖,這有時會導(dǎo)致一些問題。因此,有explain這個指令讓我們可以知道MySQL的具體執(zhí)行過程。
以上說的都是服務(wù)層面的,一些通用的功能,還包括了用戶權(quán)限驗證啊等等。最下面的則是存儲引擎,負(fù)責(zé)對磁盤數(shù)據(jù)的存取。看似對磁盤的數(shù)據(jù)存取只需調(diào)用API就行,實(shí)際上MySQL在存儲引擎這做了很多工作,例如事務(wù)控制啊、并發(fā)控制啊。
二 引擎
目前最常用的是InnoDB引擎,據(jù)說95 %的情況下使用它就行了。主要有點(diǎn)有:
1. 采用聚簇索引,數(shù)據(jù)本體存在主鍵索引的葉子結(jié)點(diǎn)下,查詢速度非常快。
2.還會視情況建立自適應(yīng)的hash索引(根據(jù)數(shù)據(jù)的值散列運(yùn)算得到地址,O(1)復(fù)雜度),hash索引雖然查詢速度非常快但地址隨機(jī)不適合范圍查找,而且沖突多的時候表現(xiàn)不佳。
3. 相比MyISAM額外支持事務(wù)、支持行鎖。雖然行鎖不是任何情況優(yōu)于表鎖,表鎖雖然并發(fā)量很低但是加鎖開銷小適合死鎖率很高的情況。InnoDB二者都有,可以靈活選擇。
4. InnoDB有完善的事務(wù)日志及熱備份機(jī)制,可用性很強(qiáng),崩潰后恢復(fù)很方便
MyISAM實(shí)現(xiàn)非常簡單,功能非常有限:非聚簇索引(索引文件與數(shù)據(jù)文件單獨(dú)保存,通過索引查詢時總是需要回行)、不支持事務(wù)、鎖只支持表鎖。只適合小型項目、讀操作占大都數(shù)寫操作非常少的場景。
三 鎖與并發(fā)控制
鎖:MySQL的鎖從模式上來說有 共享鎖S(讀鎖)和 排他鎖X(寫鎖),從粒度上或加鎖策略上分又分為行鎖與表鎖。一個引擎支持行鎖說明它可以一次給若干行加鎖,只支持表鎖的話就說明一次加鎖過程要么不鎖要么把全表鎖住。兩個事務(wù)獲取同一目標(biāo)(若干行或整個表)的不同模式的鎖時,沖突情況如下:
上圖的√表示兩個事務(wù)獲取這兩種鎖時不會沖突,×表示這兩種鎖不能被兩個事務(wù)同時獲取,會沖突。上圖內(nèi)容其實(shí)總結(jié)就是:兩個鎖同時有‘S’,或同時有‘I’是可以同時被兩個事務(wù)獲取的,不會沖突。
MVCC多版本控制并發(fā):由于加鎖的開銷比較大,導(dǎo)致并發(fā)量的下降,因此很多數(shù)據(jù)庫都會有MVCC這個機(jī)制。這個機(jī)制簡單的說是通過給每個事務(wù)分配一個版本號,這個事務(wù)修改刪除等操作影響的行將被打上事務(wù)版本號存起來作為一個快照版本,通過這種做法可以在并發(fā)環(huán)境下避免很多的加鎖操作同時也能保證數(shù)據(jù)庫的正確性,從而大幅提高并發(fā)量。可以說MVCC是一種變種的行級鎖(不是簡單無腦給行加鎖),當(dāng)然只有支持行級鎖的引擎支持。
具體是MySQL有一個系統(tǒng)版本號,每創(chuàng)建一個事務(wù)后就遞增,并把這個版本號給新建事務(wù),可以作為這個事務(wù)的標(biāo)識。每個行有隱藏的兩列,分別為創(chuàng)建時間、刪除時間,實(shí)際中我們把這兩列存放創(chuàng)建這一行的事務(wù)的版本號、刪除這一行的版本號。進(jìn)行各個操作的具體實(shí)現(xiàn)是:
insert: 把插入行的創(chuàng)建標(biāo)識置為當(dāng)前版本號(即當(dāng)前事務(wù)版本號)。
delete: 把刪除行的刪除標(biāo)識置為當(dāng)前版本號。
update: 先新建一行,把當(dāng)前版本號作為新建行的創(chuàng)建標(biāo)識,把當(dāng)前版本號作為原先行的刪除標(biāo)識。
select:讀取數(shù)據(jù)時有兩個條件? a:行的創(chuàng)建標(biāo)識要早于或等于本事務(wù)版本號(保證不會讀到后到的事務(wù)修改的數(shù)據(jù),即解決不可重復(fù)讀);? b:行的刪除標(biāo)識要晚于或未定義本事務(wù)版本號(保證不會讀到被之前事務(wù)執(zhí)行刪除操作的數(shù)據(jù),即數(shù)據(jù)不失效)。
MVCC總結(jié)就是:變種的行級鎖,增刪改時維護(hù)行的創(chuàng)建標(biāo)識和刪除標(biāo)識,讀取時對這兩個標(biāo)識加點(diǎn)限制條件。實(shí)現(xiàn)了不加鎖的情況下讀取到正確的數(shù)據(jù),少加了這么多鎖,大幅提高了并發(fā)量。
四 事務(wù)與實(shí)際加鎖策略
事務(wù):事務(wù)的概念及ACID特性都是老生常談了,這里總結(jié)一下‘I’隔離性的每個隔離級別下的加鎖策略以及解決的問題。
RU級別臟讀原因:此級別事務(wù)可以讀到其他事務(wù)修改的且未提交的數(shù)據(jù),如事務(wù)A將x = 1,事務(wù)B此時讀到了x = 1,但是A回滾了,數(shù)據(jù)庫中x肯定也不為1了,所以B的數(shù)據(jù)是臟數(shù)據(jù)。
RC級別: 此隔離級別規(guī)定只有事務(wù)提交了,數(shù)據(jù)才能被其他事務(wù)讀到,自然解決了臟讀。但是卻沒解決不可重復(fù)讀的問題:事務(wù)A先讀到x = 1,然后事務(wù)B修改x = 2并提交,這時事務(wù)A再讀就會發(fā)現(xiàn)x = 2,與之前不一樣了。
RR級別: 不可重復(fù)讀使一個事務(wù)可能會讀到后來的事務(wù)修改的數(shù)據(jù),為了避免這種情況,有了MVCC機(jī)制,讀數(shù)據(jù)時對每一行的版本做一些限制(MVCC在上一部分已總結(jié)原理)。
S級別: 這個級別下,操作表時會鎖住整個表,所以事務(wù)A操作此表時,其他事務(wù)不能對這張表做任何操作,包括了插入操作,自然也沒了幻讀的事情。
間隙鎖:間隙鎖會鎖住額外的行(即不止受影響的行),讓其他事務(wù)沒法刪和改后面的行。間隙鎖的作用具體例子:當(dāng)事務(wù)A在修改刪除 id>10的數(shù)據(jù)時,還沒執(zhí)行完事務(wù)B插了進(jìn)來添加了10條數(shù)據(jù)id都大于10,這時事務(wù)A再恢復(fù)執(zhí)行就會把事務(wù)B插進(jìn)來的數(shù)據(jù)也給改/刪咯。總之就是間隙鎖只有在刪/改操作才會觸發(fā),鎖住其他行防止中途插數(shù)據(jù),這樣被無辜污染。
上述都是理論上各個隔離級別解決的問題,在實(shí)際的MySQL中,可以加一些額外操作在RR隔離級別就避免幻讀問題了,一般使用當(dāng)前讀和GAP鎖,快照讀不存在幻讀,但是update等操作是當(dāng)前讀,舉個例子:
事務(wù)1:
select * from A where p_id = 10; //1
insert into A (id,p_id) values(1,10); //2
事務(wù)2:
insert into A (id,p_id) values(1,10); //1
如果事務(wù)2在事務(wù)1的第1行和第2行之間插入執(zhí)行完畢,那么事務(wù)1的第二行就出了duplicate_key錯誤,可以總結(jié)幻讀的后果就是目前where條件讀到的數(shù)據(jù)不足以支持后續(xù)的操作的正確性。基于此有兩個辦法,一:如果where條件篩選的列是唯一索引說明只有一條符合條件的行,則讀數(shù)據(jù)時改為當(dāng)前讀(select for update)將該行鎖住防止其他事務(wù)操作;二:如果當(dāng)前where條件篩選的列不是唯一索引說明后續(xù)事務(wù)插入的行也可能符合條件,這時需要間隙鎖鎖住其他暫時不存在的行防止后續(xù)事務(wù)插入符合條件的行。
MySQL的實(shí)際加鎖策略:前面也說了,由于有了MVCC,RR及以下級別讀操作無需加鎖,增刪改操作影響的行加X鎖,刪/改還會有有間隙鎖,除非sql中顯示指明select .... lock in share mode則為讀操作影響的行加S鎖。S隔離級別有點(diǎn)特殊,為了防止幻讀,讀操作時會對整個表加S鎖,寫操作時會給整個表加X鎖。
五 優(yōu)化總結(jié)
合理建表:
變長字段與定長字段盡量分離,每一行的大小固定方便跳躍計算
常用字段與非常用字段盡量分離,合理分配訪問量
列的選擇:
優(yōu)先選用: int -> date time -> enum -> char -> vchar -> text
避免可為null的列,不適合索引的建立以及比較等計算,還會浪費(fèi)額外的空間記錄
建立合適的索引:
查詢時應(yīng)該用獨(dú)立的列,像where id+1 = 5是用不上索引的
選擇區(qū)分度大的列建立索引,像性別這種列就沒必要
使用頻率高的聯(lián)合查詢 where 列1..and 列2...則需要考慮為這幾個列建一個聯(lián)合索引
由于最左原則,聯(lián)合索引建立時應(yīng)該把區(qū)分度高的放到左邊建立,查詢語句也要注意左前綴原則
用到索引覆蓋最好,像select * 則幾乎用不到索引覆蓋
SQL語句的優(yōu)化:
少用In查詢
where ... or ...這類的查詢可以拆分為幾個select 再用union合并,性能提升明顯
表在連接之前先用where篩選,也要避免過多表的連接
select ..時要幾個列就寫幾個,不要多寫,會增加數(shù)據(jù)傳送量
總結(jié)
以上是生活随笔為你收集整理的mysql 运行模式_MySQL的运行模式及一些特性,引擎、事务、并发控制、优化总结...的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: mysql 多数据库实例_Mysql多实
- 下一篇: mysql登陆salt_salt把返回写