在数据库上每秒运行多个查询的最佳方式,性能明智吗?

时间:2014-06-10 21:19:35

标签: java mysql sql database jdbc

我目前正在使用Java每秒多次插入和更新数据。从未使用过Java数据库,我不确定需要什么,以及如何获得最佳性能。

我目前有一个方法用于我需要做的每种类型的查询(例如,更新数据库中的一行)。我还有一个创建数据库连接的方法。以下是我的简化代码。

    public static void addOneForUserInChannel(String channel, String username) throws SQLException {
    Connection dbConnection = null;
    PreparedStatement ps = null;

    String updateSQL = "UPDATE " + channel + "_count SET messages = messages + 1 WHERE username = ?";

    try {
        dbConnection = getDBConnection();           

        ps = dbConnection.prepareStatement(updateSQL);
        ps.setString(1, username);
        ps.executeUpdate();
    } catch(SQLException e) {
        System.out.println(e.getMessage());
    } finally {
        if(ps != null) {
            ps.close();
        }

        if(dbConnection != null) {
            dbConnection.close();
        }
    }
}

我的数据库连接

    private static Connection getDBConnection() {
    Connection dbConnection = null;

    try {
        Class.forName(DB_DRIVER);
    } catch (ClassNotFoundException e) {
        System.out.println(e.getMessage());
    }

    try {
        dbConnection = DriverManager.getConnection(DB_CONNECTION, DB_USER,DB_PASSWORD);
        return dbConnection;
    } catch (SQLException e) {
        System.out.println(e.getMessage());
    }

    return dbConnection;
}

这个似乎现在正常工作,每秒大约1-2个查询,但我担心一旦我扩展并且它运行了更多,我可能会遇到一些问题。我的问题:

  1. 是否有办法在整个流程运行时间内建立持久数据库连接?如果是的话,我应该这样做吗?
  2. 我是否应该采取其他任何优化来帮助提高性能?
  3. 由于

5 个答案:

答案 0 :(得分:2)

我不知道应用程序其余部分的上下文是什么,但连接应该是长期存在的。在Web应用程序中,通常连接来自连接池。如果您正在构建一个独立的命令行应用程序,那么您应该考虑将连接建立为单例,然后在整个程序中使用它。再次,上下文很重要,所以你可以在这里给出的任何细节都会有所帮助。

对所有数据库流量使用单个连接后,您可以考虑将多个更新作为批处理的一部分执行。如果代码中某处存在紧密循环,那么这绝对是获得更高性能的方法。根据我的经验,通过批处理可以看到10-100倍的性能提升并不罕见。参见" ExectueBatch"在这里:http://docs.oracle.com/javase/7/docs/api/java/sql/Statement.html

通过执行批处理,您可以重复使用准备好的语句,而不是每次都重新准备它。但请注意,在当前形式中,您有一个潜在的SQL注入漏洞,因为您正在根据通道名称进行字符串插值。您可以考虑使用不同的架构结构,以便可以将通道名称作为值而不是表名称传递。如果这样做,查询将更安全,并且具有在准备好后可以轻松重复使用的副作用。

答案 1 :(得分:0)

Oracle数据库JDBC开发人员指南和参考可以帮助您提高查询的性能。基本上你可以试试Batching

阅读描述Java数据库连接(JDBC)标准的Oracle性能扩展的章节Performance Extensions

本章包含以下主题:

  • 更新批处理

  • 其他Oracle Performance Extensions


一些要点:

  • 每次需要连接时都 加载驱动程序类。

    我已经发布了一个很好的 ConnectionUtil 类来管理整个应用程序的单个类中的所有连接。

  • 使用PreparedStatement代替您已使用的Statement

答案 2 :(得分:0)

您要做的是使用连接池进行数据库连接。数据库连接池意味着数据库连接可以在您的查询/更新中保持打开状态,这意味着每次都不会产生连接数据库的开销。 Apache DBCP是一个受欢迎的。例如:

package com.journaldev.jdbc.datasource;

import java.io.FileInputStream;
import java.io.IOException;
import java.util.Properties;

import javax.sql.DataSource;

import org.apache.commons.dbcp.BasicDataSource;

public class DBCPDataSourceFactory {

    public static DataSource getDataSource(String dbType){
        Properties props = new Properties();
        FileInputStream fis = null;
        BasicDataSource ds = new BasicDataSource();

        try {
            fis = new FileInputStream("db.properties");
            props.load(fis);
        }catch(IOException e){
            e.printStackTrace();
            return null;
        }
        if("mysql".equals(dbType)){
            ds.setDriverClassName(props.getProperty("MYSQL_DB_DRIVER_CLASS"));
            ds.setUrl(props.getProperty("MYSQL_DB_URL"));
            ds.setUsername(props.getProperty("MYSQL_DB_USERNAME"));
            ds.setPassword(props.getProperty("MYSQL_DB_PASSWORD"));
        }else if("oracle".equals(dbType)){
            ds.setDriverClassName(props.getProperty("ORACLE_DB_DRIVER_CLASS"));
            ds.setUrl(props.getProperty("ORACLE_DB_URL"));
            ds.setUsername(props.getProperty("ORACLE_DB_USERNAME"));
            ds.setPassword(props.getProperty("ORACLE_DB_PASSWORD"));
        }else{
            return null;
        }

        return ds;
    }
}


package com.journaldev.jdbc.datasource;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

import javax.sql.DataSource;

public class ApacheCommonsDBCPTest {

    public static void main(String[] args) {
        testDBCPDataSource("mysql");
        System.out.println("**********");
        testDBCPDataSource("oracle");
    }

    private static void testDBCPDataSource(String dbType) {
        DataSource ds = DBCPDataSourceFactory.getDataSource(dbType);

        Connection con = null;
        Statement stmt = null;
        ResultSet rs = null;
        try {
            con = ds.getConnection();
            stmt = con.createStatement();
            rs = stmt.executeQuery("select empid, name from Employee");
            while(rs.next()){
                System.out.println("Employee ID="+rs.getInt("empid")+", Name="+rs.getString("name"));
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }finally{
                try {
                    if(rs != null) rs.close();
                    if(stmt != null) stmt.close();
                    if(con != null) con.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
        }
    }

}

此处的教程:http://www.journaldev.com/2509/jdbc-datasource-example-oracle-mysql-and-apache-dbcp-tutorial

答案 3 :(得分:0)

我假设您通常熟悉Java,而不是JDBC。如果是这样,我认为在您进行认真的Java-RDBMS工作之前,您应该阅读JDBC教程或涵盖JDBC的书籍。

除了常识之外,示例代码中最大的性能杀手是:

您正在为每次通话创建新的连接。 Connection是一个非常昂贵的对象。它应尽可能重用。

解决方案:使用连接池(例如" dbcp")

答案 4 :(得分:0)

除连接池外,此处的另一个考虑因素是您拥有的UPDATE频率。大多数数据库引擎中的UPDATE实际上是一个DELETE后跟一个INSERT,并且因为你正在递增计数器,这可能非常昂贵。它通过锁,很多磁盘I / O创建了很多潜在的争用。

如果你确保你的UPDATE是一个密钥(最好是一个PRIMARY KEY),这将有所帮助,但它仍然不会限制数据库引擎需要执行那么多的工作量。

考虑到频率很高,执行此操作的一种好方法是在内存中或在较轻的数据引擎中执行增量值,然后定期写入磁盘。如果你使用内存来累积值,问题就是在崩溃时会丢失数据,所以如果你的数据很关键,这不是一个很好的选择。

可能非常好的一个选项是使用Java是MapDB(http://www.mapdb.org)。此引擎可用于在本地累积值,然后从中读取并定期将累积的总数写入数据库,从而降低实际UPDATE的数量。 MapDB非常快,基本上是一个持久的,基于磁盘的Map(它也有内存中的选项,但如果你需要准确性,那么磁盘存储最适合这样的东西)。

这可以是一个非常快速的解决方案,可以实现极高的数据量,同时保持数据库的速度更快(UPDATEs使数据库不那么优化,因为它必须经常更新索引)。

另一个选项是Redis用于内存中的累加器,也非常快,但这不会在进程中运行。 Redis确实具有操作日志功能,因此您不会丢失数据,但通过网络写入Redis可能不如进程中的MapDB选项快。