日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 人文社科 > 生活经验 >内容正文

生活经验

事务库事务隔离级别

發(fā)布時(shí)間:2023/11/27 生活经验 24 豆豆
生活随笔 收集整理的這篇文章主要介紹了 事务库事务隔离级别 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

為了快速同步數(shù)據(jù)的需要,我分段執(zhí)行了兩次python腳本,即開啟了兩個(gè)進(jìn)程同步數(shù)據(jù),結(jié)果服務(wù)器不時(shí)報(bào)出數(shù)據(jù)庫死鎖異常,通過排查代碼和數(shù)據(jù)庫日志發(fā)現(xiàn),是由長事務(wù)并發(fā)引起的。代碼中有入賬和出賬兩個(gè)方法,里面涉及操作較多,都為其加了事務(wù),拋出異常時(shí)可自動(dòng)回滾,采用數(shù)據(jù)庫(mysql)默認(rèn)的隔離級(jí)別(Repeatable read)。提到并發(fā),一般就會(huì)想到用同步代碼塊的方法的處理,但是由于項(xiàng)目是分布式的,共用一個(gè)主庫,單單在代碼加鎖是不能保證數(shù)據(jù)的準(zhǔn)確的,那就只能在數(shù)據(jù)庫層面去考慮加鎖了。由于數(shù)據(jù)量暫時(shí)不大,一開始我的解決方法是將隔離級(jí)別調(diào)整為最高(Serializable),這樣雖然代價(jià)較大,但是能保證數(shù)據(jù)的準(zhǔn)確性,數(shù)據(jù)庫的鎖也會(huì)相互等待,但當(dāng)我再重新同步數(shù)據(jù)時(shí),還是報(bào)出了大量死鎖異常(com.mysql.jdbc.exceptions.jdbc4.MySQLTransactionRollbackException: Deadlock found when trying to get lock; try restarting transaction),仔細(xì)追蹤數(shù)據(jù)庫日志,發(fā)現(xiàn)有兩個(gè)鎖在兩條update語句上互相等待(操作的是一張統(tǒng)計(jì)表),但這兩條update語句不是落到同一行上的,因?yàn)椴樵儣l件并不一樣。那就奇怪了,一般來說是行鎖的問題,最后排查發(fā)現(xiàn),是該表未加索引的原因,造成了事務(wù)鎖住了這張表,而由于多個(gè)事務(wù)是同時(shí)執(zhí)行到這里,造成了鎖的互相等待,最終數(shù)據(jù)庫選擇回滾了其中一個(gè)事務(wù)。發(fā)現(xiàn)問題后,我將表加了索引,再運(yùn)行時(shí),已經(jīng)沒報(bào)這個(gè)錯(cuò)誤了(子查詢也是會(huì)導(dǎo)致鎖表的)。為了保證代碼的運(yùn)行效率,我將數(shù)據(jù)庫級(jí)別設(shè)置為Repeatable read,但隨之而來發(fā)現(xiàn)了一些數(shù)據(jù)出現(xiàn)了幻讀,最后我是在update語句那里加了一個(gè)beforeMoney的過濾(update wallet_stat set money = afterMoney where id = xxx and money = beforeMoney),這樣就能保證數(shù)據(jù)的準(zhǔn)確性了,不過這里不足的是,雖保證了數(shù)據(jù)的正確,但并發(fā)的那個(gè)事務(wù)卻必須回滾。這里也可以考慮借助隊(duì)列或者樂觀鎖等方式來解決這個(gè)問題,就不一一詳述了。

以上就是我處理這個(gè)問題的全過程,為了查找解決方案,我在查找了很多文章,獲益良多。以下是我認(rèn)為比較好的資料:

什么是事務(wù):

數(shù)據(jù)庫事務(wù)的隔離級(jí)別有4個(gè),由低到高依次為Read uncommitted、Read committed、Repeatable read、Serializable,這四個(gè)級(jí)別可以逐個(gè)解決臟讀、不可重復(fù)讀、幻讀這幾類問題。

???????????????????????????????????????????????????????????????????????

注意:我們討論隔離級(jí)別的場景,主要是在多個(gè)事務(wù)并發(fā)的情況下,因此,接下來的講解都圍繞事務(wù)并發(fā)。

Read uncommitted 讀未提交

公司發(fā)工資了,領(lǐng)導(dǎo)把5000元打到singo的賬號(hào)上,但是該事務(wù)并未提交,而singo正好去查看賬戶,發(fā)現(xiàn)工資已經(jīng)到賬,是5000元整,非常高興。可是不幸的是,領(lǐng)導(dǎo)發(fā)現(xiàn)發(fā)給singo的工資金額不對(duì),是2000元,于是迅速回滾了事務(wù),修改金額后,將事務(wù)提交,最后singo實(shí)際的工資只有2000元,singo空歡喜一場。

出現(xiàn)上述情況,即我們所說的臟讀,兩個(gè)并發(fā)的事務(wù),“事務(wù)A:領(lǐng)導(dǎo)給singo發(fā)工資”、“事務(wù)B:singo查詢工資賬戶”,事務(wù)B讀取了事務(wù)A尚未提交的數(shù)據(jù)。

當(dāng)隔離級(jí)別設(shè)置為Read uncommitted時(shí),就可能出現(xiàn)臟讀,如何避免臟讀,請(qǐng)看下一個(gè)隔離級(jí)別。

Read committed 讀提交

singo拿著工資卡去消費(fèi),系統(tǒng)讀取到卡里確實(shí)有2000元,而此時(shí)她的老婆也正好在網(wǎng)上轉(zhuǎn)賬,把singo工資卡的2000元轉(zhuǎn)到另一賬戶,并在singo之前提交了事務(wù),當(dāng)singo扣款時(shí),系統(tǒng)檢查到singo的工資卡已經(jīng)沒有錢,扣款失敗,singo十分納悶,明明卡里有錢,為何......

出現(xiàn)上述情況,即我們所說的不可重復(fù)讀,兩個(gè)并發(fā)的事務(wù),“事務(wù)A:singo消費(fèi)”、“事務(wù)B:singo的老婆網(wǎng)上轉(zhuǎn)賬”,事務(wù)A事先讀取了數(shù)據(jù),事務(wù)B緊接了更新了數(shù)據(jù),并提交了事務(wù),而事務(wù)A再次讀取該數(shù)據(jù)時(shí),數(shù)據(jù)已經(jīng)發(fā)生了改變。

當(dāng)隔離級(jí)別設(shè)置為Read committed時(shí),避免了臟讀,但是可能會(huì)造成不可重復(fù)讀。

大多數(shù)數(shù)據(jù)庫的默認(rèn)級(jí)別就是Read committed,比如Sql Server , Oracle。如何解決不可重復(fù)讀這一問題,請(qǐng)看下一個(gè)隔離級(jí)別。

Repeatable read 重復(fù)讀

當(dāng)隔離級(jí)別設(shè)置為Repeatable read時(shí),可以避免不可重復(fù)讀。當(dāng)singo拿著工資卡去消費(fèi)時(shí),一旦系統(tǒng)開始讀取工資卡信息(即事務(wù)開始),singo的老婆就不可能對(duì)該記錄進(jìn)行修改,也就是singo的老婆不能在此時(shí)轉(zhuǎn)賬。

雖然Repeatable read避免了不可重復(fù)讀,但還有可能出現(xiàn)幻讀。

singo的老婆工作在銀行部門,她時(shí)常通過銀行內(nèi)部系統(tǒng)查看singo的信用卡消費(fèi)記錄。有一天,她正在查詢到singo當(dāng)月信用卡的總消費(fèi)金額(select sum(amount) from transaction where month = 本月)為80元,而singo此時(shí)正好在外面胡吃海塞后在收銀臺(tái)買單,消費(fèi)1000元,即新增了一條1000元的消費(fèi)記錄(insert transaction ... ),并提交了事務(wù),隨后singo的老婆將singo當(dāng)月信用卡消費(fèi)的明細(xì)打印到A4紙上,卻發(fā)現(xiàn)消費(fèi)總額為1080元,singo的老婆很詫異,以為出現(xiàn)了幻覺,幻讀就這樣產(chǎn)生了。

注:mysql的默認(rèn)隔離級(jí)別就是Repeatable read。

Serializable 序列化

Serializable是最高的事務(wù)隔離級(jí)別,同時(shí)代價(jià)也花費(fèi)最高,性能很低,一般很少使用,在該級(jí)別下,事務(wù)順序執(zhí)行,不僅可以避免臟讀、不可重復(fù)讀,還避免了幻像讀。

?

mysql鎖阻塞分析:

查看鎖阻塞線程信息
這里用幾中方法進(jìn)行分析:
使用show processlist查看

MySQL [(none)]> show processlist;
+----+------+-----------+------+---------+------+--------------+------------------------------------------+
| Id | User | Host | db | Command | Time | State | Info |
+----+------+-----------+------+---------+------+--------------+------------------------------------------+
| 2 | root | localhost | NULL | Query | 0 | init | show processlist |
| 3 | root | localhost | test | Query | 70 | Sending data | select count(*) from t3 a,t3 b |
| 4 | root | localhost | test | Query | 65 | updating | delete from emp where empno=7788 |
| 7 | root | localhost | test | Query | 68 | updating | update emp set sal=3500 where empno=7788 |
+----+------+-----------+------+---------+------+--------------+------------------------------------------+
4 rows in set (0.00 sec)
如果數(shù)據(jù)庫存在較多線程的話,這種方法確實(shí)不太好確認(rèn)的。

直接使用show engine innodb status查看

------------
TRANSACTIONS
------------
Trx id counter 4131
Purge done for trx's n:o < 4119 undo n:o < 0 state: running but idle
History list length 126
LIST OF TRANSACTIONS FOR EACH SESSION:
---TRANSACTION 0, not started
MySQL thread id 2, OS thread handle 0x7f953ffff700, query id 115 localhost root init
show engine innodb status
---TRANSACTION 4130, ACTIVE 41 sec starting index read
mysql tables in use 1, locked 1
LOCK WAIT 2 lock struct(s), heap size 360, 1 row lock(s)
MySQL thread id 4, OS thread handle 0x7f953ff9d700, query id 112 localhost root updating
delete from emp where empno=7788
------- TRX HAS BEEN WAITING 41 SEC FOR THIS LOCK TO BE GRANTED: ## 等待了41s
RECORD LOCKS space id 16 page no 3 n bits 88 index `PRIMARY` of table `test`.`emp` trx id 4130 lock_mode X locks rec but not gap waiting
Record lock, heap no 9 PHYSICAL RECORD: n_fields 10; compact format; info bits 0 ## 線程4在等待往test.emp中的主鍵上加X鎖,page num=3
0: len 4; hex 80001e6c; asc l;;
1: len 6; hex 000000001018; asc ;;
2: len 7; hex 91000001420084; asc B ;;
3: len 5; hex 53434f5454; asc SCOTT;;
4: len 7; hex 414e414c595354; asc ANALYST;;
5: len 4; hex 80001d8e; asc ;;
6: len 4; hex 208794f0; asc ;;
7: len 4; hex 80000bb8; asc ;;
8: SQL NULL;
9: len 4; hex 80000014; asc ;;

------------------
---TRANSACTION 4129, ACTIVE 45 sec starting index read
mysql tables in use 1, locked 1
LOCK WAIT 2 lock struct(s), heap size 360, 1 row lock(s)
MySQL thread id 7, OS thread handle 0x7f953ff6c700, query id 111 localhost root updating
update emp set sal=3500 where empno=7788
------- TRX HAS BEEN WAITING 45 SEC FOR THIS LOCK TO BE GRANTED: ## 等待了45s
RECORD LOCKS space id 16 page no 3 n bits 88 index `PRIMARY` of table `test`.`emp` trx id 4129 lock_mode X locks rec but not gap waiting
Record lock, heap no 9 PHYSICAL RECORD: n_fields 10; compact format; info bits 0 ## 線程7在等待往test.emp中的主鍵上加X鎖,page num=3
0: len 4; hex 80001e6c; asc l;;
1: len 6; hex 000000001018; asc ;;
2: len 7; hex 91000001420084; asc B ;;
3: len 5; hex 53434f5454; asc SCOTT;;
4: len 7; hex 414e414c595354; asc ANALYST;;
5: len 4; hex 80001d8e; asc ;;
6: len 4; hex 208794f0; asc ;;
7: len 4; hex 80000bb8; asc ;;
8: SQL NULL;
9: len 4; hex 80000014; asc ;;

------------------
---TRANSACTION 4128, ACTIVE 51 sec
2 lock struct(s), heap size 360, 1 row lock(s)
MySQL thread id 3, OS thread handle 0x7f953ffce700, query id 110 localhost root cleaning up

我們知道,主要根因還是thread=3引起的,但從innodb status中卻無法分析得到這個(gè)結(jié)果。

從上面來看,線程4和線程7都在等待往test.emp中的主鍵上加X鎖,page num=3,但是線程7等待的時(shí)間為45s,而線程4等待的時(shí)間為41s,是較線程7之后申請(qǐng)的鎖,所以可以判斷是線程7阻塞了線程4。至于線程7為什么出現(xiàn)等待,這里分析不到根因。

使用mysqladmin debug查看
# mysqladmin -S /tmp/mysql3306.sock debug

然后在error日志中,會(huì)看到:

Thread database.table_name Locked/Waiting Lock_type

3 test.t3 Locked - read Low priority read lock
7 test.emp Locked - write High priority write lock
這種方法中,能找到線程ID=3和7是阻塞者,但還是不太準(zhǔn)確,判斷不出來線程7也是被線程ID=3阻塞的。

使用innodb_lock_monitor來獲取阻塞鎖線程

MySQL [test]> CREATE TABLE innodb_lock_monitor (a INT) ENGINE=INNODB; ## 隨便在一個(gè)數(shù)據(jù)庫中創(chuàng)建這個(gè)表,就會(huì)打開lock monitor
Query OK, 0 rows affected, 1 warning (0.07 sec)

MySQL [test]> show warnings\G
*************************** 1. row ***************************
Level: Warning
Code: 131
Message: Using the table name innodb_lock_monitor to enable diagnostic output is deprecated and may be removed in future releases. Use INFORMATION_SCHEMA or PERFORMANCE_SCHEMA tables or SET GLOBAL innodb_status_output=ON.
1 row in set (0.00 sec)

說明:這個(gè)在5.6中有一個(gè)warning,但不影響使用。

然后再使用show engine innodb status查看:

------------
TRANSACTIONS
------------
Trx id counter 4667
Purge done for trx's n:o < 4659 undo n:o < 0 state: running but idle
History list length 138
LIST OF TRANSACTIONS FOR EACH SESSION:
---TRANSACTION 0, not started
MySQL thread id 9, OS thread handle 0x7f813c5f7700, query id 152 localhost root init
show engine innodb status
---TRANSACTION 4663, ACTIVE 78 sec starting index read
mysql tables in use 1, locked 1
LOCK WAIT 2 lock struct(s), heap size 360, 1 row lock(s)
MySQL thread id 4, OS thread handle 0x7f813c628700, query id 149 localhost root updating
delete from emp where empno=7788
------- TRX HAS BEEN WAITING 78 SEC FOR THIS LOCK TO BE GRANTED: ## 等待了78s
RECORD LOCKS space id 16 page no 3 n bits 88 index `PRIMARY` of table `test`.`emp` trx id 4663 lock_mode X locks rec but not gap waiting
Record lock, heap no 9 PHYSICAL RECORD: n_fields 10; compact format; info bits 0 ## 線程4在等待往test.emp中的主鍵上加X鎖,page num=3
0: len 4; hex 80001e6c; asc l;;
1: len 6; hex 000000001018; asc ;;
2: len 7; hex 91000001420084; asc B ;;
3: len 5; hex 53434f5454; asc SCOTT;;
4: len 7; hex 414e414c595354; asc ANALYST;;
5: len 4; hex 80001d8e; asc ;;
6: len 4; hex 208794f0; asc ;;
7: len 4; hex 80000bb8; asc ;;
8: SQL NULL;
9: len 4; hex 80000014; asc ;;

------------------
TABLE LOCK table `test`.`emp` trx id 4663 lock mode IX ## 在給主鍵行上加X鎖之前,先要在表上加意向鎖IX
RECORD LOCKS space id 16 page no 3 n bits 88 index `PRIMARY` of table `test`.`emp` trx id 4663 lock_mode X locks rec but not gap waiting
Record lock, heap no 9 PHYSICAL RECORD: n_fields 10; compact format; info bits 0
0: len 4; hex 80001e6c; asc l;;
1: len 6; hex 000000001018; asc ;;
2: len 7; hex 91000001420084; asc B ;;
3: len 5; hex 53434f5454; asc SCOTT;;
4: len 7; hex 414e414c595354; asc ANALYST;;
5: len 4; hex 80001d8e; asc ;;
6: len 4; hex 208794f0; asc ;;
7: len 4; hex 80000bb8; asc ;;
8: SQL NULL;
9: len 4; hex 80000014; asc ;;

---TRANSACTION 4662, ACTIVE 81 sec starting index read
mysql tables in use 1, locked 1
LOCK WAIT 2 lock struct(s), heap size 360, 1 row lock(s)
MySQL thread id 7, OS thread handle 0x7f813c5c6700, query id 148 localhost root updating
update emp set sal=3500 where empno=7788
------- TRX HAS BEEN WAITING 81 SEC FOR THIS LOCK TO BE GRANTED: ## 等待了81s
RECORD LOCKS space id 16 page no 3 n bits 88 index `PRIMARY` of table `test`.`emp` trx id 4662 lock_mode X locks rec but not gap waiting
Record lock, heap no 9 PHYSICAL RECORD: n_fields 10; compact format; info bits 0 ## 線程7在等待往test.emp中的主鍵上加X鎖,page num=3
0: len 4; hex 80001e6c; asc l;;
1: len 6; hex 000000001018; asc ;;
2: len 7; hex 91000001420084; asc B ;;
3: len 5; hex 53434f5454; asc SCOTT;;
4: len 7; hex 414e414c595354; asc ANALYST;;
5: len 4; hex 80001d8e; asc ;;
6: len 4; hex 208794f0; asc ;;
7: len 4; hex 80000bb8; asc ;;
8: SQL NULL;
9: len 4; hex 80000014; asc ;;

------------------
TABLE LOCK table `test`.`emp` trx id 4662 lock mode IX ## 在給主鍵行上加X鎖之前,先要在表上加意向鎖IX
RECORD LOCKS space id 16 page no 3 n bits 88 index `PRIMARY` of table `test`.`emp` trx id 4662 lock_mode X locks rec but not gap waiting
Record lock, heap no 9 PHYSICAL RECORD: n_fields 10; compact format; info bits 0
0: len 4; hex 80001e6c; asc l;;
1: len 6; hex 000000001018; asc ;;
2: len 7; hex 91000001420084; asc B ;;
3: len 5; hex 53434f5454; asc SCOTT;;
4: len 7; hex 414e414c595354; asc ANALYST;;
5: len 4; hex 80001d8e; asc ;;
6: len 4; hex 208794f0; asc ;;
7: len 4; hex 80000bb8; asc ;;
8: SQL NULL;
9: len 4; hex 80000014; asc ;;

---TRANSACTION 4615, ACTIVE 1579 sec, thread declared inside InnoDB 1222
mysql tables in use 2, locked 0
2 lock struct(s), heap size 360, 1 row lock(s)
MySQL thread id 3, OS thread handle 0x7f813c659700, query id 147 localhost root Sending data
select count(*) from t3 a,t3 b ## 這是線程3當(dāng)前正在執(zhí)行的SQL
Trx read view will not see trx with id >= 4662, sees < 4659
TABLE LOCK table `test`.`emp` trx id 4615 lock mode IX ## 線程3中正在擁有表上的意向IX鎖,并且有test.emp表上主鍵的行級(jí)X鎖,page num=3
RECORD LOCKS space id 16 page no 3 n bits 88 index `PRIMARY` of table `test`.`emp` trx id 4615 lock_mode X locks rec but not gap
Record lock, heap no 9 PHYSICAL RECORD: n_fields 10; compact format; info bits 0
0: len 4; hex 80001e6c; asc l;;
1: len 6; hex 000000001018; asc ;;
2: len 7; hex 91000001420084; asc B ;;
3: len 5; hex 53434f5454; asc SCOTT;;
4: len 7; hex 414e414c595354; asc ANALYST;;
5: len 4; hex 80001d8e; asc ;;
6: len 4; hex 208794f0; asc ;;
7: len 4; hex 80000bb8; asc ;;
8: SQL NULL;
9: len 4; hex 80000014; asc ;;

為什么線程3當(dāng)前執(zhí)行的是一個(gè)select t3表操作,但卻鎖住了test.emp表上page num=3?
有可能是線程3之前對(duì)test.emp表的操作事務(wù)沒有及時(shí)提交導(dǎo)致。
所以得出:線程3阻塞了線程7,而線程7又阻塞了線程4,所以根因就是線程3,讓線程3盡快提交或是kill掉即可。

結(jié)論
在分析innodb中鎖阻塞時(shí),幾種方法的對(duì)比情況:

(1)使用show processlist查看不靠譜;
(2)直接使用show engine innodb status查看,無法判斷到問題的根因;
(3)使用mysqladmin debug查看,能看到所有產(chǎn)生鎖的線程,但無法判斷哪個(gè)才是根因;
(4)開啟innodb_lock_monitor后,再使用show engine innodb status查看,能夠找到鎖阻塞的根因。

?

注:文章出自

?http://blog.csdn.net/fg2006/article/details/6937413

?http://blog.csdn.net/hw_libo/article/details/39080809

轉(zhuǎn)載于:https://www.cnblogs.com/zhuanghuang/p/5516041.html

總結(jié)

以上是生活随笔為你收集整理的事务库事务隔离级别的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。