JAVA缺陷,什么时候可以弥补
用Java读文件实现这么简单的要求都不行:我要读一大文件,300万行,我想将文件指针移动到指定行,读取指定内容。一句话,就是读取指定行的内容。
看了很多API,都没有该功能,RandomAccessFile ,LineNumberReader等类都没用,最多能实现的是,跳过N个字符。
特别是LineNumberReader类,不知道有什么用,骗人的setLineNumber方法,根本无法改变文件指针。
大家有什么方案吗 --------------------编程问答-------------------- 内存映射文件 --------------------编程问答-------------------- JSR(Java 规范请求)是指向JCP(Java Community Process)提出新增一个标准化技术规范的正式请求。任何人都可以提交JSR(Java 规范请求),以向Java平台增添新的API和服务。JSR已成为Java界的一个重要标准。
楼主可以试试 --------------------编程问答--------------------
兄弟有方法吗,能否说详细点 --------------------编程问答-------------------- 我现在想了个办法,就是让文件每行都一样多的数据,这样利用seek方法就可以间接实现跳到指定行,不过插入更新数据的时候就不爽了,麻烦。 --------------------编程问答-------------------- 看下源代码,不行自己改了再编译啊 --------------------编程问答-------------------- 我之所以没有选择数据库,就是想自己写文件可能速度快,因为数据库也是写文件。直接写文件可能效率高,但是JAVA没有这个功能好麻烦,如果数据库也这么循环读的话,那不慢死了 --------------------编程问答-------------------- LineNumberReader这个类真有点忽悠人 --------------------编程问答-------------------- 占个地儿,学习。 --------------------编程问答-------------------- 这个文件格式你是否可以适当修改?
把一个大文件拆分成若干小文件,比如1000行一个,文件名后缀代表开始位置。
然后读某一特定行的时候,先打开对应的文件,然后跳过相应行数(用readLine()) --------------------编程问答-------------------- 晕,就是那个1ms的问题?
你这个方案很不好,我想速度能达到1S就不错了。 --------------------编程问答-------------------- 哈哈,楼上的兄弟,也是搞JAVA的吗?我本来想在那边问,但是我怕那边是MYSQL板块。 --------------------编程问答--------------------
不错,我也这么想的,这其实就是类似数据库索引的机制,像MYSQL的2进制日志文件就是这么处理的,不过1000行一个文件的话,那我不是要3000个文件。。。。是否太多了。
--------------------编程问答--------------------
jAVA里的readLine()只是读取某行的数据,没法实现跳过多少行????只能跳过多少字节。 --------------------编程问答-------------------- 帮你顶 --------------------编程问答-------------------- 行?
只不过是按换行符分隔而已.
读第几行本质上和读第几个'a'也没有区别.能跳过byte或char难道不是提供了跳过行?
java没有直接API,其他语言有?
除了ed那种行处理程序,好像都不会有吧?
要不,lz用shell吧,shell干这个事情比java搞效得多. --------------------编程问答-------------------- 还是用数据库吧,正常点的都是用数据库,处理数据不用数据库用什么i/o流 --------------------编程问答-------------------- 再重复一遍,为了达到1MS的目地,直接用Disk I/O绝对会让你失望。
首先,不要完全通过\r\n来分割数据。所有高效的I/O处理,都只有跳过多上字节,而没有跳过多少行。
比如在“每行”(从这里开始,“行”指数据行,并非文件行)开始,固定2个字节代表该行长度,也就是跳过一行需要跳过多少字节。
然后,再外部做一个行索引,保留每1000行的起始位置。
还有一种方案,固定数据行长度,也就是VARCHAR->CHAR,以空间换速度(MySQL里面也可以试试此招,不知在读取数据时,是否会更快) --------------------编程问答-------------------- 好像有一个方法可以实现定位 下次还从哪里读取 跳过指定行就不知道了 --------------------编程问答--------------------
我主要是想借鉴数据库读文件的方法,数据库读文件那么高效,JAVA要可以达到那样的效率就好了。 --------------------编程问答--------------------
其实就有点类似数据库的原理了。
300万行,真恐怖。谁设计的把那么多内容写到一个文件里? --------------------编程问答--------------------
没打算放一个文件,不管分几个文件,跳到某行读取的问题不解决,效率就很低。 --------------------编程问答-------------------- 数据库不可能把几百万条数据存放到很多文件的,看看MYSQL就知道了,MYISAM是一个表一个数据文件,其他数据库类似。 --------------------编程问答--------------------
shell 不可能高效的。程序(不管java/c)可以保持输入流。 --------------------编程问答-------------------- 一次跳n行,如果有那个API,那么一定知道怎么去识别跳过了几行,两种方法,顺序读,读了几行就是几行,另一种就是索引,把每一行或每几行开头记录到索引。
前者需要顺序读,效率不会高,后者能提高效率,但是需要自己建索引。 --------------------编程问答-------------------- 你有见过其他语言能将文件指针跳到指定的行数么? --------------------编程问答--------------------
我是说用ed或awk之类的行编辑软件. --------------------编程问答--------------------
我认为也不用去想数据库的方法,数据库是怎么实现的了。数据库管理系统的研发难度是高远操作系统和文件系统的。 --------------------编程问答--------------------
你说第索引方法,我不是很明白,我就是知道了我要取第10000行的数据,可没办法直接取,要循环过去才行,而不能直接去取。 --------------------编程问答--------------------
这个我赞成,不过如果要研究的话,要用底层开发语言来实现才行,直接用JAVA可能就不行。 --------------------编程问答-------------------- 其实文件说白了 也只是一种数据载体 但是载体的形式是千变万化的 而java的io处理 是从最基本的流入手来实现的 因此针对你这样特定的问题 是很有可能没有针对性的办法
但是从另一方面来看 既然文件的形式可以千变万化 那么如何组织这个文件 就是有学问的了 数据库访问大数据量的数据很快 是因为数据库是一种典型的空间换时间的设计 如果你能把文件组织的像数据库那样高效 那访问速度自然也就提高了很多 --------------------编程问答--------------------
数据库如果采用你所想的那种方式的话,那也不叫数据库了,那数据库也不值钱了!
你根本不可能做到找到指定的行,然后再插入一行数据,就完事了,你可能看到很多文本编辑工具好像是直接插入的,其实远没有你想象得那么简单,文本编辑工具是将所有的数据或者部分数据读入到内存中,修改完成后再全部重新写入。你要插入一行数据,并不仅仅是操作这一行,而是要将后面所有行的数据都往后挪 N 个字节长度,腾出位置再把新数据放进去的。 --------------------编程问答--------------------
什么方法?直接定位到行? --------------------编程问答--------------------
HSQLDB, Derby(Java DB) 这些都是使用纯 Java 实现的关系型数据库。 --------------------编程问答--------------------
他说的应该就是RandomAccessFile类 --------------------编程问答-------------------- 他已经以行为单位,
可行的办法是记录一个索引文件,保存每行的具体索引,然后维护索引文件和实际文件之间的同步,搜索行数的时候从索引文件中读取,修改后再同步更新索引文件。
说实话,确实没见过什么地方有直接跳到某个文件某一行的功能,楼主是拍脑袋想出来的吧?做程序的,胡思乱想可不太好。 --------------------编程问答-------------------- hsqldb可不是基于二进制的数据库引擎
它基于的是sql语句,虽然号称可以支持8G的存储,可实际上连基本的事务控制都没有。
拿hsqldb来说事情,可以说明java确实可以写小小数据库,但是和这里的需求没太大关系哦,有点跑题了。 --------------------编程问答--------------------
RandomAccessFile这个类不行,只能跳过多少个字节,不能跳过多少行。 --------------------编程问答-------------------- 问题是,文件格式既然没有额外的信息,你也不知道里边到底第几行的偏移值是多少,所以只能循环。
就算谁写了一个可以跳过去的方法,里边的实现也一样是循环过去。
何苦呢,与其要求底层提供不可能的功能,不如从本源上考虑一下解决方案,现在不是又兴起一种叫nosql的日志分析技术么? --------------------编程问答-------------------- 太死脑筋了,java不提供的功能就不能自己实现一个?
完全可以自己实现一套这类的底层库。
单是考虑\r\n, \n就够麻烦了,
首先遍历整个文件,动态生成index索引,记录每一行的偏移,这样就简单多了。想要哪一行直接从索引里找就行。 --------------------编程问答-------------------- 作为字符文件,不可能做到像二进制数据文件那样通过数学公式计算快速定位的。
对于字符文件来说,每条数据的长度是不一样的,而不像二进制数据文件,二进制数据文件的每条数据的长度是一样的,数据库也是使用二进制存储的,其内部有很高效、复杂的查找机制。
作为字符文本,除了一行一行读到那儿为止,我认为没有其他办法!就算使用内存文件映射,也做不到,因为根本就不知道 1230000 行在什么地方。 --------------------编程问答-------------------- 不过300w行,遍历一遍也够喝一壶的。
有数据库不用,非要自己造轮子的同志啊。
要我说,国内技术水平,自己造的轮子,100个里面有一个是圆的,就谢天谢地了。 --------------------编程问答--------------------
单纯一个文件,没有额外辅助的话,肯定是没戏的。
文件的编码的问题,换行的标记问题,都是要考虑的。
与其拍脑瓜凭空想出一个需求来,不如换位思考,如果自己如何实现。 --------------------编程问答--------------------
数据库能实现insert,update,select 1毫秒以下吗,我测试了,300万行的数据,大部分情况都要1ms的?不过文件也很难实现? --------------------编程问答-------------------- 一句话,有300多万条数据,持久存储,插入更新查询都在1ms以下,(不用说全放内存里,这个方案不考虑)怎么实现?数据库我考虑了,测试了,希望渺茫?所以考虑文件, --------------------编程问答--------------------
不能! --------------------编程问答--------------------
但是数据库就是读文件的,先不考虑实现难度,从理论上分析,直接读写文件肯定比数据库要快。 --------------------编程问答-------------------- 检索速度的提高都是靠索引的。
数据库也是索引,全文检索也是索引。
它们都是从文件存储格式上下工夫。
首先水平分区存储数据,别300w行进一个文件。
然后建立索引文件,加快搜索速度。
update操作就要靠文件算法了。
上头已经有人提到过,顺序存储的结构,在中间进行插入更新,删除,都会造成后面所有部分的内容发生变化。
想提高更新速度,最佳选择就是实现链表。
但是链表又会导致搜索的难度加大。
这些东西都整合在一起,可以说实现一个通用的工具难度有点儿大。但是一般数据本身都有会自己的规则,善加利用可以提高不少效率。 --------------------编程问答-------------------- 考虑文件的话,先从io和数据结构两方面考虑,不能直接说java有缺陷吧?
呵呵~
加油。 --------------------编程问答--------------------
这个方案,我认为可行,我现在准备测试下,其实还需要其他手段同时运用。不过确实麻烦。 --------------------编程问答--------------------
有索引就能直接取。就像给数据库加索引一样,不加索引挨个找,加了索引直接定位。建议可以看一下数据结构,里面有索引文件、顺序文件之类的介绍。流是属于读写顺序文件的。 --------------------编程问答--------------------
db虽然确实是读文件,但是里边文件的格式可不是像你这么简单分行的。精确的设计才能实现复杂的需求,简单设计简单实现,可惜一般满足不了复杂需求啊。 --------------------编程问答--------------------
1ms基本上不可能,I/O读写的时间数量级就是毫秒级,能做5ms已经很不错了,1ms以下除非硬盘读写技术发生易做图性更新。 --------------------编程问答-------------------- 1ms不知道又是哪个大领导一拍脑袋想出来的结果。估计下一步可以考虑自己做硬件了。 --------------------编程问答-------------------- 个人认为:“1ms”和“分行定位数据”。
这两者本身就有矛盾。 --------------------编程问答-------------------- 抛砖引玉一下,没有做任何异常、边界处理
public class Test {
// 数据流
static RandomAccessFile data;
// 索引流
static RandomAccessFile index;
public static void main(String[] args) throws Exception {
try {
open();
// 第一次初始化数据
// init();
long start = System.currentTimeMillis();
for (int i = 0; i < 1000; i++) {
System.out.println(read(100));
System.out.println(read(1000));
System.out.println(read(10000));
}
long end = System.currentTimeMillis();
System.out.println(end - start);
} finally {
close();
}
}
static String read(int idx) throws Exception {
long indexPos = idx << 3;
index.seek(indexPos);
long dataPos = index.readLong();
long nextDataPos = index.readLong();
data.seek(dataPos);
int length = (int) (nextDataPos - dataPos) >> 1; // pos: bytes -> length: chars
char[] buff = new char[length];
for (int i = 0; i < buff.length; i++) {
buff[i] = data.readChar();
}
return new String(buff);
}
static void init() throws Exception {
for (int i = 0; i < 100000; i++) {
index.writeLong(data.getFilePointer());
data.writeChars("No. " + i);
}
}
static void open() throws Exception {
data = new RandomAccessFile("D:/test/test.dat", "rw");
index = new RandomAccessFile("D:/test/test.idx", "rw");
}
static void close() throws Exception {
if (data != null) {
try {
data.close();
data = null;
} catch (Exception ex) {
}
}
if (index != null) {
try {
index.close();
index = null;
} catch (Exception ex) {
}
}
}
}
--------------------编程问答-------------------- 没想到在我写程序期间,一下子这么火,慢慢看一下。 --------------------编程问答-------------------- 再重复一遍,LZ千万不要把文件行等同于数据行,这样只是自己给自己设定陷阱 --------------------编程问答-------------------- mark学习 --------------------编程问答-------------------- 所有的时候采用lucene之类的全文检索类的index应该没啥问题。
问题是update时,想当然以为添加一行很简单,太天真了。 --------------------编程问答-------------------- 文件本来就没有行的概念!
如果非要加入行的概念,你就得自己扩展File类了。 --------------------编程问答--------------------
那个是扩展File类吗???好像在说:我要一个只有月日的Date,由于这些Date全部放在List中,我应当扩展List类。
特别是你的Varchar字段本身存在换行的情况下。 --------------------编程问答-------------------- 能不能把每一行之前都标个号 --------------------编程问答-------------------- 刚才,试了一个300w行,每行平均长度3000字节(1500字符),总数据量8.44G的数据文件,
分别读取第100行、1,000、10,000、1,000,000行,循环1000遍,平均每行处理时间8ms
而且,用文件的话,似乎必须使用固定长度,否则update操作涉及到长度变化时将很麻烦。
本来lucene也是一个方案。但是据LZ原帖讲,读写比很低。怕帮不了什么忙。
于事无补,反而添乱 --------------------编程问答-------------------- 正好在考虑类似问题,准备这几天做。
最后还是要自己建立一个index把各行Position,Length记录下来
index可以是动态的,随实际dat自动增长。
假设该方法为readLine(intLineId)
0、查看index是否存在?不在转10,存在转20
10、若index不存在,遍历dat,建立index,转步骤999读取
20、检查index中标识(data,size)是否和dat同步?同步转999读取,不在转步骤30
30、获取index的最后一行,再次基础上遍历data,重建index,转步骤999
999、读取index.getLine(intLineId),根据line.Offset,Line,Length用RandomAccessFile读取文件
这里Index可以用数组,Serialize保存
如果多线程,考虑到同步,需要确保只有一个Index实例
class CLine
public int Offset,Length;
end class
class CIndex
private static HashMap<File,CIndex> m_hmOpenIndex=new HashMap<..>();
private int ma_intOffset[],ma_intLength[];
private CIndex(){}
public CIndex open(File fle){
if (m_hmOpenIndex.containsKey(fle)) return m_hmOpenIndex.get(fle);
//初始化一个新类
ma_intOffset=new int[3000000]; //效率原因,使用数组而不是vector,如果行非常多,就不用serialize,直接把index用io随机读写,但这样同步问题就很难处理了。
ma_intLength=new int[3000000];
scan(fle);
.......
}
public getLine(int intId){
return newLine(ma_intOffset[intId],ma_intLength[intId]);
}
end class
--------------------编程问答-------------------- 写文件的时候,我是一条数据readLine()一下,所以就有了行的概念,这里的行也就是数据行。
现在想想,确实没有必要记录这个数据行,因为方法调用者也不可能知道这个行号。
考虑这个方案:在写文件的时候,把当前的位置(第几个字节),和该条数据的KEY,记录到索引文件,下次读的时候,知道了KEY,根据KEY从索引文件找到位置信息,再根据位置信息从数据文件直接定位到要读的数据。
其实索引文件也要300万行数据,虽然只记录2个字段,还是慢,还是没有解决性能问题。因此这个方案不可行。 --------------------编程问答--------------------
我也认为保证数据长度固定,不够用一些其他字符补齐。 --------------------编程问答--------------------
牺牲点内存,将所有的索引存到一个数组里面,这样读取的时候就可以随机读取了 --------------------编程问答--------------------
这个我知道,这是最简单的方法,就是因为这个不行,所以才考虑其他方法, --------------------编程问答-------------------- 我测试了下,用文件这样处理,读取的性能跟数据库差不多,比数据库还差点。
我用MySQL SELECT 1000条 1000ms左右就搞定了。(如果有查询缓存只要600多ms,这里不适合用查询缓存,更新频繁),刚用文件读1000条,基本上要1500-2000ms. --------------------编程问答-------------------- 建议lz虚心研究一下解决方法,
不要动不动就企图把责任推得一干二净。 --------------------编程问答--------------------
只要Offset和Length大小固定,索引可以用RandomAccessFile解决啊。
这样你的开销只要IO就可以了,一般Os都有文件缓存,实际只会更快。
但是同步更新之类比纯载入内存麻烦。
class CPosition
public int Offset,Length
public final static Size=8;
end class
class CIndex
public CPostion get(int intLine){
raf.seek(intLine * CPosition.Size);
return new CPosition(raf.readInt(),raf.readInt());
}
end class
--------------------编程问答-------------------- 可不可以把着300万行只取3000(eg)个索引,每一个索引代表一块的开始 每块有一千行
不知这样可不可以 --------------------编程问答-------------------- 谢谢大家的建议和参与,看来分数不够呀 --------------------编程问答--------------------
我在研究呀,没有推卸责任,这位兄弟何出此言。 --------------------编程问答-------------------- 建议LZ去看下MYSQL的开源数据文件结构,记的是C语言版本的,
替你更正一下:高性能数据库(如ORACLE等)是绕过操作系统和文件系统直接读写硬盘的!
你所用的JAVA I/O流还是基于封装的,而且还得过操作系统和文件系统(取决于操作平台),
不懂JAVA,搞C/C++的飘过...
--------------------编程问答-------------------- 期待LZ结贴 --------------------编程问答-------------------- 300w还好吧,具体处理视你的文件数据,用RandomAccessFile 绝对可以(比如读取每行的时候让seek自增行长,然后将行长维护起来,可以维护到文件方便下次使用,到时候想读哪里就读哪里),但是非常的低效,如果是规则型的数据,使用其他的流也没有问题,如果是无规则的,那只能自己适当重写部分方法
按照搂主描述,既然还要对文件插入和删除,那规则型的数据可能性比较大,如果能使用数据库,那自然是应该使用数据库的了 --------------------编程问答-------------------- 300w小kiss啊,估计代码和架构写得不好 --------------------编程问答-------------------- 期待结贴 --------------------编程问答-------------------- 抱歉,开始不仔细,没照顾到读写比小的问题。
update频繁,就要考虑hash散列和链表结构了,顺序结构的效率没法保证这么高。 --------------------编程问答-------------------- 期待 --------------------编程问答-------------------- 不要怪java,是你自己的要求太无理。
如果每一行的长度都一样的话,自己算一下就知道从什么地方开始读。
如果每一行的长度不一样的话,就算是神仙也不可能一下子就知道第5000行从什么地方开始。除非一行行地读,别无他法。
你可以建一个数组,存放每一行的起始位置,当然要遍历一遍整个文件。
LineNumberReader类本来就不是给你用的,是jdk自用的一个类,可以在发生异常的时候报告错误发生在源文件的第几行。 --------------------编程问答-------------------- 不知道lz追求的速度是多少,实际用数据库的速度是多少,我想应该不慢吧, --------------------编程问答--------------------
需求是:添加数据,更新数据和读取数据要分别要在1ms内完成,读取都是根据KEY查询的,很简单的查询,如果用数据库,类似select * from mytable where key = ?这样的语句。我用数据库测试了,基本上在1-2ms之间,数据库还是在本地,数据量170多万,所以达不到要求,在研究其他办法。 --------------------编程问答-------------------- 期待出现较好的解决方案 --------------------编程问答-------------------- 听说好多用mysql的公司为了提高其性能,都修改其源代码,比如腾讯好像是这样。 --------------------编程问答-------------------- 但是修改MYSQL源代码的,投入成本相对高,一般公司都搞不起,而且对技术能力要求还是比较高,起码要先理解别人的设计。理解总体设计思路不困难,但是要理解其代码,还是需要时间的,而且不是简单的修改,是优化性能。总之,工程量比较大。 --------------------编程问答--------------------
建索引呢? --------------------编程问答--------------------
建了索引的,最多查询勉强可以达到1ms,加上查询缓存到可以达到500ms查询1000条,但是因为每次查询后一般都要update,所以查询缓存效果不佳,另外update基本无法达到1ms。如果一个事务要求:先select,然后update,这个事务的时间就超过了1ms. 主要是现在的数据量才170多万,而且数据库在本地,如果数据量300万,数据库不在本地,可能表现更差。我总体上研究了下,查询同步处理,更新异步处理可能有效,但是数据一致性处理起来较为复杂 --------------------编程问答-------------------- 谁知道 mysql工具 show profiles 显示的duration的时间的单位是什么,秒,毫秒,微妙,皮秒,飞秒??我搜索了半天没这个说明,找到的都是使用方法。 --------------------编程问答-------------------- 哪个工具或者命令? --------------------编程问答-------------------- 学习 --------------------编程问答--------------------
show profiles 呀 --------------------编程问答-------------------- 秒 --------------------编程问答-------------------- 建议Lz用hadoop吧,一般文件存储都不建议update,delete,一个update操作将整个文件都lock,多方update,delete那更不用说 --------------------编程问答-------------------- 如今分布式文件系统火热,如果通了我相信可行的 --------------------编程问答--------------------
绝不可能符合LZ需要,LZ不是为了解决海量数据的存储,而是为了解决300万左右数据1ms内SELECT/UPDATE --------------------编程问答-------------------- 二分搜索 300W 多说搜索22次 --------------------编程问答--------------------
二分搜索确实效率高,如果在内存中确实快,300万个数字,比如搜索229033这个数字,大约117054纳秒,也就是0.1ms就出来了,但是文件系统怎么搜索??数据库搜索能否用上??结合实际问题,我需要研究下。 --------------------编程问答-------------------- 总体的性能,select ,update,能达到0.8ms就解决我的问题了。
补充:Java , Java SE