最近學習測試mybatis,單個增刪改查都沒問題,最后使用mvn test的時候發現了幾個問題:
1.update失敗,原因是數據庫死鎖
2.select等待,原因是connection連接池被用光了,需要等待
get:
1.要勇于探索,堅持就是勝利。剛看到錯誤的時候直接懵逼,因為錯誤完全看不出來,屬于框架內部報錯,在猶豫是不是直接睡
覺得了,畢竟也快12點了。最后還是給我一點點找到問題所在了。
2.同上,要敢于去深入你不了解的代碼,敢于研究不懂的代碼。
3.距離一個合格的碼農越來越遠了,因為越學越覺得漏洞百出,自己的代碼到處都是坑。所以,一定要記錄下來。
下面記錄這兩個問題。
1.mysql數據庫死鎖
這里,感謝http://www.cnblogs.com/lin-xuan/p/5280614.html,我找到了答案。在這里,我還是重現一下:
數據庫死鎖是事務性數據庫 (如SQL Server, MySql等)經常遇到的問題。除非數據庫死鎖問題頻繁出現導致用戶無法操作,一般情況下數據庫死鎖問題不嚴重。在應用程序中進行try-catch就可以。那么數據死鎖是如何產生的呢?
InnoDB實現的是行鎖 (row level lock),分為共享鎖 (S) 和 互斥鎖 (X)。
•共享鎖用于事務read一行。
•互斥鎖用于事務update或delete一行。
當客戶A持有共享鎖S,并請求互斥鎖X;同時客戶B持有互斥鎖X,并請求共享鎖S。以上情況,會發生數據庫死鎖。如果還不夠清楚,請看下面的例子。
雙開兩個mysql客戶端
客戶端A:
開啟事務,并鎖定共享鎖S 在id=12的時候:
1
2
3
4
5
6
7
8
9
|
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:
開啟事務,嘗試刪除id=12:
1
2
3
|
mysql> START TRANSACTION ; Query OK, 0 rows affected (0.00 sec) mysql> DELETE FROM blog WHERE id = 12; |
刪除操作需要互斥鎖 (X),但是互斥鎖X和共享鎖S是不能相容的。所以刪除事務被放到鎖請求隊列中,客戶B阻塞。
這時候客戶端A也想要刪除12:
1
2
|
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導致死鎖,commit后就ok了。在這里,我想說,數據庫的東西全還給老師了,關于鎖以及事務需要重新溫習一下了。
2.Mybatis中datasource的數據庫連接數
當我mvn test的時候,我發現有個查詢的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.
于是,果然等了一段時間后才執行成功。跟蹤源碼,找到這處日志就明白了。首先,我這里使用的數據庫連接配置是mybatis默認的:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
|
<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> 當數據庫連接池的連接數用光了之后就要等2s再去獲?。?/code> 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." ); } } } } } |
當連接數少于10個的時候回創建,超過10個就會等待,不然就報錯。
以上所述是小編給大家介紹的Mybatis update數據庫死鎖之獲取數據庫連接池等待,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復大家的。在此也非常感謝大家對服務器之家網站的支持!
原文鏈接:http://www.cnblogs.com/woshimrf/p/5693673.html