Perl模块介绍之Storable
2013-12-13 17:13:20 阿炯

Storable - persistence for Perl data structures

该模块用于将perl脚本中运行时的变量数据结构等存储到文件之中,或从文件中读取变量的结构值。

The Storable package brings persistence to your Perl data structures containing SCALAR, ARRAY, HASH or REF objects, i.e. anything that can be conveniently stored to disk and retrieved at a later time.

可以存储像标量、数组、哈希或引用对象。

It can be used in the regular procedural way by calling store with a reference to the object to be stored, along with the file name where the image should be written.

可调用store方法来实现将变量结构写入文件之中。

The routine returns undef for I/O problems or other internal error, a true value otherwise. Serious errors are propagated as a die exception.

出错时返回undef或直接退出程序。

To retrieve data stored to disk, use retrieve with a file name. The objects stored into that file are recreated into memory for you, and a reference to the root object is returned. In case an I/O error occurs while reading, undef is returned instead. Other serious errors are propagated via die.

调用retrieve方法可将从文件中载入具体的变量结构的引用,读入脚本的运行地址空间。

Since storage is performed recursively, you might want to stuff references to objects that share a lot of common data into a single array or hash table, and then store that object. That way, when you retrieve back the whole thing, the objects will continue to share what they originally shared.

它将会把像单独的数组或散列表引用递归地存储到文件之中,所以在读取时会以同样的方法来取得最初的变量对象。

The Storable engine can also store data into a Perl scalar instead, to later retrieve them. This is mainly used to freeze a complex structure in some safe compact memory place (where it can possibly be sent to another process via some IPC, since freezing the structure also serializes it in effect). Later on, and maybe somewhere else, you can thaw the Perl scalar out and recreate the original complex structure in memory.

相反Storable引擎还可以存储数据到一个Perl标量,稍后取出它们。这是主要用于freeze一个复杂紧凑的结构到一些内存中安全的地方(它可能被发送到另一个进程通过IPC、自冻结结构实际上也序列化)。也许在其他地方,你可以解冻Perl标量并在内存中重新创建原始的数据结构。

Surprisingly, the routines to be called are named freeze and thaw . If you wish to send out the frozen scalar to another machine, use nfreeze instead to get a portable image.

令人惊讶的是,要调用的例程是freeze和thaw。如果你想发送freeze标量到另一台主机,使用nfreeze来处理便携式图像。

序列化(serialization)

序列化是将对象状态转换为可保持或传输的格式的过程。与序列化相对的是反序列化,它将流转换为对象。这两个过程结合起来,可以轻松地存储和传输数据。

在序列化期间,对象将其当前状态写入到临时或永久性存储区(存入文件中)。以后可以通过从存储区中读取或反序列化对象的状态,重新创建该对象。序列化的机制,把对象表示成一连串的字节,里面包含了对象的数据,对象的类型信息,对象内部的数据的类型信息等等。因此,序列化可以看成是为了把对象存储在磁盘上或者是从磁盘上读出来并重建对象而把对象扁平化的一种方式。反序列化是把对象从扁平状态转化成活动对象的相反的步骤。


相关操作应用接口
store与retrieve是默认导入的函数。

use Storable;
store \%table, 'file';
$hashref = retrieve('file');

这些函数是需要声明导入的。
use Storable qw(nstore store_fd nstore_fd freeze thaw dclone);

# Network order
nstore \%table, 'file';
$hashref = retrieve('file');    # There is NO nretrieve()

下面这种存储变量的方式是可以跨主机器(网络)使用的,具有可移植性的。

# Storing to and retrieving from an already opened file
从文件句柄或socket中导入、导出
store_fd \@array, \*STDOUT;
nstore_fd \%table, \*STDOUT;
$aryref = fd_retrieve(\*SOCKET);

# Serializing to memory
串行化处理,多见于memcache处理数据时。
$serialized = freeze \%table;
%table_clone = %{ thaw($serialized) };
$hashref = fd_retrieve(\*SOCKET);

# Deep (recursive) cloning
深度克隆
$cloneref = dclone($ref);

Storable provides you with a dclone interface which does not create that intermediary scalar but instead freezes the structure in some internal memory space and then immediately thaws it out.

Storable模块提供了dclone接口,并不创建中间标量,而是在一些内存空间中冻结住结构,完成后立刻消融。

# Advisory locking
高级锁,防止同时修改变量引用,使写入更安全
use Storable qw(lock_store lock_nstore lock_retrieve)
lock_store \%table, 'file';
lock_nstore \%table, 'file';
$hashref = lock_retrieve('file');

应用场景
1、临时存放一些数据变量值,以便下次运行时方便读取,比如,计算两个时间点内(crontab)的网络流量,就需要取的两个时间点的流量值,取差后除以时间来得到。

2、判断url列表中的url访问是否正常,如果第二次不能正常访问,就触动报警等。就需要临时记录下上次不能正常访问的url,并将这次结果存入。


Perl使用Storable模块对数据序列化和持久化(转自骏马金龙博客)

Perl内置的Storable模块可用来对数据结构进行序列化(serialization,Perl中称为冻结),也就是将数据结构保存为二进制数据。其特点如下:
1.序列化后的数据可以写入文件实现持久化,可以将持久化文件拷贝给远程机器
2.也可以通过网络套接字将序列化数据传递给远程机器
3.序列化后的数据在任意机器上都可以反序列化(deserialization,Perl中称为解冻)得到原始的数据结构
4.序列化数据结构时是进行深拷贝的,序列化完成后,修改原始数据,不会影响反序列化的结果

序列化:freeze、nfreeze和thaw

Storabel的freeze和thaw函数分别用来冻结(序列化)和解冻(反序列化):
1.freeze冻结的是数据对象,不包括它们的引用和名称
2.freeze的参数需要是引用,可以是多个引用参数,返回的是二进制的冻结序列,各数据结构序列化在不同行
3.thaw的参数是引用变量,返回的是一个匿名列表,列表各元素对应freeze冻结时的各数据结构
4.nfreeze也是冻结,但是按照网络字节序进行序列化,适合远程传输序列化时的标量名称,见下一小节对主机字节序和网络字节序的描述

use v5.32;
use Storable qw(freeze thaw);

my %hash=(
        'longshuai'=>{
                      'gender'=>'male',
                      'age'   =>18,
                      'prov'  =>'jiangxi',
                     },
        'wugui'=>{
                  'gender'=>'male',
                  'age'   =>20,
                  'prov'  =>'zhejiang',
                 },
        'xiaofang'=>{
                     'gender'=>'female',
                     'age'   =>19,
                     'prov'  =>'fujian',
                    },
);

my @name=('fairy',[qw(longshuai wugui xiaofang)]);

$frozen = freeze [\%hash,\@name];   # 冻结引用,返回一个冻结后的列表
#say $frozen;    # 输出一堆二进制码

my $thaw_out=thaw($frozen);    # 解冻并返回引用列表

say $thaw_out->[0];    # 输出:HASH(0x557171a4cff8)
say $thaw_out->[0]{wugui}{prov};    # 输出:zhejiang
say $thaw_out->[1];    # 输出:ARRAY(0x557171a4d220)
say $thaw_out->[1][1][2];    # 输出:xiaofang

上面的示例中,使用freeze冻结两个数据结构后,冻结后的二进制数据内容将赋值给一个标量变量,注意它返回的是类似于字符串那种形式的,只不过这段字符串是二进制格式的。使用thaw解冻后,将返回一个匿名列表,列表中的元素是各冻结的数据结构的引用。对于上面的示例来说,返回值类似如此结构[$ref_hash,$ref_name],将其赋值给一个引用变量$thaw_out,然后就可以通过$thaw_out->[0]和$thaw_out->[1]分别访问这两个引用。如下图描述。

freeze序列化过程:


thaw反序列化过程:


持久化:store、nstore和retrieve

Storable模块可以将数据结构序列化后持久化保存到文件中,或通过TCP套接字传输出去。

store和nstore用于将序列化数据进行持久化,其用法一样,如下:
store \%ref_hash, 'file';
store [\%ref_hash,\@ref_arr], 'file';
nstore \%ref_hash, 'file';
nstore [\%ref_hash,\@ref_arr], 'file';

但是store存储序列化数据时默认采用的是主机字节序(host byte order),nstore默认采用的是网络字节序(network byte order),采用网络字节序可以保证被TCP套接字传输出去时,远程主机能以完全一致的字节序方式读取数据。所以要想通过网络传输序列化的对象时,需要使用nstore。

小知识:端序(主机字节序和网络字节序)

多字节数据对象在存储时,必须考虑两个问题:
1.这段数据对象要存储到哪个地址
2.存储时如何排列这些字节

这里不考虑存储的地址问题。对于待存储的数值"0x1122"来说,11属于高位字节,22属于低位字节。对于存储时考虑以何种字节排列方式来说,有两种方式:大端字节序和小端字节序。假设要存储的数据对象"0x1234567":
1.大端字节序(big-endian):存储时,高位在前,低位在后,所以存储的时候,和上面源数据格式一样"01 23 45 67"
2.小端字节序(little-endian):存储时,高位在后,低位在前,所以存储的时候,和上面源数据格式相反"67 45 23 01"

大端字节序对人类来说比较容易理解,但几乎所有计算机都是采用小端字节序存储的,所以也称为主机字节序。而TCP/IP协议规定,网络传输时的网络字节序都采用大端字节序传输,这样一来所有网络传输的数据都规范化,远程主机总会按照大端字节序去读取传输过来的数据。

store和nstore持久化的序列化数据可以通过retrieve函数读取并反序列化。retrieve返回的值和thaw的返回结果是一样的:
my $ref_list = retrieve 'file';

以下是nstore和retrieve的一个示例:
use v5.32;
use Data::Dumper;
use Storable qw(nstore retrieve);

my %hash=(
        'longshuai'=>{
                      'gender'=>'male',
                      'age'   =>18,
                      'prov'  =>'jiangxi',
                     },
        'wugui'=>{
                  'gender'=>'male',
                  'age'   =>20,
                  'prov'  =>'未知',
                 },
        'xiaofang'=>{
                     'gender'=>'female',
                     'age'   =>19,
                     'prov'  =>'fujian',
                    },
      );

my @name=('fairy',[qw(longshuai wugui xiaofang)]);

nstore [\%hash,\@name],'/tmp/st.data';   # 将数据序列化并持久化到文件

my $ref_list=retrieve '/tmp/st.data';   # 反序列化并读取数据

say Dumper($ref_list);

say $ref_list->[0];    # 输出:HASH(0x55a2a5ceeec8)
say $ref_list->[0]{wugui}{prov};    # 输出:未知
say $ref_list->[1];    # 输出:ARRAY(0x55a2a5ee1250)
say $ref_list->[1][1][2];    # 输出:xiaofang

序列化到文件描述符:store_fd、nstore_fd和fd_retrieve
1.store_fd、nstore_fd用于将数据结构冻结到指定的文件描述符,比如文件、管道、套接字、字符串中
2.retrieve_fd则从给定的文件描述符中读取

下面示例涉及到文件句柄的标量引用,如将数据结构冻结到一个字符串$string中存储起来:
use Storable;
open my $string_fh,">",\my $string or die "...$!";
nstore_fd \@data,$string_fh;
close $string_fh;

从持久化数据的变量$string中解冻:
open my $string_fh1,"<",\$string or die "$!";
$new_hash = fd_retrieve($string_fh1);
close $string_fh1;

如此就将数据结构就存储到$new_hash这个引用中。下面将前文的数据结构%hash存储起来,并立即解冻:
use v5.32;
use Storable qw(store_fd nstore_fd fd_retrieve);
use Data::Dumper;

my %hash=(
...
);

open my $string_fh,">",\my $string;
nstore_fd [\%hash],$string_fh;
close $string_fh;
print Dumper($string);  # 输出:一大堆二进制码
say '-' x 32;
open my $string_fh1,"<",\$string or die "$!";
my $new_hash = fd_retrieve($string_fh1);
close $string_fh1;
print Dumper($new_hash);   # 输出:hash数据结构,不过还是会带出一点二进制来。。
say '-' x 32;

浅拷贝、深拷贝(dclone)可见《Perl变量的复制》。


参考文档
Storable