0

PHP & memcached


一、memcached 简介

在很多场合,我们都会听到 memcached 这个名字,但很多同学只是听过,并没有用过或实际了解过,只知道它是一个很不错的东东。这里简单介绍一下,memcached 是高效、快速的分布式内存对象缓存系统,主要用于加速 动态应用程序。

二、memcached 安装

首先是下载 memcached 了,目前最新版本是 1.1.12,直接从官方网站即可下载到 memcached-1.1.12.tar.gz。除此之外,memcached 用到了 libevent,我下载的是 libevent-1.1a.tar.gz

接下来是分别将 libevent-1.1a.tar.gz 和 memcached-1.1.12.tar.gz 解开包、编译、安装:

# tar -xzf libevent-1.1a.tar.gz# cd libevent-1.1a# ./configure --prefix=/usr# make# make install# cd ..# tar -xzf memcached-1.1.12.tar.gz# cd memcached-1.1.12# ./configure --prefix=/usr# make# make install

安装完成之后,memcached 应该在 /usr/bin/memcached。

三、运行 memcached 守护程序

运行 memcached 守护程序很简单,只需一个命令行即可,不需要修改任何配置文件(也没有配置文件给你修改 :) ):

/usr/bin/memcached -d -m 128 -l 192.168.1.1 -p 11211 -u httpd

参数解释:

-d 以守护程序(daemon)方式运行 memcached;-m 设置 memcached 可以使用的内存大小,单位为 M;-l 设置监听的 IP 地址,如果是本机的话,通常可以不设置此参数;-p 设置监听的端口,默认为 11211,所以也可以不设置此参数;-u 指定用户,如果当前为 root 的话,需要使用此参数指定用户。

当然,还有其它参数可以用,man memcached 一下就可以看到了。

四、memcached 的工作原理

首先 memcached 是以守护程序方式运行于一个或多个服务器中,随时接受客户端的连接操作,客户端可以由各种语言编写,目前已知的客户端 API 包括 Perl/PHP////C#/C 等等。PHP 等客户端在与 memcached 服务建立连接之后,接下来的事情就是存取对象了,每个被存取的对象都有一个唯一的标识符 key,存取操作均通过这个 key 进行,保存到 memcached 中的对象实际上是放置内存中的,并不是保存在 文件中的,这也是为什么 memcached 能够如此高效快速的原因。注意,这些对象并不是持久的,服务停止之后,里边的数据就会丢失。

三、PHP 如何作为 memcached 客户端

有两种方法可以使 PHP 作为 memcached 客户端,调用 memcached 的服务进行对象存取操作。

第一种,PHP 有一个叫做 memcache 的扩展 下编译时需要带上 –enable-memcache[=DIR] 选项,Window 下则在 php.ini 中去掉 php_memcache.dll 前边的注释符,使其可用。

除此之外,还有一种方法,可以避开扩展、重新编译所带来的麻烦,那就是直接使用 php-memcached-client

本文选用第二种方式,虽然效率会比扩展库稍差一些,但问题不大。

四、PHP memcached 应用示例

首先 下载 memcached-client.php,在下载了 memcached-client.php 之后,就可以通过这个文件中的类“memcached”对 memcached 服务进行操作了。其实代码调用非常简单,主要会用到的方法有 add()、get()、replace() 和 delete(),方法说明如下:

add ($key, $val, $exp = 0)
往 memcached 中写入对象,$key 是对象的唯一标识符,$val 是写入的对象数据,$exp 为过期时间,单位为秒,默认为不限时间;

get ($key)
从 memcached 中获取对象数据,通过对象的唯一标识符 $key 获取;

replace ($key, $value, $exp=0)
使用 $value 替换 memcached 中标识符为 $key 的对象内容,参数与 add() 方法一样,只有 $key 对象存在的情况下才会起作用;

delete ($key, $time = 0)
删除 memcached 中标识符为 $key 的对象,$time 为可选参数,表示删除之前需要等待多长时间。

下面是一段简单的测试代码,代码中对标识符为 ‘mykey’ 的对象数据进行存取操作:

<pre><?php//  包含 memcached 类文件require_once(‘memcached-client.php’);//  选项设置$options = array(    ’servers’ => array(‘192.168.1.1:11211′), //memcached 服务的地址、端口,可用多个数组元素表示多个 memcached 服务    ’ => true,  //是否打开 debug    ‘compress_threshold’ => 10240,  //超过多少字节的数据时进行压缩    ‘persistant’ => false  //是否使用持久连接    );//  创建 memcached 对象实例$mc = new memcached($options);//  设置此脚本使用的唯一标识符$key ‘mykey’;//  往 memcached 中写入对象$mc->add($key’some random strings’);$val $mc->get($key);echo “n”.str_pad(‘$mc->add() ’60‘_’).“n”;var_dump($val);//  替换已写入的对象数据值$mc->replace($key, array(’some’=>‘haha’‘array’=>‘xxx’));$val $mc->get($key);echo “n”.str_pad(‘$mc->replace() ’60‘_’).“n”;var_dump($val);//  删除 memcached 中的对象$mc->delete($key);$val $mc->get($key);echo “n”.str_pad(‘$mc->delete() ’60‘_’).“n”;var_dump($val);?></pre>

是不是很简单,在实际应用中,通常会把数据库查询的结果集保存到 memcached 中,下次访问时直接从 memcached 中获取,而不再做数据库查询操作,这样可以在很大程度上减轻数据库的负担。通常会将 SQL 语句 md5() 之后的值作为唯一标识符 key。下边是一个利用 memcached 来缓存数据库查询结果集的示例(此代码片段紧接上边的示例代码):

<?php$sql ‘SELECT * FROM users’;$key md5($sql);   //memcached 对象标识符if ( !($datas $mc->get($key)) ) {    //  在 memcached 中未获取到缓存数据,则使用数据库查询获取记录集。    echo “n”.str_pad(‘Read datas from MySQL.’60‘_’).“n”;    $conn mysql_connect(‘localhost’‘test’‘test’);    mysql_select_db(‘test’);    $result mysql_query($sql);    while ($row mysql_fetch_object($result))        $datas[] = $row;    //  将数据库中获取到的结果集数据保存到 memcached 中,以供下次访问时使用。    $mc->add($key$datas);} else {    echo “n”.str_pad(‘Read datas from memcached.’60‘_’).“n”;}var_dump($datas);?>

可以看出,使用 memcached 之后,可以减少数据库连接、查询操作,数据库负载下来了,脚本的运行速度也提高了。

之前我曾经写过一篇名为《PHP 实现多服务器共享 SESSION 数据》文章,文中的 SESSION 是使用数据库保存的,在并发访问量大的时候,服务器的负载会很大,经常会超出 MySQL 最大连接数,利用 memcached,我们可以很好地解决这个问题,工作原理如下:

  • 用户访问网页时,查看 memcached 中是否有当前用户的 SESSION 数据,使用 session_id() 作为唯一标识符;如果数据存在,则直接返回,如果不存在,再进行数据库连接,获取 SESSION 数据,并将此数据保存到 memcached 中,供下次使用;
  • 当前的 PHP 运行结束(或使用了 session_write_close())时,会调用 My_Sess::write() 方法,将数据写入数据库,这样的话,每次仍然会有数据库操作,对于这个方法,也需要进行优化。使用一个全局变量,记录用户进入页面时的 SESSION 数据,然后在 write() 方法内比较此数据与想要写入的 SESSION 数据是否相同,不同才进行数据库连接、写入数据库,同时将 memcached 中对应的对象删除,如果相同的话,则表示 SESSION 数据未改变,那么就可以不做任何操作,直接返回了;
  • 那么用户 SESSION 过期时间怎么解决呢?记得 memcached 的 add() 方法有个过期时间参数 $exp 吗?把这个参数值设置成小于 SESSION 最大存活时间即可。另外别忘了给那些一直在线的用户延续 SESSION 时长,这个可以在 write() 方法中解决,通过判断时间,符合条件则更新数据库数据。

五、相关资源

肖理达 (KrazyNio AT hotmail.com), 2006.04. 06, 转载请注明出处

转载自:http://nio.infor96.com/php-memcached/

0

泛型:类型化缓存(I)


Andrei Alexandrescu

 想象本篇你正要读的“泛型<编程>”部分的开头是:“本文关于怎样用C++处理内存缓冲”。
 当你轻率地关掉浏览器时,如果竖起耳朵,你还会听到成千上万的鼠标在做和你一样的事情。因为谁会对处理内存缓冲这样的小事感兴趣呢?
 但本文确实关于怎样在C++中处理内存缓冲,但这里有两个特殊之处。首先,缓冲是泛型的,这意味着缓冲里面能放置类型化数据(相反于原始字节)。第二,我们要讨论的缓存的效率和存放在里面的类型及所在操作系统容许的最高效率相当,这种效率是指在任何方面的效率——包括分配策略,和数据操作。
 你会很快看到,写一个泛型缓存是个关于模板的小小练习。写一个高效的的缓存也不是一个复杂的任务。写一个意外安全的缓存难一些,但还不是很难,特别在你读过关于意外安全的书[1]之后。但写一个同时具备泛型特征和高效率的缓存就象爬一个险峰。就象在C++中常常发生的,你在山顶欣赏到的景色使你的努力获得足够回报——只要你能克服路上遇到困难。

真空地带
 有些人总会时不时在Usenet新闻组comp.lang.c++.moderated中张贴这样的问题:为什么autp_ptr不能给你在析构函数中使用delete[]而不是delete的机会?接下去的讨论通常会这样:
“你不应该使用C风格的数组而应该用std::vector,它高效又强大”
“但std::vector在我要用到的地方不够高效。”
“为什么?”等等
 事实是,std::vector确实是个用来处理连续对象序列的设计精良的类。当我首次看到std::vector的优良设计时,我根本没有把它扔掉而自己来写一个的需要(当我看到,比如说,某些MFC的东东时我感觉到了这种冲动)然而。std::vector是有些不够高效之处,而这可能会严重影响到某些使用者。

* 不必要的数据初始化。通常情况,你需要创建一个大小合适的vector,带有基本类型(比如char)并把它传递给低层C函数来用一个socket或文件中读取的数据来填充它。问题在于,尽管你不需要,整个vector都会通过vector的构造函数或resize函数初始化。如果你必须和C函数打夹道,你就没办法避免这个开销。
* 指数增长。标准vector规则要求快速的(平均为常量时间)的增长策略。这就要求std::vector以乘法来增长——大多数vector实现当需要自动增长时分配倍增的内存。比如如果你要在一个包含一百万对象的vector后面添加一个对象,这个vector为了将来短时间内不再增长会分配能容纳三百万对象的空间,但这可能不是你想要的。
* “强制性”内存分配策略。标准vector永远不会收缩而只会增长。如果你集中了三百万数据样本,然后你觉得做插值计算的话只需要保留一百万数据样本,你就会有能存放二百万对象的空闲空间。处理这种情况建议使用的常用手段是把你的vector拷到一个新建的vector中然后互换这两个vector

std::vector<double> data;
 …处理数据…
 //收缩数据
 std::vector<double>(data.begin(), data.end()).swap(data);
 
这个常用法不是很优化,因为你必须拷贝里面的数据。更糟的是,就是这样一个常用法也不能保证对内存百分之百的利用率。因为std::vector会总是分配比需要多的内存。如果你能够“就地”收缩vector会更好,也就是说,这需要告诉内存分配器它可以使用你的vector尾端之后的内存。许多内存分配器允许你这样做。
* 低效的对象移动。std::vector对拷贝和移动做无差别处理。对一个std::vector来说,一个移动操作就是一个拷贝操作后面跟一个对源对象的析构操作。所以如果你有一个保存字符串的vector并且为它重新分配,你没办法写一个函数来快速地移动几个指针(译注:此处指针指的是比如指向所管理内存的开始和结尾等这些地方的指针)到新的内存来使std::vector使用它。每个单个的字符串都毫无必要地拷贝到一个新的字符串。当被移动对象不是CRC[2]成员,那这将会是个低效率的巨大来源。这也是常常说到的怎样的std::vector<std::vector<T> >会被禁止的原因。
* 数据拷贝和移动中更多的低效之处。一些STL的实现不能区分包含旧数据类型的vector和包含用户自定类型的vector。(Metrowerks是个例外)这意味着它们使用更通用的结构来拷贝数据,比如for循环。你有权认为一个好的编译器应该把拷贝一系列整数的loop替换为更高效的memcpy调用。传说中两个能产生高效代码的编译器,Microsoft Visual C++和Metrowerks CodeWarrior,在完全速度优化方式下,使用loop来拷贝整型数据产生的代码比对应的memcpy调用明显要慢。所以你要执行效率,你需要在你自己的代码中调用memcpy。
* 不必要的意外安全。许多std::vector实现(但不是全部)假设所存类型的构造函数,拷贝构造函数,和赋值操作符重载函数可能抛出意外。但这对基本类型和其他许多类型都不会发生。所以这些实现对这些类型产生的代码可能会不必要的更大和更慢。

在许多应用中,这些问题的部分或全部根本没什么影响。但当你有苛刻的性能要求时
std::vector就不那么吸引人了。你会转而寻找一个更优化也给你更多控制权的结构。问题是你无法在C++标准中的std::vector下层找到任何容器——除了C风格数组。你要么开卡迪拉克,要么骑黄鱼车。
 要证据?证据就在你面前:STL在至少三处使用连续内存缓冲(除了std::vector本身以外):std::basic_string,std::deque的内存块管理部分和std::deque的内存块本身。然而。我没看到有STL实现使用vector作为这些结构的后端。如果说std::vector是你应用中实现连续内存唯一能用的工具,那就是说STL实现有其他特别工具来作为它们的后端。
 有一个空隙,在std::vector和C风格数组中有一个真空地带。我们定位于这个位置。我们会开发一个包含下列特性的连续内存缓冲:

* 泛型化的—可以存放任意类型序列
* 亲和的—支持std::vector语法和语义的子集
* 提供完全控制——提供细粒度的基本操作加上高层次的函数
* 产生的代码对基本类型和选定的用户定义类型尽最大可能优化
* 允许用户控制的优化
* 支持高级内存分配功能,例如就地扩展和快速重分配
* 最最重要的,能够被用做更高层连续内存容器的后端——特别是,你能用它实现vector,string或者deque.

buffer:第一线曙光
 我们来为buffer模类定义一个接口。buffer只保存两个指针——所控制内存块的头和尾。对一个buffer来说,尺寸和容积(size and capacity)没有区别。我们就从std::vector开始,下手去除和容积有关的函数,我们剩下下列成员函数集:

template < T, Allocater = std::allocator<T> >
class buffer
{
public:
 ….所有std::vector的类型定义和函数,除了:
 //size_type capacity() const;
 //void reserve(size_type n);
private:
 T* beg_;
 T* end_;
};

 有趣的是,尽管buffer没有容积概念,但它能够实现std::vector的所有功能,除了capacity和reserve,还有buffer不能满足所有这些函数的性能要求。比如说,buffer<T>::push_back有O(N)的时间复杂度,但std::vector<T>::push_back综合于大量的调用时有O(1)的时间复杂度。(这是标准所说的“摊分常量时间(amortized constant)”)你看到后面就知道能够在某些情况下提高buffer<T>::push_back的性能而仍旧不必支持容积概念。
 实现buffer的接口并不很复杂,两个地方需要注意:意外安全和正确地摧毁对象。标准函数std::uninitialized_copy和std::uninitialized_fill是两个很有用的工具。
 为了允许用户分配一块缓存而不初始化它,我们需要一个特别的构造函数和几个辅助函数。然后,我们需要一个grow_noinit工具来扩展缓存而不调用构造函数。对应的,我们需要shrink_nodestroy函数来收缩缓存而不调用析构函数,最后,需要一个稍微有些多余的函数clear_nodestroy,它清空缓存,回收内存而不调用析构函数。

template <class T, class Allocator = std::allocator<T> >
class buffer
{
 ….同上….
public:
 enum noinit_t { noinit };
 buffer(size_type n, noinit_t,
  const Allocator& a = Allocator());
 void grow_noinit (size_type growBy);
 void shrink_nodestroy(size_type shrinkBy);
 void clear_nodestroy();
};

 这个扩展的接口给了你对缓存储存在内数据的完全控制。但是注意不要不假思索地直接使用这些扩展函数。比如说你象下面那样使用grow_noinit:

void Fun()
{
 buffer<Widget> someWidgets;
 ….
 //加10个Widget的空间
 someWidgets.grow_noinit(10);
 //初始化这些Widget
 ConstructWidgets(
  SomeWidgets.end() – 10, someWidgets.end());
 ….
};

 这里的问题在于如果10个Widget的构造函数如果以任意形式失败,所有事情都会是一团糟。当Fun返回,someWidgets的析构函数会摧毁它里面包含对象——它不会知道哪些Widget构造成功哪些没有,这也是因为buffer不象std::vector有容积的概念,如果还有buffer内还有没初始化的内存,对那些内存使用Widget的析构函数很明显会导致未定义后果。

类型特性(Type Traits)
 一项优化泛型代码的关键技术是获取泛型代码操作的类型的信息。这样,泛型代码能够在编译时分配工作给指定的代码,这些代码都对专门类型执行操作。
 比如在我们的例子中,一个很重要的信息是buffer内的类型的拷贝构造函数是否抛出意外。对一个拷贝时不抛意外的类型,代码就会因不用处理意外安全而变得简单。此外,一些编译器如没有用try块会产生更好的代码。
 类型特性是一个用来推导类型信息的有名技术。Boost[4]有个库实现类型特性,Loki[5]也有(在buffer中,我们会用到的类型特性机制会和Boost和Loki的都稍有不同,这是由Kavonen通过电邮建议我的)。
 我们来看怎样推导一个类型的拷贝函数是否抛意外。坦白地说,C++中不可能知道所有类型的拷贝函数是否抛意外。但是,我们做一些工作并让用户当他们觉得需要优化时来帮我们做到。
 你不必成为歇洛克?福尔摩斯就能够知道任何一个基本类型的拷贝构造函数不会抛出意外。这样,一个保守的假设是任何除了基本类型的类型的拷贝构造函数可能抛出意外。下面是对应代码:

namespace TypeTraits
{
 //列举所有基本类型
 typedef TYPELIST_14(
const bool,
const char,
        const signed char,
        const unsigned char,
        const wchar_t,
        const short int,
        const unsigned short int,
        const int,
        const unsigned int,
        const long int,
        const unsigned long int,
        const float,
        const double,
        const long double)
    PrimitiveTypes;
 
template <typename T> struct IsPrimitive
{
 enum { value =
  Loki::TL::IndexOf<PrimitiveTypes,
  Const T<::value >= 0};
};

template <typename T> struct IsPrimitive<T*>
{
 enum {value = true };
};

template <typename T> struct CopyThrows
{
 enum {value = !IsPrimitive<T>::value };
};
}

 为了简明,上面的代码使用了Loki提供的两个工具:typelists和indexOf。typellists让你创建和操作类型串,Loki::TL::IndexOf在类型串中查找一个单独类型并且返回它在其中的索引。如果类型串中没有找到这个类型,返回的索引为负。最终,TypeTraits::CopyThrow<T>::value包含了所需信息。
 通过这个机制来获取类型信息非常非常灵活,假设你在一个应用程序里定义了下列类型:

struct Point
{
 int x;
 int y;
 ….操作函数….
};

上面这个Point不是基本类型,但它拷贝时也不会抛出意外。你可以用类型特性这个机制来告知这个信息。你所要做的就是再次打开TypeTraits命名空间然后在里面放入一个CopyThrows的显式实例化(explicit instantiation)版本

//在file"Pint.h"文件中,Point的定义后
namespace TypeTraits
{
 template <> struct CopyThrows<Point>
 {
  enum {value = false };
 };
}

 更好的是,你可以为一整个类型种类定制CopyThrows,这通过部分模板特化(partial template specialization)来实现,考虑一下标准复数类型,std::complex<T>,你可以用基本算术类型实例化std::complex,但也可以用用户定义算术类型,比如Rational或BrigInt。现在,因为拷贝一个std::complex<T>对象包括拷贝两个T对象(实数部分和虚数部分),由此可知std::complex<T>有着和T一样的CopyThrows特性。你能够通过下面代码表示这一点:

namespace TypeTraits
{
 template <class T> struct CopyThrows<std::complex<T> >
 {
  enum { value = CopyThrows<T>::value };
 };
}
 我们回到buffer。buffer如何使用CopyThrows的信息?通过使用Int2Type模板类[5][6]在编译时分派一个布尔值是非常简单的。回忆一下,Int2Type的定义有着简单的表象。

Template <int v>
Struct Int2Type
{
 enum { value = v };
};

 下面是buffer的构造函数怎样使用Int2Type来分派调用一个意外安全或意外无关的初始化函数的例子:

template <class T, class Allocator>
 std::allocator<T> >
class buffer : private Allocator
{
private:
 enum { copyThrow =
  TypeTraits::CopyThrows<T>::value != 0 };

 //无意外初始化
 void Init(size_type n, const T& value,
Loki::Int2Type<false>)
 {
  ….
 }
 
 //有可能出现意外的初始化
 void Init(size_type n, const T& value,
  Loki::Int2Type<true>)
 {
  ….
 }

public
 explicit buffer(size_type m,
  const T& value = T(),
  const Allocator& a = Allocator())
 {
  Init(n, value,
   Loki::Int2Type(copyThrows>());
 }
};

 其它buffer可能会需要的信息包括:

* 类型是否为MemCopyable,就是说,拷贝一个对象和memcpy这个对象的字节结果一样。显然,基本类型和POD结构(简单旧数据,C风格)属于这种情况。
* 类型是否为MemMoveable,就是说,在内存中从一个地方拷贝一个对象到另一个地方并且摧毁源对象,结果和memcpy对象的字节而且没有摧毁源对象一致。再一次的,基本类型和POD属于这种情况。但是,你会很快看到,有相当多的用户定义类型是MemMoveable的。

“ 下一部分的“泛型<编程>”会定义MemCopyable和MemMoveable并且用和CopyThrows类似的方式使用它们。关于MemCopyable和MemMoveable是否就此结束了?根本不是。它们和供下载代码中的reallocate和inplace_reallocate一样,都意味着我们面临内存分配的挑战。到那时——会有详尽说明!

鸣谢:
 对Tim Sharrock的详尽检查非常感谢。

参考数目和注释
[1] Herb Sutter. Exceptional C++ (Addison-Wesley, 2000).
[2] COW 爱好者俱乐部的缩写. COW 是写时拷贝的缩写. COW是可以使std::vector所用的用拷贝实现移动原则开销较小的策略。但是,许多库的作者正在去掉基于COW的实现,因为这在多线程程序中会引起问题。
[3] 总的来说, buffer必须和vector有同样级别的意外。参看Bjarne Stroustrup, Appendix E: Standard-Library Exception Safety 在 <www.research.att.com/~bs/3rd_safe0.html>.
[4] Boost 是尖端的C++库集, 参看 <www.boost.org>.
[5] Loki 是Modern C++ Design (Addison-Wesley, 2001)一书附带库,实际上是你们大家参与一起写成的.你能从 <www.moderncppdesign.com>下载下载Loki.
[6] Andrei Alexandrescu. "Generic<Programming>: Mappings between Types and Values," C/C++ Users Journal C++ Experts Forum, October 2000, <www.cuj.com/experts/1810/alexandr.htm>.
Andrei Alexandrescu 是位于西雅图的华盛顿大学的博士生,也是受到好评的《Modern C++ Design》一书的作者。可以通过www.moderncppdesign.com. 来联系他。Andrei同时也是C++研讨会 (<www.gotw.ca/cpp_seminar>).的一名有号召力的讲师。

你可以从CUJ网站或http://merced.go.nease.net/code/buffer.zip获得本文源代码。

0

大型高并发高负载网站的系统架构


一个小型的网站,比如个人网站,可以使用最简单的html静态页面就实现了,配合一些图片达到美化效果,所有的页面均存放在一个目录下,这样的网站对系统架构、性能的要求都很简单,随着互联网业务的不断丰富,网站相关的技术经过这些年的发展,已经细分到很细的方方面面,尤其对于大型网站来说,所采用的技术更是涉及面非常广,从硬件到软件、编程语言、数据库、WebServer、防火墙等各个领域都有了很高的要求,已经不是原来简单的html静态网站所能比拟的。

大型网站,比如门户网站。在面对大量用户访问、高并发请求方面,基本的解决方案集中在这样几个环节:使用高性能的服务器、高性能的数据库、高效率的编程语言、还有高性能的Web容器。但是除了这几个方面,还没法根本解决大型网站面临的高负载和高并发问题。

上面提供的几个解决思路在一定程度上也意味着更大的投入,并且这样的解决思路具备瓶颈,没有很好的扩展性,下面我从低成本、高性能和高扩张性的角度来说说我的一些经验。

1、HTML静态化
其实大家都知道,效率最高、消耗最小的就是纯静态化的html页面,所以我们尽可能使我们的网站上的页面采用静态页面来实现,这个最简单的方法其实也是最有效的方法。但是对于大量内容并且频繁更新的网站,我们无法全部手动去挨个实现,于是出现了我们常见的信息发布系统CMS,像我们常访问的各个门户站点的新闻频道,甚至他们的其他频道,都是通过信息发布系统来管理和实现的,信息发布系统可以实现最简单的信息录入自动生成静态页面,还能具备频道管理、权限管理、自动抓取等功能,对于一个大型网站来说,拥有一套高效、可管理的CMS是必不可少的。

除了门户和信息发布类型的网站,对于交互性要求很高的社区类型网站来说,尽可能的静态化也是提高性能的必要手段,将社区内的帖子、文章进行实时的静态化,有更新的时候再重新静态化也是大量使用的策略,像Mop的大杂烩就是使用了这样的策略,网易社区等也是如此。

同时,html静态化也是某些缓存策略使用的手段,对于系统中频繁使用数据库查询但是内容更新很小的应用,可以考虑使用html静态化来实现,比如论坛中论坛的公用设置信息,这些信息目前的主流论坛都可以进行后台管理并且存储再数据库中,这些信息其实大量被前台程序调用,但是更新频率很小,可以考虑将这部分内容进行后台更新的时候进行静态化,这样避免了大量的数据库访问请求。

2、图片服务器分离
大家知道,对于Web服务器来说,不管是Apache、IIS还是其他容器,图片是最消耗资源的,于是我们有必要将图片与页面进行分离,这是基本上大型网站都会采用的策略,他们都有独立的图片服务器,甚至很多台图片服务器。这样的架构可以降低提供页面访问请求的服务器系统压力,并且可以保证系统不会因为图片问题而崩溃,在应用服务器和图片服务器上,可以进行不同的配置优化,比如apache在配置ContentType的时候可以尽量少支持,尽可能少的LoadModule,保证更高的系统消耗和执行效率。

3、数据库集群和库表散列
大型网站都有复杂的应用,这些应用必须使用数据库,那么在面对大量访问的时候,数据库的瓶颈很快就能显现出来,这时一台数据库将很快无法满足应用,于是我们需要使用数据库集群或者库表散列。

在数据库集群方面,很多数据库都有自己的解决方案,Oracle、Sybase等都有很好的方案,常用的MySQL提供的Master/Slave也是类似的方案,您使用了什么样的DB,就参考相应的解决方案来实施即可。

上面提到的数据库集群由于在架构、成本、扩张性方面都会受到所采用DB类型的限制,于是我们需要从应用程序的角度来考虑改善系统架构,库表散列是常用并且最有效的解决方案。我们在应用程序中安装业务和应用或者功能模块将数据库进行分离,不同的模块对应不同的数据库或者表,再按照一定的策略对某个页面或者功能进行更小的数据库散列,比如用户表,按照用户ID进行表散列,这样就能够低成本的提升系统的性能并且有很好的扩展性。sohu的论坛就是采用了这样的架构,将论坛的用户、设置、帖子等信息进行数据库分离,然后对帖子、用户按照板块和ID进行散列数据库和表,最终可以在配置文件中进行简单的配置便能让系统随时增加一台低成本的数据库进来补充系统性能。

4、
缓存一词搞技术的都接触过,很多地方用到缓存。网站架构和网站开发中的缓存也是非常重要。这里先讲述最基本的两种缓存。高级和分布式的缓存在后面讲述。
架构方面的缓存,对Apache比较熟悉的人都能知道Apache提供了自己的缓存模块,也可以使用外加的Squid模块进行缓存,这两种方式均可以有效的提高Apache的访问响应能力。
网站程序开发方面的缓存,Linux上提供的Memory Cache是常用的缓存接口,可以在web开发中使用,比如用Java开发的时候就可以调用MemoryCache对一些数据进行缓存和通讯共享,一些大型社区使用了这样的架构。另外,在使用web语言开发的时候,各种语言基本都有自己的缓存模块和方法,PHP有Pear的Cache模块,Java就更多了,.net不是很熟悉,相信也肯定有。

5、镜像
镜像是大型网站常采用的提高性能和数据安全性的方式,镜像的技术可以解决不同网络接入商和地域带来的用户访问速度差异,比如ChinaNet和EduNet之间的差异就促使了很多网站在教育网内搭建镜像站点,数据进行定时更新或者实时更新。在镜像的细节技术方面,这里不阐述太深,有很多专业的现成的解决架构和产品可选。也有廉价的通过软件实现的思路,比如Linux上的rsync等工具。

6、负载均衡
负载均衡将是大型网站解决高负荷访问和大量并发请求采用的终极解决办法。
负载均衡技术发展了多年,有很多专业的服务提供商和产品可以选择,我个人接触过一些解决方法,其中有两个架构可以给大家做参考。
硬件四层交换
第四层交换使用第三层和第四层信息包的报头信息,根据应用区间识别业务流,将整个区间段的业务流分配到合适的应用服务器进行处理。 第四层交换功能就象是虚IP,指向物理服务器。它传输的业务服从的协议多种多样,有HTTP、FTP、NFS、Telnet或其他协议。这些业务在物理服务器基础上,需要复杂的载量平衡算法。在IP世界,业务类型由终端TCP或UDP端口地址来决定,在第四层交换中的应用区间则由源端和终端IP地址、TCP和UDP端口共同决定。
在硬件四层交换产品领域,有一些知名的产品可以选择,比如Alteon、F5等,这些产品很昂贵,但是物有所值,能够提供非常优秀的性能和很灵活的管理能力。Yahoo中国当初接近2000台服务器使用了三四台Alteon就搞定了。

软件四层交换
大家知道了硬件四层交换机的原理后,基于OSI模型来实现的软件四层交换也就应运而生,这样的解决方案实现的原理一致,不过性能稍差。但是满足一定量的压力还是游刃有余的,有人说软件实现方式其实更灵活,处理能力完全看你配置的熟悉能力。
软件四层交换我们可以使用Linux上常用的LVS来解决,LVS就是Linux Virtual ,他提供了基于心跳线heartbeat的实时灾难应对解决方案,提高系统的鲁棒性,同时可供了灵活的虚拟VIP配置和管理功能,可以同时满足多种应用需求,这对于分布式的系统来说必不可少。

一个典型的使用负载均衡的策略就是,在软件或者硬件四层交换的基础上搭建squid集群,这种思路在很多大型网站包括搜索引擎上被采用,这样的架构低成本、高性能还有很强的扩张性,随时往架构里面增减节点都非常容易。这样的架构我准备空了专门详细整理一下和大家探讨。

对于大型网站来说,前面提到的每个方法可能都会被同时使用到,我这里介绍得比较浅显,具体实现过程中很多细节还需要大家慢慢熟悉和体会,有时一个很小的squid参数或者apache参数设置,对于系统性能的影响就会很大,希望大家一起讨论,达到抛砖引玉之效。

0

应用加速


应用加速(Application Acceleration)是当前信息产业中流行的一个新的时髦词语,但它到底是什么含义呢?

缓慢的应用软件响应时间可能会让用户感到失望并且失去生产力,从而对公司的核心实力造成影响,所以,应用软件加速成为信息产业中的一个新的时髦词语也就不足为奇了,但 它到底是什么含义呢?您是否需要它呢?如果需要的话,那么最具扩展性的实现方式是什么呢?让我们来看看不同的应用软件加速技术以及如何创建应用软件加速策略来与您的公 司一同成长。

本地VS网络应用软件

在过去,大部分应用软件都是安装并运行在本地计算机,现在的小型公司也通常是这样。对于本地应用软件而言,性能主要取决于本地系统资源,它受到处理器速度、处理器数量 、物理和虚拟内存数量和共享这些资源的其他应用软件(多任务)的影响。

对于这种类型的系统环境,提高应用软件性能是在单独的机器上通过升级硬件、关闭消耗宝贵资源的视觉效果,禁用不必要的与您的应用软件争夺处理器时间和内存的后台服务, 使用预取(prefetching)技术来提高应用软件的加载时间,定期整理磁盘碎片,优化各种设置比如设定进程的优先级、调整与性能相关的注册表条目等等。

然而,在当今的大中型企业中,很多应用软件都是通过局域网、城域网或互联网传递的,这对原有的方法带来了新的问题,同时也为网络带宽和网络协议带来了新的问题。

应用软件转移到了互联网上

越来越多的应用软件正在通过互联网来传递,从便利性的角度来看,这很有意义,因为几乎所有与互联网相连的计算机都有网络浏览器,这就排除了在用户系统上安装任何软件的 需要,HTTP是最普遍的网络协议中的一个,这种应用软件的“互联网化”为用户创建了一个通用界面和环境,同时也是系统管理员的通用传递系统。

在基于网络的应用软件加速的问题上,一个关键因素是使用一个逆向代理(reverse proxy)来缓存静态对象,我们在这个栏目里所讨论的缓冲方案在以往更多地被应用于正向缓存 ,这是一种加速内部用户访问互联网内容的方法:让代理服务器在本地存储从互联网站点上下载的对象。

逆向代理,正如名字所暗示的,是反方向工作的,逆向代理服务器存储了在您的内部服务器上被访问过的对象,然后将它们返回给请求者,这样请求者不必到服务器上去检索这些 数据,对于外部用户而言就意味着性能的提高。

扩展一个逆向代理的解决方案

互联网应用软件加速经常是在企业环境下讨论的,但即使是小型公司也可以使用逆向服务器来加速基于互联网的应用软件,您不必花费很多钱来实现这一点,免费的和廉价的逆向 代理软件包括:

Squid:一个基于GPL许可的开源的代理服务器,它可以从ftp://ftp.squid-cache.org/pub/上免费下载,它 可以在Linux、FreeBSD、Mac OS X和很多版本的UNIX上运行,您可以使用Cygwin开发环境(Cygwin development environment)在Windows上编译和运行Squid。

IIS逆向代理:一个用于互联网信息服务(IIS)的开源逆向代理软件,它是基于MIT许可的,可以安装在运行IIS的Windows2000、XP或Windows 2003上,您可以在http://www.saltypickle.com/Home/16上下载到这一软件。

Orenosp安全逆向代理:一个逆向代理的共享软件,还具有平衡负载和安全端口的转发功能,它可以运行在Windows NT、2000、XP和2003,以及Linux和Mac OS X上,如果需要 更多的信息可以参见http://hp.vector.co.jp/authors/VA027031/orenosp/index_en.html

通常,随着公司的发展,您将会需要对互联网应用软件进行加速,功能不复杂的逆向代理服务器会缓存所有的数据,而高级一些的会允许您像外科手术般地来操作哪些数据需要缓 存,哪些不需要。您可能还希望考虑可扩展性的协议支持:您需要缓存的可能不仅仅是HTTP对象。

从免费的和低廉的解决方案中提升一步,您可以发现一些适合大中型公司需求的中等价位的产品,例如:

微软的ISA服务器:ISA可以作为正向的或逆向代理服务器和应用层过滤防火墙,所以您可以应用很多相关的功能。标准版可以运行在Windows 2000 Server或Windows Server 2003上,每个处理器的价格为1499美元,企业版可以部署在服务器阵列上,因而具备更强的灵活性与可扩展性,每个处理器价格为5999美元。需要更多的信息,请点击此处。

Vigos AG网站加速器:对于仅仅需要一个逆向代理而不需要ISA的附加功能的用户而言,这是一个提高他们网络站点性能的选择。Vigos还可以实时压缩发送的数据,“简易” 版(支持单一域)的价格是499美元,标准版(支持最多十个域)的价格是999美元,企业版(支持无限个域)的价格为1999美元。需要更多的信息,请点击此处。在企业级,逆向代理允许您对多个前端互联网服务器实施逆向代理方案,并提供SSL的安全连接保障。为企业级市场设计的高端逆向代理解决方案需要花费30000美元或更多,一些 可选方案包括:

Blue Coat Proxy SG:作为一套配置齐全立即可用的设备,Proxy SG有很多产品系列(400, 800和8000)以满足不通的预算和需求,您可以对上游的互联网服务器实施正向缓存 ,甚至对上游的服务器进行状态检查,高端的8000系列可以支持超过50000接入用户并支持最高300Mbps的城域网吞吐量,型号为8000-4的价格为99000美元,需要更多的信息,请点击此处。

思科内容引擎(Content Engine):这是Cisco的缓存与内容过滤设备,可以部署在您的互联网站点之前来缓存或分流请求,以及卸载互联网服务器的数据流量,以此来增加网 络应用软件的性能。内容引擎7325的价格在40000美元到50000之间。

0

C++资源之不完全导引


声明:

.本文2004年5月首发于《CSDN开发高手》,版权归该杂志与《程序员》杂志社所有。杂志限于篇幅部分内容有所删节,此处版本为相对完整版本。

.本文为介绍性文章,会随笔者学习C++语言不断更新。

在C++中,库的地位是非常高的。C++之父 Bjarne Stroustrup先生多次表示了设计库来扩充功能要好过设计更多的语法的言论。现实中,C++的库门类繁多,解决的问题也是极其广泛,库从轻量级到重量级的都有。不少都是让人眼界大开,亦或是望而生叹的思维杰作。由于库的数量非常庞大,而且限于笔者水平,其中很多并不了解。所以文中所提的一些库都是比较著名的大型库。

标准库

标准库中提供了C++程序的基本设施。虽然C++标准库随着C++标准折腾了许多年,直到标准的出台才正式定型,但是在标准库的实现上却很令人欣慰得看到多种实现,并且已被实践证明为有工业级别强度的佳作。

1、 Dinkumware C++ Library

参考站点:http://www.dinkumware.com/

P.J. Plauger编写的高品质的标准库。P.J. Plauger博士是Dr. Dobb’s程序设计杰出奖的获得者。其编写的库长期被Microsoft采用,并且最近Borland也取得了其OEM的license,在其C/C++的产品中采用Dinkumware的库。

2、 RogueWave Standard C++ Library

参考站点:http://www.roguewave.com/

这个库在Borland C++ Builder的早期版本中曾经被采用,后来被其他的库给替换了。笔者不推荐使用。

3、SGI STL

参考站点:http://www.roguewave.com/

SGI公司的C++标准模版库。

4、STLport

参考站点:http://www.stlport.org/

SGI STL库的跨平台可移植版本。

准标准库——Boost

Boost库是一个经过千锤百炼、可移植、提供源代码的C++库,作为标准库的后备,是C++标准化进程的发动机之一。 Boost库由C++标准委员会库工作组成员发起,在C++社区中影响甚大,其成员已近2000人。 Boost库为我们带来了最新、最酷、最实用的技术,是不折不扣的“准”标准库。

Boost中比较有名气的有这么几个库:
Regex
正则表达式库

Spirit
LL parser framework,用C++代码直接表达EBNF

Graph
图组件和算法

Lambda
在调用的地方定义短小匿名的函数对象,很实用的functional功能

concept check
检查泛型编程中的concept

Mpl
用模板实现的元编程框架

Thread
可移植的C++多线程库


把C++类和函数映射到Python之中

Pool
内存池管理

smart_ptr
5个智能指针,学习智能指针必读,一份不错的参考是来自CUJ的文章:

Smart Pointers in Boost,哦,这篇文章可以查到,CUJ是提供在线浏览的。中文版见笔者在《Dr. Dobb’s Journal软件研发杂志》第7辑上的译文。

Boost总体来说是实用价值很高,质量很高的库。并且由于其对跨平台的强调,对标准C++的强调,是编写平台无关,现代C++的开发者必备的工具。但是Boost中也有很多是实验性质的东西,在实际的开发中实用需要谨慎。并且很多Boost中的库功能堪称对语言功能的扩展,其构造用尽精巧的手法,不要贸然的花费时间研读。Boost另外一面,比如Graph这样的库则是具有工业强度,结构良好,非常值得研读的精品代码,并且也可以放心的在产品代码中多多利用。

参考站点:http://www.boost.org(国内镜像:http://www.c-view.org/tech/lib/boost/index.htm)

GUI

在众多C++的库中,GUI部分的库算是比较繁荣,也比较引人注目的。在实际开发中,GUI库的选择也是非常重要的一件事情,下面我们综述一下可选择的GUI库,各自的特点以及相关工具的支持。

1、 MFC

大名鼎鼎的微软基础类库(Microsoft Foundation )。大凡学过VC++的人都应该知道这个库。虽然从技术角度讲,MFC是不大漂亮的,但是它构建于Windows API 之上,能够使程序员的工作更容易,编程效率高,减少了大量在建立 程序时必须编写的代码,同时它还提供了所有一般 C++ 编程的优点,例如继承和封装。MFC 编写的程序在各个版本的Windows操作系统上是可移植的,例如,在 3.1下编写的代码可以很容易地移植到 NT 或 95 上。但是在最近发展以及官方支持上日渐势微。

2、 QT

参考网站:http://www.trolltech.com/

Qt是Trolltech公司的一个多平台的C++图形用户界面应用程序框架。它提供给应用程序开发者建立艺术级的图形用户界面所需的所用功能。Qt是完全面向对象的很容易扩展,并且允许真正地组件编程。自从1996年早些时候,Qt进入商业领域,它已经成为全世界范围内数千种成功的应用程序的基础。Qt也是流行的Linux桌面环境KDE 的基础,同时它还支持Windows、Macintosh、/X11等多种平台。

3、WxWindows

参考网站:http://www.wxwindows.org/

跨平台的GUI库。因为其类层次极像MFC,所以有文章介绍从MFC到WxWindows的代码移植以实现跨平台的功能。通过多年的开发也是一个日趋完善的GUI库,支持同样不弱于前面两个库。并且是完全开放源代码的。新近的C++ Builder X的GUI设计器就是基于这个库的。

4、Fox

开放源代码的GUI库。作者从自己亲身的开发经验中得出了一个理想的GUI库应该是什么样子的感受出发,从而开始了对这个库的开发。有兴趣的可以尝试一下。

参考网站:http://www.fox-toolkit.org/

5、 WTL

基于ATL的一个库。因为使用了大量ATL的轻量级手法,模板等技术,在代码尺寸,以及速度优化方面做得非常到位。主要面向的使用群体是开发COM轻量级供网络下载的可视化控件的开发者。

6、 GTK

参考网站:http://gtkmm.sourceforge.net/

GTK是一个大名鼎鼎的C的开源GUI库。在Linux世界中有Gnome这样的杀手应用。而GTK就是这个库的C++封装版本。

网络通信

ACE

参考网站:http://www.cs.wustl.edu/~schmidt/ACE.

C++库的代表,超重量级的网络通信开发框架。ACE自适配通信环境(Adaptive Communication Environment)是可以自由使用、开放源代码的面向对象框架,在其中实现了许多用于并发通信软件的核心模式。ACE提供了一组丰富的可复用C++包装外观(Wrapper Facade)和框架组件,可跨越多种平台完成通用的通信软件任务,其中包括:事件多路分离和事件处理器分派、信号处理、服务初始化、进程间通信、共享内存管理、消息路由、分布式服务动态(重)配置、并发执行和同步,等等。

StreamModule

参考网站:http://www.omnifarious.org/StrMod/

设计用于简化编写分布式程序的库。尝试着使得编写处理异步行为的程序更容易,而不是用同步的外壳包起异步的本质。

SimpleSocket

参考网站:http://home.hetnet.nl/~lcbokkers/simsock.htm

这个类库让编写基于socket的客户/服务器程序更加容易。

A Stream Socket API for C++

参考网站:http://www.pcs.cnu.edu/~dgame/sockets/socketsC++/sockets.html

又一个对Socket的封装库。

XML

Xerces

参考网站:http://xml.apache.org/xerces-c/

Xerces-C++ 是一个非常健壮的XML解析器,它提供了验证,以及SAX和DOM API。XML验证在文档类型定义(Document Type Definition,DTD)方面有很好的支持,并且在2001年12月增加了支持W3C XML Schema 的基本完整的开放标准。

XMLBooster

参考网站:http://www.xmlbooster.com/

这个库通过产生特制的parser的办法极大的提高了XML解析的速度,并且能够产生相应的GUI程序来修改这个parser。在DOM和SAX两大主流XML解析办法之外提供了另外一个可行的解决方案。

Pull Parser

参考网站:http://www.extreme.indiana.edu/xgws/xsoap/xpp/

这个库采用pull方法的parser。在每个SAX的parser底层都有一个pull的parser,这个xpp把这层暴露出来直接给大家使用。在要充分考虑速度的时候值得尝试。

Xalan

参考网站:http://xml.apache.org/xalan-c/

Xalan是一个用于把XML文档转换为HTML,纯文本或者其他XML类型文档的XSLT处理器。

CMarkup

参考网站:http://www.firstobject.com/xml.htm

这是一种使用EDOM的XML解析器。在很多思路上面非常灵活实用。值得大家在DOM和SAX之外寻求一点灵感。

libxml++

http://libxmlplusplus.sourceforge.net/

libxml++是对著名的libxml XML解析器的C++封装版本

科学计算

Blitz++

参考网站:http://www.oonumerics.org/blitz/

Blitz++ 是一个高效率的数值计算函数库,它的设计目的是希望建立一套既具像C++ 一样方便,同时又比Fortran速度更快的数值计算环境。通常,用C++所写出的数值程序,比 Fortran慢20%左右,因此Blitz++正是要改掉这个缺点。方法是利用C++的template技术,程序执行甚至可以比Fortran更快。Blitz++目前仍在发展中,对于常见的SVD,FFTs,QMRES等常见的线性代数方法并不提供,不过使用者可以很容易地利用Blitz++所提供的函数来构建。

POOMA

参考网站:http://www.codesourcery.com/pooma/pooma

POOMA是一个免费的高性能的C++库,用于处理并行式科学计算。POOMA的面向对象设计方便了快速的程序开发,对并行机器进行了优化以达到最高的效率,方便在工业和研究环境中使用。

MTL

参考网站:http://www.osl.iu.edu/research/mtl/

Matrix Template Library(MTL)是一个高性能的泛型组件库,提供了各种格式矩阵的大量线性代数方面的功能。在某些应用使用高性能编译器的情况下,比如Intel的编译器,从产生的汇编代码可以看出其与手写几乎没有两样的效能。

CGAL

参考网站:www.cgal.org

Computational Geometry Algorithms Library的目的是把在计算几何方面的大部分重要的解决方案和方法以C++库的形式提供给工业和学术界的用户。

游戏开发

Audio/Video 3D C++ Programming Library

参考网站:http://www.galacticasoftware.com/products/av/

AV3D是一个跨平台,高性能的C++库。主要的特性是提供3D图形,声效支持(SB,以及S3M),控制接口(键盘,鼠标和遥感),XMS。

KlayGE

参考网站:http://home.g365.net/enginedev/

国内游戏开发高手自己用C++开发的游戏引擎。KlayGE是一个开放源代码、跨平台的游戏引擎,并使用Python作脚本语言。KlayGE在LGPL协议下发行。感谢龚敏敏先生为中国游戏开发事业所做出的贡献。

OGRE

参考网站:http://www.ogre3d.org

OGRE(面向对象的图形渲染引擎)是用C++开发的,使用灵活的面向对象3D引擎。它的目的是让开发者能更方便和直接地开发基于3D硬件设备的应用程序或游戏。引擎中的类库对更底层的系统库(如:Direct3D和OpenGL)的全部使用细节进行了抽象,并提供了基于现实世界对象的接口和其它类。

线程

C++ Threads

参考网站:http://threads.sourceforge.net/

这个库的目标是给程序员提供易于使用的类,这些类被继承以提供在Linux环境中很难看到的大量的线程方面的功能。

ZThreads

参考网站:http://zthread.sourceforge.net/

一个先进的面向对象,跨平台的C++线程和同步库。

序列化

s11n

参考网站:http://s11n.net/

一个基于STL的C++库,用于序列化POD,STL容器以及用户定义的类型。

Simple XML Persistence Library

参考网站:http://sxp.sourceforge.net/

这是一个把对象序列化为XML的轻量级的C++库。

字符串

C++ Str Library

参考网站:http://www.utilitycode.com/str/

操作字符串和字符的库,支持Windows和支持gcc的多种平台。提供高度优化的代码,并且支持多线程环境和Unicode,同时还有正则表达式的支持。

Common Text Transformation Library

参考网站:http://cttl.sourceforge.net/

这是一个解析和修改STL字符串的库。CTTL substring类可以用来比较,插入,替换以及用EBNF的语法进行解析。

GRETA

参考网站:http://research.microsoft.com/projects/greta/

这是由微软研究院的研究人员开发的处理正则表达式的库。在小型匹配的情况下有非常优秀的表现。

综合

P::Classes

参考网站:http://pclasses.com/

一个高度可移植的C++应用程序框架。当前关注类型和线程安全的signal/slot机制,i/o系统包括基于插件的网络协议透明的i/o架构,基于插件的应用程序消息日志框架,访问sql数据库的类等等。

ACDK – Artefaktur Component Development Kit

参考网站:http://acdk.sourceforge.net/

这是一个平台无关的C++组件框架,类似于Java或者.NET中的框架(反射机制,线程,Unicode,废料收集,I/O,网络,实用工具,XML,等等),以及对Java, Perl, Python, TCL, Lisp, COM 和 CORBA的集成。

dlib C++ library

参考网站:http://www.cis.ohio-state.edu/~kingd/dlib/

各种各样的类的一个综合。大整数,Socket,线程,GUI,容器类,以及浏览目录的API等等。

Chilkat C++ Libraries

参考网站:http://www.chilkatsoft.com/cpp_libraries.asp

这是提供zip,e-mail,编码,S/MIME,XML等方面的库。

C++ Portable Types Library (PTypes)

参考网站:http://www.melikyan.com/ptypes/

这是STL的比较简单的替代品,以及可移植的多线程和网络库。

LFC

参考网站:http://lfc.sourceforge.net/

哦,这又是一个尝试提供一切的C++库

其他库

Loki

参考网站:http://www.moderncppdesign.com/

哦,你可能抱怨我早该和Boost一起介绍它,一个实验性质的库。作者在loki中把C++模板的功能发挥到了极致。并且尝试把类似设计模式这样思想层面的东西通过库来提供。同时还提供了智能指针这样比较实用的功能。

ATL

ATL(Active Template Library)是一组小巧、高效、灵活的类,这些类为创建可互操作的COM组件提供了基本的设施。

FC++: The Functional C++ Library

这个库提供了一些函数式语言中才有的要素。属于用库来扩充语言的一个代表作。如果想要在OOP之外寻找另一分的乐趣,可以去看看函数式程序设计的世界。大师Peter Norvig在 “Teach Yourself Programming in Ten Years”一文中就将函数式语言列为至少应当学习的6类编程语言之一。

FACT!

参考网站:http://www.kfa-juelich.de/zam/FACT/start/index.html

另外一个实现函数式语言特性的库

Crypto++

提供处理密码,消息验证,单向hash,公匙加密系统等功能的免费库。

还有很多非常激动人心或者是极其实用的C++库,限于我们的水平以及文章的篇幅不能包括进来。在对于这些已经包含近来的库的介绍中,由于并不是每一个我们都使用过,所以难免有偏颇之处,请读者见谅。

书籍

以前熊节先生曾撰文评论相对于Java程序设计语言,C++的好书多如牛毛。荣耀先生在《程序员》杂志上撰文《C++程序设计之四书五经》也将本领域内几乎所有的经典书籍作了全面的介绍,任何关于书的评论此时看来便是很多余的了。个人浅见,除非你打算以C++作为唯一兴趣或者生存之本,一般读者确实没有足够的时间和必要将20余本书籍全部阅读。更有参考价值的是荣耀先生的另一篇文章:《至少应该阅读的九本C++著作》,可以从下面的地址浏览到此文:

http://www.royaloo.com/articles/articles_2003/9CppBooks.htm

下面几本书对于走在C++初学之路上的读者是我们最愿意推荐给大家的:

《C++ Primer》

哦,也许你会抱怨我们为什么不先介绍TCPL,但对于走在学习之路上的入门者,本书内容更为全面,更为详细易懂,我们称它为“C++的超级宝典”并不过分。配有一本不错的习题解答《C++ Primer Answer Book》可以辅助你的学习之路。

《Essential C++》

如果说《C++ Primer》是C++领域的超级宝典,那么此书作为掌握C++的大局观当之无愧。正如《.NET大局观》一书能够让读者全揽.NET,本书讲述了C++中最核心的全部主题。书虽不厚,内容精炼,不失为《C++ Primer》读者茶余饭后的主题回顾之作。

《The C++ Programming Language》

Bjarne为你带来的C++教程,真正能够告诉你怎么用才叫真正的C++的唯一一本书。虽然如同“某某程序设计语言”这样的书籍会给大家一个内容全揽,入门到精通的感觉,但本书确实不太适合初学者阅读。如果你自认为是一名很有经验的C++程序员,那至少也要反复咀嚼Bjarne先生所强调的若干内容。

《Effective C++》,《More Effective C++》

是的,正如一些C++爱好者经常以读过与没有读过上述两本作品来区分你是否是C++高手。我们也极力推崇这两本著作。在各种介绍C++专家经验的书籍里面,这两本是最贴近语言本质,看后最能够有脱胎换骨感觉的书,读此书你需每日三省汝身。

技术书籍仁者见仁,过多的评论反无太多意义,由读者喜好选择最适合自己的书方为上策。

资源网站

正如我们可以通过计算机历史上的重要人物了解计算机史的发展,C++相关人物的网站也可以使我们得到最有价值的参考与借鉴,下面的人物我们认为没有介绍的必要,只因下面的人物在C++领域的地位众所周知,我们只将相关的资源进行罗列以供读者学习,他们有的工作于贝尔实验室,有的工作于知名编译器厂商,有的在不断推进语言的标准化,有的为读者撰写了多部千古奇作……

Bjarne Stroustrup http://www.research.att.com/~bs/

Stanley B. Lippman

http://blogs.msdn.com/slippman/(中文版http://www.zengyihome.net/slippman/index.htm)

Scott Meyers http://www.aristeia.com/

David Musser http://www.cs.rpi.edu/~musser/

Bruce Eckel http://www.bruceeckel.com

Nicolai M. Josuttis http://www.josuttis.com/

Herb Sutter http://www.gotw.ca/

Andrei Alexandrescu http://www.moderncppdesign.com/

侯捷先生 http://www.jjhou.com

孟岩先生 先生繁忙于工作,痴迷于技术,暂无个人主页,关于先生的作品可以通过CSDN的专栏和侯先生的主页访问到。

荣耀先生 http://www.royaloo.com/

潘爱民先生 http://www.icst.pku.edu.cn/panaimin/pam_homepage.htm

除了上述大师的主页外,以下的综合类C++学习参考站点是我们非常愿意向大家推荐的:

CodeProject http://www.codeproject.com

CodeGuru http://www.codeguru.com

Dr. Dobb’s Journal http://www.ddj.com

C/C++ Users Journal http://www.cuj.com

C维视点 http://www.c-view.org

allaboutprogram http://www.allaboutprogram.com

其他资料

ISO IEC JTC1/SC22/WG21 – C++:标准C++的权威参考

http://anubis.dkuug.dk/jtc1/sc22/wg21/

C++ FAQ LITE — Frequently Asked Questions: 最为全面的C++FAQ

http://www.sunistudio.com/cppfaq/index.html

C/C++ 新闻组:

你不妨尝试从这里提问和回答问题,很多不错的Q&A资源……
.alt.comp.lang.learn.c-c++
这个简单些,如果你和我一样是个菜鸟

.comp.lang.c++.moderated
嗯,这个显然水平高一些

.comp.std.c++
如果你需要讨论标准C++相关话题的话

不得不写的结束语

结束的时候也是总结现状,展望未来的时候。虽然C++从脱胎于C开始,一路艰难坎坷的走过来,但是无论如何C++已经取得了工业基础的地位。文章列举的大量相关资源就是最好的证明,而业界的大量用C++写成的产品代码以及大量的C++职业工程师则是最直接的证明。同时,我们可以看到各个高校的计算机专业都开设有C++这门课程,网络上对于C++的学习讨论也从来都没有停过。但是,在Java和.NET两大企业开发平台的围攻下,给人的感觉是C++越来越“不行”了。

C++在面向企业的软件开发中,在开发便捷性等方面的确要比Java和C#差很多,其中一个问题是C++语言本身比较复杂,学习曲线比较陡峭,另外一个问题是C++标准化的时间太长,丧失了很多的壮大机会,耗费了很多精力在厂商的之间的斗争上,而C++的标准库离一个完善的程序开发框架还缺少太多太多的内容,各个第三方的类库和框架又在一致性和完整性上没法和随平台提供的框架相提并论。难道C++真的要退出历史舞台了?

从C++目前的活跃程度,以及应用现状来说是完全能够肯定C++仍然是软件工业的基础,也不会退出历史舞台的。另外从Boost,Loki这些库中我们也能够看到C++的发展非常活跃,对于新技术新思维非常激进,C++仍然广泛受到关注。从ACE在高性能通信领域的应用,以及MTL这样的库在数值计算领域的出色表现,我们可以看到C++在高性能应用场合下的不可替代的作用,而嵌入式系统这样的内存受限开发平台,比如Symbian OS上,C++已经发挥着并且将发挥更大的作用。可以预见的是以后的软件无论上层的应用怎么变,它的底层核心都会是由C/C++这样的系统级软件编写的,比如Java虚拟机,.NET Framwork。因为只有这样的系统级软件才能完全彻底的发挥机器的功能。

需要看到的是两个趋势,一个趋势是C++变得更加复杂,更加学院派,通过模板等有潜力的语法因素构造越来越精巧的库成为了现代C++的热点,虽然在利用库实现新的编程范式,乃至设计模式等方面很有开创意义,也确实产生了一些能够便捷开发的工具,但是更多的是把C++变得更加强大,更加复杂,也更加难懂,似乎也更加学院派,不得不说它正在向边缘化道路发展。另一个趋势是C++在主流的企业应用开发中已经逐渐退出了,ERP这样的企业软件开发中基本上不会考虑C++,除非需要考虑性能或者和遗留代码的集成这些因素。C++退守到系统级别语言,成为软件工业的基础是大势所趋。然而反思一下,真的是退守么?自从STL出现,无数的人风起云涌的开始支持C++,他们狂呼“我看到深夜消失了,目标软件工程的出现。我看到了可维护的代码。”是的,STL在可维护性下做得如此出色。但是又怎样呢?STL为C++铺平了现代软件工程的道路,而在上层应用程序软件开发领域这块场地早不单独属于C++,很多程序设计语言都做得很出色,疯狂的支持者会毫不犹豫地说我们应当支持C++,因为它是世界上最棒的语言。而坦率地说,你的腰杆真的那么硬么?也许只是在逃避一些事实。C++是优秀的,这不可否认,STL的出现让C++一度走上了最辉煌的时刻,然而现在看来……我的一位恩师曾言:真正能够将STL应用得淋漓尽致的人很保守地说国内也不超过200人,或许不加入STL能够使C++向着它应当发展的方向发展的更好,而现在看来,C++也应当回首到真正属于他的那一片圣地上……

0

SOA and Web services 新手入门


最近因为工作需要,需要具体了解一些 SOA、SOAP 等相关概念。先看 SOA 吧,一步步来,转自 IBM。

什么是 SOA?
体系结构
SOA 生命周期
SOA 采用阶段:您可能已经开始了
辅助工具






引言:使 IT 满足您的业务需求

或许已经有人告诉您,您公司的新 IT 策略将要涉及到创建一个基于面向服务的体系结构的系统。也许您已经听到了大量的长篇大论,正想知道面向服务的体系结构(Service-Oriented Architecture,SOA)是否适合您的业务。或许您正在经历一场集成噩梦,尝试寻找让很多不同的系统彼此进行通信的方法。不管是何种情况,您都可能希望找到让 IT 基础设施为业务服务的方法,而不是其他。无论您是刚刚接触面向服务的体系结构这一概念,还是已经涉足其中,您肯定都希望找到方法来提高实现的效率,“developerWorks SOA 新手入门” 部分将为您提供了解和着手使用 SOA 所需的资源。


什么是 SOA?

我们可能应该回答的第一个问题也是最基本的问题。什么是面向服务的体系结构(Service-Oriented Architecture, SOA)?这个问题的答案实际上涉及与开发相关的若干不同方面。

SOA 是一种 IT 体系结构样式,支持将您的业务作为链接服务或可重复业务任务进行集成,可在需要时通过网络访问这些服务和任务。这个网络可能完全包含在您的公司总部内,也可能分散于各地且采用不同的技术,通过对来自纽约、伦敦和香港的服务进行组合,可让最终用户感觉似乎这些服务就安装在本地桌面上一样。需要时,这些服务可以将自己组装为按需应用程序——即相互连接的服务提供者和使用者集合,彼此结合以完成特定业务任务,使您的业务能够适应不断变化的情况和需求(在有些情况下,甚至不需要人工干预)。

这些服务是自包含的,具有定义良好的接口,允许这些服务的用户——称为客户机或使用者——了解如何与其进行交互。从技术角度而言,SOA 带来了“松散耦合”的应用程序组件,在此类组件中,代码不一定绑定到某个特定的数据库(甚至不一定绑定到特定的基础设施)。正是得益于这个松散耦合特性,才使得能够将服务组合为各种应用程序。这样还大幅度提高了代码重用率,可以在增加功能的同时减少工作量。由于服务和访问服务的客户机并未彼此绑定,因此可以完全替换用于处理订单的服务,下订单的客户机-服务将永远不会知道这个更改。所有交互都是基于“服务契约”进行的;服务契约用于定义服务提供者和客户机之间的交互。通常,您将通过创建“基于消息的”系统来实现此目标。

从业务的角度来说,面向服务的体系结构的重点在于开发能帮助您完成业务任务的技术,而不是通过技术约束来规定您的行动。例如,销售过程(制造、运输和收到货款)可能会涉及数十个步骤和若干不同的数据库和计算机系统。但就其实质而言,此过程包含一系列人工活动,例如:

  • 销售人员找到潜在客户
  • 客户订购产品
  • 生产部门制造产品
  • 生产部门发出产品
  • 收款部门开具产品帐单
  • 客户支付产品货款

面向服务的体系结构基于这些实际活动或业务服务进行组织,而不是形成公司所维护的不同的信息竖井 (Silo)。

通过实现 SOA,可以带来大量好处,包括以下各个方面:

  • 更高的业务和 IT 一致性
  • 基于组件的系统
  • 松散耦合的组件和系统
  • 基于网络的基础设施,允许分散于各地且采用不同技术的资源协同工作
  • 动态构建的按需应用程序
  • 更高的代码重用率
  • 更好地标准化整个企业内的流程
  • 更易于集中企业控制

下面提供了一些有用的资源,可帮助您了解面向服务的体系结构的总体概念:


体系结构

服务是用于实现 SOA 的最常见技术标准。不过,这并不是可以用于开发 SOA 的各个部分的唯一技术。很多 SOA——实际上是大部分——都涉及到集成遗留数据,此类数据包含在使用 MQSeriesCommon Object Request Broker Architecture (CORBA) 等技术的系统中。其中的许多技术都已针对 SOA 进行了调整,不管有无 Web 服务包装均可使用。事实上,可以仅使用 MQSeries、CORBA 甚至远程过程调用(Remote Procedure Call,RPC)技术来实现 SOA。但 Web 服务正迅速成为用于支持 SOA 的事实标准。

下面提供了一些有用的资源,可帮助您很好地理解用于实现 SOA 的一些可能方法。

Web 服务

CORBA

MQSeries

RPC

其他技术


SOA 生命周期

由于 SOA 涉及到业务的诸多方面,因此需要从一开始就对 SOA 项目进行细心的规划和设计。您需要考虑项目的整个生命周期,从最初的阶段到第一个实现,再一直到可能的修订和重用。

现在让我们看看 SOA 生命周期,如图 1 中所示。此部分概略说明了在生命周期的各个阶段发生的事项,并详细介绍了实现生命周期的各个步骤。

图 1. SOA 生命周期 图 1. SOA 生命周期


SOA 采用阶段:您可能已经开始了

已经向您介绍了面向服务的体系结构和 SOA 开发的步骤,您现在可能已经确信应该开始构建自己的 SOA 了。如果您已经构建了基于 Web 的软件服务,则已经达到了 SOA 采用的第一个阶段。在此部分,我们将分析各个采用阶段(从偶然构建服务到基于面向服务的体系结构原则对业务进行全面转换),从而帮助您了解自己目前所处的位置以及确定需要实现的目标。


辅助工具

迁移到面向服务的体系结构的好处诸多,包括提高应用程序易维护性和易开发性,让您能够更容易地使业务和 IT 紧密结合。IBM 提供了全套开发工具,无论您处在 SOA 生命周期和开发阶段中的任何位置,都可帮助您获得这些好处。下面列出了一些工具,可用于更方便地构建、集成和扩展 SOA。

0

Google网站管理员工具大更新:自定义抓取速度及更多


Google网站管理员工具即原先的Google Sitemps,后来Google给它增加了额外的功能和工具后,把它重新改名。在一个月前,Google已经开始测试一项新功能,即允许站长自定义Googlebot的抓取频率。这是非常实用的功能,因为理论上它可以加速Google对你的网站的收录。不过当时只是内测,只有极少数用户能使用它。 "   今天Google网站管理员中心blog宣布,Google网站管理员工具已进行了大更新,其中项就是向所有用户(包括简体中文)开放了自定义Googlebot抓取频率的功能。主要的更新包括:

1.自定义Googlebot抓取频率。可选择"更快"、"正常"、"更慢"三个档次,虽然比内测时少了,但已足够用:

2.Googlebot活动图表显示。这也是一项极实用的功能,可以显示在过去三个月里Googlebot在你的网站上的活动状况:

3. 开启增强型图片搜索。

"Google目前提供增强型搜索功能,可搜索您网站中的图片,这些功能包括用于 Google所托管图片的高级标签技术。如果您选择启用增强型图片搜索,Google 将使用Google ImageLabeler等工具将您网站所包含的图片与标签相关联,以优化这些图片的索引并提高搜索质量。"

这是Google在图片搜索方面进行的最新改进。也就是说,你可以允许Google把Google Image Labeler的研究成果应用于你网站上的图片。这意味着你的网站上的图片将会被Google图片搜索更好地收录,也意味着用户更容易搜索到你的网站。

4.显示某个Sitemap里已提交的网址总数。这允许用户知道自己所提交的某个Sitemap里一共包含多少个网址:

  强烈推荐每一位站长都试一下Google的网站管理员工具,因为它可以令Google更容易地收录你的网站内容。

0

用rsync对网站进行镜像备份


对系统管理员来说,平时的工作重心应该集中在维护系统正常运转,能够正常提供服务上,这里往往牵涉到一个数据备份的问题,在我所了解

的情况中,有80%的系统管理员不是太关心自己服务器的安全性,但往往对备分镜像的技术相当感兴趣,但由于商业产品的软硬件价格都相当高

昂,因此往往会选择自由软件。这里准备介绍的rsync就是这样的软件,它可以满足绝大多数要求不是特别高的备份需求。

一、特性简介

rsync是类unix系统下的数据镜像备份工具,从软件的命名上就可以看出来了——remote sync。它的特性如下:

1、可以镜像保存整个目录树和文件系统。
2、可以很容易做到保持原来文件的权限、时间、软硬链接等等。
3、无须特殊权限即可安装。
4、优化的流程,文件传输效率高。
5、可以使用rcp、ssh等方式来传输文件,当然也可以通过直接的socket连接。
6、支持匿名传输。

二、使用方法

rsync的使用方法很简单,我就举自己使用的例子来说明吧。

1、系统环境

rsync支持大多数的类unix系统,无论是Linux、Solaris还是BSD上都经过了良好的测试。我的系统环境为:

: FreeBSD 4.3 ip: 192.168.168.52
client: Solaris 8 ip: 192.168.168.137
rsync 版本 2.4.6(可以从http://rsync.samba.org/rsync/获得最新版本)

2、配置server端的/etc/rsyncd.conf文件

bash-2.03# cat /etc/rsyncd.conf

uid = nobody
gid = nobody
use chroot = no # 不使用chroot
max connections = 4 # 最大连接数为4
pid file = /var/run/rsyncd.pid
lock file = /var/run/rsync.lock
log file = /var/log/rsyncd.log # 日志记录文件

[inburst] # 这里是认证的模块名,在client端需要指定
path = /home/inburst// # 需要做镜像的目录
comment = BACKUP CLIENT IS SOLARIS 8 E250
ignore errors # 可以忽略一些无关的IO错误
read only = yes # 只读
list = no # 不允许列文件
auth users = inburst # 认证的用户名,如果没有这行,则表明是匿名
secrets file = /etc/inburst.pas # 认证文件名

[]
path = /usr/local/apache/htdocs/
comment = inburst.org web server

3、在server端生成一个密码文件/etc/inburst.pas

bash-2.03# cat /etc/inburst.pas
inburst:hack

出于安全目的,文件的属性必需是只有属主可读。

4、在server端将rsync以守护进程形式启动

bash-2.03# rsync –daemon

如果要在启动时把服务起来,有几种不同的方法,比如:

a、加入inetd.conf

编辑/etc/services,加入rsync 873/tcp,指定rsync的服务端口是873
编加/etc/inetd.conf,加入rsync stream tcp nowait root /bin/rsync rsync –daemon

b、加入rc.local

在各种操作系统中,rc文件存放位置不尽相同,可以修改使系统启动时rsync –daemon加载进去。

5、从client端进行测试

下面这个命令行中-vzrtopg里的v是verbose,z是压缩,r是recursive,topg都是保持文件原有属性如属主、时间的参数。–progress是指显示

出详细的进度情况,–delete是指如果服务器端删除了这一文件,那么客户端也相应把文件删除,保持真正的一致。后面的inburst@ip中,

inburst是指定密码文件中的用户名,之后的::inburst这一inburst是模块名,也就是在/etc/rsyncd.conf中自定义的名称。最后的/tmp是备份

到本地的目录名。

在这里面,还可以用-e ssh的参数建立起加密的连接。可以用–password-file=/password/path/file来指定密码文件,这样就可以在脚本中使

用而无需交互式地输入验证密码了,这里需要注意的是这份密码文件权限属性要设得只有属主可读。

bash-2.03# rsync -vzrtopg –progress –delete inburst@192.168.168.52::inburst /tmp/
Password:
receiving file list … done
./
1
785 (100%)
1.py
4086 (100%)
2.py
10680 (100%)
a
0 (100%)
ip
3956 (100%)
./
wrote 190 bytes read 5499 bytes 758.53 bytes/sec
total size is 19507 speedup is 3.43

6、创建更新脚本

如果有比较复杂的工作,利用一些常见的脚本语言可以有帮助。比如:

bash-2.03# cat /usr/local/bin/rsync.sh

#!/bin/sh
DATE=`date +%w`

rsync -vzrtopg –progress –delete inburst@192.168.168.52::inburst /home/quack/backup/$DATE –password-file=/etc/rsync.pass >

/var/log/rsync.$DATE

7、修改/etc/crontab做好定时

比如:

bash-2.03# echo "15 4 * * 6 root rsync.sh">>/etc/crontab

三、FAQ

Q:如何通过ssh进行rsync,而且无须输入密码?
A:可以通过以下几个步骤

1. 通过ssh-keygen在server A上建立SSH keys,不要指定密码,你会在~/.ssh下看到identity和identity.pub文件
2. 在server B上的home目录建立子目录.ssh
3. 将A的identity.pub拷贝到server B上
4. 将identity.pub加到~[user b]/.ssh/authorized_keys
5. 于是server A上的A用户,可通过下面命令以用户B ssh到server B上了
e.g. ssh -l userB serverB
这样就使server A上的用户A就可以ssh以用户B的身份无需密码登陆到server B上了。

Q:如何通过在不危害安全的情况下通过防火墙使用rsync?
A:解答如下:

这通常有两种情况,一种是服务器在防火墙内,一种是服务器在防火墙外。
无论哪种情况,通常还是使用ssh,这时最好新建一个备份用户,并且配置sshd仅允许这个用户通过RSA认证方式进入。
如果服务器在防火墙内,则最好限定客户端的IP地址,拒绝其它所有连接。
如果客户机在防火墙内,则可以简单允许防火墙打开TCP端口22的ssh外发连接就ok了。

Q:我能将更改过或者删除的文件也备份上来吗?
A:当然可以:

你可以使用如:rsync -other -options -backupdir = ./backup-2000-2-13 …这样的命令来实现。
这样如果源文件:/path/to/some/file.c改变了,那么旧的文件就会被移到./backup-2000-2-13/path/to/some/file.c,这里这个目录需要自己

手工建立起来

Q:我需要在防火墙上开放哪些端口以适应rsync?
A:视情况而定

rsync可以直接通过873端口的tcp连接传文件,也可以通过22端口的ssh来进行文件传递,但你也可以通过下列命令改变它的端口:

rsync –port 8730 otherhost::
或者
rsync -e ‘ssh -p 2002′ otherhost:

Q:我如何通过rsync只复制目录结构,忽略掉文件呢?
A:rsync -av –include ‘*/’ –exclude ‘*’ source-dir dest-dir

Q:为什么我总会出现"Read-only file system"的错误呢?
A:看看是否忘了设"read only = no"了

Q:为什么我会出现’@ERROR: invalid gid’的错误呢?
A:rsync使用时默认是用uid=nobody;gid=nobody来运行的,如果你的系统不存在nobody组的话,就会出现这样的错误,可以试试gid =

nogroup或者其它

Q:绑定端口873失败是怎么回事?
A:如果你不是以root权限运行这一守护进程的话,因为1024端口以下是特权端口,会出现这样的错误。你可以用–port参数来改变。

Q:为什么我认证失败?
A:从你的命令行看来:

你用的是:
> bash$ rsync -a 144.16.251.213::test test
> Password:
> @ERROR: auth failed on module test
>
> I dont understand this. Can somebody explain as to how to acomplish this.
> All suggestions are welcome.

应该是没有以你的用户名登陆导致的问题,试试rsync -a max@144.16.251.213::test test

四、一些可借鉴的脚本

这里这些脚本都是rsync网站上的例子:

1、每隔七天将数据往中心服务器做增量备份

#!/bin/sh

# This script does personal backups to a rsync backup server. You will end up
# with a 7 day rotating incremental backup. The incrementals will go
# into subdirectories named after the day of the week, and the current
# full backup goes into a directory called "current"
# tridge@linuxcare.com

# directory to backup
BDIR=/home/$USER

# excludes file – this contains a wildcard pattern per line of files to exclude
EXCLUDES=$HOME/cron/excludes

# the name of the backup machine
BSERVER=owl

# your password on the backup server
export RSYNC_PASSWORD=XXXXXX

########################################################################

BACKUPDIR=`date +%A`
OPTS="–force –ignore-errors –delete-excluded –exclude-from=$EXCLUDES
–delete –backup –backup-dir=/$BACKUPDIR -a"

export PATH=$PATH:/bin:/usr/bin:/usr/local/bin

# the following line clears the last weeks incremental directory
[ -d $HOME/emptydir ] || mkdir $HOME/emptydir
rsync –delete -a $HOME/emptydir/ $BSERVER:USER/$BACKUPDIR/
rmdir $HOME/emptydir

# now the actual transfer
rsync $OPTS $BDIR $BSERVER:USER/current

2、备份至一个空闲的硬盘

#!/bin/sh

export PATH=/usr/local/bin:/usr/bin:/bin

LIST="rootfs usr data data2"

for d in $LIST; do
mount /backup/$d
rsync -ax –exclude fstab –delete /$d/ /backup/$d/
umount /backup/$d
done

DAY=`date "+%A"`
rsync -a –delete /usr/local/apache /data2/backups/$DAY
rsync -a –delete /data/solid /data2/backups/$DAY

3、对vger.rutgers.edu的cvs树进行镜像

#!/bin/bash

cd /var/www/cvs/vger/
PATH=/usr/local/bin:/usr/freeware/bin:/usr/bin:/bin

RUN=`lps x | grep rsync | grep -v grep | wc -l`
if [ "$RUN" -gt 0 ]; then
echo already running
exit 1
fi

rsync -az vger.rutgers.edu::cvs/CVSROOT/ChangeLog $HOME/ChangeLog

sum1=`sum $HOME/ChangeLog`
sum2=`sum /var/www/cvs/vger/CVSROOT/ChangeLog`

if [ "$sum1" = "$sum2" ]; then
echo nothing to do
exit 0
fi

rsync -az –delete –force vger.rutgers.edu::cvs/ /var/www/cvs/vger/
exit 0

4、利用find的一种巧妙方式

rsync -avR remote:’`find /home -name "*.[ch]"`’ /tmp/

可以用这种方法列出需要备份的文件列表——这种方法似乎比较少人用到。

五、参考资料:

1、http://rsync.samba.org/
2、rsync examples
3、rsync FAQ

文章来源:http://xfocus.org/

0

[转]C/C++/Perl/汇编/Java效率比较


本文适合初学编程的程序员阅读,它对比了几种编程语言在解决同一问题的时候的运效率。并通过具体的例子进行了量化分析。主要目的是帮助初学者认识各种编程语言的特质,并且能够理性的选择适合的编程语言来进行工作。

事发

我无聊的翻着散落案头的书籍,这些都是五花八门的关于编程和系统管理的著作。干了这么多年程序员,大大小小的软件和项目也做了无数。每每有新入行的朋友问我这个所谓的"老前辈":哪种语言最好之类的问题,我总会作出一副知识渊博的样子,复述着从更老的老前辈那里听来的或者某些名著上看来的"知识"。就好比我们从学习编程的第一天起,就被计算机老师告知,COBOL语言是擅长处理商务事务、FOTRAN语言是用于科学计算一样。类似的知识还有"汇编语言比C语言快得多"以及"JAVA是一种效率很低的语言环境"在一代又一代的程序员中口耳相传,几乎成为了毋庸置疑的真理。

我产生了一个想法,能不能对于同一个应用用几种编程语言分别实现,来比较一下看看到底哪种语言效率最高?

老实说我自己都觉得这个想法很无聊,想想谁会反复用不同的语言写同一个程序呢?下雨天打孩子,闲着也是闲着。再说,对于某种语言的弱点和优势有一个量化的分析,对于我们今后在做项目的时候面临工具选择也少许有一点指导意义。另外,觉得好玩才是我做这件事情的真正原因。

选题

选择一个什么样的程序问题进行这样的测试呢?这是一个很关键的问题,也最容易影响测试的公平性。另外的,对于每种语言,各自的优势都是不同的。程序员的偏爱也是各不相同的。在网上和现实中,对于什么语言更好一些的争论从来就没有停止过。甚至的,各门各派的程序员所构成的各种阵营,把某种语言奉若神明的也不在少数。不信,你在CSDN的JAVA论坛说一句"JAVA执行效率太低了云云"试试?立刻会被铺天盖地的板砖掀翻在地。类似的,还有管理员对于操作系统的偏好和争论:在Linux论坛你要是表扬Windows,其惨烈程度简直是难以言状。因此,从这个意义上来说,程序员们对于编程语言的偏好,类似于战士之喜爱枪械,赛手之喜爱赛车,已经上升为一种精神层面的东西了。蔡学镛先生说得好:有人逢微软必反,有人逢微软必捧。这是一种纯粹的精神上的爱,但它可能会影响正常的、科学的思考。

可以预料的,我这篇文章一定会遭到各路豪杰的迎头痛击。

好了,让我们言归正转吧。首先的,我们的选题中要使用的各种程序语言的最常用的要素。什么是最常用的要素呢?当然了,大家都有的就是赋值、数组操作、循环、判断等。另外,对IO的操作也是编程语言重要的内容。其次的,操作时间一定要长,否则,对于解释性的语言来说是极不公平的:解释器还没调入内存呢,人家编译派的已经运行完了。最后,就是程序不能太复杂。除了我没有那么大的毅力用各种语言完成一个复杂算法的决心外,程序过于复杂,算法在测试中起的作用就越来越大,影响运行效率的原因也就增加了。算法过于复杂,开发工具的扩展部分用得也就越多。于是就成了语言附加库之间的竞赛了,这是我不愿意看到的。

考虑上述因素,我设计了一个简单的选题:从指定文本文件中搜索指定字符串,计算个数。并且打印出搜索到的个数作为结果输出。作为程序员的你粗粗过一下脑子,马上会想到这个算法里面包含了条件判断、循环、数组操作等基本的程序语言因素。这满足了上面第一个条件。另外的,为了满足第二个条件,我准备了一个多达2G的文本文件,总共有文本1500万行多。这保怔了足够的运行时间(但应该不会太长),而决不会一眨眼就执行完了。最后的,我们都知道,在文本串里面搜索子串的算法是数据结构课本中的一个典型的例子(考试也经常被考到的),也满足算法简单的要求。同时,为了让每个程序的环境都一样,我得每测试一次就重新启动一次机器,避免CACHE的影响。

准备

比赛嘛,就需要公平。首先的,硬件平台要统一。我找了一台看起来还不错的机器(服务器):两颗PIII800,1G内存。操作系统嘛,原来的机器上有新装的Windows2000Server版本。几乎没装什么别的应用。我偷懒了一下,没有重新安装OS,就这样用吧。

第一个选手:PERL

如果别人交给我这个题目,我会马上决定用PERL语言来做这件事。这个题目是完全的文本处理问题,还有比用PERL来做更合适的吗?因为PERL是专门为了文本处理而编制的语言。事实上也是这样,我用了2分钟,写了几行代码,就轻松实现了这个问题。这也说明了,选择适用的编程语言工具,比选择喜爱的工具更重要。

#!/usr/bin/perl
$filename="d:\access.log_";
$count = 0;
open(FILE , "<$filename");
while(<FILE>)
{
@match_list = ($_ =~ /HIT/g);
$count=$count+@match_list;
}
close(FILE);
print "Count = $count ";
exit

PERL是一位语言学家Larry Wall发明的,事实上,早期这种语言是专门用于在UNIX平台处理文字文件的(Perl=Practical Extraction Report Language:实用报表析取语言)。后来人们发现有大量文本构成的HTML页面用PERL来做CGI程序生成动态页面再合适不过了。因为互联网的兴起,PERL跟着发大了起来。这种语言的语法和C语言基本类似,因此比较好掌握,并且的,其关于"正则表达式"处理的强大功能目前基本上无人能够望其项背。事实上,类似于"过滤出含有TOM或者ABC的、并且后者的第一个和第三个字母大写,前者最少出现2次,后者出现5次、而且中间间隔8个或4个字母或空格的文本行"。我猜你正在反复的揣摩这句话,事实上,这就是所谓正则表达式,这样的问题,在PERL只需要一行语句就可以完成。在C语言中需要多少语句才能实现呢。

我略略解释一下上面的程序,让没有用过PERL语言的程序员也有个感性认识。

第一行是在UNIX中才用得到,因为PERL是一种基于解释的脚本语言。

第四行是打开文件

下面的循环是一行一行的读文件的内容。循环中间的第一句话是把凡是文本行中含有的HIT全部放到一个数组中;循环中中的第二句话是统计一下刚才的数组中有几个HIT,然后累加起来。循环完成了,我们的任务也就完成了。怎么样,很简单吧?"/HIT/g"就是最简单的正则表达式。

现在的PERL语言早已经不是原来的脚本语言形象了,现代PERL几乎具备了其特语言的所有特性,并且的在模块的功能帮助下,可以实现很大的应用。而且还增加了一些面向对象的特点。尽管大多数人仍然在用它处理大量的文本,但也有使用PERL完成大型应用的,尤其是在WEB方面。值得一提的是PERL也是一个跨平台语言。

我的这个程序在测试平台上,使用PERL5.8解释器,用了8分18秒08完成了1500万行文本的扫描,并得出了正确的结果。

第二个选手:纯C

也许年龄大了,但是我真的很喜欢C语言。而且我最喜欢的就是使用指针和强制类型转换来任意操作数据。我甚至会在程序里通过指针手工拼凑一个长整性的数据。说句可能引起争议的话,我觉得JAVA语言抛弃可爱的指针的做法基本上就是逃避。因为掌握不好就不用,到头来就是牺牲了效率。

本文这个题目,用C语言来实现应该还是比较不错的选择。下面的代码就是在VC下面实现的纯C代码的字符串搜索程序(为了避免图形界面的干扰,一律做成控制台程序)。编译的时候使用速度优先编译选项。

#include <stdio.h>
#include <string.h>

void main()
{
int len=2048;
char filename[20];//文件名
char buff[10000];//文件缓冲区
char hit[5];
FILE *fd;
int i,j,flag=0,over=0;
int max,readed;
int count=0;//最后的结果
strcpy(&filename[0] , "d:\access.log_");
strcpy(&hit[0] , "HIT");
buff[0]=0×0;
buff[1]=0×0;
//打开文件:
if((fd = fopen(&filename[0] , "rb"))==NULL)
{
printf("Error : Can not open file %s ",&filename[0]);
}
//读取文件内容
while(over != 1)
{
readed = fread(&buff[2] , 1 , len , fd);
if(readed < len)
{
over=1;
max=readed;
}
else
{
max=len;
}
for(i=0;i<max;i++)
{
for(j=0;j<3;j++)
{
if(hit[j] != buff[i+j])
{
flag=0;//一旦有一个不相同就退出并且标志为0
break;
}
else
{
flag=1;//一个相同为1,如果连续都相同最后结果定是1
}
}
if(flag==1)
{
count++;
i+=j-1;
}
else
{
if(j==0)
{
i+=(j);
}
else
{
i+=(j-1);
}
}
}
//把最后两个字符转移到前面两个字节以防止切断搜索串.
buff[0]=buff[max];
buff[1]=buff[max+1];
}
fclose(fd);
printf("count:%d ",count);
}

程序很好懂,用的也是教科书上面的标准字符串搜索算法,但是比前面的PERL程序长多了吧?那是因为人家PERL已经帮你完成了大部分工作。但是看到上面这段程序的运行结果你可能会高兴起来,它最快一次只用了2分10秒52,最慢也只用了2分20秒59就完成了1500万行文本的搜索任务。平均2分15秒多。为什么每次时间不一样呢?我不清楚具体原因,但学过操作系统的朋友会明白,只有在单道单任务的系统中,代码才能有执行上的可再现性。

有经验的朋友可能会说,你的缓冲区只用了2048字节,加大它速度还会增加呢。是的,而且我相信还有高手能作出更快的程序来,但这不重要,重要的是我们要考察的是不同语言完成同一件工作的效率。而且你能够明白,在程序中,改进什么能够提高效率,这就足够了。因为C语言程序中,这些都是自由可控的。

第三个选手:C++

C++和前面的C是亲戚。我简单的把前面的C代码移植过来,然后把文件输入部分改成了流类对象。至于算法部分嘛。跟前面的C是一模一样的。最后在编译的时候,除了使用速度最佳编译选项外,当然还用了C++的编译参数,因此执行文件的长度比前面的C要长一些,这说明我加的流类代码比标准C库要复杂。是的,C++应该说是目前流行的计算机编程语言中复杂度排名靠前的。其复杂的类和继承关系,以及各种初始化的次序和构造函数执行顺序等都需要考虑。还有多态以及动态联编技术等。C++也是我非常喜欢的语言,提供了面向对象的代码重用特性和足够的安全型,但是在效率上的确比纯C略逊一筹。你知道吗,大部分的操作系统核心几乎都是用纯C写成的,尽管很复杂,但很少有使用面向对象技术的。为什么,不是面向对象技术不好,也不是操作系统核心不够复杂(那什么复杂?),主要的考虑就是效率问题。

#include <stdio.h>
#include <string.h>
#include <fstream.h>

void main()
{
int len=2048;
char filename[20];//文件名
char buff[10000];//文件缓冲区
char hit[5];
int i,j,flag=0;
int max;
int count=0;//最后的结果
strcpy(&filename[0] , "d:\access.log_");
strcpy(&hit[0] , "HIT");
buff[0]=0×0;
buff[1]=0×0;
//用输入流打开文件:
ifstream input(&filename[0]);
//读取文件内容
while(input)
{
input.getline(&buff[2] , len);
max = strlen(&buff[2]);
for(i=0;i<max;i++)
{
for(j=0;j<3;j++)
{
if(hit[j] != buff[i+j])
{
flag=0;//一旦有一个不相同就退出并且标志为0
break;
}
else
{
flag=1;//一个相同为1,如果连续都相同最后结果定是1
}
}
if(flag==1)
{
count++;
i+=j-1;
}
else
{
if(j==0)
{
i+=(j);
}
else
{
i+=(j-1);
}
}
}

}
printf("count:%d ",count);
}

这段C++程序在测试平台上用了最快4分25秒95 到最慢5分40秒68的时间完成1500万行的文本检索,并在2G的文件中检索出10951968个"HIT"字符串。这结果是正确的。

第四个选手:汇编

本以为汇编程序能够达到前所未有的高速,把前面的选手远远抛在身后而笑傲江湖。这一想法支撑我完成了艰涩的代码。可事实上测试的结果缺让我大失所望,完全用机器指令书写的程序,去掉缓冲区才几百字节,算法和前面的C程序一模一样,扫描1500万行文本竟然最快也要2分14秒56!这甚至还比不过C语言的最快纪录。而平均下来,汇编程序的速度竟然和前面的C程序在伯仲之间。恐怕这样的结果也出乎大部分人的意外。因为我们从入行的那一天起,就被告知汇编是你所能够掌握的最快的语言!尽管代码坚涩难懂,但性能的代价是值得的。而从这里的测试看,你觉得向下面这样的代码,实现和C语言一样的速度和功能值得吗?

;堆栈段
STSG SEGMENT STACK ‘S’
DW 64 DUP(?)
STSG ENDS

;数据段
DATA SEGMENT
rlength EQU 2048
fname DB ‘access.log_’,0
hit DB ‘HIT$’
fd DW ? ;文件句柄
resault DB ‘count : $’ ;结果提示
count DD 0 ;存放结果
disflag DB 0 ;显示标志
buff DB 5000 dup(0) ;缓冲区
DATA ENDS

;代码段
CODE SEGMENT
MAIN PROC FAR
ASSUME CS:CODE,DS:DATA,SS:STSG,ES:NOTHING
MOV AX,DATA
MOV DS,AX
;我的代码开始:
mov ah,3dh ;打开文件
lea dx,fname
mov al,00h ;文件打开方式
int 21h ;开始操作
;这里就不作错误处理了,偷懒喽!
;CF=0表示正确,CF=1表示错误,AX是文件句柄或者是错误代码
mov fd,ax ;保存文件句柄

READ: mov ah,3fh ;读文件
mov bx,fd ;文件句柄
mov cx,rlength ;要读length字节

lea dx,buff ;给出读缓冲区指针
add dx,2 ;缓冲区指针向后错两个(目的是解决边界问题:有一个HIT正好横跨rlength界限)
int 21h ;开始读
;AX里面是实际读出的字节数
;读完了以后,扫描缓冲区
push ax ;保存AX字节数
cmp ax,0
jz ALLEND ;文件读完了就退出

sub dx,2 ;指针向前错2个,
mov si,dx
add dx,2 ;把指针回到原来的位置
add dx,ax ;计算结尾
LOD3: cmp si,dx ;到头了就重新读一次文件
jz OVR
lods buff
lea bx,HIT
cmp al,[bx]
jnz LOD3 ;读第一个字节不相等就重新读一个

cmp si,dx
jz OVR
lods buff
cmp al,[bx+1]
jnz LOD3 ;如果第一个字节相等,就读第2个字节,不行等就从第一个字节再重比较。

cmp si,dx ;如果第二个字节也相等的话,就比较第三个字节。
jz OVR
lods buff
cmp al,[bx+2]
jnz LOD3 ;第三个字节不相等再从头开始
;有一个HIT匹配
push bx
lea bx,count
add WORD ptr [bx],1 ;计数器增加一个
adc WORD ptr [bx+2],0 ;进位
pop bx
jmp LOD3

OVR: mov ah,[si-1]
mov BYTE ptr buff+1 , ah
mov ah,[si-2]
mov BYTE ptr buff , ah

pop ax ;恢复这次总共读出的字节数
cmp ax,rlength ;看看是不是最后一次(剩余的零头)
jz READ
;如果是最后一次读文件,

ALLEND: mov ah,3eh ;关闭文件
mov bx,fd ;文件句柄
int 21h ;关闭文件

mov ah,9 ;显示结果字符串
lea dx,resault
int 21h

;转换2进制结果到10进制ACSII形式
mov bx, WORD ptr count
call TERN

mov ax,4c00h ;返回DOS
int 21h
;结束代码,最大的数字已经排到了最前面
MAIN ENDP

TERN PROC ;这个子程序是转换并显示2进制数字的
mov cx,10000
call DEC_DIV
mov cx,1000
call DEC_DIV
mov cx,100
call DEC_DIV
mov cx,10
call DEC_DIV
mov cx,1
call DEC_DIV
ret
TERN ENDP
DEC_DIV PROC
mov ax,bx
mov dx,0
div cx
mov bx,dx
mov dl,al
add dl,30H
mov ah,disflag ;read flag
cmp ah,0
jnz DISP ;已经显示过有效数字了
cmp dl,30H
jz NODISP
mov disflag,1 ;作用是第一个有效数字出现前不显示0
DISP: mov ah,2
int 21H
NODISP: ret
DEC_DIV ENDP
CODE ENDS
END MAIN

上面这段代码我猜你也懒得仔细阅读。其实他不能"显示结果"。因为最后这段负责把最终结果转换成可显示ASCII码的程序实际上只能转换二进制十六位的数据,而最终的结果高达1000万挂零,显示会出错。由于这最终结果的显示已经和程序的运行没有大关系了,因此,我也就懒得去写一个32位的ASCII转换程序了。就这样吧。

第五个选手:

JAVA是一个不能不参加比赛的选手。有如此多的人热爱他,他们中的一半人是因为JAVA的面向对象特性以及良好的跨平台特性。而另一半人纯粹就是因为JAVA不姓"微(软)",这就是意识形态在程序员头脑中对某种语言的注释。单纯从语言元素上来说,我还是比较喜欢JAVA的。因为他的语法干净、简洁。环境也好。虽然用虚拟机系统(JVM)的做法来实现跨平台特性并非什么了不得的创意(像不像30年前的BASIC解释器?别跟我说什么中间代码?几乎所有的解释器都是把语言因素翻译成中间代码的,JVM不过是分成2步来实现罢了,但从运行机制上应该是差不多的。),但JVM仍然将JAVA的跨平台特性做到了前所未有的地步。而且JVM是一个很干净的系统,让人用起来赏心悦目。说到这里我忍不住想提一下J2EE企业应用框架了。不知道有多少人能够看懂SUN出的J2EE的"理论著作"?满纸充斥着各种生造的概念,洋溢着溢美之词。JAVA的企业应用框架实在是比较复杂的东西,虽然赶不上后来的.NET框架,但足以让大多数初学者望而却步。一句话,东西太多了。事实上JAVA的企业级应用并没有想象的成功,iPlanet就随着电子商务概念的全面垮台而渐渐淡出。现在换了个名叫“SUNONE”――SUN公司员工原话。

我们回到JAVA的语言元素上来说,实际上JAVA可以被理解为被纯化的C++。JAVA去除了C++为了兼容C而增加的一些"非面向对象特质",用其他的一些变通办法实现C++直接实现的功能,比如:多继承。在实现机制上,JAVA的程序会先编译成.CLASS文件,然后这种跨平台的中间代码就可以"一次编译,到处运行"了。当然必须运行在有JVM虚拟机的环境中,连图形什么的都可以照搬。换句话说,你用JAVA程序在PC屏幕上画一个圆,在JAVA-PDA上它还是圆的。

我在本次测试中,写了下面的代码,用JAVA做了同样的测试,测试中实际上用到了:JAVA的文件流类,运行了循环、条件判断、数组操作等基本的语言因素。环境是J2SE1.3.1-06。JAVA程序做1500万行的文本扫描用了8分21秒18。应该说是几种语言中最慢的,基本上和纯解释的PERL是在同一水准。J2EE的JVM环境还是经过优化的所谓HOTSPOT。

import java.io.*;
public langtest
{
public static void main(String[] args)
{
String filename = "d:\access.log_";
try
{
count(filename);
}
catch (IOException e)
{
System.err.println(e.getMessage());
};
}

public static void count(String filename) throws IOException
{
long count=0;
long len;
String strline = "";
char hit[] = {‘H’,'I’,'T’};//要搜索的字符串
char buff[] = new char[2100];

Reader in = new FileReader(filename);//用FileReader类构造产生一个Reader类对象
LineNumberReader line = null;//生成一个空指针
try
{
line = new LineNumberReader(in);//建立LineNumberReader类对象
while((strline = line.readLine()) != null)
{
//到这里已经读出一行了,用下面的代码分析这行有几个HIT
int i=0,j=0,max=0,flag=0;
buff = strline.toCharArray();//转换成字符数组
max = strline.length();

for(i=0;i<max;i++)
{
for(j=0;j<3;j++)
{
if(hit[j] != buff[i+j])
{
flag=0;//一旦有一个不相同就退出并且标志为0
break;
}
else
{
flag=1;//一个相同为1,如果连续都相同最后结果定是1
}
}
if(flag==1)
{
count++;
i+=j-1;
}
else

{
if(j==0)
{
i+=(j);
}
else
{
i+=(j-1);
}
}
}
}
System.out.println("Count : "+count);
}
catch (IOException e)
{
System.err.println(e.getMessage());
}
finally
{
try
{
if(in != null) in.close();
}
catch (IOException e)
{
}
}
}
}

候捷先生翻译的宏篇巨著《JAVA编程思想》一书中第67页说到:"使用最原始的JAVA解释器,JAVA大概比C慢上20到50倍"之说法我在阅读的时候就心存疑虑,心想要是这样,JAVA完全没有存或与世间的必要了。在亲自动手试验过后,我觉得说JAVA在J2EE环境下,比C慢上2-3倍还是比较可靠的说法的。况且,目前越来越多的硬件JVM的诞生,也给JAVA越来越多的机会。不过我担心的正是这点,JVM的多厂家多样化很可能会造成某些兼容性方面的问题。例如我见过一篇文章就是讨论某种JAVA程序在IBM-JVM可用而在SUN-JVM上不可用之事例。但愿的,JAVA能健康成长。

总结

事实上,本文有两个基本的意义传递给初做程序员的读者:

一、 抛开你的意识形态好恶,选择最合适的编程语言来完成你的工作。每种流行的语言都有自己存在的意义。

二、 在编程中,有想法就自己做一做,你会得出自己的结论。

至此,你应该明白,前面的所有测试结果其实并不重要,重要的是你了解了这些语言的特质,也许在今后的编程生涯中会因此增加一点点"经验"呢。

后记

本来笔者还打算继续测试一下另外的一种颇为流行的解释语言Python和新贵C#以及在Linux平台完成这些测试,但终究还是被懒惰瓦解了斗志。好在的,Python和Perl比较相似,而C#和JAVA有异曲同工之妙。也可以略略做一点参考。

事实上,本文测试中有一个大大的不公平之处,相信仔细的读者已经发现了:其中C和ASM都是使用缓冲区直读的办法,不管三七二十一就进行判断(最后用指针检查缓冲区边界)。而C++等其他的语言虽然用了非常方便的流按行读出,但是多做了很多事情:每一个字符都要判断其是不是回车换行符,而按行读近来,每次缓冲的也要少很多。因此其他几种语言就大大的吃亏了。不过这并不影响结论性的东西,因为测试本身就说明越方便就效率越低。事情总是要有人做,不是吗?

0

优化MySQL数据库性能的八大“妙手”


  本文探讨了提高MySQL 数据库性能的思路,并从8个方面给出了具体的解决方法。

  1、选取最适用的字段属性

  MySQL可以很好的支持大数据量的存取,但是一般说来,数据库中的表越小,在它上面执行的查询也就会越快。因此,在创建表的时候,为了获得更好的性能,我们可以将表中字段的宽度设得尽可能小。例如,在定义邮政编码这个字段时,如果将其设置为CHAR(255),显然给数据库增加了不必要的空间,甚至使用VARCHAR这种类型也是多余的,因为CHAR(6)就可以很好的完成任务了。同样的,如果可以的话,我们应该使用MEDIUMINT而不是BIGIN来定义整型字段。

  另外一个提高效率的方法是在可能的情况下,应该尽量把字段设置为NOT NULL,这样在将来执行查询的时候,数据库不用去比较NULL值。

  对于某些文本字段,例如“省份”或者“性别”,我们可以将它们定义为ENUM类型。因为在MySQL中,ENUM类型被当作数值型数据来处理,而数值型数据被处理起来的速度要比文本类型快得多。这样,我们又可以提高数据库的性能。

  2、使用连接(JOIN)来代替子查询(Sub-Queries)

  MySQL从4.1开始支持SQL的子查询。这个技术可以使用SELECT语句来创建一个单列的查询结果,然后把这个结果作为过滤条件用在另一个查询中。例如,我们要将客户基本信息表中没有任何订单的客户删除掉,就可以利用子查询先从销售信息表中将所有发出订单的客户ID取出来,然后将结果传递给主查询,如下所示:

DELETE FROM customerinfo
WHERE CustomerID NOT in (SELECT CustomerID FROM salesinfo )

  使用子查询可以一次性的完成很多逻辑上需要多个步骤才能完成的SQL操作,同时也可以避免事务或者表锁死,并且写起来也很容易。但是,有些情况下,子查询可以被更有效率的连接(JOIN).. 替代。例如,假设我们要将所有没有订单记录的用户取出来,可以用下面这个查询完成:

SELECT * FROM customerinfo
WHERE CustomerID NOT in (SELECT CustomerID FROM salesinfo )

  如果使用连接(JOIN).. 来完成这个查询工作,速度将会快很多。尤其是当salesinfo表中对CustomerID建有索引的话,性能将会更好,查询如下:

SELECT * FROM customerinfo
LEFT JOIN salesinfoON customerinfo.CustomerID=salesinfo.
CustomerID
WHERE salesinfo.CustomerID IS NULL

 连接(JOIN).. 之所以更有效率一些,是因为 MySQL不需要在内存中创建临时表来完成这个逻辑上的需要两个步骤的查询工作。

  3、使用联合(UNION)来代替手动创建的临时表

  MySQL 从 4.0 的版本开始支持 UNION 查询,它可以把需要使用临时表的两条或更多的 SELECT 查询合并的一个查询中。在客户端的查询会话结束的时候,临时表会被自动删除,从而保证数据库整齐、高效。使用 UNION 来创建查询的时候,我们只需要用 UNION作为关键字把多个 SELECT 语句连接起来就可以了,要注意的是所有 SELECT 语句中的字段数目要想同。下面的例子就演示了一个使用 UNION的查询。

SELECT Name, Phone FROM client
UNION
SELECT Name, BirthDate FROM author
UNION
SELECT Name, Supplier FROM product

  4、事务

  尽管我们可以使用子查询(Sub-Queries)、连接(JOIN)和联合(UNION)来创建各种各样的查询,但不是所有的数据库操作都可以只用一条或少数几条SQL语句就可以完成的。更多的时候是需要用到一系列的语句来完成某种工作。但是在这种情况下,当这个语句块中的某一条语句运行出错的时候,整个语句块的操作就会变得不确定起来。设想一下,要把某个数据同时插入两个相关联的表中,可能会出现这样的情况:第一个表中成功更新后,数据库突然出现意外状况,造成第二个表中的操作没有完成,这样,就会造成数据的不完整,甚至会破坏数据库中的数据。要避免这种情况,就应该使用事务,它的作用是:要么语句块中每条语句都操作成功,要么都失败。换句话说,就是可以保持数据库中数据的一致性和完整性。事物以BEGIN 关键字开始,COMMIT关键字结束。在这之间的一条SQL操作失败,那么,ROLLBACK命令就可以把数据库恢复到BEGIN开始之前的状态。

BEGIN;
INSERT INTO salesinfo SET CustomerID=14;
UPDATE inventory SET Quantity=11
WHERE item=’book’;
COMMIT;

  事务的另一个重要作用是当多个用户同时使用相同的数据源时,它可以利用锁定数据库的方法来为用户提供一种安全的访问方式,这样可以保证用户的操作不被其它的用户所干扰。

  5、锁定表

  尽管事务是维护数据库完整性的一个非常好的方法,但却因为它的独占性,有时会影响数据库的性能,尤其是在很大的应用系统中。由于在事务执行的过程中,数据库将会被锁定,因此其它的用户请求只能暂时等待直到该事务结束。如果一个数据库系统只有少数几个用户
来使用,事务造成的影响不会成为一个太大的问题;但假设有成千上万的用户同时访问一个数据库系统,例如访问一个电子商务网站,就会产生比较严重的响应延迟。

  其实,有些情况下我们可以通过锁定表的方法来获得更好的性能。下面的例子就用锁定表的方法来完成前面一个例子中事务的功能。

LOCK TABLE inventory WRITE
SELECT Quantity FROM inventory
WHEREItem=’book’;

UPDATE inventory SET Quantity=11
WHEREItem=’book’;
UNLOCK TABLES

  这里,我们用一个 SELECT 语句取出初始数据,通过一些计算,用 UPDATE 语句将新值更新到表中。包含有 WRITE 关键字的 LOCK TABLE 语句可以保证在 UNLOCK TABLES 命令被执行之前,不会有其它的访问来对 inventory 进行插入、更新或者删除的操作。

  6、使用外键

  锁定表的方法可以维护数据的完整性,但是它却不能保证数据的关联性。这个时候我们就可以使用外键。例如,外键可以保证每一条销售记录都指向某一个存在的客户。在这里,外键可以把customerinfo 表中的CustomerID映射到salesinfo表中CustomerID,任何一条没有合法CustomerID的记录都不会被更新或插入到salesinfo中。

CREATE TABLE customerinfo
(
CustomerID INT NOT NULL ,
PRIMARY KEY ( CustomerID )
) TYPE = INNODB;

CREATE TABLE salesinfo
(
SalesID INT NOT NULL,
CustomerID INT NOT NULL,
PRIMARY KEY(CustomerID, SalesID),
FOREIGN KEY (CustomerID) REFERENCES customerinfo
(CustomerID) ON DELETECASCADE
) TYPE = INNODB;

  注意例子中的参数“ON DELETE CASCADE”。该参数保证当 customerinfo 表中的一条客户记录被删除的时候,salesinfo 表中所有与该客户相关的记录也会被自动删除。如果要在 MySQL 中使用外键,一定要记住在创建表的时候将表的类型定义为事务安全表 InnoDB类型。该类型不是 MySQL 表的默认类型。定义的方法是在 CREATE TABLE 语句中加上 TYPE=INNODB。如例中所示。

  7、使用索引

  索引是提高数据库性能的常用方法,它可以令数据库服务器以比没有索引快得多的速度检索特定的行,尤其是在查询语句当中包含有MAX(), MIN()和ORDERBY这些命令的时候,性能提高更为明显。那该对哪些字段建立索引呢?一般说来,索引应建立在那些将用于JOIN, WHERE判断和ORDER BY排序的字段上。尽量不要对数据库中某个含有大量重复的值的字段建立索引。对于一个ENUM类型的字段来说,出现大量重复值是很有可能的情况,例如customerinfo中的“province”.. 字段,在这样的字段上建立索引将不会有什么帮助;相反,还有可能降低数据库的性能。我们在创建表的时候可以同时创建合适的索引,也可以使用ALTER TABLE或CREATE INDEX在以后创建索引。此外,MySQL
从版本3.23.23开始支持全文索引和搜索。全文索引在MySQL 中是一个FULLTEXT类型索引,但仅能用于MyISAM 类型的表。对于一个大的数据库,将数据装载到一个没有FULLTEXT索引的表中,然后再使用ALTER TABLE或CREATE INDEX创建索引,将是非常快的。但如果将数据装载到一个已经有FULLTEXT索引的表中,执行过程将会非常慢。

  8、优化的查询语句

  绝大多数情况下,使用索引可以提高查询的速度,但如果SQL语句使用不恰当的话,索引将无法发挥它应有的作用。下面是应该注意的几个方面。首先,最好是在相同类型的字段间进行比较的操作。在MySQL 3.23版之前,这甚至是一个必须的条件。例如不能将一个建有索引的INT字段和BIGINT字段进行比较;但是作为特殊的情况,在CHAR类型的字段和VARCHAR类型字段的字段大小相同的时候,可以将它们进行比较。其次,在建有索引的字段上尽量不要使用函数进行操作。

  例如,在一个DATE类型的字段上使用YEAE()函数时,将会使索引不能发挥应有的作用。所以,下面的两个查询虽然返回的结果一样,但后者要比前者快得多。

SELECT * FROM order WHERE YEAR(OrderDate)<2001;

SELECT * FROM order WHERE OrderDate<"2001-01-01";

同样的情形也会发生在对数值型字段进行计算的时候:

SELECT * FROM inventory WHERE Amount/7<24;

SELECT * FROM inventory WHERE Amount<24*7;

  上面的两个查询也是返回相同的结果,但后面的查询将比前面的一个快很多。第三,在搜索字符型字段时,我们有时会使用 LIKE 关键字和通配符,这种做法虽然简单,但却也是以牺牲系统性能为代价的。例如下面的查询将会比较表中的每一条记录。

SELECT * FROM books
WHERE name like "MySQL%"

  但是如果换用下面的查询,返回的结果一样,但速度就要快上很多:

SELECT * FROM books
WHERE name>="MySQL"and name<"MySQM"

  最后,应该注意避免在查询中让MySQL进行自动类型转换,因为转换过程也会使索引变得不起作用。

Previous Page Next Page

Random Posts Recent Comments

  • Nouramohsen88 Says:

    http://goo.gl/vFWge لدينا ثلاجات عرض جديدة ومستعملة للبيع ولدينا ثلاجات عرض سوبر ماركت وحلويات في ست...

  • Nouramohsen88 Says:

    http://www.drdrahem.com/home دكتور رجيم دكتور تخسيس الكرش والارداف مركز تخسيس في مدينة نصر ...

  • Nouramohsen88 Says:

    شركه تصنيع صاعق ناموس http://www.grandelectronic-eg.com/...

  • Nouramohsen88 Says:

    شركة كشافات اضاءة في مصر http://www.grandelectronic-eg.com/ ...

  • Nouramohsen88 Says:

    http://www.grandelectronic-eg.com/ شركة كشافات طواريء في مصر...

  • Nouramohsen88 Says:

    anti-mosquitocompany.blogspot.com شركة جراند الكترونيك هي شركة مصرية متخصصة في تصنيع الكشافات الكهرب...

  • Nouramohsen88 Says:

    insect--killer.blogspot.com شركة جراند الكترونيك هي شركة مصرية متخصصة في تصنيع الكشافات الكهربية وك...

  • Nouramohsen88 Says:

    http://genius-square.com/ شركه للتدريب والاستشارات | متخصصون في التنمية البشرية...

  • Er Says:

    我了个去,我也是用的phpo ..... 看来大家的思绪差不多。。。。...

  • Fasf Says:

    SYM_TYPE * pType;改为SYM_TYPE pType;...

Tag Cloud

arm audio blog brew cache class debug flash google html j2me java javascript Joke linux lua mobile mtk php python ror ruby server shell stream unix web windows 优化 动态加载 女人 女生 平台 开发 手机 技术 流媒体 测试 漫画 生活 男人 男生 缓存 芯片