2006年11月07日

Craigslist 绝对是互联网的一个传奇公司。根据以前的一则报道:

每月超过 1000 万人使用该站服务,月浏览量超过 30 亿次,(Craigslist每月新增的帖子近 10 亿条??)网站的网页数量在以每年近百倍的速度增长。Craigslist 至今却只有 18 名员工(现在可能会多一些了)。

Tim O’reilly 采访了 Craigslist 的 Eric Scheide ,于是通过这篇 Database War Stories #5: craigslist 我们能了解一下 Craigslist 的数据库架构以及数据量信息。
数据库软件使用 MySQL 。为充分发挥 MySQL 的能力,数据库都使用 64 位 Linux 服务器, 14 块 本地磁盘(72*14=1T ?), 16G 内存。
不同的服务使用不同方式的数据库集群。
论坛

1 主(master) 1 从(slave)。Slave 大多用于备份. myIsam 表. 索引达到 17G。最大的表接近 4200 万行。

分类信息

1 主 12 从。 Slave 各有个的用途. 当前数据包括索引有 114 G , 最大表有 5600 万行(该表数据会定期归档)。 使用 myIsam。分类信息量有多大? "Craigslist每月新增的帖子近 10 亿条",这句话似乎似乎有些夸张,Eric Scheide 说昨日就超过 330000 条数据,如果这样估计的话,每个月的新帖子信息大约在 1 亿多一些。

归档数据库

1 主 1 从. 放置所有超过 3 个月的帖子。与分类信息库结构相似但是更大, 数据有 238G, 最大表有 9600 万行。大量使用 Merge 表,便于管理。

搜索数据库

4 个 集群用了 16 台服务器。活动的帖子根据 地区/种类划分,并使用 myIsam 全文索引,每个只包含一个子集数据。该索引方案目前还能撑住,未来几年恐怕就不成了。

Authdb

1 主 1 从,很小。

目前 Craigslist 在 Alexa 上的排名是 30,上面的数据只是反映采访当时(April 28, 2006)的情况,毕竟,Craigslist 数据量还在每年 200% 的速度增长。
Craigslist 采用的数据解决方案从软硬件上来看还是低成本的。优秀的 MySQL 数据库管理员对于 Web 2.0 项目是一个关键因素。
2006年11月06日
以下所有代码全是在<head>…</head>之间,具体内容有:
1,<title>…</title>
标题元素,帮助用户更好识别文件,有且只有一个。当作为首页或收藏时做文件名。

2,<link>…</link>
显示本文档和其它文档之间的关系:<link rel=“stylesheet” href=“s.css”> 和外部样式表的连接。
rel说明html文件和url两文档之间的关系,href说明文档名。
3,<style>…</style>
可以在文档中包含风格页。文档本身的内部样式。
4,<base>…</base>
为相对路径定义绝对路径url,读者下载你的文档后,也可知道从哪下的
url格式:通信协议://主机名/路径/文件名
5,<script>…</script>
用于包含脚本(一系列脚本语言写的命令)可以是 Javascript 或 VbScript。
6,<meta>…</meta>
meta标签的常见功能:
1、帮助主页被各大搜索引擎登录
2、定义页面的使用语言
3、自动刷新并指向新的页面
4、实现网页转换时的动画效果
5、网页定级评价
6、控制页面缓冲
7、控制网页显示的窗口。
meta是用来在HTML文档中模拟HTTP协议的响应头报文。
meta 的属性有两种:name和http-equiv。
name属性主要用于描述网页,对应于content(网页内容),以便于搜索引擎机器人查找、分类(目前几乎所有的搜索引擎都使用网上机器人自动查找meta值来给网页分类)。这其中最重要的是description(站点在搜索引擎上的描述)和keywords(分类关键词),所以应该给每页加一个meta值。

name 属性
1、<meta name="Generator" contect="editplus">用以说明生成工具(如Microsoft FrontPage 4.0)等;

2、<meta name="KEYWords" contect="cnbruce,cnrose">向搜索引擎说明你的网页的关键词;
3、<meta name="Description" contect="cnbruce’s blog">告诉搜索引擎你的站点的主要内容;
4、<meta name="Author" contect="cnbruce">告诉搜索引擎你的站点的制作的作者;
5、<meta name="Robots" contect="all|none|index|noindex|follow|nofollow">
其中的属性说明如下:
设定为all:文件将被检索,且页面上的链接可以被查询;
设定为none:文件将不被检索,且页面上的链接不可以被查询;
设定为index:文件将被检索;
设定为follow:页面上的链接可以被查询;
设定为noindex:文件将不被检索,但页面上的链接可以被查询;
设定为nofollow:文件将不被检索,页面上的链接可以被查询。

http-equiv属性
1、<meta http-equiv="Content-Type" contect="text/html";charset=gb_2312-80">和 <meta http-equiv="Content-Language" contect="zh-CN">用以说明主页制作所使用的文字以及语言;又如英文是ISO-8859-1字符集,还有BIG5、utf-8、shift-Jis、Euc、Koi8-2等字符集;

2、<meta http-equiv="Refresh" content="n; url=http://yourlink"> 定时让网页在指定的时间n秒内,跳转到页面http;//yourlink;
3、<meta http-equiv="Expires" contect="Mon,12 May 2001 00:20:00 GMT">可以用于设定网页的到期时间,一旦过期则必须到服务器上重新调用。需要注意的是必须使用GMT时间格式;
4、<meta http-equiv="Pragma" contect="no-cache">是用于设定禁止浏览器从本地机的缓存中调阅页面内容,设定后一旦离开网页就无法从Cache中再调出;
5、<meta http-equiv="set-cookie" contect="Mon,12 May 2004 00:20:00 GMT">cookie设定,如果网页过期,存盘的cookie将被删除。需要注意的也是必须使用GMT时间格式;
6、<meta http-equiv="Pics-label" contect="">网页等级评定,在IE的internet选项中有一项内容设置,可以防止浏览一些受限制的网站,而网站的限制级别就是通过meta属性来设置的;
7、<meta http-equiv="windows-Target" contect="_top">强制页面在当前窗口中以独立页面显示,可以防止自己的网页被别人当作一个frame页调用;
8、<meta http-equiv="Page-Enter" contect="revealTrans(duration=10,transtion= 50)">和<meta http-equiv="Page-Exit" contect="revealTrans(duration=20,transtion=6)">设定进入和离开页面时的特殊效果,这个功能即FrontPage中的“格式/网页过渡”,不过所加的页面不能够是一个frame页面。

Duration的值为网页动态过渡的时间,单位为秒。
Transition是过渡方式,它的值为0到23,分别对应24种过渡方式。如下表:
0 盒状收缩
1 盒状放射
2 圆形收缩
 3 圆形放射
4 由下往上
 5 由上往下
6 从左至右
7 从右至左
8 垂直百叶窗
9 水平百叶窗
10 水平格状百叶窗
 11垂直格状百叶窗
12 随意溶解
13从左右两端向中间展开
14从中间向左右两端展开
15从上下两端向中间展开
16从中间向上下两端展开
17 从右上角向左下角展开
18 从右下角向左上角展开
19 从左上角向右下角展开
20 从左下角向右上角展开
21 水平线状展开
22 垂直线状展开
23 随机产生一种过渡方式
head区是指首页HTML代码的<head>和</head>之间的内容。
必须加入的标签
1.公司版权注释
<!— The site is designed by Maketown,Inc 06/2000 —>
2.网页显示字符集
简体中文:<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=gb2312">
繁体中文:<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=BIG5">
英 语:<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1">
3.网页制作者信息
<META name="author" content="webmaster@maketown.com">
4.网站简介
<META NAME="DESCRIPTION" CONTENT="xxxxxxxxxxxxxxxxxxxxxxxxxx">
5.搜索关键字
<META NAME="keywords" CONTENT="xxxx,xxxx,xxx,xxxxx,xxxx,">
6.网页的css规范
<LINK href="style/style.css" rel="stylesheet" type="text/css">
(参见目录及命名规范)
7.网页标题
<title>xxxxxxxxxxxxxxxxxx</title>
.可以选择加入的标签
1.设定网页的到期时间。一旦网页过期,必须到服务器上重新调阅。
<META HTTP-EQUIV="expires" CONTENT="Wed, 26 Feb 1997 08:21:57 GMT">
2.禁止浏览器从本地机的缓存中调阅页面内容。
<META HTTP-EQUIV="Pragma" CONTENT="no-cache">
3.用来防止别人在框架里调用你的页面。
<META HTTP-EQUIV="Window-target" CONTENT="_top">
4.自动跳转。
<META HTTP-EQUIV="Refresh" CONTENT="5;URL=http://www.yahoo.com">
5指时间停留5秒。
5.网页搜索机器人向导.用来告诉搜索机器人哪些页面需要索引,哪些页面不需要索引。
<META NAME="robots" CONTENT="none">
CONTENT的参数有all,none,index,noindex,follow,nofollow。默认是all。
6.收藏夹图标
<link rel = "Shortcut Icon" href="favicon.ico">
所有的javascript的调用尽量采取外部调用.
<SCRIPT LANGUAGE="JavaScript" SRC="script/xxxxx.js"></SCRIPT>
附<body>标签:
<body>标签不属于head区,这里强调一下,为了保证浏览器的兼容性,必须设置页面背景<body bgcolor="#FFFFFF">
2006年10月12日

PHP处理对象部分的内核完全重新开发过,提供更多功能的同时也提高了性能。在以前版本的php中,处理对象和处理基本类型(数字,字符串)的方式是一样的。这种方式的缺陷是:当将对象赋值给一个变量时,或者通过参数传递对象时,对象将被完全拷贝一份。在新的版本里,上述操作将传递引用(可以把引用理解成对象的标识符),而非值。
很多PHP程序员可能甚至没有察觉到老的对象处理方式。事实上,大多数的php应用都可以很好地运行。或者仅仅需要很少的改动。
私有和受保护成员
PHP5引入了私有和受保护成员变量的概念。我们可以用它来定义类成员的可见性。
例子
受保护成员可以被子类访问, 而私有成员只能被类本身访问。

代码:——————————————————————————–

<?php
class MyClass {
private $Hello = "Hello, World!\n";
protected $Bar = "Hello, Foo!\n";
protected $Foo = "Hello, Bar!\n";

function printHello() {
print "MyClass::printHello() " . $this->Hello;
print "MyClass::printHello() " . $this->Bar;
print "MyClass::printHello() " . $this->Foo;
}
}

class MyClass2 extends MyClass {
protected $Foo;

function printHello() {
MyClass::printHello(); /* Should print */
print "MyClass2::printHello() " . $this->Hello; /* Shouldn’t print out anything */
print "MyClass2::printHello() " . $this->Bar; /* Shouldn’t print (not declared)*/
print "MyClass2::printHello() " . $this->Foo; /* Should print */
}
}

$obj = new MyClass();
print $obj->Hello; /* Shouldn’t print out anything */
print $obj->Bar; /* Shouldn’t print out anything */
print $obj->Foo; /* Shouldn’t print out anything */
$obj->printHello(); /* Should print */

$obj = new MyClass2();
print $obj->Hello; /* Shouldn’t print out anything */
print $obj->Bar; /* Shouldn’t print out anything */
print $obj->Foo; /* Shouldn’t print out anything */
$obj->printHello();
?>
——————————————————————————–

私有方法和受保护方法
PHP5也引入了私有方法和受保护方法的概念。
例子:

代码:——————————————————————————–

<?php
class Foo {
private function aPrivateMethod() {
echo "Foo::aPrivateMethod() called.\n";
}

protected function aProtectedMethod() {
echo "Foo::aProtectedMethod() called.\n";
$this->aPrivateMethod();
}
}

class Bar extends Foo {
public function aPublicMethod() {
echo "Bar::aPublicMethod() called.\n";
$this->aProtectedMethod();
}
}

$o = new Bar;
$o->aPublicMethod();
?>
——————————————————————————–

以前的不使用类的老代码,没有访问修饰符(public, protected, private)的代码可以不经改动运行。
抽象类和抽象方法
Php5也引入了抽象类和抽象方法的概念。抽象方法只是声明了方法的签名并不提供它的实现。包含抽象方法的类必须被声明成抽象类。
例子:

代码:——————————————————————————–

<?php
abstract class AbstractClass {
abstract public function test();
}

class ImplementedClass extends AbstractClass {
public function test() {
echo "ImplementedClass::test() called.\n";
}
}

$o = new ImplementedClass;
$o->test();
?>
——————————————————————————–

抽象类不能被实例化。以前的不使用抽象类的老代码可以不经改动运行。
接口
Php5引入了接口。一个类可以实现多个接口。
例子:

代码:——————————————————————————–

<?php
interface Throwable {
public function getMessage();
}

class MyException implements Throwable {
public function getMessage() {
// …
}
}
?>
——————————————————————————–

以前的不使用接口的老代码可以不经改动运行

类的型别提示
PHP5依然是弱类型的,不过在定义函数参数时,可以使用类的型别提示来声明期望传入的对象类型
Example

代码:——————————————————————————–

<?php
interface Foo {
function a(Foo $foo);
}

interface Bar {
function b(Bar $bar);
}

class FooBar implements Foo, Bar {
function a(Foo $foo) {
// …
}

function b(Bar $bar) {
// …
}
}

$a = new FooBar;
$b = new FooBar;

$a->a($b);
$a->b($b);
?>
——————————————————————————–

和其他强类型语言一样,php5类的型别提示在运行期间检查而非编译期间检查。即:

代码:——————————————————————————–

<?php
function foo(ClassName $object) {
// …
}
?>
和下面的代码是一样的:
<?php
function foo($object) {
if (!($object instanceof ClassName)) {
die("Argument 1 must be an instance of ClassName");
}
}
?>
——————————————————————————–

这个语法只适用于类,不适用于内建类型。
Final

PHP 5 引入了final关键字来声明final成员和final方法。final成员和final方法不能被子类覆盖。
Example

代码:——————————————————————————–

<?php
class Foo {
final function bar() {
// …
}
}
?>
——————————————————————————–

更进一步,可以把类声明成final。将类声明成final可以阻止这个类被继承。final类里面的方法缺省地都是final的,无需再声明一次。
Example

代码:——————————————————————————–

<?php
final class Foo {
// class definition
}

// the next line is impossible
// class Bork extends Foo {}
?>
——————————————————————————–

属性不能定义成为final.
以前的不使用final的老代码可以不经改动运行.
对象克隆
Php4没有提供一种机制来让用户自己定义复制构造子(copy constructor)控制对象的复制过程。Php4做二进制的拷贝,因而很精确地复制了对象的所有属性。
精确地复制对象的所有属性可能并不是我们一直想要的。有个例子可以很好地说明我们确实需要复制构造子:比如一个GTK Window的对象 a。 a持有它所需要的全部资源。当复制的这个GTK Window到对象b时候,我们更希望b持有新的资源对象。再举个例子:对象a包含了一个对象c, 当你把对象a 复制到对象c的时候。我们可能更希望对象b包含一个新的对象c的copy, 而不是一个对象c的引用。(译者注:这里所说的就是浅克隆和深克隆。)
对象的复制是通过clone这个关键字达到的(Clone调用被克隆对象的__clone()方法)。对象的__clone方法不能够直接被调用。

代码:——————————————————————————–

<?php
$copy_of_object = clone $object;
?>
——————————————————————————–

当developer创建对象的一份拷贝的时候,php5将会检查 __clone()方法是否存在。如果不存在,那么它就会呼叫缺省的__clone()方法,复制对象的所有属性。如果__clone()方法已经定义过,那么_clone()方法就会负责设置新对象的属性。为了方便起见,Engine会缺省地复制所有的属性。所以在__clone()方法中,只需要覆盖那些需要更改的属性就可以了。如下:
Example

代码:——————————————————————————–

<?php
class MyCloneable {
static $id = 0;

function MyCloneable() {
$this->id = self::$id++;
}

function __clone() {
$this->address = "New York";
$this->id = self::$id++;
}
}

$obj = new MyCloneable();

$obj->name = "Hello";
$obj->address = "Tel-Aviv";

print $obj->id . "\n";

$obj_cloned = clone $obj;

print $obj_cloned->id . "\n";
print $obj_cloned->name . "\n";
print $obj_cloned->address . "\n";
?>
——————————————————————————–

统一构造函数
Php5允许开发者声明一个类的构造方法。拥有构造方法的类在每次创建新的对象的时候都会呼叫这个方法,因此构造方法适合对象在被使用之前的初始化工作
Php4中,构造方法的名称和类的名称一样。考虑到从子类构造方法呼叫父类构造方法的情况是非常普遍的,而将类从一个继承体系中搬迁引起的父类变更就常常导致需要更改类的构造方法,php4的做法显然是不太合理的。
Php5引入了一个声明构建函数的标准方法: __construct().如下:
Example

代码:——————————————————————————–

<?php
class BaseClass {
function __construct() {
print "In BaseClass constructor\n";
}
}

class SubClass extends BaseClass {
function __construct() {
parent::__construct();
print "In SubClass constructor\n";
}
}

$obj = new BaseClass();
$obj = new SubClass();
?>
——————————————————————————–

为保持向后的兼容性,如果php5不能够找到 __construct(),它会寻找老式的构造方法,即与类同名的方法。简单的说,只有当老代码里包含了一个__construct()方法的时候,才存在一个兼容性的问题。
析构方法
对于面向对象的编程来说,可以定义析构方法是非常有用的一个功能。析构方法可以用来记录调试信息,关闭数据库连接等等一些清除收尾的工作。Php4中没有析构方法,尽管php4已经支持可以注册一个函数以便请求结束的时候被调用。
Php5引进的析构方法的概念和其他面向对象的语言(比如java)是一致的。当指向这个对象的最后一个引用被销毁的时候,析构方法被调用,调用完成后释放内存。注意:析构方法不接受任何参数。
Example

代码:——————————————————————————–

<?php
class MyDestructableClass {
function __construct() {
print "In constructor\n";
$this->name = "MyDestructableClass";
}

function __destruct() {
print "Destroying " . $this->name . "\n";
}
}

$obj = new MyDestructableClass();
?>
——————————————————————————–

和构建方法一样,父类的析构方法也不会被隐含调用。子类可以在自己的析构方法通过调用parent::__destruct()来显式地调用它。
Constants
Php5引入了class级别的常量。

代码:——————————————————————————–

<?php
class Foo {
const constant = "constant";
}

echo "Foo::constant = " . Foo::constant . "\n";
?>
——————————————————————————–

老的没有使用const的代码仍然正常运行。
Exceptions
Php4没有异常控制。Php5引入了和其它语言(java)相似的异常控制模式。应该注意的是php5里面支持捕捉全部异常,但是不支持finally子句。
在catch语句块里面,可以重新抛出异常。也可以有多个catch语句,在这种情况下,被捕捉到的异常从上往下依次比较和catch语句比较异常,第一个类型匹配的catch语句将会被执行。如果一直搜索到底还没有发现匹配的catch子句,则寻找下一个try/catch语句。最后不能捕捉的异常将被显示出来。如果异常被捕捉,那么程序会接着catch语句块的下面开始执行。
Example

代码:——————————————————————————–

<?php
class MyException {
function __construct($exception) {
$this->exception = $exception;
}

function Display() {
print "MyException: $this->exception\n";
}
}

class MyExceptionFoo extends MyException {
function __construct($exception) {
$this->exception = $exception;
}

function Display() {
print "MyException: $this->exception\n";
}
}

try {
throw new MyExceptionFoo(‘Hello’);
}
catch (MyException $exception) {
$exception->Display();
}
catch (Exception $exception) {
echo $exception;
}
?>
——————————————————————————–

上面的例子表明可以定义一个并不继承自 Exception的异常类,但是,最好还是从Exception继承并定义自己的异常。这是因为系统内建的Exception类能够收集到很多有用的信息, 而不继承它的异常类是得不到这些信息的。下面的php代码模仿了系统内建Exception类。每个属性后面都加了注释。每个属性都有一个getter,由于这些getter方法经常被系统内部处理调用,所以这些方法被标明了final。
Example

代码:——————————————————————————–

<?php
class Exception {
function __construct(string $message=NULL, int code=0) {
if (func_num_args()) {
$this->message = $message;
}
$this->code = $code;
$this->file = __FILE__; // of throw clause
$this->line = __LINE__; // of throw clause
$this->trace = debug_backtrace();
$this->string = StringFormat($this);
}

protected $message = ‘Unknown exception’; // exception message
protected $code = 0; // user defined exception code
protected $file; // source filename of exception
protected $line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line$line; // source line of exception

private $trace; // backtrace of exception
private $string; // internal only!!

final function getMessage() {
return $this->message;
}
final function getCode() {
return $this->code;
}
final function getFile() {
return $this->file;
}
final function getTrace() {
return $this->trace;
}
final function getTraceAsString() {
return self::TraceFormat($this);
}
function _toString() {
return $this->string;
}
static private function StringFormat(Exception $exception) {
// … a function not available in PHP scripts
// that returns all relevant information as a string
}
static private function TraceFormat(Exception $exception) {
// … a function not available in PHP scripts
// that returns the backtrace as a string
}
}
?>
——————————————————————————–

如果我们定义的一异常类都是继承自Exception基类
无兼容性问题。老的代码不会受到这一特性的影响。
Dereferencing objects returned from functions
Php4中不能再次引用函数返回的对象以进一步呼叫返回对象的方法,而php5是可以的。

代码:——————————————————————————–

<?php
class Circle {
function draw() {
print "Circle\n";
}
}

class Square {
function draw() {
print "Square\n";
}
}

function ShapeFactoryMethod($shape) {
switch ($shape) {
case "Circle":
return new Circle();
case "Square":
return new Square();
}
}

ShapeFactoryMethod("Circle")->draw();
ShapeFactoryMethod("Square")->draw();
?>
——————————————————————————–

静态成员变量能够被初始化。
Example

代码:——————————————————————————–

<?php
class foo {
static $my_static = 5;
public $my_prop = ‘bla’;
}

print foo::$my_static;
$obj = new foo;
print $obj->my_prop;
?>
——————————————————————————–

静态方法
PHP 5 引入了静态方法,可以在不实例化类的情况下呼叫静态方法。
Example

代码:——————————————————————————–

<?php
class Foo {
public static function aStaticMethod() {
// …
}
}

Foo::aStaticMethod();
?>
——————————————————————————–

伪变量$this不能够在静态方法方法中使用。
instanceof
Php5引入了instanceof关键字,允许用它来测试一个对象是一个类的实例,或者是一个派生类的实例,或者实现了某个接口
Example

代码:——————————————————————————–

<?php
class baseClass { }

$a = new baseClass;

if ($a instanceof baseClass) {
echo "Hello World";
}
?>
——————————————————————————–

Static function variables
现在,静态变量在编译阶段处理。因此程序员可以通过引用为静态变量赋值。这可以改善性能,不过,不能够使用对静态变量的间接引用了。
按引用传递的函数参数现在也可以设置缺省值了。
Example

代码:——————————————————————————–

<?php
function my_function(&$var = null) {
if ($var === null) {
die("$var needs to have a value");
}
}
?>
——————————————————————————–

__autoload()
__autoload() 拦截函数在一个未声明的类被初始化的时候自动调用。该类的名字会被自动传递给__autoload()函数。而__autoload()也只有这么唯一的一个参数。
Example

代码:——————————————————————————–

<?php
function __autoload($className) {
include_once $className . ".php";
}

$object = new ClassName;
?>
——————————————————————————–

可重载的方法呼叫和属性访问
方法呼叫和属性访问都能够通过__call, __get() and __set()方法重载。
Example: __get() and __set()

代码:——————————————————————————–

<?php
class Setter {
public $n;
public $x = array("a" => 1, "b" => 2, "c" => 3);

function __get($nm) {
print "Getting [$nm]\n";

if (isset($this->x[$nm])) {
$r = $this->x[$nm];
print "Returning: $r\n";
return $r;
} else {
print "Nothing!\n";
}
}

function __set($nm, $val) {
print "Setting [$nm] to $val\n";

if (isset($this->x[$nm])) {
$this->x[$nm] = $val;
print "OK!\n";
} else {
print "Not OK!\n";
}
}
}

$foo = new Setter();
$foo->n = 1;
$foo->a = 100;
$foo->a++;
$foo->z++;
var_dump($foo);
?>
——————————————————————————–

Example: __call()

代码:——————————————————————————–

<?php
class Caller {
private $x = array(1, 2, 3);

function __call($m, $a) {
print "Method $m called:\n";
var_dump($a);
return $this->x;
}
}

$foo = new Caller();
$a = $foo->test(1, "2", 3.4, true);
var_dump($a);
?>
——————————————————————————–

迭代
当和foreach一起使用对象的时候,迭代的方式被重载过了。缺省的行为是迭代类的所有属性。
Example

代码:——————————————————————————–

<?php
class Foo {
public $x = 1;
public $y = 2;
}

$obj = new Foo;

foreach ($obj as $prp_name => $prop_value) {
// using the property
}
?>
——————————————————————————–

一个类的所有对象都能够被迭代浏览到, 如果这个类实现了一个空的接口:Traversable. 换句话说,实现了Traversable接口的类可以和foreach一起使用。
接口 IteratorAggregate 和Iterator允许指定类的对象在代码中如何迭代。IteratorAggregate接口有一个方法:getIterator() 必须返回一个数组
Example

代码:——————————————————————————–

<?php
class ObjectIterator implements Iterator {

private $obj;
private $num;

function __construct($obj) {
$this->obj = $obj;
}
function rewind() {
$this->num = 0;
}
function valid() {
return $this->num < $this->obj->max;
}
function key() {
return $this->num;
}
function current() {
switch($this->num) {
case 0: return "1st";
case 1: return "2nd";
case 2: return "3rd";
default: return $this->num."th";
}
}
function next() {
$this->num++;
}
}

class Object implements IteratorAggregate {

public $max = 3;

function getIterator() {
return new ObjectIterator($this);
}
}

$obj = new Object;

// this foreach …
foreach($obj as $key => $val) {
echo "$key = $val\n";
}

// matches the following 7 lines with the for directive.
$it = $obj->getIterator();
for($it->rewind(); $it->hasMore(); $it->next) {
$key = $it->current();
$val = $it->key();
echo "$key = $val\n";
}
unset($it);
?>
——————————————————————————–

新的__toString方法
可以通过覆盖__toString方法来控制对象到字符串的转换。

2006年09月29日

三 DRM系统的选择

 

    我们需要考虑什么样的系统能够最有效地来管理我们手中的数字媒体内容。在选择过程中,需要考虑的因素有:作品的特性和版权、价值、发行风险和成本。无论是自己建立或者是购买商业版权管理系统,作为决策者来说,都需要有三个步骤才能正确地选择一个DRM系统。

 

    1. 在决定采用什么设备之前,首先需要明确并记录下作品的详细信息,版权,以及与此有关的特殊权利和授权。

 

    需要做的工作有:

 

    * 作品分类;

 

    * 明确作品的版权和授权,包括精神权利和使用权利;

 

    * 确认作品的作者和发行厂商;

 

    * 核算作品使用的基本情况(什么情况会使用,使用频率等等);

 

    * 考虑数字版权涉及的相关合同;

 

    * 谈判,确定数字版权的付费比率;

 

    * 充分考虑作品的敏感性以及被非法使用的可能性和造成的后果。

 

    2. 确定管理计划

 

    在这个阶段,我们已经搜集整理好了作品的基本情况和版权信息,并将这些信息与作品联系了起来。如果选择自己建立系统,就可以把这些信息做成数据库或者电子表格。通过用户名/密码方式来决定用户可以浏览哪些作品或相关的版权信息。如果选择建立使用更为复杂的系统,作品的版权信息一般会作为元数据形式保存起来,而作品会通过加密和数字水印手段加以保护。

 

    需要做的工作有:

 

    (1)建立一套内容提供和版权保护的策略机制,包括安全措施、个性化服务和水印。

 

    (2)确定策略后,再决定如何去实现,是自己开发还是买现成的。自己开发显然成本太高。购买模块并按照自己的要求进行组装或干脆购买整套解决方案。购买DRM的价格是昂贵的,而且在灵活性,功能和定制方面将受到很大的局限。最后一种办法是将版权管理交给专业机构去做,当然这需要缴纳服务费,在很多情况下,这些机构还会从你的营业收入中提成。

 

    (3)根据确定的策略和实现手段,确定能够提供此项服务的厂商。

 

    3. 实现管理计划

 

    当确定了管理计划,选择了合适的DRM系统以后,版权被侵犯的风险就被降低了,我们终于可以按照合同约定的条件来提供内容服务了。在一个简单系统中,用户能够找到自己需要的作品,并知道应该何时以及如何使用这些作品。在更复杂的系统中,用户下载、交易、修改和购买作品,系统根据用户的使用背景来决定用户如何来访问作品。系统还可以让版权所有者查询作品使用和交易的动态信息。一个理想的付费系统应该能够直接付费给版权人,同时给消费者出具收据。

 

    实现一个DRM系统通常需要的步骤有:

 

    a. 建立一个安全系统,比如密码,个人识别码,加密手段等。

 

    b. 建立电子商务环境。

 

    c.建立对交易记录和跟踪的环境。

 

    四 DRM系统的交互影响

 

    无论何种产品、服务和传输平台,对于致力于数字环境下的内容开发和销售商来说,需要克服的困难是相同的。

 

    举例来说,无论你制作交互电视还是教育课件,制作时间和成本主要包括:

 

    * 确定版权归属;

 

    * 版权费用结算;

 

    * 谈判合同条款;

 

    * 进行法律咨询;

 

    * 确保作品的使用是合法的;

 

    * 采用版权保护措施;

 

    * 管理付费系统;

 

    * 管理版权收入;

 

    * 对作品的使用进行跟踪;

 

    * 对使用者消费行为进行跟踪。

 

    研究表明,在传统媒体商业交易环境中,大量的时间耗费在了对作品使用情况的记录、跟踪和费用的结算上。采用DRM系统以后,将大大缩减这方面的时间,使得人们能够将精力集中在如何提供更多更好的内容服务上。

 

    DRM的推广使用户逐步接受对作品有授权的访问这种概念。DRM解决了困扰创作者和发行商的版权管理、收费等很多问题。随着标准版权描述语言的出现,人们将容易的确认作品的创作者、版权所有者和相关信息。根据这些信息,DRM控制用户合法访问、使用作品,并要求用户根据条款付费,生产和发行商不需要再花费时间对作品的使用版权进行谈判,所有这一切都由DRM系统来完成。用户可以合法地在自己的作品中使用其他人的劳动成果,而这些被使用的素材始终会附加版权保护的元数据,哪怕只是一个链接地址,这个地址会告诉使用者关于作品的一切版权信息。版权的保护技术可以采用以前说过的水印、密码、加密等,当然也可以根据用户使用作品的情况来判断是收费还是免费的。这时颗粒性就体现出重要性来了,因为用户要访问的作品常常是多个其他作品的组合,比如MP3歌曲集。

 

    五 DRM的未来

 

    DRM要得到成功,取决于以下几个因素:

 

    * 在保护版权人利益和为用户提供优质廉价的内容访问服务这对矛盾中,DRM如何做出恰当的平衡。

 

    * 对使用者来说要足够简单实用。无论你是最终用户,个人创作者甚至是大型音像库的管理者,都要能够方便地使用DRM系统。

 

    * 要吸取传统版权交易的经验,提供免费的服务和优惠的价格,比如类似“手机买30打50”这样的活动。DRM不能是一个死板的“要钱系统”,这样只会引起用户的厌恶并最终抛弃DRM。

 

    在未来几年里,传统商业运作将慢慢接受DRM的概念并付诸实用。如果是这样的话,众多的用户将能够得到越来越多丰富多彩的数字媒体内容,这些内容可以有很多种形式,比如数字图片、MP3、MPEG-4、REAL VIDEO等等。这些内容的获取,有些是免费的,有些是收费的,还有一些作品也许要价不菲,因为需要高额的版权费用。

 

    现在已经有创作者通过因特网来推销自己的作品将来会有更多的创作者采用这种方式来发布作品,与爱好者进行交流,并通过作品的售卖取得收入。发行商需要熟练运作多种媒体平台,将作品发布成多种数字格式,并不断升级更新自己的DRM系统,使作品能够在更多的平台上发表。销售商将各种格式的作品在DRM系统中通过某种收费机制销售出去。

 

    现在DRM的应用仍然处于起步阶段,只有具有创新意识的公司以及对此持乐观态度的企业才会投资。目前采用DRM比较多的是销售电子书籍音乐的网站。国内现在大量冒出的付费看电影、电视连续剧的网站,很多是个人建设的。这些网站具备了DRM系统的一些特点,比如内容的管理、安全性、付费系统等。但这些不是真正的DRM系统,相反,这些网站大量提供盗版、低级的内容服务。广电采用DRM系统,及早进入数字媒体发行市场已是迫在眉睫的事情了。

 

    2004年~2005年是DRM发展的关键阶段。DRM技术成熟度不断提高,应对不同使用背景和行业的解决方案不断推出。选择DRM将变得简单,同时投资成本降低,风险减少,这些都将推动内容提供商去选择应用DRM和电子商务技术来在线提供数字媒体内容。

 

    我们预测到2006年,DRM的市场将得到巩固和发展,世界主要的媒体发行商将采用DRM系统来管理销售他们的数字媒体内容,DRM会被普遍地采用。

 

    六 DRM涉及的法律问题

 

    无论对创作者、生产和发行商来说,必须认识到在DRM处理数字媒体的每个阶段,都时时刻刻在与法律打交道。针对不同的使用背景,我们要尽力找出适用的法律条文,必要的时候还要咨询律师。与DRM有关的法律主要是版权保护法。对作者来说,他自然拥有对创作作品的版权和相关权益。在DRM中,如果要使用的作品不是自己创作的,就需要争得版权所有者的同意。其他与DRM有关的法律还包括商标法、专利法、知识产权法、外观设计等等,另外合同法、涉及个人隐私保护方面的法律也是不能忽视的。

 

    对于数字媒体的知识产权保护,DRM只是技术手段,它的根基在与此相关的法律和与之匹配的管理手段。美国1998数字千年版权法(DMCA)体现了综合运用法律、DRM、管理这三种手段的原则,在法律上规定了对版权保护技术进行破解和销售破解工具都是违法的,同时也定义了版权管理信息。这些信息将使用户获得作品版权方面的信息,便于用户取得使用许可,还可跟踪、监控用户使用情况,防范侵权行为。

 

    DRM系统的应用背景是全球化的信息网络,数字作品的创作和销售已经成为全球范围内的行为,这必将与传统知识产权的区域性产生矛盾。知识产权保护不能由一个或几个国家来解决,加强国际合作是大势所趋。我国是世界知识产权组织(WIPO)和世界贸易组织(WTO)的成员国,虽然由于我国国内立法和文化、教育、经济及科技发展等原因,对一些知识产权的条款持保留态度,但最终按条约执行也只是一个时间问题。

 

    我们前面提到DRM比较好的实现方式之一是采用集中的版权管理和结算中心,其优点是更有效率,更灵活,成本更低。发达国家的经验也证明,民间的知识产权管理机构规范并管理知识产权保护,可以收到比较好的效果。我国也已经出现了第一个民间知识产权管理集体即中国音乐著作权协会。其法律依据来源于《中华人民共和国著作权法》第八条:“著作权人和与著作权有关的权利人可以授权著作权集体管理组织行使著作权或者与著作权有关的权利。著作权集体管理组织被授权后,可以以自己的名义为著作权人和与著作权有关的权利人主张权利,并可以作为当事人进行涉及著作权或者与著作权有关的权利的诉讼、仲裁活动 。”

 

    七 HELIX DRM 10 介绍

 

    目前国内数字视频内容发布大多采用REAL SYSTEM架构,比如REAL 8或HELIX 9。REAL MEDIA格式具有使用简单、适合网络传输、支持多种媒体格式等许多优点,变码率编码压缩、智能网络传输、支持多声道等众多特性,紧扣宽带网络的发展,在中国得到了普遍的应用。

 

    HELIX DRM 10是REAL公司最新推出的DRM解决方案,它号称是世界上第一个支持多种数字版权管理,多种终端的内容管理和安全发布平台。它支持的格式有REAL VIDEO、REAL AUDIO、MP3、AAC、MPEG-4、H.263、 AMR,它可以将以上内容发送到传统PC机、数字机顶盒、手机、个人信息终端和便携式音乐播放器中。HELIX DRM 10包含了一系列的产品和服务手段,通过安全加密手段来保护版权,将电影和音乐发售到全世界的因特网用户手中。

 

    HELIX DRM10能够实现的功能有:媒体内容加密封装,生成授权证书,高质量的媒体内容传输至授权的播放器中。目前REAL播放器已经具有多个主要的平台的版本,如WINDOWS、LINUX、IRIX等等,在PDA、手机中也有REAL播放器。

数字版权管理(Digital Rights Management,简称DRM)是一项涉及到技术、法律和商业各个层面的系统工程。它为数字媒体的商业运作提供了一套完整的实现手段。DRM技术的出现,使得版权所有者不用再耗费大量时间和精力与客户进行谈判,来确保数字媒体内容能够被合法的使用。DRM将使各个平台的内容提供商们,无论是因特网、流媒体还是交互数字电视,提供更多的内容,采取更灵活的节目销售方式,同时有效地保护知识产权。

 

    一 什么是DRM

 

    DRM不仅仅指版权保护,同时也提供了数字媒体内容的传输、管理和发行等一套完整的解决方案,因此DRM是一个系统概念,它包含数字版权信息使用,受版权保护的数字媒体内容的管理和分发。关于DRM,一个比较正规的说法是“DRM是对有形和无形资产版权和版权所有者关系的定义、辨别、交易、保护、监控和跟踪的手段。”

 

    DRM并不玄妙。对创作者来说,建立一张简单的电子表格,在表格里面记录下作品的版权信息和所有者,并对这些信息进行跟踪就可以实现最简单的DRM。当然这个电子表格可以做成ONLINE的形式,这样就可以随时在网络上查阅了。

 

    对于生产和发行商来说,DRM系统就复杂多了,它要能够记录、追踪、监控一系列已有的和新创作的作品,作品内容本身以数字格式被存储并加密,只有当生产商、发行商、创作者就合同达成一致,结算货款以后才能被解密使用。

 

    二 DRM的特点

 

    所有的DRM应用都是基于“系统辨别”和“信息记录”的。记录里面包括了合法版权所有者的情况和版权信息。这些情况或信息是通过元数据(Metadata)、版权管理信息(Rights Management Information, 简称RMI)或数字对象标识符(Digital Object Identifiers)来实现的。

 

    1. 元数据(Metadata)

 

    元数据是保存特定信息的数据段,所有的元数据都是由一套关键词和数据类别描述符构成的。

 

    合理使用元数据对于查找信息是非常有帮助的,它能描绘出作品的基本信息。使用元数据存在两个问题:一是不同的人在不同的时间,不用背景下会对同一事件采用不同的关键词来描述;二是元数据的结构兼容性,比如A标准会用<姓>,<名>来记录一个人,而B标准用<姓名>来记录,那么除非做转换,A标准无法和B标准兼容。因此世界上很多组织做了大量的工作,就是为了建立持久通用的元数据结构标准。我国在这方面也做了许多工作,如1996年《中国文献编目规则》,1998年《音像资料机读目录》,以及由广电总局领导,中央电视台,南京电视台等参与的媒体资产管理编目规则。

 

    2. 安全和版权保护

 

    在DRM系统的实现方案中,采用了许多技术用来避免知识产权被侵犯。每种手段都有各自的优缺点。采用什么方案,需要综合考虑其优缺点、维护成本、被保护作品的重要性、作品被侵权的可能性和打算投入的资金量。

 

    没有任何一个技术系统是100%安全的,DRM为版权作品提供了相对高水平的安全保护。最普遍的保护技术是加密和数字水印。在美国、欧盟和中国香港,都有相关的法律条文,对窜改数字水印、销售解密设备等行为给予重罚。《中华人民共和国著作权法》第47条规定“……情节严重的,著作权行政管理部门还可以没收主要用于制作侵权复制品的材料、工具、设备等……”。但我国没有明文规定限制出售可能协助盗版的解密设备。

 

    (1)加密

 

    采用一定的数学模型,对原始信息进行重新加工,使用者必须提供密码,才能提取到正确的原始信息。软件通常用加密手段来保护。

 

    (2)版权保护技术

 

    有很多技术手段可以用来保护作品不接受未授权的访问和使用。版权保护技术首先将作品可以合法使用的条款和场所进行编码,嵌入到文件中,只用当条件满足,作品才可以被允许使用。通常,被嵌入的信息包括版权管理信息(RMI),如作者、标题、版权和密钥链接。密钥用来对作品进行解密。用户需要授权证书才能访问密钥,授权证书决定了用户的权限。密钥通常与用户计算机的特性有关,这样防止密钥使用在其他计算机上,这个技术有时被称为“节点锁定”。

 

    (3)数字水印和签名

 

    数字水印是把作者、发行商信息和使用条款嵌入到数据中,只有当数据被严重破坏时,它才有可能被抹去。即使数据质量降低,只要水印有效,它就可以被识别出来。水印可以是可见的,也可以是隐形的。它能用来给某个作品打上使用者独有的印记,防止使用者非法传播和复制。

 

    3. 个性化

 

    个性化是指根据用户要求,对作品大小、格式、内容进行剪裁,同时打上可见的个人水印。打个比方,英语泛读教材包括了许多书籍和杂志的选段,并会注明本文章来自于那本书的某某章节、作者是谁等等。读者可以很方便地查找原始素材。只要做法得当,个性化是个非常有效的方式,但是它需要大量的部署工作和投资。

 

    4. 颗粒性

 

    颗粒性用来描述DRM系统对信息的细分能力。一个目标信息能够细分到多小的模块,这些模块又如何被其他创作者和制片人使用来创作新作品?讨论DRM的颗粒性是很有必要的,因为这种对基础模块的积累和混合是创作新媒体、新作品的基本手段和源泉。

 

    颗粒性使得创作开发人员能高效低成本地选择、组合新的或已有的视音频、平面素材,用来创作新的作品。举例来说,一个中学教师要创作一个课件。他购买了一本参考书的一章或一页,又购买了10秒钟的一段录像,然后他利用购买来的这些内容创作了自己的电子课件。如果所有这一切由DRM系统提供,那么就需要DRM系统既要把老师需要的内容正确地发送出去,同时又要以预先设定的方式赋予这些内容必要的权限。

 

    5. 兼容性

 

    在商业环境中,创作者、生产商、发行商需要互相沟通。制片人和使用者需要频繁地在不同的场合使用不同来源,不同格式的作品资源,这就需要发展的系统具有较强的兼容性。

 

    这方面的开发工作包括:

 

    (1)数字对象标志符(The Digital Object Identifier,DOI)

 

    DOI系统类似于用来识别物品的条形码,注册代理机构为每一个数字对象分配识别号码并记录描述该对象的元数据,假如能够用相对简单的办法跟踪对象和版权所有者情况,那么元数据就可以被不断更新。

 

    (2)版权表述语言(Rights Expression Languages)

 

    为了表述使用数字内容的法律条款,人们发明了许多形式的表述语言。这些语言具有通用性,可以被用在网站、文本文件、图片、音乐、PDF文档和流媒体中。这些表述语言中比较有名的是Open Digital Rights Language Initiative (ODRL)和Extensible Rights Makeup Language(XrML)。

 

    6. 易用性

 

    要使DRM系统能够被采用,一个很重要的原因是必须使合法用户能够轻松方便地使用这个系统。嵌入在元数据中的技术控制手段不能让用户觉得别扭,同时还要帮助合法消费者能够快捷地访问自己需要的内容,而不用再填写额外的表格。在大多数情况下,除非用户企图访问未授权的内容,否则他们一般不会感觉到DRM系统的存在。虽然用户的访问受到了限制,但对合法的访问而言,DRM应该是透明的。

 

    7. 付费系统

 

    DRM能够提供的付费方式有好几种。当然在实际应用中,没有一种方式可以应对所有的情形。因特网上最普通的付费方式是用信用卡来付费。不过使用信用卡需要缴纳高额的银行手续费,甚至有时手续费比所购买的商品价值还高。网上的单次交易额通常不是很大,因此需要采用更科学经济的付费方式。DRM需要开发新的付费手段,来应对大量的小额交易,哪怕只有几毛几分钱。

 

    (1)预付费

 

    预付费,也称订阅服务。用户需要在信用卡或专门的卡上预先缴纳一笔费用。用户享受服务一直到卡上的钱用完为止,然后用户需要继续缴纳费用。预付费系统容易维护,成本低廉。在DRM数字系统中,这些优点尤为明显。付费系统通过记录顾客的消费行为,使得供应商能够向顾客提供个性化的商品和服务。预付费系统的缺点是用户不知道卡上的钱什么时候用完,另一个缺点是预付费系统一般是按照用户使用时间或信息量来收费,销售商无法将营业收入与用户浏览过的内容联系起来,除非系统详细记录了这方面的信息。

 

    (2)累计付费

 

    顾客每次访问系统所产生的费用被累计起来,只有达到一定额度时才被要求付费。也就是先享受服务,然后付费。这与信用卡消费的观念是一致的。这种方式的缺点是对那些偶尔使用系统的顾客来说,通常会无限期的拖延付费,除非他们确定自己会经常使用该系统。

 

    (3)付费中心

 

    采取集中化管理的付费中心来处理所有销售商的预付费和累计付费账单,避免了顾客需要向多家销售商缴费带来的烦恼,同时可以有效处理大量的小额交易。付费中心可以将顾客在不同地方、不同形式的消费,包括累计消费,预付费,甚至超市购物费用累计起来并通知,用户在适当的时候一次性缴纳。这样就避免了多次小额付费产生附加的运营成本。

 

    (4)电子货币

 

    建立电子货币系统,可以大大减少银行信用卡系统高额的运行成本,带给顾客更多的实惠。虽然在理论上,已经有好几种电子货币系统在已经可行,但还没有一个成功的商业范例。

2006年09月20日
Windows Media Rights Manager 简介 idleawei@hotmail.com 2001-11-18 Windows Media Rights Manager (DRM, Microsoft Windows Media™ digital rights management) 是 微软的 Windows Media Technologies 的组成部分之一。DRM是一项用来对数字媒体文件进行加密保护,以利于其发布、销售的技术,它应用于互联网。DRM可以帮助数字媒体文件的拥有者在数字媒体领域创建一个安全性高、应用范围广、兼容性优良、扩展性强的商业模式。 一. DRM的工作流程 数字媒体文件的拥有者使用DRM平台对其拥有的数字媒体文件进行加密、发布。当使用者通过Web server 或 流媒体服务器(Streaming Media Server, 如 Windows Media 服务器) 或其他途径取得一个被DRM加密的文件并进行播放时,支持DRM的播放器首先会向一台指定的证书服务器(License Server)提出一个播放权限请求,然后经过一系列设定的流程,播放器取得该文件的播放证书,最后播放媒体文件。 下图将说明一个数字媒体文件如何被DRM加密、发布: [IMG]http://www.microsoft.com/windows/windowsmedia/images/drm/flowchart.gif[/IMG] 根据图表我们来分析一下DRM的工作流程。 1. 加密数字媒体文件 被加密文件可以是歌曲、影像或者包含其它数字媒体内容的文件。文件的拥有者(内容拥有者)使用Windows Media Right Manager 来加密文件。被加密的文件就像是被一把特定的锁来锁定,要播放文件就必须使用专用的密钥(key)来打开这把锁。密钥(key)被存储在一个加密过的证书(license)中,证书将独立于媒体文件单独进行发布。在加密媒体文件过程中,一些附加的信息将被加入文件中,例如证书(license)发布的地址(url)。加密后的文件将被以微软音频文件格式(wma格式)或者微软视频格式(wmv格式)存储。 2.发行加密的媒体文件 加密的媒体文件可以通过Web站点来发布,也可以使用流技术通过流媒体服务器发布,还可以通过CD、或者e-mail等途径来发布。Windows Media Rights Manager 允许消费者传递受版权保护的媒体文件给他们的朋友。 3.建立证书服务器 内容拥有者需要选择一个场所来存储证书和规划证书的发布,以完成DRM的证书发布服务功能。使用微软证书服务器可以担当这个任务。证书服务器会被用来审查消费者的播放申请。媒体文件和播放证书被分别独立存储和发布有利于整个DRM系统的管理和操作。 4. 获得媒体文件 5. 申请播放证书 6. 获得播放证书 消费者可以通过Web站点和流媒体服务器来获得或者播放媒体文件。当消费者第一次使用支持DRM的播放器播放加密文件时,播放器会自动向指定的证书服务器请求一个播放证书。这个证书内含的特定的密钥(key)将被用来打开加在文件上的锁。我们可以定制证书播放的过程,可以让消费者知道证书发布的过程,例如让消费者填写一些必要的申请表格,也可以不让消费者知晓,在暗中发布证书。 7.播放媒体文件 要播放加密的数字文件,必须使用支持DRM的媒体播放器。消费者可以在一定期限内播放文件,或者可以播放一定次数(例如只能播放4次文件)。内容拥有者可以在加密文件时设置这些限制,还可以设置是否允许移动设备播放被加密的文件。消费者在朋友间传递文件,但是当某位朋友需要播放文件时,他也需要向证书服务器提出申请播放证书,因为播放证书是被设计为不能被传递的。这将保证内容拥有者的利益。 以上是DRM工作流程的简单介绍,下面介绍一下有关证书和解密钥的一些特性. 二.证书和密钥的特性介绍 (License and Keys) 密钥的简单介绍 内容拥有者通过一把”锁”来加密文件,在消费者能够播放加密文件前,用来打开这把”锁”的密钥(key)会被储存在播放证书(license)中发送给消费者。下面的图表将说明Windows Media Rights Manager 如何产生密钥。 [IMG]http://www.microsoft.com/windows/windowsmedia/images/drm/license_keys.gif[/IMG] 要产生密钥,首先需要密钥种子(license key seed) 和密钥ID(key ID): ■ 密钥种子的值只有内容拥有者和证书发行者(证书服务器)知道。 ■ 密钥ID是被用来标志每个媒体文件的。它的值存储于加密后的媒体文件中。 当证书服务器获得一个证书请求时,服务器会取得加密文件中的key ID和服务器存储的license key seed 来重新产生密钥,这个密钥会被存储进证书然后发送给请求者。然后请求者就可以用证书打开和播放加密文件了。 证书的简单介绍 每一个证书都包含有一个用来打开解密文件的密钥。证书还包含了其他的一些信息,这些信息用来管理媒体文件的播放。内容拥有者可以使用默认限制的播放属性可以设置更多限制的播放属性。Windows Media Rights Manager的证书可以提供范围很广的商业规范,包含以下这些: ■ 设置文件被允许播放的次数 ■ 设置可用来播放或者传播文件的设备。例如可以设置文件可以被传输到支持SDMI(Secure Digital Music Initiative)的移动设备. ■ 设置文件可以播放的时间,不能播放的过期时间。 ■ 设置文件是否可以被刻录到CD上。 ■ 设置证书时候可以被用户备份或者存贮。 ■ 设置被用来播放文件系统的安全等级。 证书可以用不同的方式和不同的时间传递,这些都取决于商业模式。内容拥有者也许想预先发布证书,或者想让消费者在播放文件后请求发布。证书的传递可以让消费者知晓,也可以以不知晓的方式来发布。
2006年09月06日
如何在MySQL中获得更好的全文搜索结果
作者: Techrepublic.com.com
2006-04-03 11:30 AM

 

很多互联网应用程序都提供了全文搜索功能,用户可以使用一个词或者词语片断作为查询项目来定位匹配的记录。在后台,这些程序使用在一个SELECT查询中的LIKE语句来执行这种查询,尽管这种方法可行,但对于全文查找而言,这是一种效率极端低下的方法,尤其在处理大量数据的时候。
MySQL针对这一问题提供了一种基于内建的全文查找方式的解决方案。在此,开发者只需要简单地标记出需要全文查找的字段,然后使用特殊的MySQL方法在那些字段运行搜索,这不仅仅提高了性能和效率(因为MySQL对这些字段做了索引来优化搜索),而且实现了更高质量的搜索,因为MySQL使用自然语言来智能地对结果评级,以去掉不相关的项目。
这篇文章将向您讲述在MySQL中如何进行全文搜索。
1、设置基本表格
从创建例子表格开始,使用以下的SQL命令:
mysql> CREATE TABLE reviews (id INT(5) PRIMARY KEY NOT NULL AUTO_INCREMENT, data TEXT);
以上命令创建了一个简单的音乐专集资料库(主要是整段的文字),然后向这个表格中添加一些记录:
mysql> INSERT INTO `reviews` (`id`, `data`) VALUES
(1, ‘Gingerboy has a new single out called Throwing Rocks. It\’s great!’);
mysql> INSERT INTO `reviews` (`id`, `data`) VALUES
(2, ‘Hello all, I really like the new Madonna single.
One of the hottest tracks currently playing…I\’ve been listening to it all day’);
mysql> INSERT INTO `reviews` (`id`, `data`)
VALUES (3, ‘Have you heard the new band Hotter Than Hell?
They have five members and they burn their instruments when they play in concerts.
These guys totally rock! Like, awesome, dude!’);
验证数据的正确录入:
mysql> SELECT * FROM reviews;
+—-+——————————————–+
| id | data                                       |
+—-+——————————————–+
|  1 | Gingerboy has a new single out called …  |
|  2 | Hello all, I really like the new Madon … |
|  3 | Have you heard the new band Hotter Than… |
+—-+——————————————–+
3 rows in set (0.00 sec)
2、定义全文搜索字段
接下来,定义您要作为全文搜索索引的字段
mysql> ALTER TABLE reviews ADD FULLTEXT INDEX (data);
Query OK, 3 rows affected (0.21 sec)
Records: 3  Duplicates: 0  Warnings: 0
使用SHOW INDEXES命令来检查索引已经被添加了:
mysql> SHOW INDEXES FROM reviews;
+———+—————+——–+——+————+———+
| Table   | Column_name   | Packed | Null | Index_type | Comment |
———-+—————+——–+——+————+———+
| reviews |  id           | NULL   |      | BTREE      |         |
| reviews |  data         | NULL   | YES  | FULLTEXT   |         |
+———+—————+——–+——+————+———+
2 rows in set (0.01 sec)
3、运行全文搜索
当您拥有了数据和索引,就可以使用MySQL的全文搜索了,最简单的全文搜索方式是带有MATCH…AGAINST语句的SELECT查询,以下是一个简单的例子,可以来查找含有单词“single”的记录:
mysql> SELECT id FROM reviews WHERE MATCH (data) AGAINST (’single’);+—-+
| id |
+—-+
|  1 |
|  2 |
+—-+
2 rows in set (0.00 sec)
在此,MATCH()将作为参数传递给它的字段中的文字与传递给AGAINST()的参数进行比较,如果有匹配的,那就按照正常的方式返回。注意您可以传递不止一个字段用MATCH()来查看­-只需用逗号来分割字段列表。
当MySQL收到了一个全文搜索的请求,它就在内部对每个记录进行评分,不匹配的记录得分为零,而“更相关”的记录会得到比“不太相关”的记录相对更高的分数。相关性是由MySQL的一系列区分标准来决定的,查看MySQL的用户手册可以得到更多的信息。
想看到每个记录的评分如何,只需要返回MATCH()方法作为结果集的一部分,如下所示:
mysql> SELECT id, MATCH (data) AGAINST (‘rock’) FROM reviews;
+—-+——————————-+
| id | MATCH (data) AGAINST (‘rock’) |
+—-+——————————-+
|  1 |                             0 |
|  2 |                             0 |
|  3 |               1.3862514533815 |
+—-+——————————-+
3 rows in set (0.00 sec)
4、使用逻辑搜索修饰符(Boolean search modifiers
您还可以使用逻辑搜索修饰符来进行更精确的搜索,这通过在AGAINST语句中添加特殊的IN BOOLEAN MODE修饰符来实现,在以下的例子中,将查找含有单词“single”但是没有“Madonna”的记录:
mysql> SELECT id FROM reviews WHERE MATCH (data) AGAINST (‘+single -madonna’ IN BOOLEAN MODE);
+—-+
| id |
+—-+
|  1 |
+—-+
1 row in set (0.00 sec)
这一搜索特性通常用于搜索单词片断(而不是完整的词语),这可以通过在IN BOOLEAN MODE语句中的*(星号)操作符来实现,以下的例子展示了如何查找单词中含有“hot”的记录:
mysql> SELECT id FROM reviews WHERE MATCH (data) AGAINST (‘hot*’ IN BOOLEAN MODE);+—-+
| id |
+—-+
|  3 |
|  2 |
+—-+
2 rows in set (0.00 sec)
您还可以使用这种方法来查找至少一个传递到AGAINST的参数中,以下的例子查找了至少包含单词“hell”和“rocks”中的一个的记录:
mysql> SELECT id FROM reviews WHERE MATCH (data) AGAINST (‘hell rocks’ IN BOOLEAN MODE);
+—-+
| id |
+—-+
|  1 |
|  3 |
+—-+
2 rows in set (0.00 sec)
以上的这些例子演示了相对于传统的SELECT…LIKE语句,进行全文搜索的更有效的方法,当您下一次需要编写MySQL数据库搜索界面的时候,您可以尝试这一方法。
2006年08月16日

Lucene是一个高性能的java全文检索工具包,它使用的是倒排文件索引结构。该结构及相应的生成算法如下:

0)设有两篇文章1和2
文章1的内容为:Tom lives in Guangzhou,I live in Guangzhou too.
文章2的内容为:He once lived in Shanghai.

1)由于lucene是基于关键词索引和查询的,首先我们要取得这两篇文章的关键词,通常我们需要如下处理措施
a.我们现在有的是文章内容,即一个字符串,我们先要找出字符串中的所有单词,即分词。英文单词由于用空格分隔,比较好处理。中文单词间是连在一起的需要特殊的分词处理。
b.文章中的”in”, “once” “too”等词没有什么实际意义,中文中的“的”“是”等字通常也无具体含义,这些不代表概念的词可以过滤掉
c.用户通常希望查“He”时能把含“he”,“HE”的文章也找出来,所以所有单词需要统一大小写。
d.用户通常希望查“live”时能把含“lives”,“lived”的文章也找出来,所以需要把“lives”,“lived”还原成“live”
e.文章中的标点符号通常不表示某种概念,也可以过滤掉
在lucene中以上措施由Analyzer类完成

经过上面处理后
    文章1的所有关键词为:[tom] [live] [guangzhou] [i] [live] [guangzhou]
    文章2的所有关键词为:[he] [live] [shanghai]

2) 有了关键词后,我们就可以建立倒排索引了。上面的对应关系是:“文章号”对“文章中所有关键词”。倒排索引把这个关系倒过来,变成:“关键词”对“拥有该关键词的所有文章号”。文章1,2经过倒排后变成
关键词   文章号
guangzhou  1
he         2
i           1
live       1,2
shanghai   2
tom         1

通常仅知道关键词在哪些文章中出现还不够,我们还需要知道关键词在文章中出现次数和出现的位置,通常有两种位置:a)字符位置,即记录该词是文章中第几个字符(优点是关键词亮显时定位快);b)关键词位置,即记录该词是文章中第几个关键词(优点是节约索引空间、词组(phase)查询快),lucene中记录的就是这种位置。

加上“出现频率”和“出现位置”信息后,我们的索引结构变为:
关键词   文章号[出现频率]   出现位置
guangzhou 1[2]               3,6
he       2[1]               1
i         1[1]               4
live      1[2],2[1]           2,5,2
shanghai  2[1]               3
tom      1[1]               1

以live 这行为例我们说明一下该结构:live在文章1中出现了2次,文章2中出现了一次,它的出现位置为“2,5,2”这表示什么呢?我们需要结合文章号和出现频率来分析,文章1中出现了2次,那么“2,5”就表示live在文章1中出现的两个位置,文章2中出现了一次,剩下的“2”就表示live是文章2中第 2个关键字。
    
以上就是lucene索引结构中最核心的部分。我们注意到关键字是按字符顺序排列的(lucene没有使用B树结构),因此lucene可以用二元搜索算法快速定位关键词。
    
实现时 lucene将上面三列分别作为词典文件(Term Dictionary)、频率文件(frequencies)、位置文件 (positions)保存。其中词典文件不仅保存有每个关键词,还保留了指向频率文件和位置文件的指针,通过指针可以找到该关键字的频率信息和位置信息。

    Lucene中使用了field的概念,用于表达信息所在位置(如标题中,文章中,url中),在建索引中,该field信息也记录在词典文件中,每个关键词都有一个field信息(因为每个关键字一定属于一个或多个field)。

     为了减小索引文件的大小,Lucene对索引还使用了压缩技术。首先,对词典文件中的关键词进行了压缩,关键词压缩为<前缀长度,后缀>,例如:当前词为“阿拉伯语”,上一个词为“阿拉伯”,那么“阿拉伯语”压缩为<3,语>。其次大量用到的是对数字的压缩,数字只保存与上一个值的差值(这样可以减小数字的长度,进而减少保存该数字需要的字节数)。例如当前文章号是16389(不压缩要用3个字节保存),上一文章号是16382,压缩后保存7(只用一个字节)。
    
    下面我们可以通过对该索引的查询来解释一下为什么要建立索引。
假设要查询单词 “live”,lucene先对词典二元查找、找到该词,通过指向频率文件的指针读出所有文章号,然后返回结果。词典通常非常小,因而,整个过程的时间是毫秒级的。
而用普通的顺序匹配算法,不建索引,而是对所有文章的内容进行字符串匹配,这个过程将会相当缓慢,当文章数目很大时,时间往往是无法忍受的。

Apache在配置编译时可以自主的选择想要使用的MPM模块,使用./configure –with-mpm=MPM命令。我们主要了解prefork和worker这两种MPM模块。
Prefork
如果不用“–with-mpm”显式指定某种MPM,prefork就是Unix平台上缺省的MPM。它所采用的预派生子进程方式,用单独的子进程来处理不同的请求,进程之间彼此独立。在make编译和make install安装后,使用httpd -l来确定当前使用的
MPM是prefork.c。查看httpd-mpm.conf配置文件,里面包含如下默认的配置段:
<IfModule prefork.c>
StartServers 5
MinSpareServers 5
MaxSpareServers 10
MaxClients 150
MaxRequestsPerChild 0
</IfModule>
prefork控制进程在最初建立“StartServers”个子进程后,为了满足MinSpareServers设置的需要创建一个进程,等待一秒钟,继续创建两个,再等待一秒钟,继续创建四个……如此按指数级增加创建的进程数,最多达到每秒32个,直到满足MinSpareServers设置的值为止。这种模式可以不必在请求到来时再产生新的进程,从而减小了系统开销以增加性能。MaxSpareServers设置了最大的空闲进程数,如果空闲进程数大于这个值,Apache会自动kill掉一些多余进程。这个值不要设得过大,但如果设的值比MinSpareServers小,Apache会自动把其调整为MinSpareServers+1。如果站点负载较大,可考虑同时加大MinSpareServers和MaxSpareServers。MaxRequestsPerChild设置的是每个子进程可处理的请求数。每个子进程在处理了“MaxRequestsPerChild”个请求后将自动销毁。0意味着无限,即子进程永不销毁。虽然缺省设为0可以使每个子进程处理更多的请求,但如果设成非零值也有两点重要的好处:1、可防止意外的内存泄漏。2、在服务器负载下降的时侯会自动减少子进程数。因此,可根据服务器的负载来调整这个值。MaxClients是这些指令中最为重要的一个,设定的是Apache可以同时处理的请求,是对Apache性能影响最大的参数。其缺省值150是远远不够的,如果请求总数已达到这个值(可通过ps -ef|grep http|wc -l来确认),那么后面的请求就要排队,直到某个已处理请求完毕。这就是系统资源还剩下很多而HTTP访问却很慢的主要原因。虽然理论上这个值越大,可以处理的请求就越多,但Apache默认的限制不能大于256。ServerLimit指令无须重编译Apache就可以加大MaxClients。ServerLimt应该放在第一个位置,放在其他指令之间不起作用(不明白原因)。
<IfModule prefork.c>
ServerLimit  10000
StartServers 5
MinSpareServers 5
MaxSpareServers 10
MaxClients 10000
MaxRequestsPerChild 0
</IfModule>
Worker
相对于prefork,worker全新的支持多线程和多进程混合模型的MPM。由于使用线程来处理,所以可以处理相对海量的请求,而系统资源的开销要小于基于进程的服务器。但是,worker也使用了多进程,每个进程又生成多个线程,以获得基于进程服务器的稳定性。在configure –with-mpm=worker后,进行make编译、make install安装。在缺省生成的httpd-mpm.conf中有以下默认配置段:
<IfModule worker.c>
StartServers 2
MaxClients 150
MinSpareThreads 25
MaxSpareThreads 75
ThreadsPerChild 25
MaxRequestsPerChild 0
</IfModule>
Worker由主控制进程生成“StartServers”个子进程,每个子进程中包含固定的ThreadsPerChild线程数,各个线程独立地处理请求。同样,为了不在请求到来时再生成线程,MinSpareThreads和MaxSpareThreads设置了最少和最多的空闲线程数;而MaxClients设置了同时连入的clients最大总数。如果现有子进程中的线程总数不能满足负载,控制进程将派生新的子进程。MinSpareThreads和MaxSpareThreads的最大缺省值分别是75和250。这两个参数对Apache的性能影响并不大,可以按照实际情况相应调节。ThreadsPerChild是worker MPM中与性能相关最密切的指令。ThreadsPerChild的最大缺省值是64,如果负载较大,64也是不够的。这时要显式使用ThreadLimit指令,它的最大缺省值是20000。Worker模式下所能同时处理的请求总数是由子进程总数乘以ThreadsPerChild值决定的,应该大于等于MaxClients。如果负载很大,现有的子进程数不能满足时,控制进程会派生新的子进程。默认最大的子进程总数是16,加大时也需要显式声明ServerLimit(最大值是20000)。需要注意的是,如果显式声明了ServerLimit,那么它乘以ThreadsPerChild的值必须大于等于MaxClients,而且MaxClients必须是ThreadsPerChild的整数倍,否则Apache将会自动调节到一个相应值。
<IfModule worker.c>
ServerLimit 25
ThreadLimit 200
StartServers 3
MaxClients 2000
MinSpareThreads 50
MaxSpareThreads 200
ThreadsPerChild 100
MaxRequestsPerChild 0
</IfModule>
下面是利用Apache自带的测试工具ab对Server进行测试的情况(设定请求的index页面为6bytes),cpu%为cpu占用率,mem为内存使用量(M为单位),RequestsPerSecond为每秒处理的请求数。
1、Prefor方式
  (ServerLimit,StartServer,MinSpareServers,MaxSpareServers,MaxClients,MaxRequestPerChild)            
-n/-c(ab参数) Cpu% Mem
Requestspersecond
(-,5,5,10,150,0)
100000/100 28.8 285 8434
100000/200 29.2 304 8032
100000/500 25.3 323 7348
100000/1000 24.4 330 5886
(10000,5,5,10,500,0)
100000/100 28.7 371 8345
100000/200 27.4 389 7929
100000/500 24.9 417 7229
100000/1000 23.4 437 6676
(10000,5,5,10,1000,0)
100000/100 28.8 408 8517
100000/200 27.0 422 8045
100000/500 24.2 455 7236
100000/1000 22.5 470 6570
(10000,5,5,10,1500,0)
100000/100 29.6 330 8407
100000/200 28.1 349 8014
100000/500 26.4 380 7290
100000/1000 24.0 400 6686
2、Worker方式
(ServerLimt,Threadlimt,Startservers,MaxClients,MinspareThread,MaxspareThread,ThreadperChild,MaxRequestPerChild)
                   
-n/-c(ab参数) cpu% mem RequestsperSecond
(50,500,5,10000,50,200,200,0)
100000/100  18.6 188 6020
100000/200 20.1 195 5892
100000/500 19.8 209 5708
100000/1000 22.2 218 6081
(100,500,5,10000,50,200,100,0)
100000/100  24.5 240 6919
100000/200 23.6 247 6798
100000/500 24.6 254 6827
100000/1000 22.3 271 6114
(200,500,5,10000,50,200,50,0)
100000/100  27.3 301 7781
100000/200 27.4 307 7789
100000/500 26.0 320 7141
100000/1000 21.8 344 6110
相对来说,prefork方式速度要稍高于worker,然而它需要的cpu和memory资源也稍多于woker。