月度归档:2008 年十月

修改mysql字符编码成为UTF8

在某些时候,我们续要修改mysql默认数据库的编码,以保证某些迁移的程序可以正常显示,编辑my.cnf文件进行编码修改,windows可以直接用Mysql Server Instance Config Wizard 进行设置.

安装后

/etc/init.d/mysql start (stop) 为启动和停止服务器

/etc/mysql/ 主要配置文件所在位置 my.cnf

/var/lib/mysql/ 放置的是数据库表文件夹,这里的mysql相当于windows下mysql的date文件夹

启动mysql后,以root登录mysql

isher@isher-ubuntu:~$ mysql -u root

>show variables like ‘character%’; #执行编码显示

+————————–+—————————-+

| Variable_name | Value |

+————————–+—————————-+

| character_set_client | latin1 |

| character_set_connection | latin1 |

| character_set_database | latin1 |

| character_set_filesystem | binary |

| character_set_results | latin1 |

| character_set_server | latin1 |

| character_set_system | utf8 |

| character_sets_dir | /usr/share/mysql/charsets/ |

+————————–+—————————-+

在某些时候,我们需要修改mysql默认数据库的编码,以保证某些迁移的程序可以正常显示,编辑my.cnf文件进行编码修改,windows可以直接用Mysql Server Instance Config Wizard 进行设置

在linux下修改3个my.cnf的1个/etc/mysql/my.cnf文件

找到客户端配置[client] 在下面添加

default-character-set=utf8 默认字符集为utf8

在找到[mysqld] 添加

default-character-set=utf8 默认字符集为utf8

init_connect=’SET NAMES utf8′ (设定连接mysql数据库时使用utf8编码,以让mysql数据库为utf8运行)

修改好后,重新启动mysql 即可,查询一下show variables like ‘character%’;

+————————–+—————————-+

| Variable_name | Value |

+————————–+—————————-+

| character_set_client | utf8 |

| character_set_connection | utf8 |

| character_set_database | utf8 |

| character_set_filesystem | binary |

| character_set_results | utf8 |

| character_set_server | utf8 |

| character_set_system | utf8 |

| character_sets_dir | /usr/share/mysql/charsets/ |

+————————–+—————————-+

此方法用于标准mysql版本同样有效,对于/etc/my.cnf文件,需要从mysql/support-files的文件夹cp my-large.cnf一份到/etc/my.cnf。

com.mysql.jdbc.MysqlDataTruncation: Data too long

JDBC异常信息:
Caused by: com.mysql.jdbc.MysqlDataTruncation: Data truncation: Data too long for column ‘content’ at row 1

确认数据不可能过长的情况下,应该是字符集的问题。

查看表信息:

Name        Engine  Version  Row_format    Rows  Avg_row_length  Data_length  Max_data_length  Index_length  Data_free  Auto_increment  Create_time          Update_time  Check_time  Collation          Checksum  Create_options  Comment
———-  ——  ——-  ———-  ——  ————–  ———–  —————  ————  ———  ————–  ——————-  ———–  ———-  —————–  ——–  ————–  ——————–
article  InnoDB       10  Compact          1           16384        16384                0             0          0               3  2008-06-02 17:15:40  (NULL)       (NULL)      latin1_swedish_ci    (NULL)                  InnoDB free: 9216 kB

将latin1改为utf8, 问题解决。

爬虫工作原理及关键技术概述

网络爬虫是一个自动提取网页的程序,它为搜索引擎从Internet网上下载网页,是搜索引擎的重要组成。传统爬虫从一个或若干初始网页的URL开始,获 得初始网页上的URL,在抓取网页的过程中,不断从当前页面上抽取新的URL放入队列,直到满足系统的一定停止条件。聚焦爬虫的工作流程较为复杂,需要根 据一定的网页分析算法过滤与主题无关的链接,保留有用的链接并将其放入等待抓取的URL队列。然后,它将根据一定的搜索策略从队列中选择下一步要抓取的网 页URL,并重复上述过程,直到达到系统的某一条件时停止,另外,所有被爬虫抓取的网页将会被系统存贮,进行一定的分析、过滤,并建立索引,以便之后的查 询和检索;对于聚焦爬虫来说,这一过程所得到的分析结果还可能对以后的抓取过程给出反馈和指导。

相对于通用网络爬虫,聚焦爬虫还需要解决三个主要问题:
(1) 对抓取目标的描述或定义;
(2) 对网页或数据的分析与过滤;
(3) 对URL的搜索策略。
抓取目标的描述和定义是决定网页分析算法与URL搜索策略如何制订的基础。而网页分析算法和候选URL排序算法是决定搜索引擎所提供的服务形式和爬虫网页抓取行为的关键所在。这两个部分的算法又是紧密相关的。

2 抓取目标描述
现有聚焦爬虫对抓取目标的描述可分为基于目标网页特征、基于目标数据模式和基于领域概念3种。
基于目标网页特征的爬虫所抓取、存储并索引的对象一般为网站或网页。根据种子样本获取方式可分为:
(1) 预先给定的初始抓取种子样本;
(2) 预先给定的网页分类目录和与分类目录对应的种子样本,如Yahoo!分类结构等;
(3) 通过用户行为确定的抓取目标样例,分为:

  • 用户浏览过程中显示标注的抓取样本;
  • 通过用户日志挖掘得到访问模式及相关样本。

其中,网页特征可以是网页的内容特征,也可以是网页的链接结构特征,等等。
现有的聚焦爬虫对抓取目标的描述或定义可以分为基于目标网页特征,基于目标数据模式和基于领域概念三种。
基 于目标网页特征的爬虫所抓取、存储并索引的对象一般为网站或网页。具体的方法根据种子样本的获取方式可以分为:(1)预先给定的初始抓取种子样本;(2) 预先给定的网页分类目录和与分类目录对应的种子样本,如Yahoo!分类结构等;(3)通过用户行为确定的抓取目标样例。其中,网页特征可以是网页的内容 特征,也可以是网页的链接结构特征,等等。

基于目标数据模式的爬虫针对的是网页上的数据,所抓取的数据一般要符合一定的模式,或者可以转化或映射为目标数据模式。

另一种描述方式是建立目标领域的本体或词典,用于从语义角度分析不同特征在某一主题中的重要程度。

3 网页搜索策略
网页的抓取策略可以分为深度优先、广度优先和最佳优先三种。深度优先在很多情况下会导致爬虫的陷入(trapped)问题,目前常见的是广度优先和最佳优先方法。
3.1 广度优先搜索策略
广 度优先搜索策略是指在抓取过程中,在完成当前层次的搜索后,才进行下一层次的搜索。该算法的设计和实现相对简单。在目前为覆盖尽可能多的网页,一般使用广 度优先搜索方法。也有很多研究将广度优先搜索策略应用于聚焦爬虫中。其基本思想是认为与初始URL在一定链接距离内的网页具有主题相关性的概率很大。另外 一种方法是将广度优先搜索与网页过滤技术结合使用,先用广度优先策略抓取网页,再将其中无关的网页过滤掉。这些方法的缺点在于,随着抓取网页的增多,大量 的无关网页将被下载并过滤,算法的效率将变低。

3.2 最佳优先搜索策略
最佳优先搜索策略按照一定的网页分 析算法,预测候选URL与目标网页的相似度,或与主题的相关性,并选取评价最好的一个或几个URL进行抓取。它只访问经过网页分析算法预测为“有用”的网 页。存在的一个问题是,在爬虫抓取路径上的很多相关网页可能被忽略,因为最佳优先策略是一种局部最优搜索算法。因此需要将最佳优先结合具体的应用进行改 进,以跳出局部最优点。将在第4节中结合网页分析算法作具体的讨论。研究表明,这样的闭环调整可以将无关网页数量降低30%~90%。

4 网页分析算法

网页分析算法可以归纳为基于网络拓扑、基于网页内容和基于用户访问行为三种类型。
4.1 基于网络拓扑的分析算法
基于网页之间的链接,通过已知的网页或数据,来对与其有直接或间接链接关系的对象(可以是网页或网站等)作出评价的算法。又分为网页粒度、网站粒度和网页块粒度这三种。
4.1.1 网页(Webpage)粒度的分析算法
PageRank 和HITS算法是最常见的链接分析算法,两者都是通过对网页间链接度的递归和规范化计算,得到每个网页的重要度评价。PageRank算法虽然考虑了用户 访问行为的随机性和Sink网页的存在,但忽略了绝大多数用户访问时带有目的性,即网页和链接与查询主题的相关性。针对这个问题,HITS算法提出了两个 关键的概念:权威型网页(authority)和中心型网页(hub)。

基于链接的抓取的问题是相关页面主题团之间的隧道现象,即很 多在抓取路径上偏离主题的网页也指向目标网页,局部评价策略中断了在当前路径上的抓取行为。文献[21]提出了一种基于反向链接(BackLink)的分 层式上下文模型(Context Model),用于描述指向目标网页一定物理跳数半径内的网页拓扑图的中心Layer0为目标网页,将网页依据指向目标网页的物理跳数进行层次划分,从外 层网页指向内层网页的链接称为反向链接。

4.1.2 网站粒度的分析算法
网站粒度的资源发现和管理策略也比网页粒度的更 简单有效。网站粒度的爬虫抓取的关键之处在于站点的划分和站点等级(SiteRank)的计算。SiteRank的计算方法与PageRank类似,但是 需要对网站之间的链接作一定程度抽象,并在一定的模型下计算链接的权重。
网站划分情况分为按域名划分和按IP地址划分两种。文献[18]讨 论了在分布式情况下,通过对同一个域名下不同主机、服务器的IP地址进行站点划分,构造站点图,利用类似PageRank的方法评价SiteRank。同 时,根据不同文件在各个站点上的分布情况,构造文档图,结合SiteRank分布式计算得到DocRank。文献[18]证明,利用分布式的 SiteRank计算,不仅大大降低了单机站点的算法代价,而且克服了单独站点对整个网络覆盖率有限的缺点。附带的一个优点是,常见PageRank 造假难以对SiteRank进行欺骗。
4.1.3 网页块粒度的分析算法
在一个页面中,往往含有多个指向其他页面的链接,这些 链接中只有一部分是指向主题相关网页的,或根据网页的链接锚文本表明其具有较高重要性。但是,在PageRank和HITS算法中,没有对这些链接作区 分,因此常常给网页分析带来广告等噪声链接的干扰。在网页块级别(Blocklevel)进行链接分析的算法的基本思想是通过VIPS网页分割算法将网 页分为不同的网页块(page block),然后对这些网页块建立pagetoblock和blocktopage的链接矩阵,分别记为Z和X。于是,在 pagetopage图上的网页块级别的PageRank为Wp=X×Z;在blocktoblock图上的BlockRank为 Wb=Z×X。已经有人实现了块级别的PageRank和HITS算法,并通过实验证明,效率和准确率都比传统的对应算法要好。
4.2 基于网页内容的网页分析算法
基 于网页内容的分析算法指的是利用网页内容(文本、数据等资源)特征进行的网页评价。网页的内容从原来的以超文本为主,发展到后来动态页面(或称为 Hidden Web)数据为主,后者的数据量约为直接可见页面数据(PIW,Publicly Indexable Web)的400~500倍。另一方面,多媒体数据、Web Service等各种网络资源形式也日益丰富。因此,基于网页内容的分析算法也从原来的较为单纯的文本检索方法,发展为涵盖网页数据抽取、机器学习、数据 挖掘、语义理解等多种方法的综合应用。本节根据网页数据形式的不同,将基于网页内容的分析算法,归纳以下三类:第一种针对以文本和超链接为主的无结构或结 构很简单的网页;第二种针对从结构化的数据源(如RDBMS)动态生成的页面,其数据不能直接批量访问;第三种针对的数据界于第一和第二类数据之间,具有 较好的结构,显示遵循一定模式或风格,且可以直接访问。

Nutch爬虫工作流程及文件格式详细分析(收藏)

Crawler和Searcher两部分尽量分开的目的主要是为了使两部分可以分布式配置在硬件平台上,例如将Crawler和Searcher分别放在两个主机上,这样可以提升性能。

爬虫,Crawler:

Crawler的重点在两个方面,Crawler的工作流程和涉及的数据文件的格式和含义。数据文件主要包括三类,分别是 web database,一系列的segment加上index,三者的物理文件分别存储在爬行结果目录下的db目录下webdb子文件夹内, segments文件夹和index文件夹。那么三者分别存储的信息是什么呢?

Web database,也叫WebDB,其中存储的是爬虫所抓取网页之间的链接结构信息,它只在爬虫Crawler工作中使用而和 Searcher的工作没有任何关系。WebDB内存储了两种实体的信息:page和link。Page实体通过描述网络上一个网页的特征信息来表征一个 实际的网页,因为网页有很多个需要描述,WebDB中通过网页的URL和网页内容的MD5两种索引方法对这些网页实体进行了索引。Page实体描述的网页 特征主要包括网页内的link数目,抓取此网页的时间等相关抓取信息,对此网页的重要度评分等。同样的,Link实体描述的是两个page实体之间的链接 关系。WebDB构成了一个所抓取网页的链接结构图,这个图中Page实体是图的结点,而Link实体则代表图的边。

一次爬行会产生很多个segment,每个segment内存储的是爬虫Crawler在单独一次抓取循环中抓到的网页以及这些网页的索引。 Crawler爬行时会根据WebDB中的link关系按照一定的爬行策略生成每次抓取循环所需的fetchlist,然后Fetcher通过 fetchlist中的URLs抓取这些网页并索引,然后将其存入segment。Segment是有时限的,当这些网页被Crawler重新抓取后,先 前抓取产生的segment就作废了。在存储中。Segment文件夹是以产生时间命名的,方便我们删除作废的segments以节省存储空间。

Index是Crawler抓取的所有网页的索引,它是通过对所有单个segment中的索引进行合并处理所得的。Nutch利用Lucene 技术进行索引,所以Lucene中对索引进行操作的接口对Nutch中的index同样有效。但是需要注意的是,Lucene中的segment和 Nutch中的不同,Lucene中的segment是索引index的一部分,但是Nutch中的segment只是WebDB中各个部分网页的内容和 索引,最后通过其生成的index跟这些segment已经毫无关系了。

Crawler工作流程:

在分析了Crawler工作中设计的文件之后,接下来我们研究一下Crawler的抓取流程以及这些文件在抓取中扮演的角色。Crawler的 工作原理主要是:首先Crawler根据WebDB生成一个待抓取网页的URL集合叫做Fetchlist,接着下载线程Fetcher开始根据 Fetchlist将网页抓取回来,如果下载线程有很多个,那么就生成很多个Fetchlist,也就是一个Fetcher对应一个Fetchlist。 然后Crawler根据抓取回来的网页WebDB进行更新,根据更新后的WebDB生成新的Fetchlist,里面是未抓取的或者新发现的URLs,然 后下一轮抓取循环重新开始。这个循环过程可以叫做“产生/抓取/更新”循环。

指向同一个主机上Web资源的URLs通常被分配到同一个Fetchlist中,这样的话防止过多的Fetchers对一个主机同时进行抓取造 成主机负担过重。另外Nutch遵守Robots Exclusion Protocol,网站可以通过自定义Robots.txt控制Crawler的 抓取。

在Nutch中,Crawler操作的实现是通过一系列子操作的实现来完成的。这些子操作Nutch都提供了子命令行可以单独进行调用。下面就是这些子操作的功能描述以及命令行,命令行在括号中。

1.       创建一个新的WebDb (admin db -create).

2.       将抓取起始URLs写入WebDB中 (inject).

3.       根据WebDB生成fetchlist并写入相应的segment(generate).

4.       根据fetchlist中的URL抓取网页 (fetch).

5.       根据抓取网页更新WebDb (updatedb).

6.       循环进行3-5步直至预先设定的抓取深度。

7.       根据WebDB得到的网页评分和links更新segments (updatesegs).

8.       对所抓取的网页进行索引(index).

9.       在索引中丢弃有重复内容的网页和重复的URLs (dedup).

10.   将segments中的索引进行合并生成用于检索的最终index(merge).

Crawler详细工作流程是:在创建一个WebDB之后(步骤1), “产生/抓取/更新”循环(步骤3-6)根据一些种子URLs开始启 动。当这个循环彻底结束,Crawler根据抓取中生成的segments创建索引(步骤7-10)。在进行重复URLs清除(步骤9)之前,每个 segment的索引都是独立的(步骤8)。最终,各个独立的segment索引被合并为一个最终的索引index(步骤10)。

其中有一个细节问题,Dedup操作主要用于清除segment索引中的重复URLs,但是我们知道,在WebDB中是不允许重复的URL存在 的,那么为什么这里还要进行清除呢?原因在于抓取的更新。比方说一个月之前你抓取过这些网页,一个月后为了更新进行了重新抓取,那么旧的segment在 没有删除之前仍然起作用,这个时候就需要在新旧segment之间进行除重。

另外这些子操作在第一次进行Crawler运行时可能并不用到,它们主要用于接下来的索引更新,增量搜索等操作的实现。这些在以后的文章中我再详细讲。

个性化设置:

Nutch中的所有配置文件都放置在总目录下的conf子文件夹中,最基本的配置文件是conf/nutch-default.xml。这个文 件中定义了Nutch的所有必要设置以及一些默认值,它是不可以被修改的。如果你想进行个性化设置,你需要在conf/nutch-site.xml进行 设置,它会对默认设置进行屏蔽。

Nutch考虑了其可扩展性,你可以自定义插件plugins来定制自己的服务,一些plugins存放于plugins子文件夹。Nutch 的网页解析与索引功能是通过插件形式进行实现的,例如,对HTML文件的解析与索引是通过HTML document parsing plugin,  parse-html实现的。所以你完全可以自定义各种解析插件然后对配置文件进行修改,然后你就可以抓取并索引各种类型的文件了。

php提高访问效率的方法——页面静态化——缓存

在速度上,静态页面要比动态页面的比方php快很多,这是毫无疑问的,但是静态页面的灵活性较差。
做静态页面的几个关键:
其实页面静态化就是页面级缓存,相当于把整个html页面缓存起来,用的时候跳过数据库直接读文件。

ob_start()函数:打开输出缓冲区.
函数格式 void ob_start(void)
说明:当缓冲区激活时,所有来自PHP程序的非文件头信息均不会发送,而是保存在内部缓冲区。为了输出缓冲区的内容,可以使用ob_end_flush()或flush()输出缓冲区的内容。

Flush:刷新缓冲区的内容,输出。
函数格式:flush()
说明:这个函数经常使用,效率很高。

ob_get_contents :返回内部缓冲区的内容。
函数格式:string ob_get_contents(void)
说明:这个函数会返回当前缓冲区中的内容,如果输出缓冲区没有激活,则返回 FALSE.

ob_get_length:返回内部缓冲区的长度。
函数格式:int ob_get_length(void)
说明:这个函数会返回当前缓冲区中的长度;和ob_get_contents一样,如果输出缓冲区没有激活,则返回 FALSE.

ob_end_clean:删除内部缓冲区的内容,并且关闭内部缓冲区
函数格式:void ob_end_clean(void)
说明:这个函数不会输出内部缓冲区的内容而是把它删除

ob_end_flush:发送内部缓冲区的内容到浏览器,并且关闭输出缓冲区
函数格式:void ob_end_flush(void)
说明:这个函数发送输出缓冲区的内容(如果有的话)

ob_implicit_flush:打开或关闭绝对刷新
函数格式:void ob_implicit_flush ([int flag])
说明:默认为关闭缓冲区,打开绝对输出后,每个脚本输出都直接发送到浏览器,不再需要调用 flush()
具体应用中有ob_start()和ob_get_contents()就足够了。
关于缓存:包括页面级缓存,数据库级缓存,页面级缓存一般访问键,数据库级缓存现在流行的是写内存,这里要介绍的也是写文件。。(转载)

SQL查询缓存

适合读者

本教程适合于那些对缓存SQL查询以减少数据库连接与执行的负载、提高脚本性能感兴趣的PHP程序员。

概述

许多站点使用数据库作为站点数据存储的容器。数据库包含了产器信息、目录结构、文章或者留言本,有些数据很可能是完全静态的,这些将会从一个缓存系统中得到的极大好处。

这样一个系统通过把SQL查询的结果缓存到系统的一个文件中存储,从而阻止连接数据库,构造查询与取得返回结果而提高了响应时间。

有些系统数据库并不是放在WEB服务器上的,这样需要一个远程连接(TCP或者其它类似的),或者从数据库中获取大量的数据,这样你得忍受更多时间,这决定于系统响应时间与资源利用。

前提

本教程使用MySQL作为数据库。你需要安装MySQL(www.mysql.com下载是有效的)和激活PHP MYSQL扩展(默认情况是激活的)

由于要查询数据库,你需要知识一些SQL(结构化查询语言)的基本常识。

缓存SQL查询结果

为什么要缓存查询结果?

缓存查询结果能极大地改进脚本执行时间和资源需求。

缓存SQL查询结果也允许你通过后期处理数据。如果你用文件缓存去存储全部脚本的输出结果(HTML输出),这样可能是行不通的。

当你执行一个SQL查询时,点典的处理过程是:

l 连接数据库

l 准备SQL查询

l 发送查询到数据库

l 取得返回结果

l 关闭数据库连接

以上方法非常占用资源并且相反的影响了脚本的性能。只能通过取得的大量返回数据和数据库服务器的位置这二个要素来相互协调。尽管持续连接可以改进连接数据库时的负载,但非常耗费内存资源,如果获取的是大量的数据,那么存储的全部时间会非常短暂。

创建一条SQL查询:

SQL(结构化查询语言)查询被用作操作数据库及它内容的接口。SQL可用于定义和编辑表的结构,插入数据到表,更新或删除表中的信息。

SQL是用于与数据通讯的语言,在大多数PHP数据库扩展(MySQL,ODBC,Oracle)通过传递SQL查询到数据库中来管理整个过程。

本教程中,仅仅用select语言来获取数据库中的数据。这些数据将被缓存,之后将用作数据源。

决定什么时候更新缓存:

根据程序的需要,缓存可以采取多种形式。最常见的3种方式是:

l 时间触发缓存(过期的时间戳)

l 内容改变触发缓存(发现数据改变后,相应地更新缓存)

l 人工触发缓存(人工的方式告知系统信息超期并且强制产生新的缓存)

你的缓存需求可能是以上原理的一个或多个的综合。本教程将讨论时间触发方式。然而,在一个全面的缓存机制中,3种方式的综合将被使用。

缓存结果:

基本的缓存是用PHP的两个函数serialize()unserialize()(译注:这二个函数分别代表序列化与反序列化)

函数serialize()用于存储PHP的值,它能保证不失去这些值的类型和结构。

事实上,PHPsession扩展是用序列化过的变量,把session变量($_SESSION)存储在系统的一个文件中。

函数unserialize()与以上操作相反并且使序列化过的字符串返回到它原来的结构和数据内容。

在本例中,以一个电子商务商店为例。商店有2个基本表,categoriesproducts(此处为原始数据库表名).product表可能每天都在变化,categories仍然是不变静止的。

要显示产品,你可以用一个输出缓存脚本来存储输出的HTML结果到一个文件中。然而categories表可能需要后期处理。例如,所有的目录通过变量category_id(通过$_REQUEST['category_id']来取得)被显示,你可能希望高亮当前被选择的目录。

categories结构

Field

Type

Key

Extra

category_id

category_name

category_description

int(10) unsigned

varchar(255)

text

PRI

auto_incremen

在本例中,通过时间触发缓存技术被运用,设定一段时间后让其缓存SQL输出过期。在此特殊的例子中,定一段时间为24小时。

序列化例子:

l 连接数据库

l 执行查询

l 取得所有结果构成一个数组以便后面你可以访问

l 序列化数组

l 保存序列化过的数组到文件中

$file = ‘sql_cache.txt’;

$link = mysql_connect(‘localhost’,'username’,'password’)

or die (mysql_error());

mysql_select_db(‘shop’)

or die (mysql_error());

/* 构造SQL查询 */

$query = “SELECT * FROM categories”;

$result = mysql_query($query)

or die (mysql_error());

while ($record = mysql_fetch_array($result) )

{

$records[] = $record;

}

$OUTPUT = serialize($records);

$fp = fopen($file,”w”); // 以写权限的方式打开文件

fputs($fp, $OUTPUT);

fclose($fp);

查看sql_cache.txt文件,里面的内容可能类似这样的:

a:1:{i:0;a:6:{i:0;s:1:”1″;s:11:”category_id”;s:1:”1″;i:1;s:9:”Computers”;s:13:”category_name”;s:9:

“Computers” ;i:2;s:25:”Description for computers”;s:20:”category_description”

;s:25:”Description for computers”;}}

这个输出是它的变量和类型的内部表现形式。假若你用mysql_fetch_array()函数返回数字索引的数组和一个关联的数组(这就是为什么数据看起来像是发生了两次),一个是数字索引,另一个是字符串索引。

使用缓存:

要用缓存,你需要用函数unserialize()来使数据还原成原始格式与类型。

你可以用file_get_contents()这个函数来读取sql_cache.txt文件的内容,把它赋给一个变量。

请注意:这个函数在PHP4.3.0及以上版本有效。若你使用的是一个老版本的PHP,一个简单的方法是用file()函数(读整个文件到一个数组,每行变成一个数组)implode()函数用于把数组的各元素连接成一个字符串然后使用unserialize()反序列化。

// file_get_contents() 适合于for PHP < 4.3.0

$file = ‘sql_cache.txt’;

$records = unserialize(implode(”,file($file)));

现在你可以通过$records数组并且取得原始查询的数据:

foreach ($records as $id=>$row) {

print $row['category_name'].”<br>”;

}

注意$records是数组(一个包含了查询结果的数字索引列——每行是一个数字和一个字符串真是混乱)的一排。

把它们放在一块:

基于本例子中的时间来决定是否缓存。如果文件修改的时间戳比当前时间戳减去过期时间戳大,那么就用缓存,否则更新缓存。

l 检查文件是否存在并且时间戳小于设置的过期时间

l 获取存储在缓存文件中的记录或者更新缓存文件

$file = ‘sql_cache.txt’;

$expire = 86400; // 24 小时 (单位:秒)

if (file_exists($file) &&

filemtime($file) > (time() – $expire))

{

// 取得缓存中的记录

$records = unserialize(file_get_contents($file));

} else {

// 通过 serialize() 函数创建缓存

}

附加其它可能的:

l 把缓存结果存储在共享内存中以获取更快的速度

l 增加一个功能随机地运行SQL查询并且检查是否输出与缓存输出一致。如果不一致,则更新缓存(本函数运行次数的概率可以定为1/100)。通过哈希算法(如MD5())可以协助判断字符串或者文件是否改变。

l 增加一个管理员的功能,人工的删除这个缓存文件,以强制更新缓存(如file_exists()函数返回false时)。你可以用函数unlink()删除文件。

脚本:

$file = ‘sql_cache.txt’;

$expire = 86400; // 24 小时

if (file_exists($file) &&

filemtime($file) > (time() – $expire)) {

$records = unserialize(file_get_contents($file));

} else {

$link = mysql_connect(‘localhost’,'username’,'password’)

or die (mysql_error());

mysql_select_db(‘shop’)

or die (mysql_error());

/* 构造SQL查询 */

$query = “SELECT * FROM categories”;

$result = mysql_query($query)

or die (mysql_error());

while ($record = mysql_fetch_array($result) ) {

$records[] = $record;

}

$OUTPUT = serialize($records);

$fp = fopen($file,”w”);

fputs($fp, $OUTPUT);

fclose($fp);

} // end else

// 查询结果在数组 $records

foreach ($records as $id=>$row) {

if ($row['category_id'] == $_REQUEST['category_id']) {

// 被选择的目录显示粗体字

print ‘<B>’.$row['category_name'].’</B><BR>’;

} else {

// 其它目录显示用常规字体

print $row['category_name'].’<br>’;

}

} // end foreach

关于作者

Ori Staub是专注于研究基于WEB解决方案的中级系统分析员,开发者和顾问。可以通过电子邮件os@zucker-staub.com联系他。

法国人眼中的中国

这两天陪一个法国女孩在上海逛街,用蹩脚的英文加上富有幽默感的汉语亲身体验了一把与老外的面对面的交流,感触颇深。

1.她对中国了解其实非常少,对于除了几个国际巨星(包括毛泽东,但也只知道他是中国人民最崇拜的人)之外的中国名人(包括科学家,数学家,企业家。。。)几乎一概不知,对中国为什么落后也更是一知半解。

2.来到中国,似乎有很多事情想不通,不明白上海怎么有这么多的高楼(人多不要紧,不用扎一堆);不明白为什么背包不能背在身后面(你们谁是贼?站出来给俺老乡看看);不明白为什么见到路边无缘无故提供唱歌服务并向你伸手要钱的人就要逃开或者只给一个铜板(虽然没有大牌名星那么动听,也没人鼓掌,但至少还算付出劳动了吧--只有几个人知道唱的是“小草”);为什么那么多人都觉得外滩很美(明明水很脏);为什么放假了大家都往一个地方跑(明明知道人会那么多,挤都挤s了,哪还有心情玩。但中国那么大,别的地方都不好玩了吗?);为什么大家在公共场所吃饭的时候嗓门都那么大(跟吵架似的);为什么中国人用餐都n多人share同一份菜,我的解释是中国人都是有福同享、有难同当的(万一有一个人有个啥病,其他人也都会很自然地跟着受罪,并一直在猜想:认命吧,这是遗传);为什么中国人很多喜欢吃辣椒(难道没了它就没了胃口?);为什么中国人觉得英语很难学,明明语法和中文很像啊,看看英文电影,交个英美朋友就完全搞定了(法语语法才叫个难呢,而且是越学越难,不过用的人少,不用刻意去学);为什么路边有那么多卖盗版光盘的摊子(中国人真幸福!捡了个大便宜);

3.感叹中国人口真是众多,光上海就有1500万(北京1600万,整个法国也不过就9000多万)。

4.在他们的印象中,中国人的奴化思想比较严重,大部分没有多少主见(准确地说是没有优生优育吧,没有能力让孩子享福也要滥生无辜)。

5.在他们的印象中,中国人很穷很落后,连竞争电影广告费的钱都出不起,害得她们想看中国电影都不知道到哪家影院(在我的启发之下,同时也承认中国近些年发展很快,很多欧洲人都想来中国发展,学汉语的人也在增多,慢慢回想起还看过《卧虎藏龙》,但不承认是中国人拍的,而香港人拍的,汗!)。

6.在他们与台湾人香港人的接触过程中已经有了一个印象:台湾和香港的一部分人都不愿意说自己是中国人(觉得中国人没本事,在香港处于殖民地的期间不愿意收复,待到发达了才收回来,我也解释了半天:打仗也是要会出代价的,只要大陆的经济及各方面都超过了美国,就不信他们还不想当中国人)。

7.在她的眼里好像我这年龄(快奔三了!!!)也差不多该应该有车了(可以开着car而不是by bus,其实我是可以买车了,不过解决住房问题似乎优先级更高一些)。