2

我需要在磁盘上存储多达数千万甚至数亿条数据。每条数据都包含以下信息:

id=23425
browser=firefox
ip-address=10.1.1.1
outcome=1.0

可以以每毫秒最多 1 条的速度添加新数据。

所以它是一组相对简单的键值对,其中的值可以是字符串、整数或浮点数。有时我可能需要用特定的 id 更新一条数据,将 flag 字段从 0 更改为 1。换句话说,我需要能够通过 id 进行随机键查找,并修改数据(实际上只有浮动点“结果”字段 - 所以我永远不需要修改值的大小)。

另一个要求是我需要能够有效地从磁盘流式传输这些数据(顺序不是特别重要)。这意味着硬盘磁头不需要在磁盘周围跳跃来读取数据,而是应该在连续的磁盘块中读取。

我正在用 Java 写这个。

我曾考虑过使用嵌入式数据库,但 DB4O 不是一个选项,因为它是 GPL,而我的其余代码不是。考虑到与 SQL 查询之间的转换开销,我还担心使用嵌入式 SQL 数据库的效率。

有没有人有任何想法?我是否必须为此构建一个自定义解决方案(我直接处理 ByteBuffers,并处理 id 查找)?

4

9 回答 9

2

H2怎么样?许可证应该适合您。

  • 您可以免费使用 H2。您可以将其集成到您的应用程序(包括商业应用程序)中,并且可以分发它。
  • 此许可证不涵盖仅包含您的代码的文件(它是“商业友好的”)。
  • 必须发布对 H2 源代码的修改。
  • 如果您没有修改任何内容,则无需提供 H2 的源代码。

我明白了

1000000 次插入 22492 毫秒(44460.252534234394 行/秒)

9565 毫秒内 100000 次更新(10454.783063251438 行/秒)

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.Random;


/**
 * @author clint
 *
 */
public class H2Test {

  static int testrounds = 1000000;

  public static void main(String[] args) {
    try {
      Class.forName("org.h2.Driver");

    Connection conn = DriverManager.
        getConnection("jdbc:h2:/tmp/test.h2", "sa", "");
    // add application code here
    conn.createStatement().execute("DROP TABLE IF EXISTS TEST");
    conn.createStatement().execute("CREATE TABLE IF NOT EXISTS TEST(id INT PRIMARY KEY, browser VARCHAR(64),ip varchar(16), outcome real)"); 
    //conn.createStatement().execute("CREATE INDEX IDXall ON TEST(id,browser,ip,outcome");


    PreparedStatement ps = conn.prepareStatement("insert into TEST (id, browser, ip, outcome) values (?,?,?,?)");
    long time = System.currentTimeMillis();
    for ( int i = 0; i < testrounds; i++ ) {
      ps.setInt(1,i);
      ps.setString(2,"firefox");
      ps.setString(3,"000.000.000.000");
      ps.setFloat(4,0);
      ps.execute();
    }
    long last = System.currentTimeMillis() ;
    System.out.println( testrounds + " insert in " + (last - time) + "ms (" + ((testrounds)/((last - time)/1000d)) + " row/sec)" );

    ps.close();
    ps = conn.prepareStatement("update TEST set outcome = 1 where id=?");
    Random random = new Random();
    time = System.currentTimeMillis();

    /// randomly updadte 10% of the entries
    for ( int i = 0; i < testrounds/10; i++ ) {
      ps.setInt(1,random.nextInt(testrounds));
      ps.execute();
    }

    last = System.currentTimeMillis();
    System.out.println( (testrounds/10) + " updates in " + (last - time) + "ms (" + ((testrounds/10)/((last - time)/1000d)) + " row/sec)" );

    conn.close();

    } catch (ClassNotFoundException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    } catch (SQLException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    }
  }

}
于 2009-08-13T22:19:25.197 回答
1

JDBM是一个出色的 Java 嵌入式数据库(不像 Berkley 的 Java 版本那样受许可的限制)。值得一试。如果您不需要 ACID 保证(即您可以在发生崩溃时数据库损坏),请关闭事务管理器(显着提高速度)。

于 2009-08-14T03:09:47.783 回答
0

我认为你会更成功地编写一些东西来缓存内存中最活跃的记录并将数据更改作为低优先级插入到数据库中排队。

我知道使用这种方法会稍微增加 IO,但如果您谈论的是数百万条记录,我认为它仍然会更快,因为您创建的任何搜索算法都将大大优于成熟的数据库引擎。

于 2009-08-13T21:14:43.870 回答
0

您可以尝试现在由 Oracle 拥有的Berkley DB 。他们拥有开源和商业许可证。它使用键/值模型(如果需要其他形式的查询,可以选择创建索引)。有纯 Java 版本和带有 Java 绑定的本机版本。

于 2009-08-13T21:17:21.273 回答
0

http://www.zentus.com/sqlitejdbc/

SQLite 数据库(公共领域),具有 BSD 许可证的 JDBC 连接器,适用于一大堆平台(OSX、Linux、Windows),其余为仿真。

于 2009-08-13T21:45:42.703 回答
0

您可以使用与 JDK 捆绑在一起的 Apache Derby(或 JavaDB)。但是,如果 DBMS 不能提供所需的速度,您可以自己实现特定的文件结构。如果只需要精确的键查找,您可以使用哈希文件来实现它。哈希文件是满足此类要求的最快文件结构(比数据库中使用的 B 树和网格等通用文件结构快得多)。它还提供了可接受的流传输效率。

于 2009-08-14T00:59:37.753 回答
0

最后,我决定在数据进入时将其记录到磁盘,并将其保存在内存中以便我可以更新它。一段时间后,我将数据写入磁盘并删除日志。

于 2009-08-27T18:53:37.717 回答
0

您看过 Oracle 的“TimesTen”数据库吗?它是一个内存数据库,应该是非常高性能的。不知道成本/许可证等,但请查看 Oracles 网站并搜索它。评估下载应该可用。

于 2009-09-02T17:38:52.283 回答
-1

我还想看看是否有任何基于 EHCache 或 JCS 的东西可能会有所帮助。

于 2009-08-13T22:38:01.737 回答