如何将数据保存到磁盘,并且随机更新数据并将其高效地传回RAM?
我需要在磁盘上存储多达数十甚至数百万的数据。每条数据包含如下信息:如何将数据保存到磁盘,并且随机更新数据并将其高效地传回RAM?
id=23425
browser=firefox
ip-address=10.1.1.1
outcome=1.0
新的数据片段可以以每毫秒最多1个的速率添加。
所以它是一组相对简单的键值对,其中的值可以是字符串,整数或浮点数。有时候我可能需要用特定的id更新数据片段,将标志字段从0更改为1.换句话说,我需要能够通过id进行随机密钥查找,并修改数据(实际上只是浮点数点“结果”字段 - 所以我永远不需要修改值的大小)。
另一个要求是我需要能够有效地从磁盘上流这个数据(顺序并不特别重要)。这意味着硬盘磁头不需要绕过磁盘来读取数据,而应该在连续的磁盘块中读取数据。
我正在用Java写这篇文章。
我想过使用嵌入式数据库,但DB4O不是一个选项,因为它是GPL,而我的代码的其余部分不是。考虑到向SQL查询转换和从SQL查询转换的开销,我还担心使用嵌入式SQL数据库的效率。
有没有人有任何想法?可能我必须为此构建一个自定义解决方案(我直接与ByteBuffers打交道,并处理id查找)?
最后,我决定将数据记录到磁盘中,并将它保存在内存中,以便我可以更新它。一段时间后,我将数据写入磁盘并删除日志。
我想你会有更多的成功,写一些缓存内存中最活跃的记录和队列数据更改作为低优先级插入到数据库中。
我知道使用这种方法的IO有轻微的增加,但是如果你在谈论数百万条记录,我认为它会更快,因为你创建的任何搜索算法都将大大超过完全成熟的数据库引擎。
您可以试试,现在Oracle拥有它。他们拥有开源和商业许可证。它使用键/值模型(如果需要其他形式的查询,可以选择创建索引)。有一个纯Java版本和一个带有Java绑定的本地版本。
我希望我能找到一些免费的东西,不幸的是,除非我愿意GPL我的代码,否则Berkeley DB不是一种选择。 – sanity 2009-08-14 03:43:09
http://www.zentus.com/sqlitejdbc/
SQLite数据库(公共领域),JDBC连接器与BSD许可证,原生了一大堆的平台(OSX,Linux和Windows)中,仿真的休息。
- 您可以免费使用H2。您可以将 集成到您的应用程序 (包括商业应用程序), ,您可以分发它。
- 文件 只包含你的代码是不是 该许可证所覆盖(这是 “商业友好”)。
- 修改为 的H2源代码必须为 发布。
- 如果你没有 修改任何东西,你不需要提供H2的源代码 。
我得到了22492ms(44460.252534234394行/秒)
100000更新在9565ms(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();
}
}
}
百万插入
我会所以请看看是否存在基于EHCache或JCS的任何可能的帮助。
您可以使用与JDK捆绑在一起的Apache Derby(或JavaDB)。但是,如果DBMS不提供所需的速度,则可以自己实现特定的文件结构。如果只需要精确的密钥查找,则可以使用散列文件来实现它。散列文件是这种需求的最快文件结构(比通用文件结构(例如DB中使用的B树和网格)快得多)。它还提供可接受的流媒体效率。
JDBM是一个伟大的Java嵌入式数据库(并不像Berkley的Java版本那样受到许可限制)。这将是值得尝试的。如果您不需要ACID保证(即在发生崩溃时数据库可能损坏),请关闭事务管理器(显着提高速度)。
你看过Oracle的'TimesTen'数据库吗?它的内存分贝应该是非常高性能的。不知道费用/许可证等,但看看Oracles网站并搜索它。评估下载应该可用。
“DB4O不是一个选项,因为它是GPL,我的其他代码不是” - 只有在您计划分发代码时才重要。 – 2009-08-14 03:13:25
我打算分发我的代码 – sanity 2009-08-14 03:42:18