Mybatis update數(shù)據(jù)庫死鎖之獲取數(shù)據(jù)庫連接池等待
最近學(xué)習(xí)測試mybatis,單個增刪改查都沒問題,最后使用mvn test的時候發(fā)現(xiàn)了幾個問題:
1.update失敗,原因是數(shù)據(jù)庫死鎖
2.select等待,原因是connection連接池被用光了,需要等待
get:
1.要勇于探索,堅持就是勝利。剛看到錯誤的時候直接懵逼,因為錯誤完全看不出來,屬于框架內(nèi)部報錯,在猶豫是不是直接睡
覺得了,畢竟也快12點了。最后還是給我一點點找到問題所在了。
2.同上,要敢于去深入你不了解的代碼,敢于研究不懂的代碼。
3.距離一個合格的碼農(nóng)越來越遠了,因為越學(xué)越覺得漏洞百出,自己的代碼到處都是坑。所以,一定要記錄下來。
下面記錄這兩個問題。
1.mysql數(shù)據(jù)庫死鎖
這里,感謝http://www.cnblogs.com/lin-xuan/p/5280614.html,我找到了答案。在這里,我還是重現(xiàn)一下:
數(shù)據(jù)庫死鎖是事務(wù)性數(shù)據(jù)庫 (如SQL Server, MySql等)經(jīng)常遇到的問題。除非數(shù)據(jù)庫死鎖問題頻繁出現(xiàn)導(dǎo)致用戶無法操作,一般情況下數(shù)據(jù)庫死鎖問題不嚴重。在應(yīng)用程序中進行try-catch就可以。那么數(shù)據(jù)死鎖是如何產(chǎn)生的呢?
InnoDB實現(xiàn)的是行鎖 (row level lock),分為共享鎖 (S) 和 互斥鎖 (X)。
•共享鎖用于事務(wù)read一行。
•互斥鎖用于事務(wù)update或delete一行。
當客戶A持有共享鎖S,并請求互斥鎖X;同時客戶B持有互斥鎖X,并請求共享鎖S。以上情況,會發(fā)生數(shù)據(jù)庫死鎖。如果還不夠清楚,請看下面的例子。
雙開兩個mysql客戶端
客戶端A:
開啟事務(wù),并鎖定共享鎖S 在id=12的時候:
mysql> START TRANSACTION; Query OK, 0 rows affected (0.00 sec) mysql> SELECT * FROM blog WHERE id = 12 LOCK IN SHARE MODE; +----+-------+-----------+ | id | name | author_id | +----+-------+-----------+ | 12 | testA | 50 | +----+-------+-----------+ 1 row in set (0.00 sec)
客戶端B:
開啟事務(wù),嘗試刪除id=12:
mysql> START TRANSACTION; Query OK, 0 rows affected (0.00 sec) mysql> DELETE FROM blog WHERE id = 12;
刪除操作需要互斥鎖 (X),但是互斥鎖X和共享鎖S是不能相容的。所以刪除事務(wù)被放到鎖請求隊列中,客戶B阻塞。
這時候客戶端A也想要刪除12:
mysql> DELETE FROM blog WHERE id = 12; Query OK, 1 row affected (0.00 sec)
和參考文章不同的是,居然刪除成功了,但客戶端B出錯了:
ERROR 1213 (40001): Deadlock found when trying to get lock; try restarting transaction
于是,我嘗試刪除13,這下都阻塞了:
我的mybatis測試代碼中,因為上一個測試沒有commit導(dǎo)致死鎖,commit后就ok了。在這里,我想說,數(shù)據(jù)庫的東西全還給老師了,關(guān)于鎖以及事務(wù)需要重新溫習(xí)一下了。
2.Mybatis中datasource的數(shù)據(jù)庫連接數(shù)
當我mvn test的時候,我發(fā)現(xiàn)有個查詢的test打印日志:
2016-07-21 23:43:53,356 DEBUG [org.apache.ibatis.transaction.jdbc.JdbcTransaction] - Opening JDBC Connection
2016-07-21 23:43:53,356 DEBUG [org.apache.ibatis.datasource.pooled.PooledDataSource] - Waiting as long as 20000 milliseconds for connection.
于是,果然等了一段時間后才執(zhí)行成功。跟蹤源碼,找到這處日志就明白了。首先,我這里使用的數(shù)據(jù)庫連接配置是mybatis默認的:
<environment id="development"> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <property name="driver" value="${jdbc.driver}"/> <property name="url" value="${jdbc.url}"/> <property name="username" value="${jdbc.username}"/> <property name="password" value="${jdbc.password}"/> </dataSource> </environment> 當數(shù)據(jù)庫連接池的連接數(shù)用光了之后就要等2s再去獲?。? while (conn == null) { synchronized (state) { if (!state.idleConnections.isEmpty()) { // Pool has available connection conn = state.idleConnections.remove(0); if (log.isDebugEnabled()) { log.debug("Checked out connection " + conn.getRealHashCode() + " from pool."); } } else { // Pool does not have available connection if (state.activeConnections.size() < poolMaximumActiveConnections) { // Can create new connection conn = new PooledConnection(dataSource.getConnection(), this); if (log.isDebugEnabled()) { log.debug("Created connection " + conn.getRealHashCode() + "."); } } else { // Cannot create new connection PooledConnection oldestActiveConnection = state.activeConnections.get(0); long longestCheckoutTime = oldestActiveConnection.getCheckoutTime(); if (longestCheckoutTime > poolMaximumCheckoutTime) { // Can claim overdue connection state.claimedOverdueConnectionCount++; state.accumulatedCheckoutTimeOfOverdueConnections += longestCheckoutTime; state.accumulatedCheckoutTime += longestCheckoutTime; state.activeConnections.remove(oldestActiveConnection); if (!oldestActiveConnection.getRealConnection().getAutoCommit()) { try { oldestActiveConnection.getRealConnection().rollback(); } catch (SQLException e) { log.debug("Bad connection. Could not roll back"); } } conn = new PooledConnection(oldestActiveConnection.getRealConnection(), this); oldestActiveConnection.invalidate(); if (log.isDebugEnabled()) { log.debug("Claimed overdue connection " + conn.getRealHashCode() + "."); } } else { // Must wait try { if (!countedWait) { state.hadToWaitCount++; countedWait = true; } if (log.isDebugEnabled()) { log.debug("Waiting as long as " + poolTimeToWait + " milliseconds for connection."); } long wt = System.currentTimeMillis(); state.wait(poolTimeToWait); state.accumulatedWaitTime += System.currentTimeMillis() - wt; } catch (InterruptedException e) { break; } } } } if (conn != null) { if (conn.isValid()) { if (!conn.getRealConnection().getAutoCommit()) { conn.getRealConnection().rollback(); } conn.setConnectionTypeCode(assembleConnectionTypeCode(dataSource.getUrl(), username, password)); conn.setCheckoutTimestamp(System.currentTimeMillis()); conn.setLastUsedTimestamp(System.currentTimeMillis()); state.activeConnections.add(conn); state.requestCount++; state.accumulatedRequestTime += System.currentTimeMillis() - t; } else { if (log.isDebugEnabled()) { log.debug("A bad connection (" + conn.getRealHashCode() + ") was returned from the pool, getting another connection."); } state.badConnectionCount++; localBadConnectionCount++; conn = null; if (localBadConnectionCount > (poolMaximumIdleConnections + 3)) { if (log.isDebugEnabled()) { log.debug("PooledDataSource: Could not get a good connection to the database."); } throw new SQLException("PooledDataSource: Could not get a good connection to the database."); } } } } }
當連接數(shù)少于10個的時候回創(chuàng)建,超過10個就會等待,不然就報錯。
以上所述是小編給大家介紹的Mybatis update數(shù)據(jù)庫死鎖之獲取數(shù)據(jù)庫連接池等待,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復(fù)大家的。在此也非常感謝大家對腳本之家網(wǎng)站的支持!
相關(guān)文章
基于@RequestBody和@ResponseBody及Stringify()的作用說明
這篇文章主要介紹了基于@RequestBody和@ResponseBody及Stringify()的作用說明,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-06-06Springboot 中使用 Aop代碼實戰(zhàn)教程
AOP的編程思想是把對類對象的橫切問題點,從業(yè)務(wù)邏輯中分離出來,從而達到解耦的目的,增加代碼的復(fù)用性,提高開發(fā)效率,這篇文章主要介紹了Springboot中使用Aop代碼實戰(zhàn)教程,需要的朋友可以參考下2023-07-07Spring Cloud Gateway(讀取、修改 Request Body)的操作
這篇文章主要介紹了Spring Cloud Gateway(讀取、修改 Request Body)的操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-12-12MyBatis實現(xiàn)Mysql數(shù)據(jù)庫分庫分表操作和總結(jié)(推薦)
這篇文章主要介紹了MyBatis實現(xiàn)Mysql數(shù)據(jù)庫分庫分表操作和總結(jié),需要的朋友可以參考下2017-08-08SpringAOP實現(xiàn)自定義接口權(quán)限控制
本文主要介紹了SpringAOP實現(xiàn)自定義接口權(quán)限控制,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-11-11java基于netty NIO的簡單聊天室的實現(xiàn)
這篇文章主要介紹了java基于netty NIO的簡單聊天室的實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-07-07