在Spring Boot中使用国产数据库连接池Druid-CSDN博客

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6

在我们实际开发过程中我们经常使用的是DriverManager来获取通过每次都向数据库建立连接时将Connection加载到内存中然后验证用户名和密码这段时间的消耗大致在0.0 5s - 1s左右每次当我们需要获取数据库连接的时候就需要向数据库要求一次执行之后在断开连接这样的重复的操作在一定程度上也会消耗部分时间以及资源数据库的连接资源并未得到充分的利用假设当同时有几百人或者几千人在线那么频繁的进行数据库连接操作将会占用很多系统资源眼中的甚至造成服务器崩溃。

为了避免这种传统开发过程中的数据库连接问题开发者便尝试采用数据库连接池技术。其基本思想就是: 为数据库连接建立一个“缓冲池“。预先在缓冲池中放入一定数量的连接当需要建立数据库连接时候只需要从“缓冲池“中取出一个然后再使用完毕后放回去。

在此文中我们将使用由我们阿里巴巴数据库事业部出品的一个开源项目他除了是一个高性能数据库连接池之外更是一个自带监控的数据库连接池。我们在《Spring Boot中配置默认的HikariCP数据源》中围绕了Hikari进行了相关讲解知道他虽然很优秀但是对于国内的用户来说我们可能对Druid更加熟悉故而关于在Spring Boot中使用Druid是我们作为后端开发人员需要掌握的一项基本技能。

配置Druid数据源

仍旧是新建一个Spring Boot项目
第一步,引入相关依赖。

	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-jdbc</artifactId>
		</dependency>
		<!--引入Druid依赖-->
		<dependency>
			<groupId>com.alibaba</groupId>
			<artifactId>druid-spring-boot-starter</artifactId>
			<version>1.1.21</version>
		</dependency>
<!--进行监控-->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-actuator</artifactId>
		</dependency>

		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
		</dependency>

		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
			<scope>provided</scope>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>

		<dependency>
			<groupId>com.oracle.ojdbc</groupId>
			<artifactId>ojdbc8</artifactId>
			<version>19.3.0.0</version>
		</dependency>
	</dependencies>

第二步在Spring Boot的application.properties中配置数据库连接信息。
Druid的配置都是以spring.datasource.druid作为前缀的

# MySQL基础配置
#spring.datasource.druid.url=jdbc:mysql://localhost:3306/test
#spring.datasource.druid.username=root
#spring.datasource.druid.password=
#spring.datasource.druid.driver-class-name=com.mysql.cj.jdbc.Driver

server.port=8088

# Oracle
spring.datasource.druid.url=jdbc:oracle:thin:@localhost:1521/orcl
spring.datasource.druid.username=root
spring.datasource.druid.password=root
spring.datasource.druid.driver-class-name=oracle.jdbc.driver.OracleDriver
# 连接池配置
spring.datasource.druid.initialSize=10
spring.datasource.druid.maxActive=20
spring.datasource.druid.maxWait=60000
spring.datasource.druid.minIdle=1
spring.datasource.druid.timeBetweenEvictionRunsMillis=60000
spring.datasource.druid.minEvictableIdleTimeMillis=300000
spring.datasource.druid.testWhileIdle=true
spring.datasource.druid.testOnBorrow=true
spring.datasource.druid.testOnReturn=false
spring.datasource.druid.poolPreparedStatements=true
spring.datasource.druid.maxOpenPreparedStatements=20
spring.datasource.druid.validationQuery=SELECT 1 from useradd
spring.datasource.druid.validation-query-timeout=500
spring.datasource.druid.filters=stat,wall

# 监控配置
spring.datasource.druid.stat-view-servlet.enabled=true
spring.datasource.druid.stat-view-servlet.url-pattern=/druid/*
spring.datasource.druid.stat-view-servlet.reset-enable=true
spring.datasource.druid.stat-view-servlet.login-username=admin
spring.datasource.druid.stat-view-servlet.login-password=admin

属性参照报表:

配置缺省值说明
name 名字配置这个属性的意义在于如果存在多个数据源监控的时候可以通过名字来区分开来。如果没有配置将会生成一个名字格式是“DataSource-” + System.identityHashCode(this). 另外配置此属性至少在1.0.5版本中是不起作用的强行设置name会出错。详情-点此处
url连接数据库的url不同数据库不一样。例如 mysql : jdbc:mysql://10.20.153.104:3306/druid2 oracle : jdbc:oracle:thin:@10.20.149.85:1521:ocnauto
username 用户名连接数据库的用户名
password连接数据库的密码。如果你不希望密码直接写在配置文件中可以使用ConfigFilter。详细看这里
driverClassName 驱动程序类名称根据url自动识别这一项可配可不配如果不配置druid会根据url自动识别dbType然后选择相应的driverClassName
initialSize0初始化时建立物理连接的个数。初始化发生在显示调用init方法或者第一次getConnection时
maxActive8最大连接池数量
maxIdle 最大空闲8已经不再使用配置了也没效果
minIdle最小连接池数量
maxWait获取连接时最大等待时间单位毫秒。配置了maxWait之后缺省启用公平锁并发效率会有所下降如果需要可以通过配置useUnfairLock属性为true使用非公平锁。
poolPreparedStatements poolPrepared语句false是否缓存preparedStatement也就是PSCache。PSCache对支持游标的数据库性能提升巨大比如说oracle。在mysql下建议关闭。
maxPoolPreparedStatementPerConnectionSize-1要启用PSCache必须配置大于0当大于0时poolPreparedStatements自动触发修改为true。在Druid中不会存在Oracle下PSCache占用内存过多的问题可以把这个数值配置大一些比如说100
validationQuery用来检测连接是否有效的sql要求是一个查询语句常用select ‘x’。如果validationQuery为nulltestOnBorrow、testOnReturn、testWhileIdle都不会起作用。
validationQueryTimeout单位秒检测连接是否有效的超时时间。底层调用jdbc Statement对象的void setQueryTimeout(int seconds)方法
testOnBorrowtrue申请连接时执行validationQuery检测连接是否有效做了这个配置会降低性能。
testOnReturn testOnReturn测试返回false归还连接时执行validationQuery检测连接是否有效做了这个配置会降低性能。
testWhileIdlefalse 假建议配置为true不影响性能并且保证安全性。申请连接的时候检测如果空闲时间大于timeBetweenEvictionRunsMillis执行validationQuery检测连接是否有效
keepAlivefalse1.0.28连接池中的minIdle数量以内的连接空闲时间超过minEvictableIdleTimeMillis则会执行keepAlive操作。
timeBetweenEvictionRunsMillis1分钟1.0.14有两个含义1) Destroy线程会检测连接的间隔时间如果连接空闲时间大于等于minEvictableIdleTimeMillis则关闭物理连接。2) testWhileIdle的判断依据详细看testWhileIdle属性的说明
numTestsPerEvictionRun30分钟1.0.14不再使用一个DruidDataSource只支持一个EvictionRun
minEvictableIdleTimeMillis连接保持空闲而不被驱逐的最小时间
connectionInitSqls物理连接初始化的时候执行的sql
exceptionSorter 例外分类器根据dbType自动识别当数据库抛出一些不可恢复的异常时抛弃连接
filters属性类型是字符串通过别名的方式配置扩展插件常用的插件有监控统计用的filter:stat日志用的filter:log4j防御sql注入filter:wall
proxyFilters类型是List<com.alibaba.druid.filter.Filter>如果同时配置了filters和proxyFilters是组合关系并非替换关系

配置开启stat监控统计的界面以及监控内容的相关配置

spring.datasource.druid.stat-view-servlet.url-pattern访问地址规则
spring.datasource.druid.stat-view-servlet.reset-enable是否允许清空统计数据
spring.datasource.druid.stat-view-servlet.login-username监控页面的登录账户
spring.datasource.druid.stat-view-servlet.login-password监控页面的登录密码

第三步创建相关实体类User

public class User {

    private String name;
    private Integer age;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

    public User(String name, Integer age) {
        this.name = name;
        this.age = age;
    }

    public User() {
    }
}

第四步创建相关业务接口

public interface UserService {

    /**
     * 新增一个用户
     *
     * @param name
     * @param age
     */
    int create(String name, Integer age);

    /**
     * 根据name查询用户
     *
     * @param name
     * @return
     */
    List<User> getByName(String name);

    /**
     * 根据name删除用户
     *
     * @param name
     */
    int deleteByName(String name);

    abstract int getAllUsers();

    /**
     * 删除所有用户
     */
    int deleteAllUsers();

}

第五步实现接口

@Service
public class UserServiceImpl implements UserService {

    private JdbcTemplate jdbcTemplate;

    UserServiceImpl(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
    }

    @Override
    public int create(String name, Integer age) {
        return jdbcTemplate.update("insert into USERADD(NAME, AGE) values(?, ?)", name, age);
    }

    @Override
    public List<User> getByName(String name) {
        List<User> users = jdbcTemplate.query("select NAME, AGE from USERADD where NAME = ?", (resultSet, i) -> {
            User user = new User();
            user.setName(resultSet.getString("NAME"));
            user.setAge(resultSet.getInt("AGE"));
            return user;
        }, name);
        return users;
    }

    @Override
    public int deleteByName(String name) {
        return jdbcTemplate.update("delete from USERADD where NAME = ?", name);
    }

    @Override
    public int getAllUsers() {
        return jdbcTemplate.queryForObject("select count(1) from USERADD", Integer.class);
    }

    @Override
    public int deleteAllUsers() {
        return jdbcTemplate.update("delete from USERADD");
    }

}

第六步创建相关控制层调用业务层

@RestController
public class UserController {

    @Autowired
    private UserService userService;

    @PostMapping("/user")
    public int create(@RequestBody User user) {
        return userService.create(user.getName(), user.getAge());
    }

    @GetMapping("/user/{name}")
    public List<User> getByName(@PathVariable String name) {
        return userService.getByName(name);
    }

    @DeleteMapping("/user/{name}")
    public int deleteByName(@PathVariable String name) {
        return userService.deleteByName(name);
    }

    @GetMapping("/user/count")
    public int getAllUsers() {
        return userService.getAllUsers();
    }

    @DeleteMapping("/user/all")
    public int deleteAllUsers() {
        return userService.deleteAllUsers();
    }

}

在这里插入图片描述
在这里插入图片描述

http://localhost:8088/druid/login.html
admin – admin
在这里插入图片描述
在这里插入图片描述
数据源这里可以看到之前我们配置的数据库连接池信息以及当前使用情况的各种指标。
在这里插入图片描述

SQL监控该数据源中执行的SQL语句极其统计数据。在这个页面上我们可以很方便的看到当前这个Spring Boot都执行过哪些SQL这些SQL的执行频率和执行效率也都可以清晰的看到。如果你这里没看到什么数据别忘了我们之前创建了一个Controller用这些接口可以触发UserService对数据库的操作。所以这里我们可以通过调用接口的方式去触发一些操作这样SQL监控页面就会产生一些数据

在这里插入图片描述
图中监控项上执行时间、读取行数、更新行数都通过区间分布的方式表示将耗时分布成8个区间

0 - 1 耗时0到1毫秒的次数
1 - 10 耗时1到10毫秒的次数
10 - 100 耗时10到100毫秒的次数
100 - 1,000 耗时100到1000毫秒的次数
1,000 - 10,000 耗时1到10秒的次数
10,000 - 100,000 耗时10到100秒的次数
100,000 - 1,000,000 耗时100到1000秒的次数
1,000,000 - 耗时1000秒以上的次数

SQL防火墙该页面记录了与SQL监控不同维度的监控数据更多用于对表访问维度、SQL防御维度的统计。
在这里插入图片描述
该功能数据记录的统计需要在spring.datasource.druid.filters中增加wall属性才会进行记录统计比如这样

spring.datasource.druid.filters=stat,wall

注意这里的所有监控信息是对这个应用实例的数据源而言的而并不是数据库全局层面的可以视为应用层的监控不可能作为中间件层的监控。

编写测试类

@Slf4j
@RunWith(SpringRunner.class)
@SpringBootTest
public class Chapter33ApplicationTests {

    @Autowired
    private UserService userSerivce;

    @Autowired
    private DataSource dataSource;

    @Before
    public void setUp() {
        // 准备清空user表
        userSerivce.deleteAllUsers();

    }

    @Test
    public void test() throws Exception {
        // 插入5个用户
        userSerivce.create("Tom", 10);
        userSerivce.create("Mike", 11);
        userSerivce.create("Didispace", 30);
        userSerivce.create("Oscar", 21);
        userSerivce.create("Linda", 17);

        // 查询名为Oscar的用户判断年龄是否匹配
        List<User> userList = userSerivce.getByName("Oscar");
        Assert.assertEquals(21, userList.get(0).getAge().intValue());

        // 查数据库应该有5个用户
        Assert.assertEquals(5, userSerivce.getAllUsers());

        // 删除两个用户
        userSerivce.deleteByName("Tom");
        userSerivce.deleteByName("Mike");

        // 查数据库应该有5个用户
        Assert.assertEquals(3, userSerivce.getAllUsers());

    }

}
阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6
标签: Spring数据库