perl多进程实战之二
发布时间:2020-12-15 23:50:41 所属栏目:大数据 来源:网络整理
导读:每个进程都独立拥有自己的资源,包括内存页、文件指针等等,同时,linux系统提供了多种进程之间的交互方式,比较简单的方式就是写到文件里,DB_File就是一种解决方案。? ?? #!/usr/bin/perl # test?create of DB_File # create by lianming: 2009-08-10 # la
每个进程都独立拥有自己的资源,包括内存页、文件指针等等,同时,linux系统提供了多种进程之间的交互方式,比较简单的方式就是写到文件里,DB_File就是一种解决方案。?
?? #!/usr/bin/perl # test?create of DB_File # create by lianming: 2009-08-10 # last modify by lianming: 2009-08-13? use strict; use warnings; use DB_File;? my %hash; my $file_name = "test_btree"; unlink $file_name;? tie(%hash,'DB_File',$file_name,O_CREAT|O_RDWR,0666,$DB_BTREE) ??? || die "Cannot open $file_name: $!n";? ## == Add some info == $hash{"me"} = "lianming"; $hash{"else"} = "nothing"; $hash{"job"} = "monitor";? print "Print as hash:n"; foreach my $key (keys (%hash)) { ??????? print "$key->$hash{$key}n"; } print "Endn";? untie %hash;? ????执行结果为:? Print as hash: else->nothing job->monitor me->lianming End? ????tie命令,本来是用于变量绑定,在DB_File中,是将一个哈希变量,和一个文件绑在了一起,简单的来讲,我们可以认为变量'%hash',是存在这个文件中的,所以我们对这个哈希变量进行的所有修改,它都会在文件中做相应的操作。? ????tie的参数,第一个为要绑定的变量? ????????第二个为要绑定的文件? ????????第三个为flag(是否创建,只读,只写,可读写)? ????????第四个为权限(和linux文件的权限类似,不过没有执行,最大是6)? ????????第五个为存储引擎,有三种,hash、btree、recno,一般来讲,hash和btree是存储key/value类型数据的,而recno是顺序存储,相当于数组。使用上的区别,请看下文。? ????在DB_File中添加信息,只要直接给哈希变量添加信息即可,和一般的赋值是一样的。? ????DB_File也提供了类似BerkeleyDB的方法来添加对象,要将tie的结果返回给一个值,例如$db,然后利用$db->put($key,$value)的方式来添加,这种办法我不是很习惯用。? ????同理,在DB_File中读取信息,就直接从哈希变量中读取就ok了。? ????这样,我们就已经把信息存在了文件中,下次,如果另外一个进程要来修改这个文件,直接打开即可。? # test read/write of DB_File # last modify by lianming: 2009-08-10? my $file_name = "test_btree";? for (my $i = 0; $i < 10; $i ++) { ??????? $hash{$i} = $i."new"; }? ????执行结果如下:? 0->0new 1->1new 2->2new 3->3new 4->4new 5->5new 6->6new 7->7new 8->8new 9->9new ????只要记住,打开文件的时候要和创建的时候采用同样的存储算法,否则会报无法打开文件的错 一般来讲,哈希是不允许存储重复键的,就是说,对同一个key,赋两次value,那么后一次赋值会将前一次赋的值给覆盖掉,但是btree是可以存储这样的信息的。? # test duplicate keys of DB_File # == enable duplicate records $DB_BTREE->{'flags'} = R_DUP;? $hash{'me'} = "Sangyb"; $hash{'me'} = "Lianming"; $hash{'me'} = "Here";? me->Sangyb ????问题出现了……虽然它存储了3个相同的key,但是我明明存储的是不同的value,打出来的却是相同的value。? ????原因是一个叫做联想数组的东西,当你请求同样的key的时候,只会返回第一个value。所以,我们的数据其实已经存储好了,只是在读出来的时候,由于某些原因,出现了这样的问题。? ????? ????这个时候,就必须利用api来进行操作了。? ????它提供了一个seq的函数,用来对btree中的对象进行指向:? my $db = tie(%hash,238)">my ($k,$v,$status);? print "Print as api:n";? ## == use api: seq == $k = $v = 0; for ($status = $db->seq($k,R_FIRST); ??????? $status == 0; ??????? $status = $db->seq($k,R_NEXT)) { ??????? print "$k->$vn"; undef $db; Print as api: me->Lianming me->Here ????在读取btree文件的时候,会有一个指针指向一个特定的key/value对,然后读取,写入的操作,就对这个指针指向的pair进行操作。seq函数就是控制这个指针的移动,它有三个参数:? ????第一个,指针指向的pair的key? ????第二个,指针指向的pair的value? ????第三个,flag,调用函数时,指针做的操作:? ????????R_CURSOR:当前? ????????R_FIRST:最前面的一个pair? ????????R_LAST:最后的一个pair? ????????R_NEXT:下一个pair? ????????R_PREV:前一个pair? ????如果调用失败了,就会返回0。? ????针对duplicate key,还有别的几个实用的函数,例如get_dup,del_dup等。? ????get_dup可以获取重复键的一些信息,del_dup可以删除特定的pair。? ????当多个进程同时对一个文件进行操作的时候,必然要涉及到一个锁的问题,多个进程如果同时对一个文件进行写,那么肯定会挂掉,我们可以做一些测试。? 1、两个进程同时进行读操作? # test multi read of DB_File for (my $i = 0; $i < 20; $i ++) { ??????? $hash{$i} = $i."test"; ## == multi read ==? my $pid = fork();? if (!defined($pid)) { ??????? print "Fork error: $!n"; ??????? exit 1; if ($pid == 0) { ??????? ## == child == ??????? my $key;? ??????? for (my $i = 0; $i < 1000; $i ++) { ??????????????? $key = int(rand()*20); ??????????????? print "Child: $key->$hash{$key}n"; ??????? } ??????? undef $db; ??????? untie %hash; ??????? exit 0; } else { ??????? ## == parent == ??????????????? print "Parent: $key->$hash{$key}n"; ????执行结果是正常的,child和parent会交替出现,打出正确的读取结果。? ...? Parent: 15->15test Child: 1->1test Parent: 5->5test Child: 10->10test Parent: 3->3test Child: 5->5test Parent: 4->4test Child: 9->9test Parent: 17->17test Child: 17->17test Parent: 9->9test Child: 7->7test Child: 2->2test Parent: 15->15test? ????2、两个进程同时写? # test?multi write?of DB_File my $pid_1 = fork();? if (!defined($pid_1)) { if ($pid_1 == 0) { ??????????????? $key = int(rand()*10); ??????????????? $hash{$key} = $key."abc"; my $pid_2 = fork();? if (!defined($pid_2)) { if ($pid_2 == 0) { ??????????????? $key = int(rand()*10)+10; wait();? ## == parent == my $cnt = 0;? foreach my $key (keys(%hash)) { ??????? $cnt ++; print "ncount is $cntn";? 10->10abc 11->11abc 12->12abc 13->13abc 14->14abc 15->15abc 16->16abc 17->17abc 18->18abc 19->19abc? count is 10? 我们本来是想让他有20个值的,结果只有第二个子进程的修改生效了,第一个子进程的修改并没有生效。? ????我们可以得到一个结论,那就是DB_File可以支持多个进程同时读,但是无法同时写。? ????具体原因,我只能猜测一下了,那就是tie,在文件开始的tie,是将文件内容和哈希变量绑定,我们可以认为,其实就是一个指针,指到了文件的开 始位置,读的话,是互不干涉的,所以可以多个进程同时读。但是写的时候,也许写的操作并不是直接写文件,而是修改哈希的值,最后在untie,或者需要的 时候,才会把对哈希的操作刷到文件中(也可以用$db->sync来实现),所以,虽然两个进程都对文件进行了写的操作,但是只有最后untie的 一个才会生效。? ????如果要测试的话,可以在第二个子进程undef之前,sleep几秒钟,就可以看到,生效的就是第一个进程的写入了。? ????当然,这只是最简单的情况,我测试过多个进程同时对它进行大量的写操作,那最后文件就会乱七八糟,甚至有可能没法打开。? ????这种情况下,是需要对文件上锁的,在锁住文件之后,才对它进行tie,在解锁之前,要进行untie。有两个给DB_File上锁的包,就拿DB_File::Lock来做例子:? # test lock of DB_File use DB_File; use DB_File::Lock; use Fcntl qw(:flock O_RDWR O_CREAT);? ??????????????? $key = int(rand()*12)+122;? ??????????????? ## == lock and tie ==? ????????????????tie(%hash,'DB_File::Lock',$DB_BTREE,"write") || die "Cannot open $file_name: $!n"; ??????????????? untie %hash; ??????????????? $key = int(rand()*10)+10;? ??????????????? ## == lock and tie == ??????????????? tie(%hash,238)">## == parent ==? 122->122abc 123->123abc 124->124abc 125->125abc 126->126abc 127->127abc 128->128abc 129->129abc 130->130abc 131->131abc 132->132abc 133->133abc count is 22? ????这就是我们想要的结果。在tie的时候,同时上锁,在untie的时候解锁。和DB_File的tie参数一样,最后加一个read或者write就ok? ????锁有两种,read和write,read锁可以多个进程同时持有,但是write锁只能一个进程持有。? ????如果说btree和hash的存储算法,是相当于把哈希变量存在了文件中,那recno就是把数组变量存在了文件中,recno因为用的不多,也没有具体看过。? ????对DB_File的性能做一些测试,具体测试代码和结果就不写了,如果大家有兴趣可以自己去搞一下,只写一下测试的结果。? ????1、测试随机的写性能:分别用两种引擎,单进程对一个文件随机写入100w条数据,测试结果如下:? ????????HASH:耗时53s,文件大小为83M? ????????BTREE:耗时12s,文件大小为120M? ????可见写的话,btree性能稍好,但是占用空间较大。? ????2、测试随机读的性能:分别用两种引擎,单进程对一个文件随机写入100w条数据,测试结果如下:? ????????HASH:耗时28s? ????????BTREE:耗时28s? ????读性能两个引擎差不多。? ????3、并发性能:? ????a、用BTREE引擎,开启10个进程,每个进程对一个文件写入1w条数据,写入每条数据之前加锁,写完后解锁。测试结果耗时1000多s,可见它的并发性是何等的差劲……? ????b、用BTREE引擎,单进程,对一个文件写入10w条数据,写入每条数据前加锁,写完后解锁。测试结果是2分钟。? ????可见,单进程的读写,还是很快的,但是涉及到加锁后就很慢很慢,慢的主要是tie和untie的过程,这部分的过程涉及到文件的读写,没有任何并 发的机制,所以只有等每次锁了之后才可以tie和untie,这部分的时间消耗是很可观的。并发的问题,其实有另外的办法可以解决,下一篇文档就讨论一种 并发性很高的文件存储策略。 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |