加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 大数据 > 正文

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,这部分的时间消耗是很可观的。并发的问题,其实有另外的办法可以解决,下一篇文档就讨论一种 并发性很高的文件存储策略。

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读