Hi,

I've tested performance of H2 on the insertion and deleting of ~1M
records.  (java source code is in the end of the message).

Test stend configuration:
Windows 7, NTFS
Java(TM) SE Runtime Environment (build 1.6.0_13-b03)
Java HotSpot(TM) Client VM (build 11.3-b02, mixed mode, sharing)

Custom H2 options:
MAX_MEMORY_UNDO=120000
MAX_LOG_SIZE=256
MAX_OPERATION_MEMORY=4194304
WRITE_DELAY=0

H2 speed [build-131]:
INSERT: 115706 ms
DELETE: 103225 ms

For comparison the speed of DERBY using the same data:
INSERT: 135150 ms
DELETE: 53156 ms

Profiler: top 3 stack trace(s) of 220109 ms [build-131]
1512/4258
at java.io.WinNTFileSystem.getLastModifiedTime(Native Method)
at java.io.File.lastModified(File.java:826)
at org.h2.store.fs.FileSystemDisk.getLastModified(FileSystemDisk.java:
278)
at org.h2.util.IOUtils.getLastModified(IOUtils.java:678)
at org.h2.util.TempFileDeleter.updateAutoDelete(TempFileDeleter.java:
79)
at org.h2.store.FileStore.autoDelete(FileStore.java:466)
at org.h2.engine.UndoLog.add(UndoLog.java:133)
at org.h2.engine.Session.log(Session.java:603)
at org.h2.engine.Session.log(Session.java:588)
at org.h2.command.dml.Insert.insertRows(Insert.java:121)
at org.h2.command.dml.Insert.update(Insert.java:82)
at org.h2.command.CommandContainer.update(CommandContainer.java:70)
at org.h2.command.Command.executeUpdate(Command.java:198)
at
org.h2.jdbc.JdbcPreparedStatement.executeUpdateInternal(JdbcPreparedStatement.java:
141)
at
org.h2.jdbc.JdbcPreparedStatement.executeUpdate(JdbcPreparedStatement.java:
127)
at TestH2.testSql(TestH2.java:52)
1451/4258
at java.io.WinNTFileSystem.getLastModifiedTime(Native Method)
at java.io.File.lastModified(File.java:826)
at org.h2.store.fs.FileSystemDisk.getLastModified(FileSystemDisk.java:
278)
at org.h2.util.IOUtils.getLastModified(IOUtils.java:678)
at org.h2.util.TempFileDeleter.updateAutoDelete(TempFileDeleter.java:
79)
at org.h2.store.FileStore.autoDelete(FileStore.java:466)
at org.h2.engine.UndoLog.add(UndoLog.java:133)
at org.h2.engine.Session.log(Session.java:603)
at org.h2.engine.Session.log(Session.java:588)
at org.h2.command.dml.Delete.update(Delete.java:74)
at org.h2.command.CommandContainer.update(CommandContainer.java:70)
at org.h2.command.Command.executeUpdate(Command.java:198)
at
org.h2.jdbc.JdbcPreparedStatement.executeUpdateInternal(JdbcPreparedStatement.java:
141)
at
org.h2.jdbc.JdbcPreparedStatement.executeUpdate(JdbcPreparedStatement.java:
127)
at TestH2.testSql(TestH2.java:60)
at TestH2.testH2(TestH2.java:35)
158/4258
at java.io.RandomAccessFile.writeBytes(Native Method)
at java.io.RandomAccessFile.write(RandomAccessFile.java:466)
at org.h2.store.FileStore.write(FileStore.java:333)
at org.h2.engine.UndoLogRecord.save(UndoLogRecord.java:160)
at org.h2.engine.UndoLog.saveIfPossible(UndoLog.java:139)
at org.h2.engine.UndoLog.add(UndoLog.java:131)
at org.h2.engine.Session.log(Session.java:603)
at org.h2.engine.Session.log(Session.java:588)
at org.h2.command.dml.Delete.update(Delete.java:74)
at org.h2.command.CommandContainer.update(CommandContainer.java:70)
at org.h2.command.Command.executeUpdate(Command.java:198)
at
org.h2.jdbc.JdbcPreparedStatement.executeUpdateInternal(JdbcPreparedStatement.java:
141)
at
org.h2.jdbc.JdbcPreparedStatement.executeUpdate(JdbcPreparedStatement.java:
127)
at TestH2.testSql(TestH2.java:60)
at TestH2.testH2(TestH2.java:35)
at TestH2.main(TestH2.java:18)
.

Similar situation is observed in Windows 2003 and Windows XP.

I've made a build with quick&dirty hack. I commented out the call of
lastModified method.
And got this rates:
INSERT: 32437 ms
DELETE: 23276 ms

The speed of operations was increased ~4-5 times!

I propose to get rid of checking the temporary files with
lastModified.
Instead, use the guaranteed unique names.
Now file names are generated like this:
‘databaseName’ + [random] + ‘.temp.db’
I suggest doing it like this:
‘databaseName.’ + [counter] + ’.’ + [random] + ‘.temp.db’

[counter] can look like this:


   private static final Object CNT_LOCK = new Object();
   private static int CNT = 0;

   private static int nextTempCounter() {
       synchronized(CNT_LOCK) {
           return ++CNT;
       }
   }

Or for Java 5 and above:

   private static final AtomicInteger CNT = new AtomicInteger();

   private static int nextTempCounter() {
       return CNT.incrementAndGet();
   }

It can be put either in IOUtils or FileSystem.

It remains to remove possible conflicts between different instances of
engines in
two cases:
1) When working with database in FileLock.LOCK_SERIALIZED mode. We can
add uniqueId from FileLock to name of a temporary file.
2) When working with the same database in zip-file. May be it would be
possible to create a separate unique subfolder tmp for each db engine?
Or we should leave lastModified mechanism only for files created in
tmp.

--- TestH2.java

import org.h2.util.Profiler;
import java.sql.*;

public class TestH2 {

    private static final int _1M = 1000000;
    private static final String CREATE = "CREATE TABLE TEST " +
            "(id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY," +
            "a VARCHAR(256), b VARCHAR(256), c VARCHAR(256))";
    private static final String INSERT = "INSERT INTO TEST (a,b,c)
VALUES ('aaa', 'bbbb', 'ccccc')";
    private static final String DELETE = "DELETE FROM TEST";

    public static void main(String args[]) throws Exception {
        if(args.length!=2) {
            System.out.println("usage: TestH2 [-h2|-derby]
databaseName");
            System.exit(-1);
        }
        String db = args[0];
        String dbanme = args[1];
        if ("-h2".equals(db)) {
            testH2(dbanme);
        } else if ("-derby".equals(db)) {
            testDerby(dbanme);
        }
    }

    private static void testDerby(String dbname) throws
ClassNotFoundException, SQLException {
        System.out.println("=== DERBY");
        Class.forName("org.apache.derby.jdbc.EmbeddedDriver");
        testSql("jdbc:derby:" + dbname + ";create=true");
    }

    private static void testH2(String dbname) throws
ClassNotFoundException, SQLException {
        System.out.println("=== H2");
        Class.forName("org.h2.Driver");
        Profiler profiler = new Profiler();
        profiler.startCollecting();
        testSql("jdbc:h2:" + dbname);
        profiler.stopCollecting();
        System.out.println(profiler.getTop(3));
    }

    private static void testSql(String url) throws SQLException {

        Connection conn = DriverManager.getConnection(url);

        Statement st = conn.createStatement();
        st.executeUpdate(CREATE);
        st.close();

        conn.setAutoCommit(false);
        long t = System.currentTimeMillis();
        PreparedStatement ps = conn.prepareStatement(INSERT);
        for (int i = 0; i < _1M; i++) {
            ps.executeUpdate();
        }
        conn.commit();
        ps.close();
        System.out.println("INSERT: " + (System.currentTimeMillis() -
t) + " ms");

        t = System.currentTimeMillis();
        ps = conn.prepareStatement(DELETE);
        ps.executeUpdate();
        ps.close();
        conn.commit();
        System.out.println("DELETE: " + (System.currentTimeMillis() -
t) + " ms");

        conn.close();
    }
}

-- 
You received this message because you are subscribed to the Google Groups "H2 
Database" group.
To post to this group, send email to h2-datab...@googlegroups.com.
To unsubscribe from this group, send email to 
h2-database+unsubscr...@googlegroups.com.
For more options, visit this group at 
http://groups.google.com/group/h2-database?hl=en.

Reply via email to