📄 ch10_01.htm
字号:
<?label 10. Data Persistence?><html><head><title>Data Persistence (CGI Programming with Perl)</title><link href="../style/style1.css" type="text/css" rel="stylesheet" /><meta name="DC.Creator" content="Scott Guelich, Gunther Birznieks and Shishir Gundavaram" /><meta scheme="MIME" content="text/xml" name="DC.Format" /><meta content="en-US" name="DC.Language" /><meta content="O'Reilly & Associates, Inc." name="DC.Publisher" /><meta scheme="ISBN" name="DC.Source" content="1565924193L" /><meta name="DC.Subject.Keyword" content="stuff" /><meta name="DC.Title" content="CGI Programming with Perl" /><meta content="Text.Monograph" name="DC.Type" /></head><body bgcolor="#ffffff"><img src="gifs/smbanner.gif" alt="Book Home" usemap="#banner-map" border="0" /><map name="banner-map"><area alt="CGI Programming with Perl" href="index.htm" coords="0,0,466,65" shape="rect" /><area alt="Search this book" href="jobjects/fsearch.htm" coords="467,0,514,18" shape="rect" /></map><div class="navbar"><table border="0" width="515"><tr><td width="172" valign="top" align="left"><a href="ch09_07.htm"><img src="../gifs/txtpreva.gif" alt="Previous" border="0" /></a></td><td width="171" valign="top" align="center"><a href="index.htm">CGI Programming with Perl</a></td><td width="172" valign="top" align="right"><a href="ch10_02.htm"><img src="../gifs/txtnexta.gif" alt="Next" border="0" /></a></td></tr></table></div><hr align="left" width="515" /><h1 class="chapter">Chapter 10. Data Persistence</h1><div class="htmltoc"><h4 class="tochead">Contents:</h4><p><a href="ch10_01.htm">Text Files</a><br><a href="ch10_02.htm">DBM Files</a><br><a href="ch10_03.htm">Introduction to SQL</a><br><a href="ch10_04.htm">DBI</a><br></p></div><p>Many basic web applications can be created that output only email andweb documents. However, if you begin building larger webapplications, you will eventually need to store data and retrieve itlater. This chapter will discuss various ways to do this withdifferent levels of complexity. Text files are the simplest way tomaintain data, but they quickly become inefficient when the databecomes complex or grows too large. A <a name="INDEX-1950" />DBM file provides much faster access, evenfor large amounts of data, and DBM files are very easy to use withPerl. However, this solution is also limited when the data grows toocomplex. Finally, we will investigate relational databases. A<a name="INDEX-1951" />relational database management system(RDBMS) provides high performance even with complex queries. However,an RDBMS is more complicated to set up and use than the othersolutions.</p><p>Applications evolve and grow larger. What may start out as a short,simple CGI script may gain feature upon feature until it has grown toa large, complex application. Thus, when you design web applications,it is a good idea to develop them so that they are easily expandable.</p><p>One solution is to make your solutions modular. You should try toabstract the code that reads and writes data so the rest of the codedoes not know how the data is stored. By reducing the dependency onthe data format to a small chunk of code, it becomes easier to changeyour data format as you need to grow.</p><div class="sect1"><a name="ch10-62299" /><h2 class="sect1">10.1. Text Files</h2><p>One of Perl's greatest strengths is its ability to parse text,and this makes it especially easy to get a web application onlinequickly using <a name="INDEX-1952" />textfiles as the means of storing data. Although it does not scale tocomplex queries, this works well for small amounts of data and isvery common for Perl <a name="INDEX-1953" /> <a name="INDEX-1,954" />CGI applications. We're not going todiscuss how to use text files with Perl, since most Perl programmersare already proficient at that task. We're also not going tolook at strategies like creating random access files to improveperformance, since that warrants a lengthy discussion, and a DBM fileis generally a better substitute. We'll simply look at theissues that are particular to using text files with CGI scripts.</p><a name="ch10-77048" /><div class="sect2"><h3 class="sect2">10.1.1. Locking</h3><p>If you write to <a name="INDEX-1955" /><a name="INDEX-1956" /><a name="INDEX-1957" />any filesfrom a CGI script, then you must use some form of file locking. Webservers support numerous concurrent connections, and if two users tryto write to the same file at the same time, the result is generallycorrupted or truncated data.</p><a name="ch10-1-fm2xml" /><div class="sect3"><h3 class="sect3">10.1.1.1. flock</h3><p>If your system supports it, using the<em class="emphasis">flock</em><a name="INDEX-1958" /><a name="INDEX-1959" /> <a name="INDEX-1,960" /> <a name="INDEX-1,961" /> command is the easiest way to do this.How do you know if your system supports <tt class="function">flock</tt>?Try it: <tt class="function">flock</tt> will die with a fatal error ifyour system does not support it. However, <tt class="function">flock</tt>works reliably only on local files; <tt class="function">flock</tt> doesnot work across most <a name="INDEX-1962" />NFS systems, even if your systemotherwise supports it.<a href="#FOOTNOTE-19">[19]</a> <tt class="function">flock</tt> offers two different modes oflocking: <a name="INDEX-1963" /><a name="INDEX-1964" />exclusive and shared. Many processes canread from a file simultaneously without problems, but only oneprocess should write to the file at a time (and no other processshould read from the file while it is being written). Thus, youshould obtain an exclusive lock on a file when writing to it and ashared lock when reading from it. The shared lock verifies that noone else has an exclusive lock on the file and delays any exclusivelocks until the shared locks have been released.</p><blockquote><a name="FOOTNOTE-19" /><p>[19]If you need to lock a fileacross NFS, refer to the File::LockDir module in <em class="citetitle">PerlCookbook </em>(O'Reilly & Associates, Inc.).</p></blockquote><p>To use <tt class="function">flock</tt>, call it with a<a name="INDEX-1965" />filehandle to an open file and anumber indicating the type of lock you want. These numbers aresystem-dependent, so the easiest way to get them is to use the<a name="INDEX-1966" /><a name="INDEX-1967" /><a name="INDEX-1968" /><a name="INDEX-1969" />Fcntlmodule. If you supply the <tt class="literal">:flock</tt> argument toFcntl, it will export LOCK_EX, LOCK_SH, LOCK_UN, and LOCK_NB for you.You can use them as follows:</p><blockquote><pre class="code">use Fcntl ":flock";open FILE, "some_file.txt" or die $!;flock FILE, LOCK_EX; # Exclusive lockflock FILE, LOCK_SH; # Shared lockflock FILE, LOCK_UN; # Unlock</pre></blockquote><p>Closing a filehandle releases any locks, so there is generally noneed to specifically unlock a file. In fact, it can be dangerous todo so if you are locking a filehandle that uses<a name="INDEX-1970" /> <a name="INDEX-1,971" />Perl's <tt class="function">tie</tt>mechanism. See file locking in the DBM section of this chapter formore information.</p><p>Some systems do not support shared file locks and use exclusive locksfor them instead. You can use the script in <a href="ch10_01.htm#ch10-64147">Example 10-1</a> to test what <tt class="function">flock</tt>supports on your system.</p><a name="ch10-64147" /><div class="example"><h4 class="objtitle">Example 10-1. flock_test.pl </h4><blockquote><pre class="code">#!/usr/bin/perl -wTuse IO::File;use Fcntl ":flock";*FH1 = new_tmpfile IO::File or die "Cannot open temporary file: $!\n";eval { flock FH1, LOCK_SH };$@ and die "It does not look like your system supports flock: $@\n";open FH2, ">> &FH1" or die "Cannot dup filehandle: $!\n";if ( flock FH2, LOCK_SH | LOCK_NB ) { print "Your system supports shared file locks\n";}else { print "Your system only supports exclusive file locks\n";}</pre></blockquote></div><p>If you need to both read and write to a file, then you have twooptions: you can open the file exclusively for read/write access, orif you only have to do limited writing and what you're writingdoes not depend on the contents of the file, you can open and closethe file twice: once shared for reading and once exclusive forwriting. This is generally less efficient than opening the file once,but if you have lots of processes needing to access the file that aredoing lots of reading and little writing, it may be more efficient toreduce the time that one process is tying up the file while holdingan exclusive lock on it.</p><p>Typically when you use <tt class="function">flock</tt> to lock a file, ithalts the <a name="INDEX-1972" />execution of your script until it canobtain a lock on your file. The LOCK_NB option tells<tt class="function">flock</tt> that you do not want it to blockexecution, but allow your script to continue if it cannot obtain alock. Here is one way to time out if you cannot obtain a lock on afile:</p><blockquote><pre class="code">my $count = 0;my $delay = 1;my $max = 15;open FILE, ">> $filename" or error( $q, "Cannot open file: your data was not saved" );until ( flock FILE, LOCK_SH | LOCK_NB ) { error( $q, "Timed out waiting to write to file: " . "your data was not saved" ) if $count >= $max; sleep $delay; $count += $delay;}</pre></blockquote><p>In this example, the code tries to get a lock. If it fails, it waitsa second and tries again. After fifteen seconds, it gives up andreports an <a name="INDEX-1973" /> <a name="INDEX-1,974" /> <a name="INDEX-1,975" /> <a name="INDEX-1,976" />error.</p></div><a name="ch10-2-fm2xml" /><div class="sect3"><h3 class="sect3">10.1.1.2. Manual lock files</h3><p>If your system does not support flock, you will need to<a name="INDEX-1977" /><a name="INDEX-1978" /> <a name="INDEX-1,979" /><a name="INDEX-1980" />manually create your own lock files. Asthe Perl FAQ points out (see <em class="citetitle">perlfaq5 </em>), thisis not as simple as you might think. The problem is that you mustcheck for the existence of a file and create the file as oneoperation. If you first check whether a lock file exists, and thentry to create one if it does not, another process may have createdits own lock file after you checked, and you just overwrote it.</p><p>To create your own lock file, use the following command:</p><blockquote><pre class="code">use Fcntl;...sysopen LOCK_FILE, "$filename.lock", O_WRONLY | O_EXCL | O_CREAT, 0644 or error( $q, "Unable to lock file: your data was not saved" ):</pre></blockquote><p>The O_EXCL function provided by Fcntl tells the system to open thefile only if it does not already exist. Note that this will notreliably work on an NFS filesystem.</p></div></div><a name="ch10-3-fm2xml" /><div class="sect2"><h3 class="sect2">10.1.2. Write Permissions</h3><p>In order to create or update a <a name="INDEX-1981" /><a name="INDEX-1982" /> <a name="INDEX-1,983" /><a name="INDEX-1984" />text file, you must have theappropriate permissions. This may sound basic, but it is a commonsource of errors in CGI scripts, especially on Unix filesystems.Let's review how <a name="INDEX-1985" />Unix file permissions work.</p><p>Files have both an owner and a group. By default, these match theuser and group of the user or process who creates the file. There arethree different levels of permissions for a file: the owner'spermissions, the group's permissions, and everyone else'spermissions. Each of these may have read access, write access, and/orexecute access for a file.</p><p>Your CGI scripts can only modify a file if<em class="emphasis">nobody</em><a name="INDEX-1986" /> (or the user your web<a name="INDEX-1987" /><a name="INDEX-1988" />server runs as) has writeaccess to the file. This occurs if the file is writable by everyone,if it is writable by members of the file's group and<em class="emphasis">nobody</em> is a member of that group, or if<em class="emphasis">nobody</em> owns the file and the file is writable byits owner.</p><p>In order to create or remove a file, <em class="emphasis">nobody</em> musthave write permission to the directory containing the file. The samerules about owner, group, and other users apply to<a name="INDEX-1989" />directories as they do for files.In addition, the <a name="INDEX-1990" />execute bit must be set for the directory.For directories, the execute bit determines scan access, which is theability to change to the directory.</p><p>Even though your CGI script may <a name="INDEX-1991" />not modifya file, it may be able to replace it. If <em class="emphasis">nobody</em>has permission to write to a directory, then it can remove files inthe directory in addition to creating new files, even with the samename. Write permissions on the file do not typically <a name="INDEX-1992" /> <a name="INDEX-1,993" /> <a name="INDEX-1,994" />affect the abilityto remove or replace the file as a whole.</p></div><a name="ch10-92014" /><div class="sect2"><h3 class="sect2">10.1.3. Temporary Files</h3><p>Your CGI <a name="INDEX-1995" /><a name="INDEX-1996" />scripts mayneed to create temporary files for a number of reasons. You canreduce memory consumption by creating files to hold data as youprocess it; you gain efficiency by sacrificing performance. You mayalso use external commands that perform their actions on text files.</p><a name="ch10-4-fm2xml" /><div class="sect3"><h3 class="sect3">10.1.3.1. Anonymous temporary files</h3><p>Typically, <a name="INDEX-1997" /><a name="INDEX-1998" /><a name="INDEX-1999" /><a name="INDEX-2000" />temporary files are anonymous; theyare created by opening a <a name="INDEX-2001" />handle to a new file and thenimmediately deleting the file. Your CGI script will continue to havea filehandle to access the file, but the data cannot be accessed byother processes, and the data will be reclaimed by the filesystemonce your CGI script closes the filehandle. (Not all systems supportthis feature.)</p><p>As for most common tasks, there is a Perl module that makes managingtemporary files much simpler. <a name="INDEX-2002" /><a name="INDEX-2003" />IO::File will create anonymoustemporary files for you with the <tt class="function">new_tmpfile</tt>class method; it takes no arguments. You can use it likethis:<a href="#FOOTNOTE-20">[20]</a></p><blockquote>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -