可串行化——SERIALIZABLE
成都創(chuàng)新互聯(lián)公司專注為客戶提供全方位的互聯(lián)網綜合服務,包含不限于網站建設、網站制作、江津網絡推廣、小程序定制開發(fā)、江津網絡營銷、江津企業(yè)策劃、江津品牌公關、搜索引擎seo、人物專訪、企業(yè)宣傳片、企業(yè)代運營等,從售前售中售后,我們都將竭誠為您服務,您的肯定,是我們最大的嘉獎;成都創(chuàng)新互聯(lián)公司為所有大學生創(chuàng)業(yè)者提供江津建站搭建服務,24小時服務熱線:028-86922220,官方網址:www.cdcxhl.com
事務的最高級別,在每個讀的數(shù)據(jù)行上,加上鎖,使之不可能相互沖突,因此,會導致大量的超時現(xiàn)象
設置b賬戶,事務的隔離級別
B賬戶,首先,將b賬戶的隔離級別設置為SERIALIZABLE
可以看出,b賬戶的事務隔離級別設置為了SERIALIZABLE
演示可串行化
B賬戶,開啟一個事務,查詢各個賬戶的余額
A賬戶,開啟一個事務,在事務中執(zhí)行插入操作
可以看出,當b賬戶正在事務中,查詢余額信息時,a賬戶中的操作是不能立即執(zhí)行的
提交事務
B賬戶,執(zhí)行完查詢余額,提交當前事務
A賬戶,當b賬戶中的事務提交之后,a賬戶中的添加操作,才能執(zhí)行成功
查詢余額
執(zhí)行成功 可以看出,如果一個事務,使用了SERIALIZABLE——可串行化隔離級別時,在這個事務沒有被提交之前 其他的線程,只能等到當前操作完成之后,才能進行操作,這樣會非常耗時,而且,影響數(shù)據(jù)庫的性能,通常情況下,不會使用這種隔離級別
Mysql到底是怎么實現(xiàn)MVCC的
Mysql到底是怎么實現(xiàn)MVCC的?這個問題無數(shù)人都在問,但google中并無答案,本文嘗試從Mysql源碼中尋找答案。
在Mysql中MVCC是在Innodb存儲引擎中得到支持的,Innodb為每行記錄都實現(xiàn)了三個隱藏字段:
6字節(jié)的事務ID(DB_TRX_ID )
7字節(jié)的回滾指針(DB_ROLL_PTR)
隱藏的ID
6字節(jié)的事物ID用來標識該行所述的事務,7字節(jié)的回滾指針需要了解下Innodb的事務模型。
1. Innodb的事務相關概念
為了支持事務,Innbodb引入了下面幾個概念:
redo log
redo log就是保存執(zhí)行的SQL語句到一個指定的Log文件,當Mysql執(zhí)行recovery時重新執(zhí)行redo log記錄的SQL操作即可。當客戶端執(zhí)行每條SQL(更新語句)時,redo log會被首先寫入log buffer;當客戶端執(zhí)行COMMIT命令時,log buffer中的內容會被視情況刷新到磁盤。redo log在磁盤上作為一個獨立的文件存在,即Innodb的log文件。
undo log
與redo log相反,undo log是為回滾而用,具體內容就是copy事務前的數(shù)據(jù)庫內容(行)到undo buffer,在適合的時間把undo buffer中的內容刷新到磁盤。undo buffer與redo buffer一樣,也是環(huán)形緩沖,但當緩沖滿的時候,undo buffer中的內容會也會被刷新到磁盤;與redo log不同的是,磁盤上不存在單獨的undo log文件,所有的undo log均存放在主ibd數(shù)據(jù)文件中(表空間),即使客戶端設置了每表一個數(shù)據(jù)文件也是如此。
rollback segment
回滾段這個概念來自Oracle的事物模型,在Innodb中,undo log被劃分為多個段,具體某行的undo log就保存在某個段中,稱為回滾段。可以認為undo log和回滾段是同一意思。
鎖
Innodb提供了基于行的鎖,如果行的數(shù)量非常大,則在高并發(fā)下鎖的數(shù)量也可能會比較大,據(jù)Innodb文檔說,Innodb對鎖進行了空間有效優(yōu)化,即使并發(fā)量高也不會導致內存耗盡。
對行的鎖有分兩種:排他鎖、共享鎖。共享鎖針對對,排他鎖針對寫,完全等同讀寫鎖的概念。如果某個事務在更新某行(排他鎖),則其他事物無論是讀還是寫本行都必須等待;如果某個事物讀某行(共享鎖),則其他讀的事物無需等待,而寫事物則需等待。通過共享鎖,保證了多讀之間的無等待性,但是鎖的應用又依賴Mysql的事務隔離級別。
隔離級別
隔離級別用來限制事務直接的交互程度,目前有幾個工業(yè)標準:
- READ_UNCOMMITTED:臟讀
- READ_COMMITTED:讀提交
- REPEATABLE_READ:重復讀
- SERIALIZABLE:串行化
Innodb對四種類型都支持,臟讀和串行化應用場景不多,讀提交、重復讀用的比較廣泛,后面會介紹其實現(xiàn)方式。
2. 行的更新過程
下面演示下事務對某行記錄的更新過程:
1. 初始數(shù)據(jù)行
F1~F6是某行列的名字,1~6是其對應的數(shù)據(jù)。后面三個隱含字段分別對應該行的事務號和回滾指針,假如這條數(shù)據(jù)是剛INSERT的,可以認為ID為1,其他兩個字段為空。
2.事務1更改該行的各字段的值
當事務1更改該行的值時,會進行如下操作:
用排他鎖鎖定該行
(1)三個事務允許并發(fā)執(zhí)行,有6種結果
T1-T2-T3 16
T1-T3-T2 8
T2-T1-T3 4
T2-T3-T1 2
T3-T1-T2 4
T3-T2-T1 2
2)
可串行化的調度:T1-T2-T3 16
(3)
非串行化的調度:T1-T2-T3
XLOCK A WAIT-READ A=0 WAIT-A=A+2-
WRITE A=2-UNLOCK A -
XLOCK A WAIT-READ A=2 -A=A*2-
WRITE A=4-UNLOCL A -
XLOCK A WAIT-READ A=4 -A=A**2-
WRITE A=16-UNLOCK A
MySQL通過內部兩階段提交協(xié)議來提交事務,如下圖
具體實現(xiàn)如下圖:
第一階段 :InnoDB prepare,持有prepare_commit_mutex,并且write/sync redo log;將rollback設置為Prepared狀態(tài),binlog prepare不作任何操作;
第二階段 :包含兩步,write/sync Binlog及 InnoDB commit (寫入COMMIT標記后釋放prepare_commit_mutex);
考慮mysql以binlog的寫入與否作為事務提交成功與否的標志,如果 在寫入innodb commit標志時崩潰(binglog已經寫文件但是還沒有提交) ,則恢復時,會重新對commit標志進行寫入;此時的事務崩潰恢復過程如下:
1)掃描最后一個Binlog文件,提取其中的xid;
2)InnoDB維持了狀態(tài)為Prepare的事務鏈表,將這些事務的xid和Binlog中記錄的xid做比較,如果在Binlog中存在,則提交,否則回滾事務。
但其中也會存在2個問題:
并發(fā)危機:全局大鎖prepare_commit_mutex
Mysql5.6.5前的做法,加鎖,串行化
無鎖方案:如果能保證binlog write 和? Innodb commit的順序一致性就可以解決該問題。
性能問題:參數(shù)sync_binlog =1 ,innodb_flush_log_at_trx_commit =1時,fsync操作頻繁
數(shù)據(jù)持久化到磁盤:調用fsync將緩存中的數(shù)據(jù)刷新到磁盤(普通硬盤150次/s和SSD 1200次/S),影響TPS;Group Commit操作,在多個事務并發(fā)時,將等待fsync的多個事務合并為僅調用一次fsync操作,以解決innodb fsync的問題,對binlog 的fsync也適用
對上述兩個問題的解決:
針對并發(fā)問題
Group操作,三個階段都在維護一個隊列。第一個進隊列的線程稱為leader線程,負責對隊列里所有線程進行操作;之后進入隊列的線程稱作follower線程,follower 線程進入隊列后睡眠,等待leader完成操作后將他們喚醒。注意:前一個隊列l(wèi)eader進入后一個隊列時,會把自己原隊列的follower全加入進去。
針對一致性問題?
Group commit 分為三個階段,每個階段有一個線程在執(zhí)行。分階段的目的在于各個階段可以并發(fā)執(zhí)行,提升效率。
涉及參數(shù)說明:
sync_binlog =1 :啟用group commit之后,其實已經不是一個事務去刷一次磁盤了,而是一組事務刷一次磁盤。圖中1、2分別代表sync_binlog 不同配置下,通知其他線程(如dump線程)binlog 已經更新了,當配置為1時,要嚴格等到sync完畢之后才會發(fā)送廣播通知, 如果sync_binlog配的是別的值,MySQL會把通知提前到1的位置
binlog_group_commit_sync_no_delay_count(組提交sync無延遲時間最大event數(shù))及binlog_group_commit_sync_delay(組提交sync延遲時間,單位:毫秒):一般來說我們認為group commit 中最耗時的操作是sync階段,于是我們可以在sync階段在leader真正sync之前進行一個等待,以便讓fsync一次性刷新更多的事務。這對需要等待sync 完之后才能進行的操作(比如dump線程)可能有性能提升。
兩階段提交:
MYSQL_BIN_LOG作為協(xié)調者
mysql如何實現(xiàn)多行查詢結果合并成一行,mysql如何實現(xiàn)多行查詢結果合并成一行網站簡介信息
利用函數(shù):group_concat(),實現(xiàn)一個ID對應多個名稱時,原本為多行數(shù)據(jù),把名稱合并成一行。
其完整語法:
GROUP_CONCAT(expr)
該函數(shù)返回帶有來自一個組的連接的非NULL值的字符串結果。其完整的語法如下所示:
GROUP_CONCAT([DISTINCT] expr [,expr ...]
[ORDER BY {unsigned_integer | col_name | expr}
[ASC | DESC] [,col_name ...]]
[SEPARATOR str_val])
mysql SELECT student_name,
- GROUP_CONCAT(test_score)
- FROM student
- GROUP BY student_name;
Or:
mysql SELECT student_name,
- GROUP_CONCAT(DISTINCT test_score
- ORDER BY test_score DESC SEPARATOR ' ')
- FROM student
- GROUP BY student_name;
在MySQL中,你可以獲取表達式組合的連接值。你可以使用DISTINCT刪去重復值。假若你希望多結果值進行排序,則應該使用 ORDER BY子句。若要按相反順序排列,將 DESC (遞減) 關鍵詞添加到你要用ORDER BY 子句進行排序的列名稱中。默認順序為升序;可使用ASC將其明確指定。 SEPARATOR 后面跟隨應該被插入結果的值中間的字符串值。默認為逗號 (‘,')。通過指定SEPARATOR '' ,你可以刪除所有分隔符。
使用group_concat_max_len系統(tǒng)變量,你可以設置允許的最大長度。 程序中進行這項操作的語法如下,其中 val 是一個無符號整數(shù):
SET [SESSION | GLOBAL] group_concat_max_len = val;