一、數(shù)據(jù)庫事務隔離級別
數(shù)據(jù)庫事務的隔離級別有4個,由低到高依次為Read uncommitted?、Read committed?、Repeatable read?、Serializable,這四個級別可以逐個解決臟讀?、不可重復讀?、幻讀?這幾類問題。
Read uncommitted 讀未提交
公司發(fā)工資了,領導把5000元打到singo的賬號上,但是該事務并未提交,而singo正好去查看賬戶,發(fā)現(xiàn)工資已經(jīng)到賬,是5000元整,非常高 興??墒遣恍业氖?,領導發(fā)現(xiàn)發(fā)給singo的工資金額不對,是2000元,于是迅速回滾了事務,修改金額后,將事務提交,最后singo實際的工資只有 2000元,singo空歡喜一場。
出現(xiàn)上述情況,即我們所說的臟讀?,兩個并發(fā)的事務,“事務A:領導給singo發(fā)工資”、“事務B:singo查詢工資賬戶”,事務B讀取了事務A尚未提交的數(shù)據(jù)。
當隔離級別設置為Read uncommitted?時,就可能出現(xiàn)臟讀,如何避免臟讀,請看下一個隔離級別。
Read committed 讀提交
singo拿著工資卡去消費,系統(tǒng)讀取到卡里確實有2000元,而此時她的老婆也正好在網(wǎng)上轉賬,把singo工資卡的2000元轉到另一賬戶,并在 singo之前提交了事務,當singo扣款時,系統(tǒng)檢查到singo的工資卡已經(jīng)沒有錢,扣款失敗,singo十分納悶,明明卡里有錢,為 何......
出現(xiàn)上述情況,即我們所說的不可重復讀?,兩個并發(fā)的事務,“事務A:singo消費”、“事務B:singo的老婆網(wǎng)上轉賬”,事務A事先讀取了數(shù)據(jù),事務B緊接了更新了數(shù)據(jù),并提交了事務,而事務A再次讀取該數(shù)據(jù)時,數(shù)據(jù)已經(jīng)發(fā)生了改變。
當隔離級別設置為Read committed?時,避免了臟讀,但是可能會造成不可重復讀。
大多數(shù)數(shù)據(jù)庫的默認級別就是Read committed,比如Sql Server , Oracle。如何解決不可重復讀這一問題,請看下一個隔離級別。
Repeatable read 重復讀
當隔離級別設置為Repeatable read?時,可以避免不可重復讀。當singo拿著工資卡去消費時,一旦系統(tǒng)開始讀取工資卡信息(即事務開始),singo的老婆就不可能對該記錄進行修改,也就是singo的老婆不能在此時轉賬。
雖然Repeatable read避免了不可重復讀,但還有可能出現(xiàn)幻讀?。
singo的老婆工作在銀行部門,她時常通過銀行內(nèi)部系統(tǒng)查看singo的信用卡消費記錄。有一天,她正在查詢到singo當月信用卡的總消費金額 (select sum(amount) from transaction where month = 本月)為80元,而singo此時正好在外面胡吃海塞后在收銀臺買單,消費1000元,即新增了一條1000元的消費記錄(insert transaction ... ),并提交了事務,隨后singo的老婆將singo當月信用卡消費的明細打印到A4紙上,卻發(fā)現(xiàn)消費總額為1080元,singo的老婆很詫異,以為出 現(xiàn)了幻覺,幻讀就這樣產(chǎn)生了。
注:MySQL的默認隔離級別就是Repeatable read。
Serializable 序列化
Serializable?是最高的事務隔離級別,同時代價也花費最高,性能很低,一般很少使用,在該級別下,事務順序執(zhí)行,不僅可以避免臟讀、不可重復讀,還避免了幻像讀。
1.InnoDB引擎
InnoDB是MySQL的默認引擎,使用的是可重復讀級別的隔離,B+樹是InnoDB的默認索引類型,并且支持事務和行鎖,以及外鍵約束。
它的設計的目標就是處理大數(shù)據(jù)容量的數(shù)據(jù)庫系統(tǒng)。MySQL 運行的時候,InnoDB 會在內(nèi)存中建立緩沖池,用于緩沖數(shù)據(jù)和索引。但是該引擎是不支持全文搜索,同時啟動也比較的慢,它是不會保存表的行數(shù)的,所以當進行 selectcount(*) from table 指令的時候,需要進行掃描全表。由于鎖的粒度小,寫操作是不會鎖定全表的,所以在并發(fā)度較高的場景下使用會提升效率的。
2.MyISAM引擎
Myisam 的存儲文件有三個,后綴名分別是 .frm、.MYD、MYI,其中 .frm 是表的定義文件,.MYD 是數(shù)據(jù)文件,.MYI 是索引文件。
Myisam 只支持表鎖,且不支持事務。Myisam 由于有單獨的索引文件,在讀取數(shù)據(jù)方面的性能很高 。MyIASM 引擎是保存了表的行數(shù),于是當進行 select count(*) from table 語句時,可以直接的讀取已經(jīng)保存的值而不需要進行掃描全表。所以,如果表的讀操作遠遠多于寫操作時,并且不需要事務的支持的,可以將 MyIASM 作為數(shù)據(jù)庫引擎的首選。
MySQL表級鎖有兩種模式:表共享鎖(Table Read Lock)和表獨占寫鎖(Table Write Lock)。
<1>對MyISAM的讀操作,不會阻塞其他用戶對同一表請求,但會阻塞對同一表的寫請求;
<2>對MyISAM的寫操作,則會阻塞其他用戶對同一表的讀和寫操作;
<3>MyISAM表的讀操作和寫操作之間,以及寫操作之間是串行的。
當一個線程獲得對一個表的寫鎖后,只有持有鎖線程可以對表進行更新操作。其他線程的讀、寫操作都會等待,直到鎖被釋放為止。
1.為什么數(shù)據(jù)庫索引不使用哈希表?
1、hash表只能匹配是否相等,不能實現(xiàn)范圍查找
2、當需要按照索引進行order by時,hash值沒辦法支持排序
3、組合索引可以支持部分索引查詢,如(a,b,c)的組合索引,查詢中只用到了阿和b也可以查詢的,如果使用hash表,組合索引會將幾個字段合并hash,沒辦法支持部分索引
4、當數(shù)據(jù)量很大時,hash沖突的概率也會非常大
5、B+樹作為索引時,非葉子節(jié)點只保存索引,葉子節(jié)點才會保存數(shù)據(jù),這樣方便掃庫,只需要掃一遍葉子結點即可,但是B樹因為其分支結點同樣存儲著數(shù)據(jù),我們要找到具體的數(shù)據(jù),需要進行一次中序遍歷按序來掃,所以B+樹更加適合在區(qū)間查詢的情況,所以通常B+樹用于數(shù)據(jù)庫索引。
2.為什么數(shù)據(jù)庫索引使用B+樹,不使用B樹?
1、 B+樹的磁盤讀寫代價更低:B+樹的內(nèi)部節(jié)點并沒有指向關鍵字具體信息的指針,因此其內(nèi)部節(jié)點相對B樹更小,如果把所有同一內(nèi)部節(jié)點的關鍵字存放在同一盤塊中,那么盤塊所能容納的關鍵字數(shù)量也越多,一次性讀入內(nèi)存的需要查找的關鍵字也就越多,相對IO讀寫次數(shù)就降低了。
2、B+樹的查詢效率更加穩(wěn)定:B+樹所有關鍵字數(shù)據(jù)地址都存在葉子節(jié)點上,所以每次查找的次數(shù)都相同所以查詢速度要比B樹更穩(wěn)定;
3、B+樹全節(jié)點遍歷更快:B+樹遍歷整棵樹只需要遍歷所有的葉子節(jié)點即可,,而不需要像B樹一樣需要對每一層進行遍歷,這有利于數(shù)據(jù)庫做全表掃描。
4.B+樹天然具備排序功能:B+樹所有的葉子節(jié)點數(shù)據(jù)構成了一個有序鏈表,在查詢大小區(qū)間的數(shù)據(jù)時候更方便,數(shù)據(jù)緊密性很高,緩存的命中率也會比B樹高。
B樹相對于B+樹的優(yōu)點是,如果經(jīng)常訪問的數(shù)據(jù)離根節(jié)點很近,而B樹的非葉子節(jié)點本身存有關鍵字其數(shù)據(jù)的地址,所以這種數(shù)據(jù)檢索的時候會要比B+樹快。