如何提高插入10万条记录的速度

时间:2022-10-11 23:36:37
最近在做一个测试,看向数据库插入10条需要多长时间!
系统:redhat + oracle 9.2.0.4,+ RAID5 + 一万转的SCSI硬盘

条件:每插入一条就commit.
         在客户端生程insert语句,然后发到数据库。

用尽了方法,发现每秒只能插入大约600条,但是相同条件下sql server能每秒插入1000多条,令我这个Oracle DBA很每面子,所以决定调优一把。

于是我想尽了可能优化的方法:
将数据文件与联机日志文件建在裸设备上。
用INSERT /*+ append */ INTO .....

但都不见起色,插入速度提高到每秒65条!

在v$system_event视图中发现大量的:
log file parallel write                            27088      27078         11
log file sync                                      26935          0        784
等待。

谁能帮帮我?

我也来说两句 查看全部评论 相关评论

  • zhanghuajie (2004-7-20 09:39:33)

    不能将这10万条记录做成文件,用SQLLOAD 导入吗??或者做成EXCLE文件也可以的啊!!!!
  • SunnyXu (2004-7-20 09:55:27)

    不行,因为通过程序做的,我们有许多并发访问!
  • tim_wong (2004-7-20 11:12:40)

    2000条commit一次。
  • tim_wong (2004-7-20 11:15:32)

    sorry,

    把条件看漏了。

    可能你的表约束太多了吧。
  • SunnyXu (2004-7-20 12:07:50)

    这个表没有任何索引,也没有任何约束.

    同样条件下,sql server数据库每秒插入1200多行!
  • jametong (2004-7-20 12:15:04)

    为什么要没插入一条就commit一次呢,可不可以批量插入,这样的事务导致,回滚段分配过于频繁,写日志文件过于频繁了,
  • zhanhu_hou (2004-7-20 12:56:42)

    没学过优化,不过个人感觉基本上oracle的数据处理速度可能是赶不上sql server的,oracle可能更多的体现在稳定性上和跨平台上。基本上我对它数据处理速度上没有什么指望。
  • jametong (2004-7-20 12:59:44)

    不同意楼上的看法,oracle的性能是可以调整的,
    具体情况需要dba去调整和取舍(稳定性,高可用户,灵活性,可管理性)
  • zhanhu_hou (2004-7-20 13:23:30)

    这样一来到引起了我的兴趣,目前在oracle已经适当优化的条件下(sql server还没有优化),是650条/s (看作者意思是这样),大伙看如何再优化,我想到的方法是先修改程序,不必插入每一条记录后都要commit。所以先优化sql 语句。
  • jametong (2004-7-20 13:29:28)

    在当前情况下是这样,如果改为一万条commit一次的话,我相信效率翻番是没有问题的,具体试验今天没有时间做.
    以前我做过一次sql loader的导入测试.
    20万条数据,在默认情况下,导入数据库的时间为1分50几秒,
    在我修改了一下参数readsize=21971520的时候,加载的时间为40秒左右吧,
    再添加一个direcy=y参数的情况下,加载时间为10秒左右

    下面这片文章是一个dbaspecialist的工程师做的测试

    如何提高插入10万条记录的速度Load Your Data Faster.htm
    (, Size: 29.5 kB, Downloads: 138)

  • zhanhu_hou (2004-7-20 13:49:10)

    看来oracle的伸缩性真的很大阿,不同的数据加载方法,居然相差这么大。
    由此看来,真的要管理好数据库,不是一件很容易的事情。
  • jametong (2004-7-20 14:31:51)

    呵呵
  • SunnyXu (2004-7-20 15:58:52)

    我得前提为没插入一条就commit. 这是应用的的要求.
    我不是load部分数据做测试,这是一个正式的应用.
  • d.c.b.a (2004-7-20 20:30:25)

    偶用Java写的文本导入工具,在Oracle中每秒1万条没有问题。
    这肯定是程序的问题,程序是运行在windows上的。
  • jametong (2004-7-20 20:39:32)

    兄弟可不可以考虑修改修改回滚段的参数啊
  • chao_ping (2004-7-20 23:04:25)

    每插入一条就commit,那个是design 上面, 就限制了他的scale.

    这方面, Oracle的实现机制决定了不会很快, 比不上SQL Server我想是可以理解的. 但是差别应该不会如此悬殊.

    还有一个可能是不是每次你的连接都是通过一次sql*net传输,这样也限制了你的速度.


    我做了一个简单测试, PC server, Raid5 的CX300磁盘阵列.
    速度可以达到 3000 条/妙.
    当然如果用batch commit, 效率应该可以提高很多.

    1  begin
      2  for x in 1..100000 loop
      3   insert into test values(x);
      4  commit;
      5  end loop;
      6* end;
    SQL> /

    PL/SQL procedure successfully completed.

    Elapsed: 00:00:35.93
    SQL> select 100000/35 from dual;

    100000/35
    ----------
    2857.14286

    Elapsed: 00:00:00.0
  • d.c.b.a (2004-7-21 08:05:39)

    还应当贴出索引的情况,ms sql的主键一般相当于Oracle的iot。
  • rollingpig (2004-7-21 08:51:29)

    研究一下oracle插入一条数据并commit作的事情

    再研究一下sql server插入一条数据并commit作的事情

    你应该会发现oracle比sql-server多做了一些事情
    而这些事情是保证oracle的稳定/高并发/etc.. 所必需的

    以上纯属按常理推测,并未仔细验证
  • SunnyXu (2004-7-21 10:14:24)

    谢谢大家的踊跃参与!

    我来做个总结:

    测试前提:
    由于是一个网站,对每个人的访问次数进行计数,业务要求规定插入一条必须commit;

    用测试程序1进行测试:
    插入100000条,平均每秒600条.

    用测试程序2进行测试:
    插入100000条,平均每秒1000条

    用测试程序3进行测试:
    将插入操作该用存储过程实现,在程序中调用该存储过程实现插入操作:
    插入100000条,平均每秒500条

    用测试程序4进行测试:
    用的测试程序同测试程序1,只不过将cursor_sharing参数设为similar
    插入100000条,平均每秒800条


    原因分析
    在Oracle 9.2.0.1中,默认情况下,cursor_sharing参数设为exact,这样由于每次插入语句只是在数值上不一样,但是Oracle还是会导致重新parse,耗费相当的时间。所以将插入语句改为用PreparedStatement方式实现,有效避免了重新parse,所以性能提高很快。同样道理将cursor_sharing参数设为similar也可以达到类似的功效。

    至于用存储过程来实现插入性能差的原因,可以这样分析:由于用该存储过程实现的任务并不多,只是简单的插入一条记录,此时,体现不出存储过程的好处,反而由于每插入一条就调用一次存储过程,反而会增加开销。即,如果用存储过程实现大数据量的访问或进行复杂的查询,才会体现出用存储过程的好处。


    至于sqlserver为什么插入100000条要比Oracle要快,我觉得与优化器才用的优化方法有关。如sqlserver默认就采用Oracle的cursor_sharing参数为force的模式进行执行sql语句。

    我还用存储过程在Oracle与sqlserver上做测试,同样的条件下,Oracle可以至少每秒插入5万条,远远大于sql server的(大约1万)。这也说明Oracle数据自身的处理能力是很强的。


    测试程序1:
    import java.sql.*;
    import java.net.*;
    import java.io.*;
    import java.util.*;
    //import oracle.jdbc.OracleConnection;


    public class FailOver {

      static final String user = "test";
      static final String password = "test";
      static final String driver_class = " 如何提高插入10万条记录的速度racle.jdbc.driver.OracleDriver";
      static final String URL = "jdbc:oracle:thin 如何提高插入10万条记录的速度192.168.11.180:1521:sensky";
      
      
    //    static final String user = "sa";
    //    static final String password = "sa";
    //    static final String driver_class = "com.microsoft.jdbc.sqlserver.SQLServerDriver";
    //   static final String URL = "jdbc:microsoft:sqlserver://192.168.11.172:1433;DatabaseName=wap";

      public static void main (String[] args) throws Exception {

        Connection conn = null;
        String     msg = null;
        Statement  stmt = null;
        ResultSet rset = null;

        // Load JDBC driver
        try {
          Class.forName(driver_class);
        }
        catch(Exception e) {
          System.out.println(e);
        }

        // Connect to the database
        conn = DriverManager.getConnection(URL, user, password);
    //        conn.setAutoCommit(false);

        // Create a Statement
        stmt = conn.createStatement ();
            Thread.sleep(100000);
            long start=System.currentTimeMillis();
            //conn.beginTrans();
        for (long i=800000; i<900000; i++) {
          // Select the NAMEs column from the EMPLOYEES table
          stmt.execute("INSERT INTO tbl_test(ID, name, age, addr) values("+ i +",'asdfasdfsa',22,'aaaaaaaaaaaaaa')");
          //stmt.execute("INSERT INTO tbl_test(ID, name, age, addr) values(100,'asdfasdfsa',22,'aaaaaaaaaaaaaa')");
    //        if (i % 2000 == 1)
    //        {
    //     conn.commit();
    //     }
        } // End for
    //    conn.commit();
        long end=System.currentTimeMillis();
        System.out.println("waste time:"+(end-start));
        // Close the Statement
        stmt.close();
        // Close the connection
        conn.close();
      } // End Main()
    }


    测试程序2:
    import java.sql.*;
    import java.net.*;
    import java.io.*;
    import java.util.*;
    import oracle.jdbc.OracleConnection;


    public class PreStat {

      static final String user = "test";
      static final String password = "test";
      static final String driver_class = " 如何提高插入10万条记录的速度racle.jdbc.driver.OracleDriver";
      static final String URL = "jdbc:oracle:thin 如何提高插入10万条记录的速度192.168.11.180:1521:sensky";


      public static void main (String[] args) throws Exception {

        Connection conn = null;
        String     msg = null;
        Statement  stmt = null;
        ResultSet rset = null;

        // Load JDBC driver
        try {
          Class.forName(driver_class);
        }
        catch(Exception e) {
          System.out.println(e);
        }

        // Connect to the database
        conn = DriverManager.getConnection(URL, user, password);
    //        conn.setAutoCommit(false);

        // Create a Statement
        PreparedStatement pstat = conn.prepareStatement("insert into tbl_test values (?, ? , ? , ?)");
           
            long start=System.currentTimeMillis();
            //conn.beginTrans();
        for (int i=0; i<100000; i++) {
          // Select the NAMEs column from the EMPLOYEES table
                  pstat.setInt(1, i);
                    pstat.setString(2, "aaaaaaaa");
                    pstat.setInt(3, i);
                    pstat.setString(4, "bbbbbbb");
                    pstat.executeUpdate(); //JDBC queues this for later execution
    //              conn.commit();
          // Sleep one second to make it possible to shutdown the DB.
        } // End for
    //    conn.commit();
        long end=System.currentTimeMillis();
        System.out.println("waste time:"+(end-start));
        // Close the Statement
        pstat.close();

        // Close the connection
        conn.close();


      } // End Main()

    } // End class jdemofo


    测试程序3:
    import java.sql.*;
    import java.net.*;
    import java.io.*;
    import java.util.*;
    //import oracle.jdbc.OracleConnection;


    public class PrceJava {

      static final String user = "test";
      static final String password = "test";
      static final String driver_class = " 如何提高插入10万条记录的速度racle.jdbc.driver.OracleDriver";
      static final String URL = "jdbc:oracle:thin 如何提高插入10万条记录的速度192.168.11.180:1521:sensky";
      
      
    //    static final String user = "sa";
    //    static final String password = "sa";
    //    static final String driver_class = "com.microsoft.jdbc.sqlserver.SQLServerDriver";
    //   static final String URL = "jdbc:microsoft:sqlserver://192.168.11.172:1433;DatabaseName=wap";

      public static void main (String[] args) throws Exception {

        Connection conn = null;
        String     msg = null;
        //Statement  stmt = null;
        ResultSet rset = null;

        // Load JDBC driver
        try {
          Class.forName(driver_class);
        }
        catch(Exception e) {
          System.out.println(e);
        }

        // Connect to the database
        conn = DriverManager.getConnection(URL, user, password);
    //        conn.setAutoCommit(false);

        // Create a Statement
        CallableStatement stmt = conn.prepareCall ("begin pro_ins (?,?,?,?,?); end;");
            long start=System.currentTimeMillis();
            //conn.beginTrans();
            int j;
        for (int i=0; i<100000; i++) {
          // Select the NAMEs column from the EMPLOYEES table
          //stmt.execute("INSERT INTO tbl_test(ID, name, age, addr) values("+ i +",'asdfasdfsa',22,'aaaaaaaaaaaaaa')");
          //stmt.execute("INSERT INTO tbl_test(ID, name, age, addr) values(100,'asdfasdfsa',22,'aaaaaaaaaaaaaa')");
                   
                    stmt.setInt(1, i);
                    stmt.setString(2, "aaaaaaaa");
                    stmt.setInt(3, i);
                    stmt.setString(4, "bbbbbbb");
                    stmt.registerOutParameter(5, Types.TINYINT);
                    stmt.execute();
                    //j = stmt.getInt(1);
        } // End for
    //    conn.commit();
        long end=System.currentTimeMillis();
        System.out.println("waste time:"+(end-start));
        // Close the Statement
        stmt.close();
        // Close the connection
        conn.close();
      } // End Main()
    }


    //创建存储过程的语句
    //create or replace procedure pro_ins(p_id in number,p_name in varchar2 , p_age in number , p_addr in varchar2,p_return out number)
    //as
    //begin
    //        INSERT INTO tbl_test(ID, name, age, addr) values(p_id,p_name,p_age,p_addr);
    //        p_return := 1;
    //end;
    ///

    //创建表的语句
    //create tabel tbl_test(ID number(10,0), name varchar2(50), age number(10,0), addr varchar2(50));
  • tengrid (2004-7-22 18:23:26)

    从你的测试程序看,好像有两个问题吧?
    1,在sqlserver上测试时是每600条提交一次.而oracle上则是1条提交一次.
    2, 另外,你用的是jdbc thin driver, 建议换成jdbc oci driver试试.