背景
我们在一个大型的分布式单元化系统中,由于服务机器数量众多(1000台左右),造成对一些公共数据库连接数量过多的问题,以至于数据库光是维护连接就不堪重负。
解决方法
为了降低数据库连接,我们让所有只读操作,共享少数几条只读数据库连接的方法。
经测试,大部分数据库JDBC连接,在只读情况下,都是线程安全的。所以,我们就顺着这个思路,自己做了一个数据源扩展,在获取数据库时,先判断当前数据库事务是否只读,如果是只读的,则让所有读数据的操作,共用n条连接。
当然为了连接的可用性,还是要在有用时,不释放,大家都用完了,就地归还到连接池,以便于连接池对连接进行正常的维护。
代码我已经上传到了码云上,共享只读数据源链接
使用示例:https://gitee.com/johnnyzhou/ali-cloud-sample/tree/dubbo-gz-spi/shared-connection-demo
我们提供了druid和hikari两种实现,大家可根据自己的需要选择。
经测试,数据库连接数据可降低到原来的30%左右
压测结果
理论上,java处理逻辑越多,或读取的数据量越大,一个服务耗时越长,共享效果也就越明显。
以下结果是基于最简单的服务测试,服务直接从数据库中读取3条记录返回,不做其它处理。
TPS | 共享1条 | 共享2条 | 共享3条 | 独享1条 | 独享2条 | 独享3条 |
---|---|---|---|---|---|---|
Hikari | 234 | 446 | 651 | 59 | 109 | 149 |
Druid | 250 | 401 | 600 |
使用要点
定义数据源
spring.datasource.driver-class-name=org.postgresql.Driver
spring.datasource.url=jdbc:postgresql://localhost:5432,localhost:5433/postgres
spring.datasource.username=postgres
spring.datasource.password=123456Aa
spring.datasource.type=com.ibm.ibank.shareddatasource.hikari.SharedDataSource
#读写事务是否允许共享,如果你确定些数据源只读不写,可以设置为true, 缺省为false
spring.datasource.hikari.share-on-write-tx=true
#共享数据库连接条数
spring.datasource.hikari.shared-size=2
spring.datasource.hikari.initial-size=1
spring.datasource.hikari.minimum-idle=1
spring.datasource.hikari.maximum-pool-size=2 #不能小于共享数据库连接条数
spring.datasource.hikari.idle-timeout=500000
spring.datasource.hikari.max-lifetime=540000
服务代码中,把缺省事务定义为只读,再把读写操作特别定义为读写
@Service
@Transactional(readOnly = true)
public class TestServiceImpl implements TestService {
public TestDTO getTestDTO(String id) { //这个是只读事务,会共享一条连接
...
return ...;
}
@Transactional(readOnly = false)
public void AddTestDTO(TestDTO testDTO) { //这个是读写事务,会独享一条连接
...
}
}