今天抽空去补了一下数据库连接相关的知识,结合Druid
源码记录一下,加深理解。
我们首先来理一下,正常JDBC
执行一个操作主要分为以下四步:
- 加载驱动
- 获取连接
- 执行语句,取得结果
- 关闭连接
一开始都会需要加载驱动,如果只是单一驱动其实还好,直接使用Class.forName("com.mysql.cj.jdbc.Driver")
就可以,但是作为一个框架,兼容性要很好,不可能只针对一种驱动,然后这种使用在并发较大的场景下效率是十分低效的。因为连接其实是一个创建代价很大的东西,每次创建获取连接都会有一部分损耗,于是就引入了池化技术,运用数据库连接池去批量管理连接,当要用的时候在拿出来。
Druid
封装了一个 DruidDriver
,它里面持有一个ConcurrentMap,里面持有所有的数据源,然后在具体获取连接的时候,根据传入的url作为Key从map里面得到实际的数据源。
这里简单理一下数据源的概念,我的理解是数据源首先屏蔽了驱动的差异性,把不同驱动的差异化交给了驱动类自己去实现,专注于一些更高的逻辑操作,比如连接的处理(池化),访问数据的监控等,数据源在JDBC
和数据库之上又抽象出了一层,将JDBC
的连接操作进行了管理和缓存,正是由于多了这一层,我们多了很大的操作空间去对数据进行其他处理,比如我们以后看到的filter。这也是技术领域的一个常用手法,当我们觉得当前两层实现很麻烦的时候,再增加出新的一层实现能解决大部分问题。 因为每多一层就会新增一个维度,就多包含了一层信息在里面。
DruidDriver
这个类其实不大,主要作用有下面几个方法:
注册驱动和初始化JMX相关信息
所有驱动都需要先注册之后才能使用,JMX这个没怎么接触过,只知道是一个监控框架,Druid强大的监控能力应该底层就是靠JMX,这个内容等后续在补充,暂时跳过。
数据源和sql语句计数
这两个方法应该是用于监控显示数据的。
协议验证和获取连接
这是Driver接口最重要的两个方法,acceptsURL()
标识当前url是否被接受,如果不被接受代表该驱动不识别该种协议,也就拿不到连接。
connect()
会返回一个具体操作数据库的连接。首先会校验和对连接数+1,我们主要看下getDataSource(url, info)
实现,
其实从名字就可以看出是从数据源Map里面拿到对应驱动的DataSource
,细看一下其实思路还是很清晰的,就是做了个简单的缓存,先看缓存Map里面有没有这个url的DataSource
,有就直接返回,没有则会转换配置,创建驱动,创建数据源,里面还有对filter的操作,这块暂时略过。
以上就是DruidDriver
的全部实现,这些理清之后再去看DruidDataSource
的init()
方法就清晰的多了。由于连接的获取和数据库类别判断都交给了驱动处理,DruidDataSource
只需要关系各种监控校验和数据计数,以及物理连接的管理即可,整个连接池实现其实就是一个数组DruidConnectionHolder[] connections
。
本来想把昨天没写完的DruidDataSource
写完的,没想到DruidDriver
写了这么多,那物理连接创建实现明天继续。