Mysql
事務(wù)
特征:
- 原子性:不可分割的操作單元,事務(wù)中所有操作,要么全部成功撞蚕;要么撤回到執(zhí)行事務(wù)之前的狀態(tài)
- 一致性:如果在執(zhí)行事務(wù)之前數(shù)據(jù)庫是一致的,那么在執(zhí)行事務(wù)之后數(shù)據(jù)庫也還是一致的过牙;
- 隔離性:事務(wù)操作之間彼此獨(dú)立和透明互不影響诈豌。事務(wù)獨(dú)立運(yùn)行。這通常使用鎖來實(shí)現(xiàn)抒和。一個(gè)事務(wù)處理后的結(jié)果矫渔,影響了其他事務(wù),那么其他事務(wù)會撤回摧莽。事務(wù)的100%隔離庙洼,需要犧牲速度。
- 持久性:事務(wù)一旦提交镊辕,其結(jié)果就是永久的油够。即便發(fā)生系統(tǒng)故障,也能恢復(fù)征懈。
存儲引擎 MyISAM和InnoDB區(qū)別:
MyISAM不支持外鍵和事務(wù)石咬,innodb支持
MyISAM鎖的粒度是表級,而InnoDB支持行級鎖定
MyISAM支持全文類型索引卖哎,而InnoDB不支持全文索引鬼悠。
MyISAM相對簡單,所以在效率上要優(yōu)于InnoDB亏娜,小型應(yīng)用可以考慮使用MyISAM
Myisam創(chuàng)建表生成三個(gè)文件:
.frm
數(shù)據(jù)表結(jié)構(gòu).myd
數(shù)據(jù)文件.myi
索引文件焕窝,Innodb只生成一個(gè)frm文件,數(shù)據(jù)存放在ibdata1.logMyisAM使用delete語句刪除后并不會立刻清理磁盤空間维贺,需要定時(shí)清理它掂,命令:
OPTIMIZE table dept;
應(yīng)用場景:
- MyISAM管理非事務(wù)表。它提供高速存儲和檢索溯泣,以及全文搜索能力虐秋。如果應(yīng)用中需要執(zhí)行大量的SELECT查詢,那么MyISAM是更好的選擇垃沦。
- InnoDB用于事務(wù)處理應(yīng)用程序客给,具有眾多特性,包括ACID事務(wù)支持栏尚。如果應(yīng)用中需要執(zhí)行大量的INSERT或UPDATE操作起愈,則應(yīng)該使用InnoDB只恨,這樣可以提高多用戶并發(fā)操作的性能。
delete drop truncate區(qū)別
- truncate 和 delete只刪除數(shù)據(jù)抬虽,不刪除表結(jié)構(gòu) ,drop刪除表結(jié)構(gòu)官觅,并且釋放所占的空間。
- 刪除數(shù)據(jù)的速度阐污,一般來說: drop> truncate > delete
- delete屬于DML語言休涤,需要事務(wù)管理,commit之后才能生效笛辟。drop和truncate屬于DDL語言功氨,操作立刻生效,不可回滾
-
使用場合:
- 當(dāng)你不再需要該表時(shí)手幢, 用 drop;
- 當(dāng)你仍要保留該表捷凄,但要?jiǎng)h除所有記錄時(shí), 用 truncate;
- 當(dāng)你要?jiǎng)h除部分記錄時(shí)(always with a where clause), 用 delete.
注意: 對于有主外鍵關(guān)系的表围来,不能使用truncate而應(yīng)該使用不帶where子句的delete語句跺涤,由于truncate不記錄在日志中,不能夠激活觸發(fā)器
索引
MyISAM引擎使用B+Tree作為索引結(jié)構(gòu)监透,葉節(jié)點(diǎn)的data域存放的是數(shù)據(jù)記錄的地址桶错。因此,MyISAM中索引檢索的算法為首先按照B+Tree搜索算法搜索索引胀蛮,如果指定的Key存在院刁,則取出其data域的值,然后以data域的值為地址粪狼,讀取相應(yīng)數(shù)據(jù)記錄退腥。
InnoDB也使用B+Tree作為索引結(jié)構(gòu)。InnoDB的數(shù)據(jù)文件本身就是索引文件鸳玩。MyISAM索引文件和數(shù)據(jù)文件是分離的阅虫,索引文件僅保存數(shù)據(jù)記錄的地址(這一點(diǎn)可以通過在data目錄下查看數(shù)據(jù)庫文件驗(yàn)證。Innodb每一個(gè)數(shù)據(jù)庫只有一個(gè)數(shù)據(jù)文件不跟,而Myisam則有三個(gè)(數(shù)據(jù)文件、索引文件米碰、表結(jié)構(gòu)文件))窝革。而在InnoDB中,表數(shù)據(jù)文件本身就是按B+Tree組織的一個(gè)索引結(jié)構(gòu)吕座,這棵樹的葉節(jié)點(diǎn)data域保存了完整的數(shù)據(jù)記錄虐译。這個(gè)索引的key是數(shù)據(jù)表的主鍵,因此InnoDB表數(shù)據(jù)文件本身就是主索引吴趴。
上圖是InnoDB主索引(同時(shí)也是數(shù)據(jù)文件)的示意圖漆诽,可以看到葉節(jié)點(diǎn)包含了完整的數(shù)據(jù)記錄。這種索引叫做聚集索引。因?yàn)镮nnoDB的數(shù)據(jù)文件本身要按主鍵聚集厢拭,所以InnoDB要求表必須有主鍵(MyISAM可以沒有)兰英,如果沒有顯式指定,則MySQL系統(tǒng)會自動選擇一個(gè)可以唯一標(biāo)識數(shù)據(jù)記錄的列作為主鍵供鸠,如果不存在這種列畦贸,則MySQL自動為InnoDB表生成一個(gè)隱含字段作為主鍵,這個(gè)字段長度為6個(gè)字節(jié)楞捂,類型為長整形
第二個(gè)與MyISAM索引的不同是InnoDB的輔助索引(非主鍵索引)data域存儲相應(yīng)記錄主鍵的值而不是地址薄坏。換句話說,InnoDB的所有輔助索引都引用主鍵作為data域寨闹。例如胶坠,圖11為定義在Col3上的一個(gè)輔助索引:這里以英文字符的ASCII碼作為比較準(zhǔn)則。聚集索引這種實(shí)現(xiàn)方式使得按主鍵的搜索十分高效繁堡,但是輔助索引搜索需要檢索兩遍索引:首先檢索輔助索引獲得主鍵涵但,然后用主鍵到主索引中檢索獲得記錄。了解不同存儲引擎的索引實(shí)現(xiàn)方式對于正確使用和優(yōu)化索引都非常有幫助帖蔓,例如知道了InnoDB的索引實(shí)現(xiàn)后矮瘟,就很容易明白為什么不建議使用過長的字段作為主鍵,因?yàn)樗休o助索引都引用主索引塑娇,過長的主索引會令輔助索引變得過大澈侠。再例如,用非單調(diào)的字段作為主鍵在InnoDB中不是個(gè)好主意埋酬,因?yàn)镮nnoDB數(shù)據(jù)文件本身是一顆B+Tree哨啃,非單調(diào)的主鍵會造成在插入新記錄時(shí)數(shù)據(jù)文件為了維持B+Tree的特性而頻繁的分裂調(diào)整,十分低效写妥,而使用自增字段作為主鍵則是一個(gè)很好的選擇拳球。
基于B+樹
B樹的定義:有序數(shù)組+平衡多叉樹
多叉排序樹
通常會定義一個(gè)最小度:t>=2(等于1的時(shí)候退化成為二叉搜索樹):
- 每個(gè)節(jié)點(diǎn)最多含有(2t-1)個(gè)關(guān)鍵字,每個(gè)節(jié)點(diǎn)最多有2t的孩子節(jié)點(diǎn)珍特。
- 當(dāng)節(jié)點(diǎn)的關(guān)鍵字個(gè)數(shù)為2t-1的時(shí)候祝峻,稱該節(jié)點(diǎn)已滿,需要分裂扎筒。分裂從中間節(jié)點(diǎn)一分為二莱找,中間結(jié)點(diǎn)被提升到父節(jié)點(diǎn),如果父節(jié)點(diǎn)也滿嗜桌,則父節(jié)點(diǎn)接著分裂奥溺,直到根節(jié)點(diǎn)。
- 查找可以在非葉子節(jié)點(diǎn)完成骨宠,一個(gè)鍵值只會出現(xiàn)一次浮定,而B+樹的鍵一定出現(xiàn)在葉子節(jié)點(diǎn)相满,并且可能在非葉子節(jié)點(diǎn)重復(fù)出現(xiàn),以維持平衡桦卒。
B+樹是一種變種的B-樹立美。有序數(shù)組鏈表+平衡多叉樹
基本和B樹類似,只有葉子節(jié)點(diǎn)存放數(shù)據(jù)闸盔,而且葉子節(jié)點(diǎn)之間通過指針相連悯辙。
B+-tree的磁盤讀寫代價(jià)更低:B+-tree的內(nèi)部結(jié)點(diǎn)并沒有指向關(guān)鍵字具體信息的指針嘀掸。因此內(nèi)部結(jié)點(diǎn)相對B 樹更小
B+-tree的查詢效率更加穩(wěn)定:由于非終結(jié)點(diǎn)并不是最終指向文件內(nèi)容的結(jié)點(diǎn)喷楣,而只是葉子結(jié)點(diǎn)中關(guān)鍵字的索引。所以任何關(guān)鍵字的查找必須走一條從根結(jié)點(diǎn)到葉子結(jié)點(diǎn)的路弧圆。所有關(guān)鍵字查詢的路徑長度相同击费,導(dǎo)致每一個(gè)數(shù)據(jù)的查詢效率相當(dāng)拢蛋。
索引種類
- 主鍵索引(把某列設(shè)為主鍵,則該列就是索引蔫巩,主鍵不能重復(fù))
- 唯一索引(unique)谆棱,該列具有唯一性,同時(shí)又是索引
- index 普通索引
- 全文索引 (fulltext) 只有MyISAM存儲引擎支持 (注:mysql 5.6之后圆仔,Innodb也開始支持全文索引垃瞧,mysql5.6較之前版本有較大更新,有興趣的小伙伴可以去查一查)
適合加索引的列:
經(jīng)常查詢的列上加索引
唯一性太差的列不要加索引(重復(fù)太多)坪郭,相反唯一性比較好的列適合加索引
不經(jīng)常修改的列適合加个从。經(jīng)常修改的列不要加索引(列修改,索引也要改)
因?yàn)樗饕膭?chuàng)建和更改是有開銷的歪沃。
索引生效條件
假設(shè)index(a,b,c)
- 最左前綴匹配:模糊查詢時(shí)嗦锐,使用%匹配時(shí):’a%‘會使用索引,’%a‘不會使用索引
- 條件中有or沪曙,索引不會生效
- a and c奕污,a生效,c不生效
- b and c液走,都不生效
- a and b > 5 and c,a和b生效碳默,c不生效。
創(chuàng)建 刪除 查看
創(chuàng)建方法一:
create [unique | fulltext] index 索引名 on 表名(列名)
創(chuàng)建方法二:
普通索引:alter table 表名 add index 索引名 (列名)
主鍵索引:alter table 表名 add primary key (列名)
唯一索引:alter table 表明 add unique (列名)
?
刪除
drop index 索引名 on 表名育灸;
alter table 表名 drop index 索引名
alter table 表明 drop primary key
?
查看
show index from 表名腻窒;
?
檢測索引的效果
show status like '%handler_read%'
越大越好
sql語句分類:
- DDL:數(shù)據(jù)定義語言(create alter drop)
- DML:數(shù)據(jù)操作語句(insert update delete)
- select 數(shù)據(jù)查詢語句
- DTL:數(shù)據(jù)事務(wù)語句(commit collback savapoint)
- DCL:數(shù)據(jù)控制語句(grant revoke)
mysql優(yōu)化:
- 數(shù)據(jù)表的優(yōu)化(符合3范式)
- 1范式:1NF是對屬性的原子性約束,要求屬性具有原子性磅崭,不可再分解;(只要是關(guān)系型數(shù)據(jù)庫都滿足1NF)
- 2范式:2NF是對記錄的惟一性約束瓦哎,要求記錄有惟一標(biāo)識砸喻,即實(shí)體的惟一性柔逼;
- 3范式:3NF是對字段冗余性的約束,即任何字段不能由其他字段派生出來割岛,它要求字段沒有冗余愉适。沒有冗余的數(shù)據(jù)庫設(shè)計(jì)可以做到
- 但是,沒有冗余的數(shù)據(jù)庫未必是最好的數(shù)據(jù)庫癣漆,有時(shí)為了提高運(yùn)行效率维咸,就必須降低范式標(biāo)準(zhǔn),適當(dāng)保留冗余數(shù)據(jù)惠爽。具體做法是:在概念數(shù)據(jù)模型設(shè)計(jì)時(shí)遵守第三范式癌蓖,降低范式標(biāo)準(zhǔn)的工作放到物理數(shù)據(jù)模型設(shè)計(jì)時(shí)考慮。降低范式就是增加字段婚肆,允許冗余租副。
- sql語句優(yōu)化 索引
- 使用order by null禁用排序。group by 默認(rèn)情況下會排序(file sorting)较性,非常費(fèi)時(shí)用僧。
- 使用join代替子查詢(子查詢會默認(rèn)創(chuàng)建臨時(shí)表)
- 分表:
- 垂直分割:將字段分開
- 水平分割:將記錄分開
- 讀寫分離
Innodb中的事務(wù)隔離級別和鎖的關(guān)系
前言:
我們都知道事務(wù)的幾種性質(zhì),數(shù)據(jù)庫為了維護(hù)這些性質(zhì)赞咙,尤其是一致性和隔離性责循,一般使用加鎖這種方式。同時(shí)數(shù)據(jù)庫又是個(gè)高并發(fā)的應(yīng)用攀操,同一時(shí)間會有大量的并發(fā)訪問院仿,如果加鎖過度,會極大的降低并發(fā)處理能力崔赌。所以對于加鎖的處理意蛀,可以說就是數(shù)據(jù)庫對于事務(wù)處理的精髓所在。這里通過分析MySQL中InnoDB引擎的加鎖機(jī)制健芭,來拋磚引玉县钥,讓讀者更好的理解,在事務(wù)處理中數(shù)據(jù)庫到底做了什么慈迈。
一次封鎖or兩段鎖若贮?
因?yàn)橛写罅康牟l(fā)訪問,為了預(yù)防死鎖痒留,一般應(yīng)用中推薦使用一次封鎖法谴麦,就是在方法的開始階段,已經(jīng)預(yù)先知道會用到哪些數(shù)據(jù)伸头,然后全部鎖住匾效,在方法運(yùn)行之后,再全部解鎖恤磷。這種方式可以有效的避免循環(huán)死鎖面哼,但在數(shù)據(jù)庫中卻不適用野宜,因?yàn)樵谑聞?wù)開始階段,數(shù)據(jù)庫并不知道會用到哪些數(shù)據(jù)魔策。
數(shù)據(jù)庫遵循的是兩段鎖協(xié)議匈子,將事務(wù)分成兩個(gè)階段,加鎖階段和解鎖階段(所以叫兩段鎖)
- 加鎖階段:在該階段可以進(jìn)行加鎖操作闯袒。在對任何數(shù)據(jù)進(jìn)行讀操作之前要申請并獲得S鎖(共享鎖虎敦,其它事務(wù)可以繼續(xù)加共享鎖,但不能加排它鎖)政敢,在進(jìn)行寫操作之前要申請并獲得X鎖(排它鎖其徙,其它事務(wù)不能再獲得任何鎖)。加鎖不成功堕仔,則事務(wù)進(jìn)入等待狀態(tài)擂橘,直到加鎖成功才繼續(xù)執(zhí)行。
- 解鎖階段:當(dāng)事務(wù)釋放了一個(gè)封鎖以后摩骨,事務(wù)進(jìn)入解鎖階段通贞,在該階段只能進(jìn)行解鎖操作不能再進(jìn)行加鎖操作。
事務(wù) | 加鎖/解鎖處理 |
---|---|
begin恼五; | |
insert into test ..... | 加insert對應(yīng)的鎖 |
update test set... | 加update對應(yīng)的鎖 |
delete from test .... | 加delete對應(yīng)的鎖 |
commit; | 事務(wù)提交時(shí)昌罩,同時(shí)釋放insert、update灾馒、delete對應(yīng)的鎖 |
這種方式雖然無法避免死鎖茎用,但是兩段鎖協(xié)議可以保證事務(wù)的并發(fā)調(diào)度是串行化(串行化很重要,尤其是在數(shù)據(jù)恢復(fù)和備份的時(shí)候)的睬罗。
事務(wù)中的加鎖方式
事務(wù)的四種隔離級別
在數(shù)據(jù)庫操作中轨功,為了有效保證并發(fā)讀取數(shù)據(jù)的正確性,提出的事務(wù)隔離級別容达。我們的數(shù)據(jù)庫鎖古涧,也是為了構(gòu)建這些隔離級別存在的。
隔離級別 | 臟讀(Dirty Read) | 不可重復(fù)讀(NonRepeatable Read) | 幻讀(Phantom Read) |
---|---|---|---|
未提交讀(Read uncommitted) | 可能 | 可能 | 可能 |
已提交讀(Read committed) | 不可能 | 可能 | 可能 |
可重復(fù)讀(Repeatable read) | 不可能 | 不可能 | 可能 |
可串行化(Serializable ) | 不可能 | 不可能 | 不可能 |
- 未提交讀(Read Uncommitted):允許臟讀花盐,也就是可能讀取到其他會話中未提交事務(wù)修改的數(shù)據(jù)
- 提交讀(Read Committed):只能讀取到已經(jīng)提交的數(shù)據(jù)羡滑。Oracle等多數(shù)數(shù)據(jù)庫默認(rèn)都是該級別 (不重復(fù)讀)
- 可重復(fù)讀(Repeated Read):可重復(fù)讀。在同一個(gè)事務(wù)內(nèi)的查詢都是事務(wù)開始時(shí)刻一致的算芯,InnoDB默認(rèn)級別柒昏。在SQL標(biāo)準(zhǔn)中,該隔離級別消除了不可重復(fù)讀熙揍,但是還存在幻象讀
- 串行讀(Serializable):完全串行化的讀职祷,每次讀都需要獲得表級共享鎖,讀寫相互都會阻塞
Read Uncommitted這種級別,數(shù)據(jù)庫一般都不會用堪旧,而且任何操作都不會加鎖削葱,這里就不討論了奖亚。
MySQL中鎖的種類
MySQL中鎖的種類很多淳梦,有常見的表鎖和行鎖,也有新加入的Metadata Lock等等,表鎖是對一整張表加鎖昔字,雖然可分為讀鎖和寫鎖爆袍,但畢竟是鎖住整張表,會導(dǎo)致并發(fā)能力下降作郭,一般是做ddl處理時(shí)使用陨囊。
行鎖則是鎖住數(shù)據(jù)行,這種加鎖方法比較復(fù)雜夹攒,但是由于只鎖住有限的數(shù)據(jù)蜘醋,對于其它數(shù)據(jù)不加限制,所以并發(fā)能力強(qiáng)咏尝,MySQL一般都是用行鎖來處理并發(fā)事務(wù)压语。這里主要討論的也就是行鎖。
Read Committed(讀取提交內(nèi)容)
在RC級別中编检,數(shù)據(jù)的讀取都是不加鎖的胎食,但是數(shù)據(jù)的寫入、修改和刪除是需要加鎖的允懂。效果如下
MySQL> select * from class_teacher;
+----+--------------+------------+
| id | class_name | teacher_id |
+----+--------------+------------+
| 1 | 初三一班 | 1 |
| 3 | 初二一班 | 2 |
| 4 | 初二二班 | 2 |
+----+--------------+------------+
由于MySQL的InnoDB默認(rèn)是使用的RR級別厕怜,所以我們先要將該session開啟成RC級別,并且設(shè)置binlog的模式
SET session transaction isolation level read committed;
SET SESSION binlog_format = 'ROW';(或者是MIXED)
事務(wù)A | 事務(wù)B |
---|---|
begin; | begin; |
update class_teacher set class_name='初三二班' where teacher_id=1; | update class_teacher set class_name='初三三班' where teacher_id=1; |
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction | |
commit; |
為了防止并發(fā)過程中的修改沖突蕾总,事務(wù)A中MySQL給teacher_id=1的數(shù)據(jù)行加鎖粥航,并一直不commit(釋放鎖),那么事務(wù)B也就一直拿不到該行鎖生百,wait直到超時(shí)递雀。
這時(shí)我們要注意到,teacher_id是有索引的置侍,如果是沒有索引的class_name呢映之?update class_teacher set teacher_id=3 where class_name = '初三一班';
那么MySQL會給整張表的所有數(shù)據(jù)行的加行鎖。這里聽起來有點(diǎn)不可思議蜡坊,但是當(dāng)sql運(yùn)行的過程中杠输,MySQL并不知道哪些數(shù)據(jù)行是 class_name = '初三一班'的(沒有索引嘛),如果一個(gè)條件無法通過索引快速過濾秕衙,存儲引擎層面就會將所有記錄加鎖后返回蠢甲,再由MySQL Server層進(jìn)行過濾。
但在實(shí)際使用過程當(dāng)中据忘,MySQL做了一些改進(jìn)鹦牛,在MySQL Server過濾條件搞糕,發(fā)現(xiàn)不滿足后,會調(diào)用unlock_row方法曼追,把不滿足條件的記錄釋放鎖 (違背了二段鎖協(xié)議的約束)窍仰。這樣做,保證了最后只會持有滿足條件記錄上的鎖礼殊,但是每條記錄的加鎖操作還是不能省略的驹吮。可見即使是MySQL晶伦,為了效率也是會違反規(guī)范的碟狞。
這種情況同樣適用于MySQL的默認(rèn)隔離級別RR。所以對一個(gè)數(shù)據(jù)量很大的表做批量修改的時(shí)候婚陪,如果無法使用相應(yīng)的索引族沃,MySQL Server過濾數(shù)據(jù)的的時(shí)候特別慢,就會出現(xiàn)雖然沒有修改某些行的數(shù)據(jù)泌参,但是它們還是被鎖住了的現(xiàn)象脆淹。
Repeatable Read(可重讀)
這是MySQL中InnoDB默認(rèn)的隔離級別。我們姑且分“讀”和“寫”兩個(gè)模塊來講解及舍。
讀
讀就是可重讀未辆,可重讀這個(gè)概念是一事務(wù)的多個(gè)實(shí)例在并發(fā)讀取數(shù)據(jù)時(shí),會看到同樣的數(shù)據(jù)行锯玛,有點(diǎn)抽象咐柜,我們來看一下效果。
RC(不可重讀)模式下的展現(xiàn)
事務(wù)A | 事務(wù)B |
---|---|
begin; | begin; |
select id,class_name,teacher_id from class_teacher where teacher_id=1;idclass_nameteacher_id1初三二班12初三一班1 | |
update class_teacher set class_name='初三三班' where id=1; | |
commit; | |
select id,class_name,teacher_id from class_teacher where teacher_id=1;idclass_nameteacher_id1初三三班12初三一班1讀到了事務(wù)B修改的數(shù)據(jù)攘残,和第一次查詢的結(jié)果不一樣拙友,是不可重讀的。 | |
commit; |
事務(wù)B修改id=1的數(shù)據(jù)提交之后歼郭,事務(wù)A同樣的查詢遗契,后一次和前一次的結(jié)果不一樣,這就是不可重讀(重新讀取產(chǎn)生的結(jié)果不一樣)病曾。這就很可能帶來一些問題牍蜂,那么我們來看看在RR級別中MySQL的表現(xiàn):
事務(wù)A | 事務(wù)B | 事務(wù)C |
---|---|---|
begin; | begin; | begin; |
select id,class_name,teacher_id from class_teacher where teacher_id=1; | ||
idclass_nameteacher_id1初三二班12初三一班1 | update class_teacher set class_name='初三三班' where id=1;commit; | |
insert into class_teacher values (null,'初三三班',1);commit; | ||
select id,class_name,teacher_id from class_teacher where teacher_id=1;idclass_nameteacher_id1初三二班12初三一班1沒有讀到事務(wù)B修改的數(shù)據(jù),和第一次sql讀取的一樣泰涂,是可重復(fù)讀的鲫竞。沒有讀到事務(wù)C新添加的數(shù)據(jù)。 | ||
commit; |
我們注意到逼蒙,當(dāng)teacher_id=1時(shí)从绘,事務(wù)A先做了一次讀取,事務(wù)B中間修改了id=1的數(shù)據(jù),并commit之后僵井,事務(wù)A第二次讀到的數(shù)據(jù)和第一次完全相同陕截。所以說它是可重讀的。那么MySQL是怎么做到的呢批什?這里姑且賣個(gè)關(guān)子农曲,我們往下看。
不可重復(fù)讀和幻讀的區(qū)別####
不可重復(fù)讀重點(diǎn)在于update和delete渊季,而幻讀的重點(diǎn)在于insert朋蔫。
如果使用鎖機(jī)制來實(shí)現(xiàn)這兩種隔離級別,在可重復(fù)讀中却汉,該sql第一次讀取到數(shù)據(jù)后,就將這些數(shù)據(jù)加鎖荷并,其它事務(wù)無法修改這些數(shù)據(jù)合砂,就可以實(shí)現(xiàn)可重復(fù)讀了。但這種方法卻無法鎖住insert的數(shù)據(jù)源织,所以當(dāng)事務(wù)A先前讀取了數(shù)據(jù)翩伪,或者修改了全部數(shù)據(jù),事務(wù)B還是可以insert數(shù)據(jù)提交谈息,這時(shí)事務(wù)A就會發(fā)現(xiàn)莫名其妙多了一條之前沒有的數(shù)據(jù)缘屹,這就是幻讀,不能通過行鎖來避免侠仇。需要Serializable隔離級別 轻姿,讀用讀鎖,寫用寫鎖逻炊,讀鎖和寫鎖互斥互亮,這么做可以有效的避免幻讀、不可重復(fù)讀余素、臟讀等問題豹休,但會極大的降低數(shù)據(jù)庫的并發(fā)能力。
所以說不可重復(fù)讀和幻讀最大的區(qū)別桨吊,就在于如何通過鎖機(jī)制來解決他們產(chǎn)生的問題威根。
上文說的,是使用悲觀鎖機(jī)制來處理這兩種問題视乐,但是MySQL洛搀、ORACLE、PostgreSQL等成熟的數(shù)據(jù)庫炊林,出于性能考慮姥卢,都是使用了以樂觀鎖為理論基礎(chǔ)的MVCC(多版本并發(fā)控制)來避免這兩種問題。
悲觀鎖和樂觀鎖####
- 悲觀鎖
它指的是對數(shù)據(jù)被外界(包括本系統(tǒng)當(dāng)前的其他事務(wù),以及來自外部系統(tǒng)的事務(wù)處理)修改持保守態(tài)度独榴,因此僧叉,在整個(gè)數(shù)據(jù)處理過程中,將數(shù)據(jù)處于鎖定狀態(tài)棺榔。悲觀鎖的實(shí)現(xiàn)瓶堕,往往依靠數(shù)據(jù)庫提供的鎖機(jī)制(也只有數(shù)據(jù)庫層提供的鎖機(jī)制才能真正保證數(shù)據(jù)訪問的排他性,否則症歇,即使在本系統(tǒng)中實(shí)現(xiàn)了加鎖機(jī)制郎笆,也無法保證外部系統(tǒng)不會修改數(shù)據(jù))。
在悲觀鎖的情況下忘晤,為了保證事務(wù)的隔離性宛蚓,就需要一致性鎖定讀。讀取數(shù)據(jù)時(shí)給加鎖设塔,其它事務(wù)無法修改這些數(shù)據(jù)凄吏。修改刪除數(shù)據(jù)時(shí)也要加鎖,其它事務(wù)無法讀取這些數(shù)據(jù)闰蛔。
- 樂觀鎖
相對悲觀鎖而言痕钢,樂觀鎖機(jī)制采取了更加寬松的加鎖機(jī)制。悲觀鎖大多數(shù)情況下依靠數(shù)據(jù)庫的鎖機(jī)制實(shí)現(xiàn)序六,以保證操作最大程度的獨(dú)占性任连。但隨之而來的就是數(shù)據(jù)庫性能的大量開銷,特別是對長事務(wù)而言例诀,這樣的開銷往往無法承受随抠。
而樂觀鎖機(jī)制在一定程度上解決了這個(gè)問題。樂觀鎖余佃,大多是基于數(shù)據(jù)版本( Version )記錄機(jī)制實(shí)現(xiàn)暮刃。何謂數(shù)據(jù)版本?即為數(shù)據(jù)增加一個(gè)版本標(biāo)識爆土,在基于數(shù)據(jù)庫表的版本解決方案中椭懊,一般是通過為數(shù)據(jù)庫表增加一個(gè) “version” 字段來實(shí)現(xiàn)。讀取出數(shù)據(jù)時(shí)步势,將此版本號一同讀出氧猬,之后更新時(shí),對此版本號加一坏瘩。此時(shí)盅抚,將提交數(shù)據(jù)的版本數(shù)據(jù)與數(shù)據(jù)庫表對應(yīng)記錄的當(dāng)前版本信息進(jìn)行比對,如果提交的數(shù)據(jù)版本號大于數(shù)據(jù)庫表當(dāng)前版本號倔矾,則予以更新妄均,否則認(rèn)為是過期數(shù)據(jù)柱锹。
Serializable
這個(gè)級別很簡單,讀加共享鎖丰包,寫加排他鎖禁熏,讀寫互斥。使用的悲觀鎖的理論邑彪,實(shí)現(xiàn)簡單瞧毙,數(shù)據(jù)更加安全,但是并發(fā)能力非常差寄症。如果你的業(yè)務(wù)并發(fā)的特別少或者沒有并發(fā)宙彪,同時(shí)又要求數(shù)據(jù)及時(shí)可靠的話,可以使用這種模式有巧。
這里要吐槽一句释漆,不要看到select就說不會加鎖了,在Serializable這個(gè)級別剪决,還是會加鎖的灵汪!
相信讀到這里的小伙伴,一定有了很大的收獲柑潦。贈人玫瑰,手留余香峻凫。喜歡的話還請點(diǎn)個(gè)贊哈渗鬼。
最后祝大家都能拿到自己滿意的offer!