执行长时间SQL

场景描述

大多数SQL执行时间短,个别SQL可能执行时间长。不调整连接参数,长时间SQL执行超时,调整连接参数会导致普通出错情况等待时间长。

实现思路

对于这种情况,可以单独创建一个用于执行长时间SQL的连接,使用这个连接执行长时间SQL。

实现步骤

1 创建长时间数据库配置类

在src/main/java/main/common/config下创建DatasourceConfig.java,完整代码下面附上,需要说明如下几点

  • 定义Bean的名字,以便使用

定义为longMainDataSource,代码如下

@Bean(name = "longMainDataSource", initMethod = "init", destroyMethod = "close")
public DruidDataSource longMainDataSource() {

在service中使用,代码如下

@Autowired
@Qualifier("longMainDataSource")
private DataSource longMainDatasource;
  • 设置模块名,模块名是环境变量的前缀,以便正确获取环境变量

在main模块中,所以设置为main,环境变量名称为大写,代码如下

private String envPrefix = "MAIN";
private String archServiceName = "main";
  • 设置连接参数

在createDataSoruce方法中设置了连接参数,可以按需修改,代码如下

private DruidDataSource createDataSoruce() {
        datasource.setMaxActive(3);//只允许三个并发
        datasource.setMaxWait(5000);
        datasource.setTimeBetweenEvictionRunsMillis(60000 * 10 * 6);
        datasource.setMinEvictableIdleTimeMillis(220000);
        datasource.setMaxEvictableIdleTimeMillis(300000);
        datasource.setConnectionErrorRetryAttempts(1);
        datasource.setTimeBetweenConnectErrorMillis(10000);    
}

为了和标准连接区分开,在url上需要添加一个参数

这一点非常关键,本例添加了useLong参数,用来和标准连接区分开,代码如下

url+= "useLong=true"; //添加useLong参数, 和标准的连接区分开

数据库配置类完整代码如下

package main.common.config;

import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

import org.dom4j.Element;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.pool.DruidDataSourceFactory;
import com.alibaba.druid.pool.vendor.MySqlValidConnectionChecker;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.justep.service.config.api.ServiceConfig;
import com.justep.service.config.api.ServiceConfigFactory;
import com.justep.util.db.DatabaseType;
import com.justep.util.db.JDBCUrl;

@Configuration
public class DatasourceConfig {
    private static final Logger logger = LoggerFactory.getLogger(DatasourceConfig.class);

    class DBResource {
        private String url;
        private String dialect;
        private String username;
        private String password;
        private String driverClassName;
        private String dbType;
        private String dbName;
        private String properties;
        private String dbHost;
        private String dbPort;
        private String dbSchema;
        private String validateSql;
        private String shardingConfig;
        private Properties connProp;

        private Element root;
        private boolean fromEnv = false;
        private boolean throughDbProxy = true;

        public DBResource() throws Exception {
            ServiceConfig config = ServiceConfigFactory.get(archServiceName,"maven");
            dbHost = config.getVar(envPrefix + "_DBHOST");
            if (StringUtils.isBlank(dbHost)) {
                throw new IllegalStateException("服务未定义数据库参数: " + archServiceName);
            } else {
                dbType = config.getVar(envPrefix + "_DBTYPE");
                dbName = config.getVar(envPrefix + "_DBNAME");
                dbPort = config.getVar(envPrefix + "_DBPORT");
                dbSchema = config.getVar(envPrefix + "_DBSCHEMA");
                username = config.getVar(envPrefix + "_DBUSER");
                password = config.getVar(envPrefix + "_DBPASSWD");
                properties = config.getProp(envPrefix + "_DBPROPERTIES");
                shardingConfig = config.getVar(envPrefix + "_DBEXTSHARDING");
                fromEnv = true;
                String envUseDbProxy = config.getVar("COMP_DBPROXY_USEDBPROXY");
                if (envUseDbProxy != null && (envUseDbProxy.equalsIgnoreCase("true") || envUseDbProxy.equalsIgnoreCase("yes") || envUseDbProxy.equalsIgnoreCase("1"))) {
                    throughDbProxy = true;
                } else {
                    throughDbProxy = false;
                }

            }
            init();
            logger.info("db resource url from env {} : {}", fromEnv, url);
        }

        private void init() throws Exception {
            if (fromEnv) {
                connProp = genConnProp();
                url = genJDBCUrl();
                DatabaseType dt = DatabaseType.getDatabaseType(url);
                validateSql = DatabaseType.getValidationSql(dt);
                driverClassName = dt.getDriverClass();
                dialect = dt.getDialect();
                if (!StringUtils.isEmpty(shardingConfig)) {
                    url += DatabaseType.getItemSeparator(dt) + "shardingConfig=" + shardingConfig;
                }
                if (useDbProxy()) {
                    driverClassName = "com.justep.dbproxy.ProxyDriver";
                }
            } else {
                throw new IllegalStateException("服务未定义数据库参数: " + archServiceName);
            }
        }
        public boolean useDbProxy() {
            if (url.indexOf("dbproxy=false") < 0 && throughDbProxy) {
                return true;
            }
            return false;
        }
        private Properties genConnProp() {
            Properties ret = new Properties();
            if (dbType.contains("oracle")) {
                ret.setProperty("oracle.jdbc.timezoneAsRegion", "false");
            }
            return ret;
        }

        private String genJDBCUrl() {
            JDBCUrl jdbcUrl = new JDBCUrl(DatabaseType.parse(dbType), dbHost, dbPort, dbName, username, password, dbSchema, properties);
            jdbcUrl.setWithUserPassword(false);
            return jdbcUrl.jdbcUrl();
        }

        public String getUrl() {
            return url;
        }

        public void setUrl(String url) {
            this.url = url;
        }

        public String getDialect() {
            return dialect;
        }

        public void setDialect(String dialect) {
            this.dialect = dialect;
        }

        public String getUsername() {
            return username;
        }

        public void setUsername(String username) {
            this.username = username;
        }

        public String getPassword() {
            return password;
        }

        public void setPassword(String password) {
            this.password = password;
        }

        public String getDriverClassName() {
            return driverClassName;
        }

        public void setDriverClassName(String driverClassName) {
            this.driverClassName = driverClassName;
        }
        public String getValidateSql() {
            return validateSql;
        }

        public Properties getConnProp() {
            return connProp;
        }
    }


    private String envPrefix = "MAIN";
    private String archServiceName = "main";
    private DBResource resource;
    public DatasourceConfig() throws Exception {
        resource = new DBResource();
    }

    @Bean(name = "longMainDataSource", initMethod = "init", destroyMethod = "close")
    public DruidDataSource longMainDataSource() {
        logger.info("creating longMainDataSource");
        DruidDataSource datasource = createDataSoruce();
        return datasource;
    }

    private DruidDataSource createDataSoruce() {
        try{
            Map<String, String> map = new HashMap<String, String>();
            map.put("init", "false");
            DruidDataSource datasource = (DruidDataSource) DruidDataSourceFactory.createDataSource(map);
            String url = resource.getUrl();
            url += (url.contains("?")) ? "&" : "?";
            url+= "useLong=true"; //添加useLong参数, 和标准的连接区分开
            datasource.setUrl(url);
            datasource.setUsername(resource.getUsername());
            datasource.setPassword(resource.getPassword());
            datasource.setDriverClassName(resource.getDriverClassName());
            if (resource.useDbProxy()) {
                datasource.setValidConnectionChecker(new MySqlValidConnectionChecker());
            }
            // configuration
            datasource.setInitialSize(1);
            datasource.setMinIdle(1);
            datasource.setMaxActive(3);//只允许三个并发
            datasource.setMaxWait(5000);
            datasource.setTimeBetweenEvictionRunsMillis(60000 * 10 * 6);
            datasource.setMinEvictableIdleTimeMillis(220000);
            datasource.setMaxEvictableIdleTimeMillis(300000);
            datasource.setTestWhileIdle(true);
            datasource.setTestOnBorrow(false);
            datasource.setTestOnReturn(false);
            datasource.setPoolPreparedStatements(false);
            datasource.setValidationQuery(resource.getValidateSql());            
            datasource.setConnectionErrorRetryAttempts(1);
            datasource.setInitExceptionThrow(false);
            datasource.setTimeBetweenConnectErrorMillis(10000);    
            datasource.setConnectProperties(resource.getConnProp());
            datasource.init();            
            return datasource;
        }catch(Exception ex){
            logger.error("datasource error",ex);
            throw new RuntimeException(ex);
        }
    }
}

2 使用长时间连接执行SQL

在service中注入上面创建的长时间数据库配置类,使用它创建连接,执行SQL,完整代码如下

package main.service;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;

import javax.sql.DataSource;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;

@Service
public class FuwuUserService {

    //自动注入长时间数据库配置类
    @Autowired
    @Qualifier("longMainDataSource")
    private DataSource longMainDatasource;

    public String zhangshijsql() throws Exception {
        Connection con = null;
        PreparedStatement stmt = null;
        ResultSet rs = null;
        String sql = "select * from orgs";
        try {
            //使用长时间数据库配置类创建连接
            con = longMainDatasource.getConnection();
            //执行SQL
            stmt = con.prepareStatement(sql,ResultSet.TYPE_SCROLL_INSENSITIVE,ResultSet.CONCUR_READ_ONLY);
            rs = stmt.executeQuery();
            int count = 0;
            if (rs.first()) {
                  rs.last();  
                  count = rs.getRow();  
            }
            return "count=" + count;
        }catch(Exception e) {
            throw new RuntimeException("" + e.getMessage(), e);
        }finally {
            try {
                if (stmt != null) {
                    stmt.close();
                }
            }catch(Exception e) {                    
            }
            try {
                if (rs != null) {
                    rs.close();
                }
            }catch(Exception e) {                    
            }
        }
    }    
}

results matching ""

    No results matching ""

    results matching ""

      No results matching ""