自用笔记,未完成。

1. 原理

1)为什么需要DB连接池

一次DB连接开销较为昂贵

  - 创建/销毁一次TCP连接的I/O开销

  - DB连接需要server为连接分配buffer缓存,频繁连接/关闭造成连接I/O开销

  - JVM GC压力

2)池化技术

(此处开始不经过大脑的typing

用于优化大请求量时系统运行效率,降低系统频繁建立连接的系统开销。线程池、对象池、数据库连接池都应用了类似的技术。

通常规定以下配置

  - 最小/最大连接数

  - 阻塞队列等

此外会有探活机制、强制回收、监控一类的配套功能,保障数据库安全。

2. DB连接池

1)JAVA连接池实现

  下面是一次完整的连接生命周期:

  1)数据层请求DataSource,要连接

  2)DataSource请求Diver打开连接

  3)创建连接,并且打开一个TCP socket连接

  4)使用连接

  5)关闭连接

  6)关闭socket

  通过连接池技术,JAVA连接池可以显著增加连接时间(from this article..600 times...),why?

  因为它在上述2)步骤后,请求了连接池,当且仅当池子里没有可用连接/没到最大连接上限时会创建新连接。而close()方法会把连接返回连接池,等待它的下一次调用。

2)Golang database/sql包实现

改包内部实现了连接池管理,仅对下层驱动暴露简单的驱动接口,这意味着,不同数据库的驱动只需要基于单个接口调用,而不需要关心连接池的实现。

具体层级关系如下:application -> API -> [including connection pooling] -> driver API -> go-sql-driver -> MYSQL

整体来说,Golang的接口与JAVA可以一一对应,Driver, Conn,Stmt, Tx, Rows

调用关系:

  1) Open取得新的Conn

  2) Conn获取Prepare方法

  3) 导入SQL获取Stmt,

  4) 调用Exec,得到结果集Result / 调用Query,得到Rows

连接池设计

  1)建立连接

    连接在执行SQL时建立,有两种建立连接的策略

      - cachedOrNewConn:从freeConn空闲连接队列获取连接,若能获取到的连接没有过期就可以正常使用,如果没有就新建连接。如果到了最大连接数,就阻塞请求。

      - alwaysNewConn:永远新建连接

    建立连接对调用连接起Connect接口,在Driver库内

  2)释放连接

    使用完毕后,连接需要还给线程池,通常要判断连接的可靠性,如果连接有发生异常关闭,需要新建连接进行替换。检查方法 : check ErrBadConn from err msg, if not nil, send a signal to new a connection stead.

  3)清理连接

    maxIdle, maxOpen, macLifeTime。MYSQL 强制清理空闲超过8h的连接,maxLifeTime设置连接被复用的最大时间,为连接建立到被回收的时间(非空闲时间!),从而保证连接活性。每一秒异步检查freeConn种的空闲连接,判断是否合标,不合标的连接加入Closing arr,然后被异步Close。

  So,

  从这篇文章可以知道,Golang sql包的连接池时间较为简单(也可能是这篇文章写的比较清晰简单。。),在DB struct里有空闲连接池(数组),被阻塞请求(map[]channel),连接数/最大连接数,最大生命周期,以及清理连接的channel。笔者没有阅读源码(TODO)初步判断是根据channel实现了简单的数据库连接池。

  在执行sql时,尝试从空闲连接池种获取连接,获取时检查连接是否失效,如果失效会继续获取下一个,如果无法获取新的连接(question:失效意味着什么?是否会建立新的连接?),且当前连接池满了,连接会进入被阻塞请求种(Question:唤醒机制?在返回新的可用连接时,是先从channel里获取,还是从请求里执行),如果没有满的话,Happy ending: create a new connection.

  获取连接执行完后,有连接回收机制,Golang会检查返回的某个错误msg,检查该连接是否可用,如果不可用就建立新的连接替代,然后放入空闲连接池。

  同时Golang还有异步调用的清理连接机制,每秒check所有连接,如果连接时间超过最大生命周期,就清理掉。