如何将数据持久保存到磁盘,并随机更新它,并将其有效地流式传输回 RAM?
Posted
技术标签:
【中文标题】如何将数据持久保存到磁盘,并随机更新它,并将其有效地流式传输回 RAM?【英文标题】:How do I persist data to disk, and both randomly update it, and stream it efficiently back into RAM? 【发布时间】:2009-08-13 21:04:08 【问题描述】:我需要在磁盘上存储多达数千万甚至数亿条数据。每条数据都包含如下信息:
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 查找)?
【问题讨论】:
“DB4O 不是一个选项,因为它是 GPL 而我的其余代码不是” - 只有在您计划分发代码时才重要。 我确实计划分发我的代码 【参考方案1】:H2 怎么样? License 应该适合你。
您可以免费使用 H2。你可以 将其集成到您的应用程序中 (包括商业应用), 你可以分发它。 文件 只包含你的代码不是 本许可证涵盖(它是 '商业友好')。 修改 到H2源代码必须是 发表。 您无需提供 如果你没有 H2 的源代码 修改任何东西。我明白了
1000000 次插入需要 22492 毫秒(44460.252534234394 行/秒)
100000 次更新在 9565 毫秒内(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();
【讨论】:
【参考方案2】:JDBM 是一个出色的 Java 嵌入式数据库(不像 Berkley 的 Java 版本那样受许可的限制)。值得一试。如果您不需要 ACID 保证(即您可以在发生崩溃时数据库损坏),请关闭事务管理器(显着提高速度)。
【讨论】:
【参考方案3】:我认为,如果您编写一些将最活跃的记录缓存在内存中并将数据更改作为低优先级插入到数据库中的队列,那么您会取得更大的成功。
我知道使用这种方法会稍微增加 IO,但如果您谈论的是数百万条记录,我认为它仍然会更快,因为您创建的任何搜索算法都将大大优于成熟的数据库引擎。
【讨论】:
【参考方案4】:您可以尝试现在由 Oracle 拥有的 Berkley DB。他们拥有开源和商业许可证。它使用键/值模型(如果需要其他形式的查询,可以选择创建索引)。有纯 Java 版本和带有 Java 绑定的本机版本。
【讨论】:
我希望我能找到免费的东西,不幸的是 Berkeley DB 不是,除非我愿意 GPL 我的代码,这不是一个选择。【参考方案5】:http://www.zentus.com/sqlitejdbc/
SQLite 数据库(公共域),带有 BSD 许可证的 JDBC 连接器,适用于各种平台(OSX、Linux、Windows),其余的可以仿真。
【讨论】:
【参考方案6】:您可以使用与 JDK 捆绑在一起的 Apache Derby(或 JavaDB)。但是,如果 DBMS 不能提供所需的速度,您可以自己实现特定的文件结构。如果只需要精确的键查找,您可以使用哈希文件来实现它。哈希文件是满足此类要求的最快文件结构(比数据库中使用的 B 树和网格等通用文件结构快得多)。它还提供了可接受的流传输效率。
【讨论】:
【参考方案7】:最后我决定在数据进入时将其记录到磁盘,并将其保存在内存中以便我可以更新它。一段时间后,我将数据写入磁盘并删除日志。
【讨论】:
【参考方案8】:您看过 Oracle 的“TimesTen”数据库吗?它是一个内存数据库,应该是非常高性能的。不知道成本/许可证等,但请查看 Oracles 站点并搜索它。评估下载应该可用。
【讨论】:
【参考方案9】:我还想看看是否存在任何基于 EHCache 或 JCS 的东西可能会有所帮助。
【讨论】:
以上是关于如何将数据持久保存到磁盘,并随机更新它,并将其有效地流式传输回 RAM?的主要内容,如果未能解决你的问题,请参考以下文章