各種パラメータ変更によるH2のパフォーマンスへの影響
せっかくどのようなコードか書いてもらったので、H2でいくつか設定を変更しつつ試してみる。
実行環境は以下の通り。
OSX 10.8.2 MacBook 13-inch, Aluminum、Late 2008 2Ghz Intel Core2 Duo 8GB 1333MHz DDR3 HDD WesternDigital 320GB 7200回転 Eclipseから実行。 JREは、JavaSE-1.7(Java SE 7)で、1.7.0_05。 VM引数としては、明示的には -Xmx2048m -Xms2048m のみ。
結果としては、下記のような感じ。各設定毎に14回実行して、実行前に毎回データベースのディレクトリは削除している。
jdbc:h2:/Volumes/Macintosh HDD/test/db/h2 --(1) H2 10万件 4.38秒 1.48秒 H2 10万件 1.79秒 0.65秒 H2 10万件 1.74秒 0.51秒 H2 10万件 1.70秒 0.52秒 H2 10万件 1.65秒 0.53秒 H2 10万件 1.60秒 0.57秒 H2 10万件 1.69秒 0.55秒 H2 10万件 1.66秒 0.62秒 H2 10万件 1.59秒 0.52秒 H2 10万件 1.59秒 0.52秒 H2 10万件 1.55秒 0.53秒 H2 10万件 1.56秒 0.53秒 H2 10万件 1.56秒 0.56秒 H2 10万件 1.60秒 0.56秒 jdbc:h2:/Volumes/Macintosh HDD/test/db/h2;CACHE_SIZE=131072 --(2) H2 10万件 1.61秒 0.18秒 H2 10万件 1.69秒 0.15秒 H2 10万件 1.58秒 0.18秒 H2 10万件 1.59秒 0.18秒 H2 10万件 1.58秒 0.18秒 H2 10万件 1.72秒 0.18秒 H2 10万件 1.59秒 0.19秒 H2 10万件 1.56秒 0.17秒 H2 10万件 1.62秒 0.70秒 H2 10万件 1.66秒 0.18秒 H2 10万件 1.65秒 0.18秒 H2 10万件 1.60秒 0.18秒 H2 10万件 1.73秒 0.18秒 H2 10万件 1.74秒 0.18秒 jdbc:h2:/Volumes/Macintosh HDD/test/db/h2;CACHE_SIZE=131072;PAGE_SIZE=32768; --(3) H2 10万件 1.31秒 0.17秒 H2 10万件 1.37秒 0.16秒 H2 10万件 1.38秒 0.16秒 H2 10万件 1.77秒 0.32秒 H2 10万件 1.34秒 0.16秒 H2 10万件 1.36秒 0.25秒 H2 10万件 1.87秒 0.16秒 H2 10万件 1.47秒 0.17秒 H2 10万件 1.34秒 0.16秒 H2 10万件 1.72秒 0.16秒 H2 10万件 1.25秒 0.16秒 H2 10万件 1.35秒 0.19秒 H2 10万件 1.29秒 0.16秒 H2 10万件 1.35秒 0.16秒 jdbc:h2:/Volumes/Macintosh HDD/test/db/h2;CACHE_SIZE=131072;LOG=0;LOCK_MODE=0;UNDO_LOG=0; --(4) H2 10万件 0.39秒 0.15秒 H2 10万件 0.32秒 0.15秒 H2 10万件 0.32秒 0.14秒 H2 10万件 0.42秒 0.15秒 H2 10万件 0.32秒 0.14秒 H2 10万件 0.32秒 0.15秒 H2 10万件 0.33秒 0.15秒 H2 10万件 0.33秒 0.24秒 H2 10万件 0.32秒 0.14秒 H2 10万件 0.32秒 0.14秒 H2 10万件 0.32秒 0.15秒 H2 10万件 0.39秒 0.15秒 H2 10万件 0.32秒 0.15秒 H2 10万件 0.32秒 0.14秒 jdbc:h2:/Volumes/Macintosh HDD/test/db/h2;CACHE_SIZE=131072;LOG=0;LOCK_MODE=0;UNDO_LOG=0;PAGE_SIZE=32768; --(5) H2 10万件 0.28秒 0.17秒 H2 10万件 0.29秒 0.20秒 H2 10万件 0.28秒 0.12秒 H2 10万件 0.29秒 0.17秒 H2 10万件 0.37秒 0.12秒 H2 10万件 0.28秒 0.12秒 H2 10万件 0.36秒 0.12秒 H2 10万件 0.34秒 0.12秒 H2 10万件 0.29秒 0.12秒 H2 10万件 0.33秒 0.19秒 H2 10万件 0.33秒 0.13秒 H2 10万件 0.28秒 0.13秒 H2 10万件 0.37秒 0.17秒 H2 10万件 0.31秒 0.12秒 jdbc:h2:mem:;LOG=0;LOCK_MODE=0;UNDO_LOG=0; --(6) H2 10万件 0.28秒 0.26秒 H2 10万件 0.17秒 0.24秒 H2 10万件 0.17秒 0.22秒 H2 10万件 0.22秒 0.26秒 H2 10万件 0.18秒 0.22秒 H2 10万件 0.18秒 0.25秒 H2 10万件 0.18秒 0.23秒 H2 10万件 0.22秒 0.23秒 H2 10万件 0.23秒 0.22秒 H2 10万件 0.17秒 0.22秒 H2 10万件 0.17秒 0.23秒 H2 10万件 0.18秒 0.22秒 H2 10万件 0.17秒 0.28秒 H2 10万件 0.17秒 0.23秒 jdbc:h2:/SSD/test/db/h2;CACHE_SIZE=131072;LOG=0;LOCK_MODE=0;UNDO_LOG=0; --(7) H2 10万件 0.38秒 0.17秒 H2 10万件 0.46秒 0.14秒 H2 10万件 0.34秒 0.14秒 H2 10万件 0.37秒 0.15秒 H2 10万件 0.35秒 0.14秒 H2 10万件 0.36秒 0.22秒 H2 10万件 0.34秒 0.14秒 H2 10万件 0.35秒 0.13秒 H2 10万件 0.35秒 0.15秒 H2 10万件 0.35秒 0.14秒 H2 10万件 0.35秒 0.25秒 H2 10万件 0.35秒 0.14秒 H2 10万件 0.37秒 0.17秒 H2 10万件 0.34秒 0.13秒 jdbc:h2:/SSD/test/db/h2;CACHE_SIZE=131072;LOG=0;LOCK_MODE=0;UNDO_LOG=0;PAGE_SIZE=32768; --(8) H2 10万件 0.38秒 0.12秒 H2 10万件 0.28秒 0.12秒 H2 10万件 0.27秒 0.12秒 H2 10万件 0.30秒 0.13秒 H2 10万件 0.28秒 0.12秒 H2 10万件 0.33秒 0.13秒 H2 10万件 0.30秒 0.12秒 H2 10万件 0.28秒 0.12秒 H2 10万件 0.27秒 0.12秒 H2 10万件 0.31秒 0.15秒 H2 10万件 0.32秒 0.21秒 H2 10万件 0.37秒 0.13秒 H2 10万件 0.33秒 0.18秒 H2 10万件 0.39秒 0.14秒
初回以降が速いのは、JITの影響だろうか。
(1)は、まったくのデフォルトの状態。(2)で、キャッシュサイズを131072kbに設定している。これでおそらくキャッシュにデータが大方乗ったのか、selectが大分速くなった。(3)はページサイズを32768bytesに設定している。現行のH2では最大(デフォルトでは2048bytes)で、ページ分割の回数が減るからこういうケースではinsert時間が減るのでは、と思ったら20%程度速くなっている。selectにもう少し悪影響が出るかなと思ったらそうでもなかった。(4)では、トランザクションログ、ロック、アンドゥログをすべて無効にしているので速い(が、当然通常使用には適さない)。(5)はページサイズとログ周り無効の組み合わせ。ログ周り無効の影響に比べれば、ページサイズの調整は効果が少ない。(6)はオンメモリデータベース。insertは最速だが、selectが若干遅くなった? あんまり関係なさそうな気もするのだけれど。(7),(8)はSSD。このケースでは思ったより効果でず。キャッシュでオンメモリになっていて、あまりランダムアクセスが発生していないのでは、と推測。
設定は若干異なるが、下記が100万件で実施。元ブログよりも結構速いが、1000万件まで増やすととたんに返ってこなくなった。今度ちょっと突っ込んで調べてみよう。
jdbc:h2:/Volumes/Macintosh HDD/test/db/h2 H2 100万件 23.51秒 7.35秒 H2 100万件 18.32秒 6.49秒 H2 100万件 18.96秒 6.55秒 jdbc:h2:/Volumes/Macintosh HDD/test/db/h2;CACHE_SIZE=131072 H2 100万件 19.31秒 9.09秒 H2 100万件 19.09秒 8.44秒 H2 100万件 18.65秒 8.93秒 jdbc:h2:/Volumes/Macintosh HDD/test/db/h2;CACHE_SIZE=131072;LOG=0;LOCK_MODE=0;UNDO_LOG=0; H2 100万件 6.73秒 6.26秒 H2 100万件 6.66秒 6.76秒 H2 100万件 9.26秒 6.62秒 jdbc:h2:/Volumes/Macintosh HDD/test/db/h2;CACHE_SIZE=131072;LOG=0;LOCK_MODE=0;UNDO_LOG=0;PAGE_SIZE=32768; H2 100万件 9.25秒 4.33秒 H2 100万件 9.17秒 5.11秒 H2 100万件 8.76秒 5.00秒 jdbc:h2:mem:;LOG=0;LOCK_MODE=0;UNDO_LOG=0; H2 100万件 2.24秒 3.59秒 H2 100万件 3.13秒 2.99秒 H2 100万件 2.37秒 4.19秒 jdbc:h2:/SSD/test/db/h2;CACHE_SIZE=131072;LOG=0;LOCK_MODE=0;UNDO_LOG=0;PAGE_SIZE=32768; H2 100万件 9.82秒 4.85秒 H2 100万件 9.83秒 4.43秒 H2 100万件 11.76秒 5.06秒
ソースコードは下記の通り。
package test; import java.io.File; import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.Statement; import org.junit.Test; public class JdbcTest { @Test public void h2_00() throws Exception { String url = "jdbc:h2:/Volumes/Macintosh HDD/test/db/h2"; System.out.println(url); for (int i = 0; i < TRY_COUNT; i++) { before(); con = DriverManager.getConnection(url); Statement st = con.createStatement(); executeUpdate(st, "drop table if exists person"); executeUpdate(st, "create table person (id integer primary key, name varchar)"); executeQuery(); after(); } } @Test public void h2_01() throws Exception { String url = "jdbc:h2:/Volumes/Macintosh HDD/test/db/h2;CACHE_SIZE=131072"; System.out.println(url); for (int i = 0; i < TRY_COUNT; i++) { before(); con = DriverManager.getConnection(url); Statement st = con.createStatement(); executeUpdate(st, "drop table if exists person"); executeUpdate(st, "create table person (id integer primary key, name varchar)"); executeQuery(); after(); } } @Test public void h2_02() throws Exception { String url = "jdbc:h2:/Volumes/Macintosh HDD/test/db/h2;CACHE_SIZE=131072;PAGE_SIZE=32768;"; System.out.println(url); for (int i = 0; i < TRY_COUNT; i++) { before(); con = DriverManager.getConnection(url); Statement st = con.createStatement(); executeUpdate(st, "drop table if exists person"); executeUpdate(st, "create table person (id integer primary key, name varchar)"); executeQuery(); after(); } } @Test public void h2_03() throws Exception { String url = "jdbc:h2:/Volumes/Macintosh HDD/test/db/h2;CACHE_SIZE=131072;LOG=0;LOCK_MODE=0;UNDO_LOG=0;"; System.out.println(url); for (int i = 0; i < TRY_COUNT; i++) { before(); con = DriverManager.getConnection(url); Statement st = con.createStatement(); executeUpdate(st, "drop table if exists person"); executeUpdate(st, "create table person (id integer primary key, name varchar)"); executeQuery(); after(); } } @Test public void h2_04() throws Exception { String url = "jdbc:h2:/Volumes/Macintosh HDD/test/db/h2;CACHE_SIZE=131072;LOG=0;LOCK_MODE=0;UNDO_LOG=0;PAGE_SIZE=32768;"; System.out.println(url); for (int i = 0; i < TRY_COUNT; i++) { before(); con = DriverManager.getConnection(url); Statement st = con.createStatement(); executeUpdate(st, "drop table if exists person"); executeUpdate(st, "create table person (id integer primary key, name varchar)"); executeQuery(); after(); } } @Test public void h2_05() throws Exception { String url = "jdbc:h2:mem:;LOG=0;LOCK_MODE=0;UNDO_LOG=0;"; System.out.println(url); for (int i = 0; i < TRY_COUNT; i++) { before(); con = DriverManager.getConnection(url); Statement st = con.createStatement(); executeUpdate(st, "drop table if exists person"); executeUpdate(st, "create table person (id integer primary key, name varchar)"); executeQuery(); after(); } } @Test public void h2_06() throws Exception { String url = "jdbc:h2:/SSD/test/db/h2;CACHE_SIZE=131072;LOG=0;LOCK_MODE=0;UNDO_LOG=0;"; System.out.println(url); for (int i = 0; i < TRY_COUNT; i++) { before("/Users/ayataro/test/db"); con = DriverManager.getConnection(url); Statement st = con.createStatement(); executeUpdate(st, "drop table if exists person"); executeUpdate(st, "create table person (id integer primary key, name varchar)"); executeQuery(); after(); } } @Test public void h2_07() throws Exception { String url = "jdbc:h2:/SSD/test/db/h2;CACHE_SIZE=131072;LOG=0;LOCK_MODE=0;UNDO_LOG=0;PAGE_SIZE=32768;"; System.out.println(url); for (int i = 0; i < TRY_COUNT; i++) { before("/Users/ayataro/test/db"); con = DriverManager.getConnection(url); Statement st = con.createStatement(); executeUpdate(st, "drop table if exists person"); executeUpdate(st, "create table person (id integer primary key, name varchar)"); executeQuery(); after(); } } // 共通メンバー ------------------------------------------- private Connection con; private static int COUNT = 10000 * 10; private static int TRY_COUNT = 14; private static final String DATA = "12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890"; public void before() throws Exception { before("/Volumes/Macintosh HDD/test/db"); } public void before(String path) throws Exception { File baseDir = new File(path); for(File f : baseDir.listFiles()){ f.delete(); } baseDir.delete(); baseDir.mkdir(); } public void after() throws Exception { if (con != null) { con.close(); } } private void executeUpdate(Statement st, String sql) { try { st.executeUpdate(sql); } catch (Exception e) { System.out.println(e.toString()); } } private void executeQuery() throws Exception { executeQuery(con.getMetaData().getDatabaseProductName()); } private void executeQuery(String databaseName) throws Exception { boolean isCassandra = databaseName.contains("Cassandra"); boolean isAutoCommit = isCassandra; System.out.printf("%-14s", databaseName); if (!isAutoCommit) { con.setAutoCommit(false); } long insertStart = System.currentTimeMillis(); PreparedStatement insertPs = con .prepareStatement("insert into person (id, name) values(?, ?)"); for (int i = 0; i < COUNT; i++) { insertPs.setInt(1, i); insertPs.setString(2, DATA); insertPs.executeUpdate(); if (!isAutoCommit && i % 10000 == 0) { con.commit(); } } if (!isAutoCommit) { con.commit(); } double insertSec = (double) (System.currentTimeMillis() - insertStart) / 1000; long selectStart = System.currentTimeMillis(); PreparedStatement selectPs = con .prepareStatement("select * from person where id = ?"); for (int i = 0; i < COUNT; i++) { selectPs.setInt(1, i); selectPs.executeQuery().next(); } double selectSec = (double) (System.currentTimeMillis() - selectStart) / 1000; String countSql = "select count(1) from person"; if (isCassandra) { countSql += " limit 100000000"; } ResultSet rs = con.createStatement().executeQuery(countSql); rs.next(); logProcessTime(rs.getInt(1), insertSec, selectSec); } private void logProcessTime(long count, double insertSec, double selectSec) { System.out.printf("%4d万件 ", count / 10000); System.out.printf("%7.2f秒 ", insertSec); System.out.printf("%7.2f秒", selectSec); System.out.println(); } }