背景
- 昨天學習總結了tomcat的http連接池和線程池相關的知識,總結的不是很完整, 自己知道的也比較少,總結的時候就在想tomcat針對client 端有連接池,並且通過NIO的機制, 以較少的thread數目來支撐角度的connection, 性能和並發數都不錯.
- 當時就想總結一下數據庫連接池, 但是這一塊內容自己接觸的非常少, 又沒見過底層的源碼,所以總結起來非常吃力,只能夠將之前遇到的簡單整理一下, 可能存在很多錯誤和缺失的地方.
參數
- springboot 內部有較多的數據庫連接池的參數, 這里可以仿照一個帖子來進行簡單說明
# Hikari pool https://github.com/brettwooldridge/HikariCP
spring.datasource.type=com.zaxxer.hikari.HikariDataSource
# 連接池中允許的最小連接數。缺省值:10
spring.datasource.hikari.minimum-idle=10
# 連接池中允許的最大連接數。缺省值:10
spring.datasource.hikari.maximum-pool-size=100
# 自動提交
spring.datasource.hikari.auto-commit=true
# 一個連接idle狀態的最大時長(毫秒),超時則被釋放(retired),缺省:10分鍾
spring.datasource.hikari.idle-timeout=30000
# 連接池名字
spring.datasource.hikari.pool-name=FlyduckHikariCP
# 一個連接的生命時長(毫秒),超時而且沒被使用則被釋放(retired),缺省:30分鍾,建議設置比數據庫超時時長少30秒
spring.datasource.hikari.max-lifetime=1800000
# 等待連接池分配連接的最大時長(毫秒),超過這個時長還沒可用的連接則發生SQLException, 缺省:30秒
spring.datasource.hikari.connection-timeout=30000
# 數據庫連接測試語句
spring.datasource.hikari.connection-test-query=SELECT 1
- 需要注意的時出去着一些的配置還需要有一個腳本的配置, yaml格式里面可以進行簡單說明
datasource:
type: com.zaxxer.hikari.HikariDataSource
url:
driver-class-name: oracle.jdbc.driver.OracleDriver
username:
password:
hikari:
pool-name: hikari-cp
connection-test-query: SELECT 1 From DUAL
maximum-pool-size: 50
minimum-idle: 1
- 這里可以簡單總結一下之前的工作情況, 但是並不一定准確.
1. driver-class-name 需要指定數據庫的連接 不同的數據庫不一樣.
2. 在jpa層還有一個dialect的處理, 針對不通的數據庫有不同的方言, 方言會涉及一些除了標准SQL之后不同數據庫之間各自獨特的處理, 注意連接不同數據庫時dialect 一定要設置准確,不然容易出現一些錯誤.
3. username password 以及url 都不需要過多解釋.
4. hikari 里面會描述部分信息 pool-name 等信息. 以及最大數據庫連接池數以及最小空閑數
這里簡單說明一下自己的理解 可能並不是很准確:
4.1 最大數據庫連接數來決定應用服務的數據庫連接池能夠同時管理的數據庫連接數目, 這個數目不能無限制的設置,因為還要考慮數據庫的情況, 並且設置過多的數據庫連接可能無益於性能提升. 數據庫管理特點連接和連接池管理太多連接可能有一些額外的損耗, 這里跟應用服務器的事務處理特點有關系, 如果都是短促的事務為主可以設置的連接池數目比較小寫,如果都是長事務, 建議將連接池設置的大一些,避免tomcat的thread連接池處於飢餓狀態無法獲取數據庫的連接用於持久化或者是查詢操作.
4.2 最小化連接池 保證環境存在的最低連接情況, tomcat的線程池創建和銷毀會有較大的資源損耗,數據庫連接池的建議要更多倍於線程池的連接的建立, 需要有tcp三步握手,數據庫認證以及建立連接等, 所以保持一個合適的數據庫連接數目對於突發的流量洪峰應該是有好處的
4.3 關於連接池泄漏, 如果業務代碼處理數據庫連接之后 沒有提交或者是回滾事務, 並且沒有對連接進行關閉操作,會導致,數據庫連接池管理線程以為該線程不能被回收進連接池供其他進程使用,如果所有的連接池都處於這種裝填就會導致無法建立新的連接,出現環境宕機的情況, 需要重點關注.
查看
- 查看當前進程的連接池數量
jmap -histo `jps |grep caf-bootstrap.jar |awk '{print $1}'` |grep -i hikariDataSource
1. 注意第一個grep 可以設置為應用服務器主線程的jar包名
2. 注意需要設置jdk的bin目錄到環境變量里面去
- 一般的結果會包含如下內容
- 注意第二個數字 2 就是當前jvm 里面連接池的對象數量.
- 這里一個不成熟的理解, jvm 里面連接池也是作為java對象進行管理的. 對象數量就可表達連接池的在用數量.
[root@centos76oracle19c ~]# jmap -histo `jps |grep caf-bootstrap.jar |awk '{print $1}'` |grep -i hikariDataSource
3331: 2 336 com.zaxxer.hikari.HikariDataSource
- 查看使用連接池的線程信息.
[root@centos76oracle19c ~]# jstack -l `jps |grep caf-bootstrap.jar |awk '{print $1}'` |grep -i hikari
"hikari-cp housekeeper" #73 daemon prio=5 os_prio=0 tid=0x00007ff725e34000 nid=0x236b waiting on condition [0x00007ff6cc6db000]
[root@centos76oracle19c ~]#
- 感覺如上結果就是連接池的相關管理線程.
在使用功能時 偶爾能夠查詢出來 在使用的數據庫連接線程信息
--
"http-nio-5200-exec-2" #225 daemon prio=5 os_prio=0 tid=0x00007ff6d2fcc800 nid=0x464d runnable [0x00007ff696b89000]
java.lang.Thread.State: RUNNABLE
at sun.nio.ch.FileDispatcherImpl.read0(Native Method)
at sun.nio.ch.SocketDispatcher.read(SocketDispatcher.java:39)
at sun.nio.ch.IOUtil.readIntoNativeBuffer(IOUtil.java:223)
at sun.nio.ch.IOUtil.read(IOUtil.java:197)
at sun.nio.ch.SocketChannelImpl.read(SocketChannelImpl.java:380)
- locked <0x00000007bfdd3f18> (a java.lang.Object)
at oracle.net.nt.TimeoutSocketChannel.read(TimeoutSocketChannel.java:174)
at oracle.net.ns.NSProtocolNIO.doSocketRead(NSProtocolNIO.java:555)
at oracle.net.ns.NIOPacket.readHeader(NIOPacket.java:258)
at oracle.net.ns.NIOPacket.readPacketFromSocketChannel(NIOPacket.java:190)
at oracle.net.ns.NIOPacket.readFromSocketChannel(NIOPacket.java:132)
at oracle.net.ns.NIOPacket.readFromSocketChannel(NIOPacket.java:105)
at oracle.net.ns.NIONSDataChannel.readDataFromSocketChannel(NIONSDataChannel.java:91)
at oracle.jdbc.driver.T4CMAREngineNIO.prepareForUnmarshall(T4CMAREngineNIO.java:764)
at oracle.jdbc.driver.T4CMAREngineNIO.unmarshalUB1(T4CMAREngineNIO.java:429)
at oracle.jdbc.driver.T4CTTIfun.receive(T4CTTIfun.java:407)
at oracle.jdbc.driver.T4CTTIfun.doRPC(T4CTTIfun.java:268)
at oracle.jdbc.driver.T4C8Oall.doOALL(T4C8Oall.java:655)
at oracle.jdbc.driver.T4CPreparedStatement.doOall8(T4CPreparedStatement.java:270)
at oracle.jdbc.driver.T4CPreparedStatement.doOall8(T4CPreparedStatement.java:91)
at oracle.jdbc.driver.T4CPreparedStatement.executeForRows(T4CPreparedStatement.java:970)
at oracle.jdbc.driver.OracleStatement.executeMaybeDescribe(OracleStatement.java:1012)
at oracle.jdbc.driver.OracleStatement.doExecuteWithTimeout(OracleStatement.java:1168)
at oracle.jdbc.driver.OraclePreparedStatement.executeInternal(OraclePreparedStatement.java:3666)
at oracle.jdbc.driver.T4CPreparedStatement.executeInternal(T4CPreparedStatement.java:1426)
at oracle.jdbc.driver.OraclePreparedStatement.execute(OraclePreparedStatement.java:3778)
- locked <0x00000007bfdcef70> (a oracle.jdbc.driver.T4CConnection)
at oracle.jdbc.driver.OraclePreparedStatementWrapper.execute(OraclePreparedStatementWrapper.java:1081)
at com.zaxxer.hikari.pool.ProxyPreparedStatement.execute(ProxyPreparedStatement.java:44)
at com.zaxxer.hikari.pool.HikariProxyPreparedStatement.execute(HikariProxyPreparedStatement.java)
- 使用功能操作時也會產生新的 數據庫連接對象, 可以看到從2個數據庫連接池對象, 變成了3個.
[root@centos76oracle19c ~]# jmap -histo `jps |grep caf-bootstrap.jar |awk '{print $1}'` |grep -i hikariDataSource
3373: 3 504 com.zaxxer.hikari.HikariDataSource
總結
- 連接池部分自己還需要多看多學習.Oracle的pga區域大小過小的話可能無法創建過多的數據庫連接需要的session, 也就是說如果數據庫配置不完備, springboot 設置的高了也沒有意義. Oracle可以設置 session和process的參數來限制存儲的數據庫連接池的大小, 注意, 一定不要將數據庫的session或者是process打滿, 如果打滿了可能就無法操作數據庫了, 如果遇到了連接池泄漏除非重啟應用等待連接池消退好像沒有別的更好的辦法了.
- pg以及mysql以及很多國產數據庫都有max_connections的操作, 懷疑這里面的connections 應該是比oracle的session和process更大宏觀概念的session信息. 也需要設置的打一些,不然可能會存在問題.
- 之前分析了 tomcat的線程池,本次分析了數據庫的連接池, 其實產品里面還有很多其他的線程信息, 比如 redis的以及定時器計划任務,GC線程,編譯線程,日志線程,類加載線程.等線程
- 等別的晚上在分析一下其他的線程 用來提高自己.