Sphinx+Mysql初使用体验

应用越来越多的需要全文搜索技术来支撑,在Java中可以使用Lucene,一个非常优秀的引擎,在Hibernate中也整合了Lucene来做检索,但在使用PHP的过程中迫切需要找一个优秀的全文搜索引擎(虽然也可以把PHP和Lucene结合起来使用,但有些另类,有些生产环境也不能同时支持),以前在网上看到一些Sphinx的文章,一直没有实践,昨天我就花了一天的时间,配置测试了一下Sphinx。

由于我的PHP没有编译Sphinx模块,所以我主要是配置Sphinx+Mysql,在Mysql上测试全文搜索的效果,Mysql、Sphinx、中文分词的编译安装过程不详述,下面两篇文章挺好,安装时可以参考

Mysql+sphinx+中文分词简介

基于Sphinx+MySQL的千万级数据全文检索(搜索引擎)架构设计

我的编译安装过程没有遇到什么麻烦,就是编译Mysql的时候比较长,而且要注意把Innodb的引擎编译进去,我的Mysql编译参数如下

./configure –with-plugins=sphinx,innobase,heap –prefix=/usr/local/mysql –enable-assembler –with-charset=utf8 –with-extra-charsets=all –enable-thread-safe-client –with-big-tables –with-readline –with-ssl –with-embedded-server –enable-local-infile

比较奇怪的是innodb是支持了,heap不支持,这个问题下次再研究。

通过编译Mysql,使Mysql支持了Sphinx存储引擎,试了一下Sphinx的例子,成功搜索到了数据,如果我们要对自己的数据做索引,就要研究一下Sphinx的配置文件了。

Sphinx的配置文件在其安装目录下的etc目录下,你可以参考其例子的sphinx.conf创建自己的配置文件,在sphinx里有主要要配置的有两大块,一部分是source(数据源),另一部分是index(索引),source里面定义了连接数据库的参数,取得源数据的SQL,也就是你要索引的数据的取得 SQL(Sphinx是支持不同数据源的,我这里只测试SQL),source可以有继承关系,继承的source可以用来做取得增量数据,index里面定义了使用哪个source,index存放的路径、字符集、辞典等等,index也可以继承,继承的index用来做增量索引。由此可见source 和index都是根据你的需要配置的,可以取得多个数据源的数据,可以建立多个索引。

增量索引的小困惑,我在数据库中增加了2条记录,执行

/usr/local/sphinx/bin/indexer –rotate –config /usr/local/sphinx/etc/sphinx.conf test1stemmed

看到有两个文档被加入索引(增量部分),之后我执行

/usr/local/sphinx/bin/indexer –rotate –merge test1 test1stemmed –merge-dst-range deleted 0 0

将增量索引并入主索引,这是可以查询到新插入的数据,这时我再继续插入一条数据,执行

/usr/local/sphinx/bin/indexer –rotate –config /usr/local/sphinx/etc/sphinx.conf test1stemmed

提示信息是有3个文档被加入索引,让我非常奇怪,因为上两条纪录已经并入主索引了,这次怎么还会索引呢?之后我执行

/usr/local/sphinx/bin/indexer –rotate –config /usr/local/sphinx/etc/sphinx.conf test1

更新主索引,之后再执行/usr/local/sphinx/bin/indexer –rotate –config /usr/local/sphinx/etc/sphinx.conf test1stemmed提示没有索引加入,这样就正确了,如果按照测试的结果,增量索引和主索引更新执行的时间要计划好。

关于中文分词–LibMMSeg:LibMMSeg 是Coreseek.com为 Sphinx 全文搜索引擎设计的中文分词软件包,其在GPL协议下发行的中文分词法,采用Chih-Hao Tsai的MMSEG算法。

同时Coreseek.com提供了一份Sphinx的中文文档,里面有比较详细的配置说明,是很好的参考资料,非常感谢开源人士做出的贡献。

Short Url(短网址)实现方式

现在提供Short Url(短网址)的网站越来越多了,特别是Twitter的助Short Url的一臂之力,越来越多的人开始使用Short Url-短网址,现在比较流行的几个Short Url网站有

TinyURL
http://tinyurl.com/

Bit.Ly
http://bit.ly/

Is.Gd
http://is.gd/

等等,Short Url的作用在于把长的Url缩成短的Url,比如,我前两天些的一篇博客,Url是https://i.laoer.com/think-about-http-get-chinese-encode-error.html,我们采用TinyURL,转成的Url是http://tinyurl.com/d4zw8x,只有25个字符,短了很多,请求http://tinyurl.com/d4zw8x的时候,tinyurl会把请求通过HTTP 301转到https://i.laoer.com/think-about-http-get-chinese-encode-error.html上。

实现Short Url的功能并不复杂,但最关键的就是这个短代码要够短,而且需要唯一,我们的例子是“d4zw8x”,6位,还有就是用户输入的同一个Url,应该返回唯一的Short Url,用户在请求长的Url之后,先从数据库查找一下这个长Url是否存在,如果存在,就直接取出其对应的短代码,如果不存在,则生成短代码,与用户的长Url同时保存在数据库中。

最核心的这个短代码的实现方式,我在网上找了两个

第一个是纯随机数的算法,来自Short URL Script

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function random($length, $pool = '')
    {
        $random = '';
 
        if (empty($pool)) {
            $pool    = 'abcdefghkmnpqrstuvwxyz';
            $pool   .= '23456789';
        }
 
        srand ((double)microtime()*1000000);
 
        for($i = 0; $i < $length; $i++) 
        {
            $random .= substr($pool,(rand()%(strlen ($pool))), 1);
        }
 
        return $random;
    }

另一个算法来自http://www.snippetit.com/2009/04/php-short-url-algorithm-implementation/

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
function shorturl($input) {
  $base32 = array (
    'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
    'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p',
    'q', 'r', 's', 't', 'u', 'v', 'w', 'x',
    'y', 'z', '0', '1', '2', '3', '4', '5'
    );
 
  $hex = md5($input);
  $hexLen = strlen($hex);
  $subHexLen = $hexLen / 8;
  $output = array();
 
  for ($i = 0; $i < $subHexLen; $i++) {
    $subHex = substr ($hex, $i * 8, 8);
    $int = 0x3FFFFFFF & (1 * ('0x'.$subHex));
    $out = '';
 
    for ($j = 0; $j < 6; $j++) {
      $val = 0x0000001F & $int;
      $out .= $base32[$val];
      $int = $int >> 5;
    }
 
    $output[] = $out;
  }
 
  return $output;
}

其返回的是一个4个元素的数组,应为存在可能的重复性,你可以依次使用这4个元素。

Short Url的算法应该还有一些,Short Url网站的作用除了缩短网址以外,在使用者不断增加之后,可以积累庞大的网址信息,这对统计分析是很有用的。

GET方式传递中文错误引发的思考

今天在“天乙社区”里,网友报了一个BUG,在版区内搜索中文的时候,分页会出现问题,我在社区里试了一下,果然有这样的问题,我记得这个问题在写代码的时候特意注意过,我做了URLEncoder和URLDecoder,但还是有问题,当时可能测试不够,把这个问题漏过了,这个问题出现的原因很简单,就是在GET方式传递中文的时候,由于编码的问题出现了字符的错误,解决的方法也有几种:

第一种,是不采用GET方式,采用POST方式,可以在页面上加入一个隐藏表单,把数据写进去,分页的时候,用JavaScript提交,POST方式发送到服务器,这样的做法可以很好的保护数据完整性,也比较安全,但是对于社区来说需要修改页面,比较麻烦。

第二种,就是将中文内容做一些编码,比如Base64,之后再做一次URLEncoder,这样应该就可以传递正确的信息,我就按照这个方法修改了一下程序,没有问题了。

这时候我突然想起了Taobao的奇怪URL编码方式,比如我在淘宝上搜索“尼康 镜头”,请求到的地址是

http://search1.taobao.com/browse/0/n-g,ytq37njax2243ny——-2———40–commend-0-all-0.htm?at_topsearch=1&ssid=e-p1-s5

注意其中的“ytq37njax2243ny”,在JavaEye里有位高人写了一篇关于淘宝URL编码规则的文章,着实很强,淘宝也用的是Base64,只不过用自己的码表,这样编码出来的字符串就像“ytq37njax2243ny”,乍看上去挺怪异的,但为什么要这么做,我似乎明白了一些,淘宝、阿里巴巴都采用他们自己的Webx框架,在其前端专门有模块是处理URL的,包括URL的生成、解析,其好处有很多,其中一个重要的原因应该是解决编码问题,附件是阿里巴巴webx的一个文档,大家可以看看,有什么启发。

阿里巴巴webx框架资料

Oracle收购SUN之后

北京时间4月20日晚一条IT界的爆炸性消息传来–《甲骨文将以74亿美元收购Sun》,这个消息确实让人非常意外,因为在之前传出IBM和Sun之间的收购谈判破裂的消息,是Oracle突然接手?应该不是,估计SUN早已有卖掉的计划,只是看谁出价合理吧。

对于Java开发阵营来说,这条消息未必是坏事,毕竟Oracle手里已经有了BEA,在Java企业级应用里就是领头羊,而SUN本身就没有在Java身上赚到钱,相信应该可以更好的整合Java的企业级应用,另外Oracle本身已经使用了非常多的Java技术,两个公司之前也有比较多的合作。

这笔收购让人最担心的还是Mysql,去年SUN收购Mysql,就让人感觉颇为另类,现在Oracle收购SUN,真不知道Oracle会怎样处理Mysql,任其自然发展,还是直接消灭,我想做为广大支持开源的开发者,都不希望Mysql倒掉,更何况当今大多数的Web2.0公司都在使用Mysql,不乏像Facebook这样的大容量用户,如果Mysql没有了,“LAMP”里的M会变成什么?PostgreSQL?还是其他?

商人逐利,这是自古以来的道理,并购的背后,一定有商业利益的存在,收购方希望整合资源、扩大市场、增加利润,被收购方希望套现,一个愿打一个愿挨,无可厚非,但他们究竟会如何面对最终客户,我们只能拭目以待了。

使用jQuery Autocomplete(自动完成)插件

jQuery的Autocomplete(自动完成、自动填充)插件有不少,但比较下来我感觉,还是bassistance.de的比较强大,我们就来写一些代码感受一下。

最简单的Autocomplete(自动完成)代码片段

1
2
3
4
5
6
7
8
9
<script type="text/javascript">
var websites = [
	"Google","NetEase", "Sohu", "Sina", "Sogou", "Baidu", "Tencent", 
	"Taobao", "Tom", "Yahoo", "JavaEye", "Csdn", "Alipay"
];
$().ready(function() {
	$("#website").autocomplete(websites);	
});
</script>
1
2
3
4
5
6
<p>
<label>Web Site:</label>
<input type="text" id="website" />
<input type="button" id="getvalue" value="Get Value" />
</p>
<div id="content"></div>

我们可以看到效果

jQuery Autocomplete Plugin

这么几行代码就完成了自动完成功能,真实太强了,不过bassistance.de的jQuery Autocomplete插件还有更丰富的功能,它的文档在http://docs.jquery.com/Plugins/Autocomplete,在API Documentation里,我们要仔细的研究一下autocomplete( url or data, [options] )方法。

autocomplete方法有两个参数,第一个用来填写URL地址或是数据,jQuery Autocomplete插件是支持Ajax方式调用数据,所以可以填写调用的地址,另外可以直接填写数据,格式为JavaScript数组,如我们的例子,autocomplete的另外一个参数 [options]是一个可选项,我们在例子中就没有写,但这个参数里面却有很多可配置的参数,我们还是先修改上面的例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
$().ready(function() {
	$("#website").autocomplete(websites,{
		minChars: 0,
		max: 5,
		autoFill: true,
		mustMatch: true,
		matchContains: true,
		scrollHeight: 220,
		formatItem: function(data, i, total) {
			return "<I>"+data[0]+"</I>";
		},
		formatMatch: function(data, i, total) {
			return data[0];
		},
		formatResult: function(data) {
			return data[0];
		}
	});
});

在options项我们增加了好几个参数

minChars表示在自动完成激活之前填入的最小字符,这里我们设置为0,在我们双击文本框,不输入字符的时候,就会把数据显示出来,效果如下

jquery-autocomplete-21

max表示列表里的条目数,我们设置了5,所以显示5条,也如上图

autoFill表示自动填充,就是在文本框中自动填充符合条件的项目,看下图,在我们输入“g”的时候,文本框里填充了“google”

jQuery Autocomplete Plugin

mustMatch表示必须匹配条目,也就是在文本框里输入的内容,必须是data参数里的数据,如果不匹配,文本框就被清空

matchContains表示包含匹配,就是data参数里的数据,是否只要包含文本框里的数据就显示,比如在上面的图中,我们输入了“g”,由于“Sogou”中也包含一个“g”,所以会显示出来,如果将matchContains设为fasle,则“Sogou”就不会显示

scrollHeight不用多解释,看文档就知道。

后面3个参数formatItem、formatMatch、formatResult非常有用,formatItem作用在于可以格式化列表中的条目,比如我们加了“I”,让列表里的字显示出了斜体,formatMatch是配合formatItem使用,作用在于,由于使用了formatItem,所以条目中的内容有所改变,而我们要匹配的是原始的数据,所以用formatMatch做一个调整,使之匹配原始数据,formatResult是定义最终返回的数据,比如我们还是要返回原始数据,而不是formatItem过的数据。

[options]里还有很多有用的参数,大家可以看它的文档。

jQuery Autocomplete插件里还有两个重要的方法,一个是result( handler ),一个是search( ),比如用户在选定了某个条目时需要触发一些别的方法时,着两个方法就可以起作用了,我们再修改以上的例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
$().ready(function() {
 
	function findValueCallback(event, data, formatted) {
		$("#content").html("<strong>"+(!data ? "No match!" : "Selected: " + formatted)+"</strong>");
	}
 
	$("#website").autocomplete(websites,{
		minChars: 0,
		max: 5,
		autoFill: true,
		mustMatch: true,
		matchContains: true,
		scrollHeight: 220,
		formatItem: function(data, i, total) {
			return "<I>"+data[0]+"</I>";
		},
		formatMatch: function(data, i, total) {
			return data[0];
		},
		formatResult: function(data) {
			return data[0];
		}
	});
	$("#website").result(findValueCallback);
	$("#getvalue").click(function() {$("#website").search()});
});

看看是什么效果,会在content div的地方显示出我们选择的内容。

jQuery Autocomplete插件所带的例子还是很好的,大家可以仔细研究一下它的例子,更加灵活的运用jQuery Autocomplete插件。

Kohana的Cache

Kohana里有个Cache Library,我今天就测试了一下。

首先要配置,将system/config下的cache.php复制到application/config下,打开cache.php文件,我们看一下内容

1
2
3
4
5
6
7
$config['default'] = array
(
    'driver'   => 'file',
    'params'   => APPPATH.'cache',
    'lifetime' => 1800,
    'requests' => 1000
);

这是一个默认配置,’driver’为驱动的缓存方式,Kohana支持6种不同的驱动,分别是File、SQlite、Memcache、APC、Eaccelerator、Xcache,配置文件默认使用的file,其原理是,写缓存时把对象序列化写入文件,读缓存时从文件读出文本反序列化,所以在文件方式下,缓存是基于I/O的,在文件多而且大的时候,性能会有下降, ‘params’是驱动参数,在file驱动模式下,就是cache文件路径,’lifetime’是cache的生命周期,单位为秒,超过这个时间,内容将被清除(设置为0代表不自动清除),’requests’为在达到请求数量之前自动垃圾回收。

在应用中,可能需要不止一个缓存,所以可以配置多个缓存,增加$config数组即可,还有在file方式下可以为每个缓存设置单独文件路径,但前提是文件路径要存在,例如我们增加一个

1
2
3
4
5
6
7
$config['my'] = array
(
    'driver'   => 'file',
    'params'   => APPPATH.'cache/my',
    'lifetime' => 1800,
    'requests' => 1000
);

接下来我们在Controller里调用cache

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Mycache_Controller extends Controller {
 
    public function index() {
        $mychache = Cache::instance("my");
        $mychache->set("name","laoer");
        echo "OK";   
    }
 
    public function name() {
        $mychache= Cache::instance("my");
        $name = $mychache->get("name");
        echo $name;
    }   
}

Cache::instance(“my”)实例化配置里$config[‘my’]的cache,如果用Cache::instance()就是实例化配置里$config[‘default’]的cache,在浏览器里执行,已经可以存取了,在application/cache/my/文件下可以看到一个名为”name~~0″的文件,里面就是序列化的数据。

文件cache还是有一定的局限性,现在越来越的网站开始使用Memcached所谓缓存的解决方案,Kohana的缓存驱动里,有Memcached的支持,但它的文档却没有给出Memcached的配置例子,看来要自己摸索一下。

将system/config下的cache_memcache.php复制到application/config下,cache_memcache.php的内容如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/**
 * @package  Cache:Memcache
 *
 * memcache server configuration.
 */
$config['servers'] = array
(
	array
	(
		'host' => '127.0.0.1',
		'port' => 11211,
		'persistent' => FALSE,
	)
);
 
/**
 * Enable cache data compression.
 */
$config['compression'] = FALSE;

根据你自己的情况修改Memcached的服务地址和端口,在application/config/cache.php里再加一段

1
2
3
4
5
6
7
$config['mem'] = array
(
	'driver'   => 'memcache',
	'params'   => '',
	'lifetime' => 1800,
	'requests' => 1000
);

在Controller里把Cache::instance(“my”)改为Cache::instance(“mem”),运行看看结果,已经可以从Memcahced里存取了。

Kohana的Memcached驱动还是有些缺陷,现在只能使用一组Memcached,即$config[‘servers’]这个参数,我觉得Memcacahed组也应该是多个,因为从业务角度会根据功能对cache做划分,我大概看了一下system/libraries/drivers/Cache/Memcache.php文件,应该是可以改造的,还有一点,编译PHP的时候要安装Memcached的支持。

Linux下Memcached安装事项

在Linux下安装Memcached,先要安装libevent,在http://www.monkey.org/~provos/libevent/下载

Memcache在http://www.danga.com/memcached/download.bml下载

可能是我将libevent安装在/usr/local/libevent目录的缘故,在启动memchached时报一个错误

./memcached: error while loading shared libraries: libevent-1.4.so.2: cannot open shared object file: No such file or directory

我们先运行下面的命令,将libevent的lib目录加入LD_LIBRARY_PATH里

export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/libevent/lib

之后再启动memcached就好了。

ORA-12547: TNS:lost contact问题

今天在Oracle Enterprise Linux上安装Oracle,后面创建数据实例的时候出现了ORA-12547: TNS:lost contact的错误,按理来说,Oracle Enterprise Linux可是就是针对Oracle的,怎么会出现这样的错误,在网上搜索了一下,可能是没有安装libaio包的缘故,我就试试吧。

在安装盘第3张里找到了libaio-0.3.105-2.i386.rpm和libaio-devel-0.3.105-2.i386.rpm,如果身边没有安装盘,可以到http://rpmfind.net上面去搜索一下,安装后,重起电脑,把以前安装过的Oracle卸载掉,重新安装,没有问题了。

以后需要注意,在安装Linux的时候就把这个包选进去。

Linux挂载USB硬盘的方法

先检查Linux是否加载usb模块

lsmod | grep usb

如果没有信息,则执行

modprobe usb-storage

之后把USB的硬盘接上,执行

fdisk -l

可以看到

Disk /dev/sda: 80.0 GB, 80026361856 bytes
255 heads, 63 sectors/track, 9729 cylinders
Units = cylinders of 16065 * 512 = 8225280 bytes

Device Boot      Start         End      Blocks   Id  System
/dev/sda1   *           1          13      104391   83  Linux
/dev/sda2              14         587     4610655   82  Linux swap
/dev/sda3             588        9729    73433115   83  Linux

Disk /dev/sdb: 1000.2 GB, 1000204886016 bytes
255 heads, 63 sectors/track, 121601 cylinders
Units = cylinders of 16065 * 512 = 8225280 bytes

Disk /dev/sdb doesn’t contain a valid partition table

可以看到/dev/sda是我们已有的硬盘,/dev/sdb就是USB硬盘了,提示改硬盘还没有分区,接下来我们就对/dev/sdb做分区,执行命令

fdisk /dev/sdb

之后会提示

Command (m for help):

可以输入m看一下help,我们输入n,新创建一个分区,之后出现

Command action
e   extended
p   primary partition (1-4)

输入p,创建主分区,之后

Partition number (1-4):

输入1,会提示

First cylinder (1-121601, default 1):

直接回车,即默认1,也就是从1开始,提示

Using default value 1
Last cylinder or +size or +sizeM or +sizeK (1-121601, default 121601):

直接回车,因为我把整个硬盘分1个区,所以就用最大值,回车就可以了,提示

Using default value 121601

Command (m for help):

这时候我们可以输入p,看一下分区情况

Disk /dev/sdb: 1000.2 GB, 1000204886016 bytes
255 heads, 63 sectors/track, 121601 cylinders
Units = cylinders of 16065 * 512 = 8225280 bytes

Device Boot      Start         End      Blocks   Id  System
/dev/sdb1               1      121601   976760001   83  Linux

Command (m for help):

这时候输入w,写入分区表,这样就完成了,会提示

The partition table has been altered!

Calling ioctl() to re-read partition table.
Syncing disks.

我们再执行fdisk -l看一下情况

Disk /dev/sda: 80.0 GB, 80026361856 bytes
255 heads, 63 sectors/track, 9729 cylinders
Units = cylinders of 16065 * 512 = 8225280 bytes

Device Boot      Start         End      Blocks   Id  System
/dev/sda1   *           1          13      104391   83  Linux
/dev/sda2              14         587     4610655   82  Linux swap
/dev/sda3             588        9729    73433115   83  Linux

Disk /dev/sdb: 1000.2 GB, 1000204886016 bytes
255 heads, 63 sectors/track, 121601 cylinders
Units = cylinders of 16065 * 512 = 8225280 bytes

Device Boot      Start         End      Blocks   Id  System
/dev/sdb1               1      121601   976760001   83  Linux

我们已经看到/dev/sdb1了,现在要对/dev/sdb1做一下格式化,在linux上可以将格式化成多种格式,比如ext3 、reiserfs 、ext2 、fat32 、msdos等,我们还是格式化成ext3的默认格式,执行

mkfs.ext3 /dev/sdb1

mke2fs 1.35 (28-Feb-2004)
Filesystem label=
OS type: Linux
Block size=4096 (log=2)
Fragment size=4096 (log=2)
122109952 inodes, 244190000 blocks
12209500 blocks (5.00%) reserved for the super user
First data block=0
Maximum filesystem blocks=247463936
7453 block groups
32768 blocks per group, 32768 fragments per group
16384 inodes per group
Superblock backups stored on blocks:
32768, 98304, 163840, 229376, 294912, 819200, 884736, 1605632, 2654208,
4096000, 7962624, 11239424, 20480000, 23887872, 71663616, 78675968,
102400000, 214990848

Writing inode tables: done     (在这个地方会等待,直到done)
Creating journal (8192 blocks): done
Writing superblocks and filesystem accounting information:

直接回车就之后就完成格式化

done

This filesystem will be automatically checked every 30 mounts or
180 days, whichever comes first.  Use tune2fs -c or -i to override.

之后我们可以把/dev/sdb1挂载到系统中,比如

mount /dev/sdb1 /u02/oradata1

用df命令看一下

Filesystem           1K-blocks      Used Available Use% Mounted on
/dev/sda3             72279384   9336512  59271220  14% /
/dev/sda1               101086      8741     87126  10% /boot
none                   1553948         0   1553948   0% /dev/shm
/dev/sdb1            961432072    110656 912483416   1% /u02/oradata1

/dev/sdb1已经mount到/u02/oradata1了。

摇滚记忆

近期Google中国发布了谷歌音乐搜索,我就在上面试听听,我习惯性的搜索“唐朝”,发现唐朝乐队的第一张专辑,速度、音质都还不错,听到熟悉的旋律,让我想起了10多年前的中国摇滚年代。

中国摇滚的黄金年代是上世纪90年代初,那时候我在中学时代,我喜欢看《北京青年报》,《北京青年报》有一篇介绍唐朝乐队的文章,当时还没有听到他们的音乐,但却被唐朝乐队深深的吸引了,后来唐朝第一张专辑发布后,我再找这份报纸,却怎么也找不到了。91年的时候黑豹率先发布了他们的同名专辑,在“无地自容”、“Don’t Break My Heart”响遍大街小巷的时候,我们手里买到的大多数都是盗版,当时还没有MP3,CD也很少,主要是卡带,后来为了专门的收藏,特意又买了黑豹的正版卡带和CD。

在整个中学的事情,我都十分的迷恋摇滚,究其原因,我现在想还是因为年轻的反叛、对未来的迷茫,但我并不认为摇滚是一种消极因素,只是一种情感的宣泄。那几年间,我买了所有摇滚的专辑,一股脑的消化吸收,报纸、杂志有些对摇滚的报到,但电视里还是看不到摇滚的影子,我搜集到的黑豹、唐朝的MTV影像也极其模糊,让我对他们这些摇滚人充满了神秘感。

我一直在考虑上世纪90年代初期,中国摇滚出现巅峰的原因,我想一方面,随着社会的变革,大众对于新音乐形式的新奇感,另一方面,那时候集中出现的优秀作品,也是这些音乐人长期对生活感悟、积累的结果,现在随着社会的开放,音乐的多元化,生活的转变,中国摇滚再出现巅峰不太可能了,只能做为一种流派而存在。

由于我对摇滚的迷恋,父母没收了我的随身听,音乐听不到,保留摇滚精神就好,94年中国摇滚红磡演唱会,可以说是中国摇滚真正的顶点,95年,在高考的前几个月,还是在北青报上看到张炬车祸离世的消息,震惊之余还是震惊,不过高考在即,也无暇顾及了,七月流火,随着高考结束,我的情绪似乎也随着中国摇滚慢慢平静的逐渐缓和下来,后来出去上学,红磡演唱会的现场的专辑也出来了,出于对张炬的追忆,这张专辑,包括后来的影像,仍然是我最喜爱的。

1998年我第一次接触互联网,我上网的第一件事情,就是在搜狐上搜索“唐朝乐队”,还真的找到了一些网站,都是网友自发创建的,我记忆比较深的是一个网站,上面有很多留言,有中文还有英文的,让我知道了很多的信息,当时就觉得,还是互联网好啊,我中学时代搜集的信息在这里简直是沧海一粟。

10多年过去了,互联网、资讯日益发达,我们可以轻而易举的得到关于中国摇滚的各种信息,中国摇滚也不再神秘,媒体也对摇滚逐渐松绑,甚至白岩松都呼吁给摇滚更多的空间,然而中国摇滚对于我慢慢成了记忆,心境的平和,让我慢慢喜欢更富旋律的音乐,只是摇滚在中学时代的烙印永远深深的印在那里。