user-0 提问: 关于大数据重复字数据的整理效率

回复:
user: user-1
content: 我想了个方法，大致2倍读写(略多于2倍)。

user: user-2
content: 引用 26 楼 zhao4zhong1 的回复:内存不够用不会用硬盘吗？ 用硬盘文件读写模拟内存读写，参考_lseeki64函数。 正解！

user: user-3
content: 这种去重不是最适合MapReduce吗

user: user-4
content: 关注一下，看看高手门的解决方案

user: user-5
content: 引用 28 楼 angel_su 的回复:[quote=引用 27 楼 mmns 的回复:] ...楼主的问题其实就是这个，如果hash table中只存行的key及其在目标文件中的偏移，是没有内存问题的。 就这个问题本身来说，数据库不是个好解决方案。 单串6-24，那么20G得假设有1G个不同条目吧，貌似32位下一次性hash一样不够内存吧...[/quote] 极端情况下是的，比如：每20个Byte一行，而且每一行都不一样，而且产生的key也不一样，那确实会产生1G个key，确实会造成内存不够，

user: user-6
content: 引用 9 楼 forcal 的回复:尝试用Lu脚本键树解决这个问题。 参考C/C++使用Lu脚本字符串键树：http://www.forcal.net/bczl/luc/insertkey.htm  9#的代码可改写为： #include <stdio.h> #include "lu32.h"  #pragma comment( lib, "lu32.lib" )  #define key_mystr	(luPriKey_User-20)	//私有键  void main(void) { 	char *StrName[]={"my","you","hello","lu","my","you","hi","lu","my","you","hi","hello","lu",""}; 	int i,k; 	void *NowKey;		//用于返回已存在的键值   	if(!InitLu()) return;	//初始化Lu  	for(i=0;StrName[i][0];i++) 	{ 		//在Lu键树中注册键值，注意参数0表示不更新键值，一定要为0 		k=InsertKey(StrName[i],(luKEY)strlen(StrName[i]),key_mystr,NULL,NULL,NULL,0,&NowKey); 		if(k==0) 		{ 			printf("第一个字符串，需要写盘： %s\n",StrName[i]); 		} 		else if(k==1) 		{ 			printf("重复字符串，忽略： %s\n",StrName[i]); 		} 		else 		{ 			printf("内存错误！由于可以使用虚拟内存，此问题大概不会出现。\n"); 		} 	}  	FreeLu();		//释放Lu }  结果：  第一个字符串，需要写盘： my 第一个字符串，需要写盘： you 第一个字符串，需要写盘： hello 第一个字符串，需要写盘： lu 重复字符串，忽略： my 重复字符串，忽略： you 第一个字符串，需要写盘： hi 重复字符串，忽略： lu 重复字符串，忽略： my 重复字符串，忽略： you 重复字符串，忽略： hi 重复字符串，忽略： hello 重复字符串，忽略： lu   无论多大数据量，以上代码中，InsertKey函数直接进行插入验证，不需要排序，效率极高。但对于大数据量，这段代码运行时唯一的问题是，它会消耗完内存，导致使用硬盘作为虚拟内存，从而运行速度显著变慢。

user: user-7
content: 引用 27 楼 mmns 的回复:...楼主的问题其实就是这个，如果hash table中只存行的key及其在目标文件中的偏移，是没有内存问题的。 就这个问题本身来说，数据库不是个好解决方案。 单串6-24，那么20G得假设有1G个不同条目吧，貌似32位下一次性hash一样不够内存吧...

user: user-5
content: 引用 25 楼 vipcxj 的回复:LZ的最大问题不是这个吧，他的最大问题是数据量太大，内存不够用，大家提议用数据库也是这个考量，虽然数据库慢，但至少对内存需求不会比将所有数据都读进内存那么大。 直接把数据都读入内存??这个不现实的方法是不用考虑的。但数据只要在硬盘上，就没有空间问题，关键就是时间问题。即使你使用数据库，也是数据库存储hash table而已,也不能把整个文件先读入数据库，把20g的数据先读入数据库，然后再让数据库来完成所需的操作，数据库也是要把数据分块并在内存和硬盘之间频繁交换，然后再把整理后库数据存成庞大的目标文件，要花费的时间也是很巨大的。 楼主的问题其实就是这个，如果hash table中只存行的key及其在目标文件中的偏移，是没有内存问题的。 就这个问题本身来说，数据库不是个好解决方案。

user: user-2
content: 内存不够用不会用硬盘吗？ 用硬盘文件读写模拟内存读写，参考_lseeki64函数。

user: user-8
content: 引用 24 楼 mmns 的回复:如果你的数据本来就存成了文件，而且目标数据也需要存成文件，那么基本上用数据库意义不大了。 假设不用数据库处理： 即使用数据库，以下两项工作的时间是不能省的： 1. 从原始文件中每次读取一行，直至读完。 2. 把不重复的的行写入目标文件。 下面工作是可以重点优化的步骤，也是核心算法所在： 3. 判断某一行是否已经存在于目标文件中。  一个简单易行的办法是在内存中创建一个hash表，每从源文件中读出一行，就为此行生成一个hash Key, 然后从hash表中查找次key是否存在，如果不存在就说明是没有重复的，可以直接写入目标文件，并把此key及其行在目标文件中的偏移存入hash表。如果次key已经存在，则需通过key从hash表中找到所有具有同一个key的行偏移，然后逐个对比......(你应该明白这个方法的大概步骤了，故此处略去更多说明，)。    关键在于你的hash key的生成算法，直接决定你的效率，在最佳的理想状态下，每一个不同的行生成的key都是不同的，此状态下效率最高。    一般这种问题需要根据文本内容的具体情况来确定hash key的生成算法。 LZ的最大问题不是这个吧，他的最大问题是数据量太大，内存不够用，大家提议用数据库也是这个考量，虽然数据库慢，但至少对内存需求不会比将所有数据都读进内存那么大。

user: user-5
content: 如果你的数据本来就存成了文件，而且目标数据也需要存成文件，那么基本上用数据库意义不大了。 假设不用数据库处理： 即使用数据库，以下两项工作的时间是不能省的： 1. 从原始文件中每次读取一行，直至读完。 2. 把不重复的的行写入目标文件。 下面工作是可以重点优化的步骤，也是核心算法所在： 3. 判断某一行是否已经存在于目标文件中。  一个简单易行的办法是在内存中创建一个hash表，每从源文件中读出一行，就为此行生成一个hash Key, 然后从hash表中查找次key是否存在，如果不存在就说明是没有重复的，可以直接写入目标文件，并把此key及其行在目标文件中的偏移存入hash表。如果次key已经存在，则需通过key从hash表中找到所有具有同一个key的行偏移，然后逐个对比......(你应该明白这个方法的大概步骤了，故此处略去更多说明，)。    关键在于你的hash key的生成算法，直接决定你的效率，在最佳的理想状态下，每一个不同的行生成的key都是不同的，此状态下效率最高。    一般这种问题需要根据文本内容的具体情况来确定hash key的生成算法。

user: user-7
content: 变成数据库好了，不想的话就按某种条件自己分割成小文件，别再合并了，所谓大而无当...

user: user-8
content: http://www.ideawu.net/blog/ssdb 可以试试这个，SSDB，我虽然没用过，但看介绍，效率还是很高的，并且有能力处理大数据

user: user-2
content: 仅供参考//文件1中的内容排序并去重,结果保存到文件2中 #include <stdio.h> #include <stdlib.h> #include <string.h> #define MAXCHARS 128      //能处理的最大行宽,包括行尾的\n和字符串尾的\0 int MAXLINES=10000,MAXLINES2; char *buf,*buf2; int c,n,hh,i,L; FILE *f; char ln[MAXCHARS]; int ignore_case=0; int icompare(const void *arg1,const void *arg2) {    return stricmp((char *)arg1,(char *)arg2); } int compare(const void *arg1,const void *arg2) {    return strcmp((char *)arg1,(char *)arg2); } int main(int argc,char **argv) {     if (argc<3) {         printf("Unique line. Designed by zhao4zhong1@163.com. 2012-08-20\n");         printf("Usage: %s src.txt uniqued.txt [-i]\n",argv[0]);         return 1;     }     if (argc>3) ignore_case=1;//若存在命令行参数3，忽略大小写     f=fopen(argv[1],"r");     if (NULL==f) {         printf("Can not find file %s!\n",argv[1]);         return 1;     }     buf=(char *)malloc(MAXLINES*MAXCHARS);     if (NULL==buf) {         fclose(f);         printf("Can not malloc(%d LINES*%d CHARS)!\n",MAXLINES,MAXCHARS);         return 2;     }     n=0;     hh=0;     i=0;     while (1) {         if (NULL==fgets(ln,MAXCHARS,f)) break;//         hh++;         L=strlen(ln)-1;         if ('\n'!=ln[L]) {//超长行忽略后面内容             printf("%s Line %d too long(>%d),spilth ignored.\n",argv[1],hh,MAXCHARS);             while (1) {                 c=fgetc(f);                 if ('\n'==c || EOF==c) break;//             }         }         while (1) {//去掉行尾的'\n'和空格             if ('\n'==ln[L] || ' '==ln[L]) {                 ln[L]=0;                 L--;                 if (L<0) break;//             } else break;//         }         if (L>=0) {             strcpy(buf+i,ln);i+=MAXCHARS;             n++;             if (n>=MAXLINES) {                 MAXLINES2=MAXLINES*2;                 if (MAXLINES2==1280000) MAXLINES2=2500000;                 buf2=(char *)realloc(buf,MAXLINES2*MAXCHARS);                 if (NULL==buf2) {                     printf("Can not malloc(%d LINES*%d CHARS)!\n",MAXLINES2,MAXCHARS);                     printf("WARNING: Lines >%d ignored.\n",MAXLINES);                     break;//                 }                 buf=buf2;                 MAXLINES=MAXLINES2;             }         }     }     fclose(f);     if (n>1) {         if (ignore_case) qsort(buf,n,MAXCHARS,icompare);         else qsort(buf,n,MAXCHARS,compare);     }     f=fopen(argv[2],"w");     if (NULL==f) {         free(buf);         printf("Can not create file %s!\n",argv[2]);         return 2;     }     fprintf(f,"%s\n",buf);     if (n>1) {         if (ignore_case) {             hh=0;             L=MAXCHARS;             for (i=1;i<n;i++) {                 if (stricmp((const char *)buf+hh,(const char *)buf+L)) {                     fprintf(f,"%s\n",buf+L);                 }                 hh=L;                 L+=MAXCHARS;             }         } else {             hh=0;             L=MAXCHARS;             for (i=1;i<n;i++) {                 if ( strcmp((const char *)buf+hh,(const char *)buf+L)) {                     fprintf(f,"%s\n",buf+L);                 }                 hh=L;                 L+=MAXCHARS;             }         }     }     fclose(f);     free(buf);     return 0; }

user: user-9
content: 要是偶尔需要这样处理下，就sort file | unique >newfile 经常这样的话，还得找下大数据排序等方法

user: user-10
content: 引用 1 楼 gz_qmc 的回复:这个可有含量了,不懂帮顶 +1我也不懂，帮顶，瞎说几句，呵呵

user: user-10
content: 引用 12 楼 ALNG 的回复:增大内存和/或配个64G的SSD盘对解决你的问题很有好处 :)  mysql慢有慢的道理，硬件的限制在那里，自己写程序不使用恰当的数据结构可能更慢。 兄弟，看你很赞成用数据库，你认为检查主键比较快吗？还有，把所有数据都插入到数据库，在索引，你认为这个索引能比排序快吗？还有， 1、读取文件 2、写入数据库 3、索引 4、读取 5、写新文件  我不太明白你“初步估计相当于复制2.5次这个文件的耗时”怎么得出的结论，随意说的？

user: user-1
content: 增大内存和/或配个64G的SSD盘对解决你的问题很有好处 :)  mysql慢有慢的道理，硬件的限制在那里，自己写程序不使用恰当的数据结构可能更慢。

user: user-1
content: 哦，4G内存不算太小，楼主机器的性能也不算太差，也许cat file.txt|sort -n |uniq > output.txt真的可行，那样最省事了。需要多久，试试就知道了。不会少于3-5倍复制时间吧。sort要完成才能够输出给uniq, 怀sort要读完文件才执行sort，因为内存不够，会反复swap,（如果sort保证前面的记录都被正确排序，swap的次数倒会少些, 取决于sort的实现)，uniq倒是简单，就是顺序读一遍。试试不行再自己写。

user: user-0
content: 数据库肯定不行 20G MYSQL撑不住 慢死 除了提高硬件配置，进行分布式处理以外，单机就没有其他办法了吗