可能是全網(wǎng)最好的MySQL重要知識(shí)點(diǎn)

作者:Snailclimb

鏈接:https://segmentfault.com/a/1190000019619667

什么是MySQL?

MySQL 是一種關(guān)系型數(shù)據(jù)庫(kù)馅巷,在Java企業(yè)級(jí)開(kāi)發(fā)中非常常用,因?yàn)?MySQL 是開(kāi)源免費(fèi)的舍哄,并且方便擴(kuò)展。阿里巴巴數(shù)據(jù)庫(kù)系統(tǒng)也大量用到了 MySQL弥锄,因此它的穩(wěn)定性是有保障的蟆沫。MySQL是開(kāi)放源代碼的籽暇,因此任何人都可以在 GPL(General Public License) 的許可下下載并根據(jù)個(gè)性化的需要對(duì)其進(jìn)行修改。MySQL的默認(rèn)端口號(hào)是3306饭庞。

事務(wù)相關(guān)

什么是事務(wù)戒悠?

事務(wù)是邏輯上的一組操作,要么都執(zhí)行舟山,要么都不執(zhí)行救崔。

事務(wù)最經(jīng)典也經(jīng)常被拿出來(lái)說(shuō)例子就是轉(zhuǎn)賬了。假如小明要給小紅轉(zhuǎn)賬1000元捏顺,這個(gè)轉(zhuǎn)賬會(huì)涉及到兩個(gè)關(guān)鍵操作就是:將小明的余額減少1000元,將小紅的余額增加1000元纬黎。萬(wàn)一在這兩個(gè)操作之間突然出現(xiàn)錯(cuò)誤比如銀行系統(tǒng)崩潰幅骄,導(dǎo)致小明余額減少而小紅的余額沒(méi)有增加,這樣就不對(duì)了本今。事務(wù)就是保證這兩個(gè)關(guān)鍵操作要么都成功,要么都要失敗。

事物的四大特性(ACID)介紹一下观挎?

image
  • 原子性: 事務(wù)是最小的執(zhí)行單位荠瘪,不允許分割。事務(wù)的原子性確保動(dòng)作要么全部完成逛艰,要么完全不起作用躏碳;

  • 一致性: 執(zhí)行事務(wù)前后,數(shù)據(jù)保持一致散怖,多個(gè)事務(wù)對(duì)同一個(gè)數(shù)據(jù)讀取的結(jié)果是相同的菇绵;

  • 隔離性: 并發(fā)訪問(wèn)數(shù)據(jù)庫(kù)時(shí)镇眷,一個(gè)用戶的事務(wù)不被其他事務(wù)所干擾永乌,各并發(fā)事務(wù)之間數(shù)據(jù)庫(kù)是獨(dú)立的圈驼;

  • 持久性:一個(gè)事務(wù)被提交之后。它對(duì)數(shù)據(jù)庫(kù)中數(shù)據(jù)的改變是持久的衙伶,即使數(shù)據(jù)庫(kù)發(fā)生故障也不應(yīng)該對(duì)其有任何影響。

并發(fā)事務(wù)帶來(lái)哪些問(wèn)題芬沉?

在典型的應(yīng)用程序中,多個(gè)事務(wù)并發(fā)運(yùn)行黄刚,經(jīng)常會(huì)操作相同的數(shù)據(jù)來(lái)完成各自的任務(wù)(多個(gè)用戶對(duì)統(tǒng)一數(shù)據(jù)進(jìn)行操作)。并發(fā)雖然是必須的业扒,但可能會(huì)導(dǎo)致以下的問(wèn)題:

  • **臟讀(Dirty read): **當(dāng)一個(gè)事務(wù)正在訪問(wèn)數(shù)據(jù)并且對(duì)數(shù)據(jù)進(jìn)行了修改,而這種修改還沒(méi)有提交到數(shù)據(jù)庫(kù)中虱肄,這時(shí)另外一個(gè)事務(wù)也訪問(wèn)了這個(gè)數(shù)據(jù)咏窿,然后使用了這個(gè)數(shù)據(jù)萝挤。因?yàn)檫@個(gè)數(shù)據(jù)是還沒(méi)有提交的數(shù)據(jù)怜珍,那么另外一個(gè)事務(wù)讀到的這個(gè)數(shù)據(jù)是“臟數(shù)據(jù)”,依據(jù)“臟數(shù)據(jù)”所做的操作可能是不正確的柔袁。

  • **丟****失修改(Lost to modify): **指在一個(gè)事務(wù)讀取一個(gè)數(shù)據(jù)時(shí),另外一個(gè)事務(wù)也訪問(wèn)了該數(shù)據(jù)腥例,那么在第一個(gè)事務(wù)中修改了這個(gè)數(shù)據(jù)后,第二個(gè)事務(wù)也修改了這個(gè)數(shù)據(jù)底瓣。這樣第一個(gè)事務(wù)內(nèi)的修改結(jié)果就被丟失拨扶,因此稱為丟失修改缩举。例如:事務(wù)1讀取某表中的數(shù)據(jù)A=20仅孩,事務(wù)2也讀取A=20,事務(wù)1修改A=A-1溅蛉,事務(wù)2也修改A=A-1欠气,最終結(jié)果A=19,事務(wù)1的修改被丟失宜鸯。

  • **不可重復(fù)讀(Unrepeatableread): **指在一個(gè)事務(wù)內(nèi)多次讀同一數(shù)據(jù)。在這個(gè)事務(wù)還沒(méi)有結(jié)束時(shí)适贸,另一個(gè)事務(wù)也訪問(wèn)該數(shù)據(jù)。那么蕊肥,在第一個(gè)事務(wù)中的兩次讀數(shù)據(jù)之間壁却,由于第二個(gè)事務(wù)的修改導(dǎo)致第一個(gè)事務(wù)兩次讀取的數(shù)據(jù)可能不太一樣。這就發(fā)生了在一個(gè)事務(wù)內(nèi)兩次讀到的數(shù)據(jù)是不一樣的情況盐肃,因此稱為不可重復(fù)讀。

  • **幻讀(Phantom read): **幻讀與不可重復(fù)讀類似。它發(fā)生在一個(gè)事務(wù)(T1)讀取了幾行數(shù)據(jù)荷辕,接著另一個(gè)并發(fā)事務(wù)(T2)插入了一些數(shù)據(jù)時(shí)控嗜。在隨后的查詢中,第一個(gè)事務(wù)(T1)就會(huì)發(fā)現(xiàn)多了一些原本不存在的記錄壁顶,就好像發(fā)生了幻覺(jué)一樣蝴猪,所以稱為幻讀嚎莉。

不可重復(fù)度和幻讀區(qū)別:

不可重復(fù)讀的重點(diǎn)是修改,幻讀的重點(diǎn)在于新增或者刪除叫确。

例1(同樣的條件, 你讀取過(guò)的數(shù)據(jù), 再次讀取出來(lái)發(fā)現(xiàn)值不一樣了 ):事務(wù)1中的A先生讀取自己的工資為 1000的操作還沒(méi)完成,事務(wù)2中的B先生就修改了A的工資為2000醉旦,導(dǎo) 致A再讀自己的工資時(shí)工資變?yōu)?2000;這就是不可重復(fù)讀桨啃。

例2(同樣的條件, 第1次和第2次讀出來(lái)的記錄數(shù)不一樣 ):假某工資單表中工資大于3000的有4人车胡,事務(wù)1讀取了所有工資大于3000的人,共查到4條記錄照瘾,這時(shí)事務(wù)2 又插入了一條工資大于3000的記錄匈棘,事務(wù)1再次讀取時(shí)查到的記錄就變?yōu)榱?條,這樣就導(dǎo)致了幻讀主卫。

事務(wù)隔離級(jí)別有哪些?MySQL的默認(rèn)隔離級(jí)別是?

SQL 標(biāo)準(zhǔn)定義了四個(gè)隔離級(jí)別:

  • READ-UNCOMMITTED(讀取未提交):最低的隔離級(jí)別异旧,允許讀取尚未提交的數(shù)據(jù)變更,可能會(huì)導(dǎo)致臟讀务唐、幻讀或不可重復(fù)讀。

  • READ-COMMITTED(讀取已提交):允許讀取并發(fā)事務(wù)已經(jīng)提交的數(shù)據(jù)拯辙,可以阻止臟讀,但是幻讀或不可重復(fù)讀仍有可能發(fā)生划纽。

  • REPEATABLE-READ(可重復(fù)讀):對(duì)同一字段的多次讀取結(jié)果都是一致的,除非數(shù)據(jù)是被本身事務(wù)自己所修改,可以阻止臟讀和不可重復(fù)讀塞祈,但幻讀仍有可能發(fā)生捅位。

  • SERIALIZABLE(可串行化):最高的隔離級(jí)別矩屁,完全服從ACID的隔離級(jí)別。所有的事務(wù)依次逐個(gè)執(zhí)行,這樣事務(wù)之間就完全不可能產(chǎn)生干擾,也就是說(shuō)这敬,該級(jí)別可以防止臟讀、不可重復(fù)讀以及幻讀。

隔離級(jí)別 臟讀 不可重復(fù)讀 幻影讀
READ-UNCOMMITTED
READ-COMMITTED ×
REPEATABLE-READ × ×
SERIALIZABLE × × ×

MySQL InnoDB 存儲(chǔ)引擎的默認(rèn)支持的隔離級(jí)別是 REPEATABLE-READ(可重讀)。我們可以通過(guò)SELECT @@tx_isolation;命令來(lái)查看

mysql> SELECT @@tx_isolation;+-----------------+| @@tx_isolation  |+-----------------+| REPEATABLE-READ |+-----------------+

這里需要注意的是:與 SQL 標(biāo)準(zhǔn)不同的地方在于InnoDB 存儲(chǔ)引擎在 REPEATABLE-READ(可重讀)事務(wù)隔離級(jí)別下使用的是Next-Key Lock 鎖算法注益,因此可以避免幻讀的產(chǎn)生浙垫,這與其他數(shù)據(jù)庫(kù)系統(tǒng)(如 SQL Server)是不同的。所以說(shuō)InnoDB 存儲(chǔ)引擎的默認(rèn)支持的隔離級(jí)別是 REPEATABLE-READ(可重讀) 已經(jīng)可以完全保證事務(wù)的隔離性要求捧杉,即達(dá)到了 SQL標(biāo)準(zhǔn)的SERIALIZABLE(可串行化)隔離級(jí)別陕见。

因?yàn)楦綦x級(jí)別越低,事務(wù)請(qǐng)求的鎖越少味抖,所以大部分?jǐn)?shù)據(jù)庫(kù)系統(tǒng)的隔離級(jí)別都是READ-COMMITTED(讀取提交內(nèi)容):评甜,但是你要知道的是InnoDB 存儲(chǔ)引擎默認(rèn)使用 REPEATABLE-READ(可重讀)并不會(huì)有任何性能損失。

InnoDB 存儲(chǔ)引擎在 分布式事務(wù) 的情況下一般會(huì)用到SERIALIZABLE(可串行化)隔離級(jí)別仔涩。

索引相關(guān)

為什么索引能提高查詢速度

以下內(nèi)容整理自:《數(shù)據(jù)庫(kù)兩大神器【索引和鎖】》作者 :Java3y

先從 MySQL 的基本存儲(chǔ)結(jié)構(gòu)說(shuō)起

MySQL的基本存儲(chǔ)結(jié)構(gòu)是頁(yè) (記錄都存在頁(yè)里邊) :

image
image
  • 各個(gè)數(shù)據(jù)頁(yè)可以組成一個(gè)雙向鏈表

  • 每個(gè)數(shù)據(jù)頁(yè)中的記錄又可以組成一個(gè)單向鏈表

- 每個(gè)數(shù)據(jù)頁(yè)都會(huì)為存儲(chǔ)在它里邊兒的記錄生成一個(gè)頁(yè)目錄忍坷,在通過(guò)主鍵查找某條記錄的時(shí)候可以在頁(yè)目錄中使用二分法快速定位到對(duì)應(yīng)的槽,然后再遍歷該槽對(duì)應(yīng)分組中的記錄即可快速找到指定的記錄- 以其他列(非主鍵)作為搜索條件:只能從最小記錄開(kāi)始依次遍歷單鏈表中的每條記錄熔脂。

所以說(shuō)佩研,如果我們寫(xiě)select * from user where indexname = 'xxx'這樣沒(méi)有進(jìn)行任何優(yōu)化的sql語(yǔ)句,默認(rèn)會(huì)這樣做:

  1. 定位到記錄所在的頁(yè):需要遍歷雙向鏈表霞揉,找到所在的頁(yè)

  2. 從所在的頁(yè)內(nèi)中查找相應(yīng)的記錄:由于不是根據(jù)主鍵查詢旬薯,只能遍歷所在頁(yè)的單鏈表了

很明顯,在數(shù)據(jù)量很大的情況下這樣查找會(huì)很慢适秩!這樣的時(shí)間復(fù)雜度為O(n)绊序。

索引做了些什么可以讓我們查詢加快速度呢?****其實(shí)就是將無(wú)序的數(shù)據(jù)變成有序(相對(duì)):

image

要找到id為8的記錄簡(jiǎn)要步驟:

image

很明顯的是:沒(méi)有用索引我們是需要遍歷雙向鏈表來(lái)定位對(duì)應(yīng)的頁(yè)秽荞,現(xiàn)在通過(guò) “目錄” 就可以很快地定位到對(duì)應(yīng)的頁(yè)上了V韫(二分查找,時(shí)間復(fù)雜度近似為O(logn))

其實(shí)底層結(jié)構(gòu)就是B+樹(shù)扬跋,B+樹(shù)作為樹(shù)的一種實(shí)現(xiàn)阶捆,能夠讓我們很快地查找出對(duì)應(yīng)的記錄。

以下內(nèi)容整理自:《Java工程師修煉之道》

什么是最左前綴原則钦听?

MySQL中的索引可以以一定順序引用多列趁猴,這種索引叫作聯(lián)合索引。如User表的name和city加聯(lián)合索引就是(name,city)彪见,而最左前綴原則指的是儡司,如果查詢的時(shí)候查詢條件精確匹配索引的左邊連續(xù)一列或幾列,則此列就可以被用到余指。如下:

select * from user where name=xx and city=xx ; //可以命中索引select * from user where name=xx ; // 可以命中索引select * from user where city=xx ; // 無(wú)法命中索引       

這里需要注意的是捕犬,查詢的時(shí)候如果兩個(gè)條件都用上了跷坝,但是順序不同,如 city= xx and name =xx碉碉,那么現(xiàn)在的查詢引擎會(huì)自動(dòng)優(yōu)化為匹配聯(lián)合索引的順序柴钻,這樣是能夠命中索引的。

由于最左前綴原則垢粮,在創(chuàng)建聯(lián)合索引時(shí)贴届,索引字段的順序需要考慮字段值去重之后的個(gè)數(shù),較多的放前面蜡吧。ORDER BY子句也遵循此規(guī)則毫蚓。

注意避免冗余索引

冗余索引指的是索引的功能相同,能夠命中就肯定能命中 昔善,那么 就是冗余索引如(name,city )和(name )這兩個(gè)索引就是冗余索引元潘,能夠命中后者的查詢肯定是能夠命中前者的 在大多數(shù)情況下,都應(yīng)該盡量擴(kuò)展已有的索引而不是創(chuàng)建新索引君仆。

MySQLS.7 版本后翩概,可以通過(guò)查詢 sys 庫(kù)的 schema_redundant_indexes 表來(lái)查看冗余索引

Mysql如何為表字段添加索引?

1.添加PRIMARY KEY(主鍵索引)

ALTER TABLE `table_name` ADD PRIMARY KEY ( `column` ) 

2.添加UNIQUE(唯一索引)

ALTER TABLE `table_name` ADD UNIQUE ( `column` ) 

3.添加INDEX(普通索引)

ALTER TABLE `table_name` ADD INDEX index_name ( `column` )

4.添加FULLTEXT(全文索引)

`ALTER TABLE `table_name` ADD FULLTEXT ( `column`)`

5.添加多列索引

ALTER TABLE `table_name` ADD INDEX index_name ( `column1`, `column2`, `column3` )

存儲(chǔ)引擎

一些常用命令

查看MySQL提供的所有存儲(chǔ)引擎

mysql> show engines;
image

從上圖我們可以查看出 MySQL 當(dāng)前默認(rèn)的存儲(chǔ)引擎是InnoDB,并且在5.7版本所有的存儲(chǔ)引擎中只有 InnoDB 是事務(wù)性存儲(chǔ)引擎返咱,也就是說(shuō)只有 InnoDB 支持事務(wù)钥庇。

查看MySQL當(dāng)前默認(rèn)的存儲(chǔ)引擎

我們也可以通過(guò)下面的命令查看默認(rèn)的存儲(chǔ)引擎。

mysql> show variables like '%storage_engine%';

查看表的存儲(chǔ)引擎

show table status like "table_name" ;
image

MyISAM和InnoDB區(qū)別

MyISAM是MySQL的默認(rèn)數(shù)據(jù)庫(kù)引擎(5.5版之前)咖摹。雖然性能極佳上沐,而且提供了大量的特性,包括全文索引楞艾、壓縮参咙、空間函數(shù)等,但MyISAM不支持事務(wù)和行級(jí)鎖硫眯,而且最大的缺陷就是崩潰后無(wú)法安全恢復(fù)蕴侧。不過(guò),5.5版本之后两入,MySQL引入了InnoDB(事務(wù)性數(shù)據(jù)庫(kù)引擎)净宵,MySQL 5.5版本后默認(rèn)的存儲(chǔ)引擎為InnoDB。

大多數(shù)時(shí)候我們使用的都是 InnoDB 存儲(chǔ)引擎裹纳,但是在某些情況下使用 MyISAM 也是合適的比如讀密集的情況下择葡。(如果你不介意 MyISAM 崩潰回復(fù)問(wèn)題的話)。

兩者的對(duì)比:

  1. 是否支持行級(jí)鎖 : MyISAM 只有表級(jí)鎖(table-level locking)剃氧,而InnoDB 支持行級(jí)鎖(row-level locking)和表級(jí)鎖,默認(rèn)為行級(jí)鎖敏储。

  2. 是否支持事務(wù)和崩潰后的安全恢復(fù):****MyISAM 強(qiáng)調(diào)的是性能,每次查詢具有原子性,其執(zhí)行比InnoDB類型更快朋鞍,但是不提供事務(wù)支持已添。但是InnoDB 提供事務(wù)支持事務(wù)妥箕,外部鍵等高級(jí)數(shù)據(jù)庫(kù)功能。具有事務(wù)(commit)更舞、回滾(rollback)和崩潰修復(fù)能力(crash recovery capabilities)的事務(wù)安全(transaction-safe (ACID compliant))型表畦幢。

  3. 是否支持外鍵: MyISAM不支持,而InnoDB支持缆蝉。

  4. 是否支持MVCC :僅 InnoDB 支持宇葱。應(yīng)對(duì)高并發(fā)事務(wù), MVCC比單純的加鎖更高效;MVCC只在 READ COMMITTEDREPEATABLE READ 兩個(gè)隔離級(jí)別下工作;MVCC可以使用 樂(lè)觀(optimistic)鎖 和 悲觀(pessimistic)鎖來(lái)實(shí)現(xiàn);各數(shù)據(jù)庫(kù)中MVCC實(shí)現(xiàn)并不統(tǒng)一。

  5. ......

《MySQL高性能》上面有一句話這樣寫(xiě)到:

不要輕易相信“MyISAM比InnoDB快”之類的經(jīng)驗(yàn)之談刊头,這個(gè)結(jié)論往往不是絕對(duì)的黍瞧。在很多我們已知場(chǎng)景中,InnoDB的速度都可以讓MyISAM望塵莫及芽偏,尤其是用到了聚簇索引,或者需要訪問(wèn)的數(shù)據(jù)都可以放入內(nèi)存的應(yīng)用弦讽。

一般情況下我們選擇 InnoDB 都是沒(méi)有問(wèn)題的污尉,但是某事情況下你并不在乎可擴(kuò)展能力和并發(fā)能力,也不需要事務(wù)支持往产,也不在乎崩潰后的安全恢復(fù)問(wèn)題的話被碗,選擇MyISAM也是一個(gè)不錯(cuò)的選擇。但是一般情況下仿村,我們都是需要考慮到這些問(wèn)題的锐朴。

樂(lè)觀鎖與悲觀鎖的區(qū)別

悲觀鎖

總是假設(shè)最壞的情況,每次去拿數(shù)據(jù)的時(shí)候都認(rèn)為別人會(huì)修改蔼囊,所以每次在拿數(shù)據(jù)的時(shí)候都會(huì)上鎖焚志,這樣別人想拿這個(gè)數(shù)據(jù)就會(huì)阻塞直到它拿到鎖(共享資源每次只給一個(gè)線程使用,其它線程阻塞畏鼓,用完后再把資源轉(zhuǎn)讓給其它線程)酱酬。傳統(tǒng)的關(guān)系型數(shù)據(jù)庫(kù)里邊就用到了很多這種鎖機(jī)制,比如行鎖云矫,表鎖等膳沽,讀鎖,寫(xiě)鎖等让禀,都是在做操作之前先上鎖挑社。Java中synchronizedReentrantLock等獨(dú)占鎖就是悲觀鎖思想的實(shí)現(xiàn)。

樂(lè)觀鎖

總是假設(shè)最好的情況巡揍,每次去拿數(shù)據(jù)的時(shí)候都認(rèn)為別人不會(huì)修改痛阻,所以不會(huì)上鎖,但是在更新的時(shí)候會(huì)判斷一下在此期間別人有沒(méi)有去更新這個(gè)數(shù)據(jù)腮敌,可以使用版本號(hào)機(jī)制和CAS算法實(shí)現(xiàn)录平。樂(lè)觀鎖適用于多讀的應(yīng)用類型麻车,這樣可以提高吞吐量,像數(shù)據(jù)庫(kù)提供的類似于write_condition機(jī)制斗这,其實(shí)都是提供的樂(lè)觀鎖动猬。在Java中java.util.concurrent.atomic包下面的原子變量類就是使用了樂(lè)觀鎖的一種實(shí)現(xiàn)方式CAS實(shí)現(xiàn)的。

兩種鎖的使用場(chǎng)景

從上面對(duì)兩種鎖的介紹表箭,我們知道兩種鎖各有優(yōu)缺點(diǎn)赁咙,不可認(rèn)為一種好于另一種,像樂(lè)觀鎖適用于寫(xiě)比較少的情況下(多讀場(chǎng)景)免钻,即沖突真的很少發(fā)生的時(shí)候彼水,這樣可以省去了鎖的開(kāi)銷,加大了系統(tǒng)的整個(gè)吞吐量极舔。但如果是多寫(xiě)的情況凤覆,一般會(huì)經(jīng)常產(chǎn)生沖突,這就會(huì)導(dǎo)致上層應(yīng)用會(huì)不斷的進(jìn)行retry拆魏,這樣反倒是降低了性能盯桦,所以一般多寫(xiě)的場(chǎng)景下用悲觀鎖就比較合適。

樂(lè)觀鎖常見(jiàn)的兩種實(shí)現(xiàn)方式

樂(lè)觀鎖一般會(huì)使用版本號(hào)機(jī)制或CAS算法實(shí)現(xiàn)渤刃。

1. 版本號(hào)機(jī)制

一般是在數(shù)據(jù)表中加上一個(gè)數(shù)據(jù)版本號(hào)version字段拥峦,表示數(shù)據(jù)被修改的次數(shù),當(dāng)數(shù)據(jù)被修改時(shí)卖子,version值會(huì)加一略号。當(dāng)線程A要更新數(shù)據(jù)值時(shí),在讀取數(shù)據(jù)的同時(shí)也會(huì)讀取version值洋闽,在提交更新時(shí)玄柠,若剛才讀取到的version值為當(dāng)前數(shù)據(jù)庫(kù)中的version值相等時(shí)才更新,否則重試更新操作诫舅,直到更新成功随闪。

舉一個(gè)簡(jiǎn)單的例子: 假設(shè)數(shù)據(jù)庫(kù)中帳戶信息表中有一個(gè) version 字段,當(dāng)前值為 1 骚勘;而當(dāng)前帳戶余額字段( balance )為 $100 铐伴。

  1. 操作員 A 此時(shí)將其讀出( version=1 ),并從其帳戶余額中扣除 50(100-$50 )俏讹。

  2. 在操作員 A 操作的過(guò)程中当宴,操作員B 也讀入此用戶信息( version=1 ),并從其帳戶余額中扣除 20 (100-$20 )泽疆。

  3. 操作員 A 完成了修改工作户矢,將數(shù)據(jù)版本號(hào)加一( version=2 ),連同帳戶扣除后余額( balance=$50 )殉疼,提交至數(shù)據(jù)庫(kù)更新梯浪,此時(shí)由于提交數(shù)據(jù)版本大于數(shù)據(jù)庫(kù)記錄當(dāng)前版本捌年,數(shù)據(jù)被更新,數(shù)據(jù)庫(kù)記錄 version 更新為 2 挂洛。

  4. 操作員 B 完成了操作礼预,也將版本號(hào)加一( version=2 )試圖向數(shù)據(jù)庫(kù)提交數(shù)據(jù)( balance=$80 ),但此時(shí)比對(duì)數(shù)據(jù)庫(kù)記錄版本時(shí)發(fā)現(xiàn)虏劲,操作員 B 提交的數(shù)據(jù)版本號(hào)為 2 托酸,數(shù)據(jù)庫(kù)記錄當(dāng)前版本也為 2 ,不滿足 “ 提交版本必須大于記錄當(dāng)前版本才能執(zhí)行更新 “ 的樂(lè)觀鎖策略柒巫,因此励堡,操作員 B 的提交被駁回。

這樣堡掏,就避免了操作員 B 用基于 version=1 的舊數(shù)據(jù)修改的結(jié)果覆蓋操作員A 的操作結(jié)果的可能应结。

2. CAS算法

compare and swap(比較與交換),是一種有名的無(wú)鎖算法泉唁。無(wú)鎖編程鹅龄,即不使用鎖的情況下實(shí)現(xiàn)多線程之間的變量同步,也就是在沒(méi)有線程被阻塞的情況下實(shí)現(xiàn)變量的同步游两,所以也叫非阻塞同步(Non-blocking Synchronization)砾层。CAS算法涉及到三個(gè)操作數(shù)

  • 需要讀寫(xiě)的內(nèi)存值 V

  • 進(jìn)行比較的值 A

  • 擬寫(xiě)入的新值 B

當(dāng)且僅當(dāng) V 的值等于 A時(shí)漩绵,CAS通過(guò)原子方式用新值B來(lái)更新V的值贱案,否則不會(huì)執(zhí)行任何操作(比較和替換是一個(gè)原子操作)。一般情況下是一個(gè)自旋操作止吐,即不斷的重試宝踪。

樂(lè)觀鎖的缺點(diǎn)

ABA 問(wèn)題是樂(lè)觀鎖一個(gè)常見(jiàn)的問(wèn)題

1、ABA 問(wèn)題

如果一個(gè)變量V初次讀取的時(shí)候是A值碍扔,并且在準(zhǔn)備賦值的時(shí)候檢查到它仍然是A值瘩燥,那我們就能說(shuō)明它的值沒(méi)有被其他線程修改過(guò)了嗎?很明顯是不能的不同,因?yàn)樵谶@段時(shí)間它的值可能被改為其他值厉膀,然后又改回A,那CAS操作就會(huì)誤認(rèn)為它從來(lái)沒(méi)有被修改過(guò)二拐。這個(gè)問(wèn)題被稱為CAS操作的 "ABA"問(wèn)題服鹅。

JDK 1.5 以后的 AtomicStampedReference 類就提供了此種能力,其中的 compareAndSet 方法就是首先檢查當(dāng)前引用是否等于預(yù)期引用百新,并且當(dāng)前標(biāo)志是否等于預(yù)期標(biāo)志企软,如果全部相等,則以原子方式將該引用和該標(biāo)志的值設(shè)置為給定的更新值饭望。

2仗哨、循環(huán)時(shí)間長(zhǎng)開(kāi)銷大

自旋CAS(也就是不成功就一直循環(huán)執(zhí)行直到成功)如果長(zhǎng)時(shí)間不成功形庭,會(huì)給CPU帶來(lái)非常大的執(zhí)行開(kāi)銷。 如果JVM能支持處理器提供的pause指令那么效率會(huì)有一定的提升厌漂,pause指令有兩個(gè)作用萨醒,第一它可以延遲流水線執(zhí)行指令(de-pipeline),使CPU不會(huì)消耗過(guò)多的執(zhí)行資源,延遲的時(shí)間取決于具體實(shí)現(xiàn)的版本桩卵,在一些處理器上延遲時(shí)間是零验靡。第二它可以避免在退出循環(huán)的時(shí)候因內(nèi)存順序沖突(memory order violation)而引起CPU流水線被清空(CPU pipeline flush),從而提高CPU的執(zhí)行效率雏节。

3胜嗓、只能保證一個(gè)共享變量的原子操作

CAS 只對(duì)單個(gè)共享變量有效,當(dāng)操作涉及跨多個(gè)共享變量時(shí) CAS 無(wú)效钩乍。但是從 JDK 1.5開(kāi)始辞州,提供了AtomicReference類來(lái)保證引用對(duì)象之間的原子性,你可以把多個(gè)變量放在一個(gè)對(duì)象里來(lái)進(jìn)行 CAS 操作.所以我們可以使用鎖或者利用AtomicReference類把多個(gè)共享變量合并成一個(gè)共享變量來(lái)操作寥粹。

鎖機(jī)制與InnoDB鎖算法

MyISAM和InnoDB存儲(chǔ)引擎使用的鎖:

  • MyISAM 采用表級(jí)鎖(table-level locking)变过。

  • InnoDB 支持行級(jí)鎖(row-level locking)和表級(jí)鎖,默認(rèn)為行級(jí)鎖

表級(jí)鎖和行級(jí)鎖對(duì)比:

  • 表級(jí)鎖: Mysql中鎖定 粒度最大 的一種鎖,對(duì)當(dāng)前操作的整張表加鎖涝涤,實(shí)現(xiàn)簡(jiǎn)單媚狰,資源消耗也比較少,加鎖快阔拳,不會(huì)出現(xiàn)死鎖崭孤。其鎖定粒度最大,觸發(fā)鎖沖突的概率最高糊肠,并發(fā)度最低辨宠,MyISAM和 InnoDB引擎都支持表級(jí)鎖。

  • 行級(jí)鎖: Mysql中鎖定 粒度最小 的一種鎖货裹,只針對(duì)當(dāng)前操作的行進(jìn)行加鎖嗤形。行級(jí)鎖能大大減少數(shù)據(jù)庫(kù)操作的沖突。其加鎖粒度最小弧圆,并發(fā)度高赋兵,但加鎖的開(kāi)銷也最大,加鎖慢搔预,會(huì)出現(xiàn)死鎖霹期。

InnoDB存儲(chǔ)引擎的鎖的算法有三種:

  • Record lock:?jiǎn)蝹€(gè)行記錄上的鎖

  • Gap lock:間隙鎖,鎖定一個(gè)范圍斯撮,不包括記錄本身

  • Next-key lock:record+gap 鎖定一個(gè)范圍经伙,包含記錄本身

相關(guān)知識(shí)點(diǎn):

  • innodb對(duì)于行的查詢使用next-key lock

  • Next-locking keying為了解決Phantom Problem幻讀問(wèn)題

  • 當(dāng)查詢的索引含有唯一屬性時(shí),將next-key lock降級(jí)為record key

  • Gap鎖設(shè)計(jì)的目的是為了阻止多個(gè)事務(wù)將記錄插入到同一范圍內(nèi),而這會(huì)導(dǎo)致幻讀問(wèn)題的產(chǎn)生

  • 有兩種方式顯式關(guān)閉gap鎖:(除了外鍵約束和唯一性檢查外帕膜,其余情況僅使用record lock) A. 將事務(wù)隔離級(jí)別設(shè)置為RC B. 將參數(shù)innodb_locks_unsafe_for_binlog設(shè)置為1

大表優(yōu)化

當(dāng)MySQL單表記錄數(shù)過(guò)大時(shí)枣氧,數(shù)據(jù)庫(kù)的CRUD性能會(huì)明顯下降,一些常見(jiàn)的優(yōu)化措施如下:

1垮刹、限定數(shù)據(jù)的范圍

務(wù)必禁止不帶任何限制數(shù)據(jù)范圍條件的查詢語(yǔ)句达吞。比如:我們當(dāng)用戶在查詢訂單歷史的時(shí)候,我們可以控制在一個(gè)月的范圍內(nèi)荒典;

2酪劫、讀/寫(xiě)分離

經(jīng)典的數(shù)據(jù)庫(kù)拆分方案,主庫(kù)負(fù)責(zé)寫(xiě)寺董,從庫(kù)負(fù)責(zé)讀覆糟;

3、垂直分區(qū)

根據(jù)數(shù)據(jù)庫(kù)里面數(shù)據(jù)表的相關(guān)性進(jìn)行拆分遮咖。 例如滩字,用戶表中既有用戶的登錄信息又有用戶的基本信息,可以將用戶表拆分成兩個(gè)單獨(dú)的表御吞,甚至放到單獨(dú)的庫(kù)做分庫(kù)麦箍。

簡(jiǎn)單來(lái)說(shuō)垂直拆分是指數(shù)據(jù)表列的拆分,把一張列比較多的表拆分為多張表陶珠。 如下圖所示挟裂,這樣來(lái)說(shuō)大家應(yīng)該就更容易理解了。

image
  • 垂直拆分的優(yōu)點(diǎn): 可以使得列數(shù)據(jù)變小揍诽,在查詢時(shí)減少讀取的Block數(shù)诀蓉,減少I(mǎi)/O次數(shù)。此外寝姿,垂直分區(qū)可以簡(jiǎn)化表的結(jié)構(gòu)交排,易于維護(hù)划滋。

  • 垂直拆分的缺點(diǎn): 主鍵會(huì)出現(xiàn)冗余饵筑,需要管理冗余列,并會(huì)引起Join操作处坪,可以通過(guò)在應(yīng)用層進(jìn)行Join來(lái)解決根资。此外,垂直分區(qū)會(huì)讓事務(wù)變得更加復(fù)雜同窘;

4玄帕、水平分區(qū)

保持?jǐn)?shù)據(jù)表結(jié)構(gòu)不變,通過(guò)某種策略存儲(chǔ)數(shù)據(jù)分片想邦。****這樣每一片數(shù)據(jù)分散到不同的表或者庫(kù)中裤纹,達(dá)到了分布式的目的。****水平拆分可以支撐非常大的數(shù)據(jù)量。

水平拆分是指數(shù)據(jù)表行的拆分鹰椒,表的行數(shù)超過(guò)200萬(wàn)行時(shí)锡移,就會(huì)變慢,這時(shí)可以把一張的表的數(shù)據(jù)拆成多張表來(lái)存放漆际。舉個(gè)例子:我們可以將用戶信息表拆分成多個(gè)用戶信息表淆珊,這樣就可以避免單一表數(shù)據(jù)量過(guò)大對(duì)性能造成影響。

image

水平拆分可以支持非常大的數(shù)據(jù)量奸汇。需要注意的一點(diǎn)是:分表僅僅是解決了單一表數(shù)據(jù)過(guò)大的問(wèn)題施符,但由于表的數(shù)據(jù)還是在同一臺(tái)機(jī)器上,其實(shí)對(duì)于提升MySQL并發(fā)能力沒(méi)有什么意義擂找,所以 水平拆分最好分庫(kù) 戳吝。

水平拆分能夠 支持非常大的數(shù)據(jù)量存儲(chǔ),應(yīng)用端改造也少贯涎,但 分片事務(wù)難以解決 骨坑,跨節(jié)點(diǎn)Join性能較差,邏輯復(fù)雜柬采』锻伲《Java工程師修煉之道》的作者推薦 盡量不要對(duì)數(shù)據(jù)進(jìn)行分片,因?yàn)椴鸱謺?huì)帶來(lái)邏輯粉捻、部署礁遣、運(yùn)維的各種復(fù)雜度 ,一般的數(shù)據(jù)表在優(yōu)化得當(dāng)?shù)那闆r下支撐千萬(wàn)以下的數(shù)據(jù)量是沒(méi)有太大問(wèn)題的肩刃。如果實(shí)在要分片祟霍,盡量選擇客戶端分片架構(gòu),這樣可以減少一次和中間件的網(wǎng)絡(luò)I/O盈包。

下面補(bǔ)充一下數(shù)據(jù)庫(kù)分片的兩種常見(jiàn)方案:

  • 客戶端代理: 分片邏輯在應(yīng)用端沸呐,封裝在jar包中,通過(guò)修改或者封裝JDBC層來(lái)實(shí)現(xiàn)呢燥。 當(dāng)當(dāng)網(wǎng)的 Sharding-JDBC 崭添、阿里的TDDL是兩種比較常用的實(shí)現(xiàn)。

  • 中間件代理: 在應(yīng)用和數(shù)據(jù)中間加了一個(gè)代理層叛氨。****分片邏輯統(tǒng)一維護(hù)在中間件服務(wù)中呼渣。 我們現(xiàn)在談的 Mycat 、360的Atlas寞埠、網(wǎng)易的DDB等等都是這種架構(gòu)的實(shí)現(xiàn)屁置。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市仁连,隨后出現(xiàn)的幾起案子蓝角,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,366評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件使鹅,死亡現(xiàn)場(chǎng)離奇詭異颇象,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)并徘,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,521評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)遣钳,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人麦乞,你說(shuō)我怎么就攤上這事蕴茴。” “怎么了姐直?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,689評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵倦淀,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我声畏,道長(zhǎng)撞叽,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,925評(píng)論 1 295
  • 正文 為了忘掉前任插龄,我火速辦了婚禮愿棋,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘均牢。我一直安慰自己糠雨,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,942評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布徘跪。 她就那樣靜靜地躺著甘邀,像睡著了一般。 火紅的嫁衣襯著肌膚如雪垮庐。 梳的紋絲不亂的頭發(fā)上松邪,一...
    開(kāi)封第一講書(shū)人閱讀 51,727評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音哨查,去河邊找鬼逗抑。 笑死,一個(gè)胖子當(dāng)著我的面吹牛解恰,可吹牛的內(nèi)容都是我干的锋八。 我是一名探鬼主播浙于,決...
    沈念sama閱讀 40,447評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼护盈,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了羞酗?” 一聲冷哼從身側(cè)響起腐宋,我...
    開(kāi)封第一講書(shū)人閱讀 39,349評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后胸竞,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體欺嗤,經(jīng)...
    沈念sama閱讀 45,820評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,990評(píng)論 3 337
  • 正文 我和宋清朗相戀三年卫枝,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了煎饼。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,127評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡校赤,死狀恐怖吆玖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情马篮,我是刑警寧澤沾乘,帶...
    沈念sama閱讀 35,812評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站浑测,受9級(jí)特大地震影響翅阵,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜迁央,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,471評(píng)論 3 331
  • 文/蒙蒙 一掷匠、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧岖圈,春花似錦槐雾、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,017評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至崇摄,卻和暖如春擎值,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背逐抑。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,142評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工鸠儿, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人厕氨。 一個(gè)月前我還...
    沈念sama閱讀 48,388評(píng)論 3 373
  • 正文 我出身青樓进每,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親命斧。 傳聞我的和親對(duì)象是個(gè)殘疾皇子田晚,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,066評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容