如何将数据持久保存到磁盘,并随机更新它,并将其有效地流回 RAM?
-
16-09-2019 - |
题
我需要在磁盘上存储多达数千万甚至数亿条数据。每条数据都包含以下信息:
id=23425
browser=firefox
ip-address=10.1.1.1
outcome=1.0
新数据的添加速度可达每毫秒 1 条。
因此它是一组相对简单的键值对,其中值可以是字符串、整数或浮点数。有时我可能需要更新具有特定 id 的数据,将标志字段从 0 更改为 1。换句话说,我需要能够通过 id 进行随机键查找,并修改数据(实际上只有浮点“结果”字段 - 所以我永远不需要修改值的大小)。
另一个要求是我需要能够有效地从磁盘流式传输这些数据(顺序并不是特别重要)。这意味着硬盘磁头不应该需要在磁盘上跳跃来读取数据,而是应该在连续的磁盘块中读取。
我正在用 Java 写这个。
我考虑过使用嵌入式数据库,但 DB4O 不是一个选项,因为它是 GPL,而我的其余代码不是。考虑到 SQL 查询之间的转换开销,我还担心使用嵌入式 SQL 数据库的效率。
有人有什么想法吗?我是否必须为此构建一个自定义解决方案(我直接处理 ByteBuffers 并处理 id 查找)?
解决方案 7
在最后,我决定于数据记录到磁盘,因为它进入,并且还保持在存储器哪里可以更新它。一段时间后,我将数据写入到磁盘和删除日志。
其他提示
- 您可以免费使用H2。您可以 将其集成到您的应用程序中 (包括商业应用), 你可以分发它。
- 文件 只包含你的代码不是 本许可证涵盖(它是 “商业友好”)。
- 必须发布对H2源代码的修改。
- 您无需提供 H2 的源代码(如果没有) 修改任何内容。
我明白了
22492 毫秒内插入 1000000 次(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();
}
}
}
JDBM 是为Java很大嵌入式数据库(并且不与许可作为担保的Java版本伯克利)。这将是值得一试。如果你不需要ACID担保(即你是与数据库OK越来越损坏在发生碰撞的情况下),关闭事务管理器(显著增加速度)。
我想你会拥有更多的成功写的东西,在内存中缓存和队列数据变化最活跃的记录为低优先级插入到数据库。
据我所知有使用这种方法在IO略有增加,但如果你正在谈论数百万条记录,我认为它仍然是速度更快,因为你创建的任何搜索算法将是由AA完全成熟的数据库引擎可以大大跑赢。
您可以尝试这是现在属于Oracle DB伯克利 。他们有开源和商业许可证。它使用一个键/值模型(用一种选择,如果需要其他形式的查询创建索引)。有一个纯Java版本,并用Java绑定的本地版本。
http://www.zentus.com/sqlitejdbc/
SQLite数据库(公共结构域),JDBC连接器与BSD许可证,原生用于一大堆平台(OSX,Linux和Windows)中,仿真的其余部分。
可以使用的捆绑JDK的Apache德比(或JavaDB之外)。但是,如果一个DBMS不提供所需的速度,你可以自己实现一个特定的文件结构。如果需要精确刚刚键查找,您可以使用一个散列文件来实现它。哈希文件是这样的要求(除了通用文件结构,例如其在使用的DB B-树和网格快得多)最快的文件结构。它还提供了可接受的流效率。
您拍照时看看甲骨文的“TimesTen的”数据库?它在内存中的数据库被认为是非常高的性能。不知道成本/许可证等,但看看甲骨文网站和搜索。 EVAL下载应该是可用的。
我也想看看,看看是否有现有的基础上的EHCache两种或JCS任何可能的帮助。