服务器的大用户量的承载方案一、前言二、编译安装三、 安装MySQL、memcache四、 安装Apache、PHP、eAccelerator、php-memcache五、 安装Squid六、后记一、前言，准备工作当前，LAMP开发模式是WEB开发的首选，如何搭建一个高效、可靠、稳定的WEB服务器一直是个热门主题，本文就是这个主题的一次尝试。我们采用的架构图如下：引用——– ———- ————- ——— ————| 客户端 | ===> |负载均衡器| ===> |反向代理/缓存| ===> |WEB服务器| ===> |数据库服务器|——– ———- ————- ——— ————Nginx Squid Apache,PHP MySQLeAccelerator/memcache准备工作：引用服务器： Intel(R) Xeon(TM) CPU 3.00GHz * 2, 2GB mem, SCISC 硬盘操作系统：CentOs4.4，内核版本2.6.9-22.ELsmp，gcc版本3.4.4软件：Apache 2.2.3（能使用MPM模式）PHP 5.2.0（选用该版本是因为5.2.0的引擎相对更高效）eAccelerator 0.9.5（加速PHP引擎，同时也可以加密PHP源程序）memcache 1.2.0（用于高速缓存常用数据）libevent 1.2a（memcache工作机制所需）MySQL 5.0.27（选用二进制版本，省去编译工作）Nginx 0.5.4（用做负载均衡器）squid-2.6.STABLE6（做反向代理的同时提供专业缓存功能）二、编译安装 安装Nginx1.) 安装Nginx发音为[engine x]，是由俄罗斯人Igor Sysoev建立的项目,基于BSD许可。据说他当初是F5的成员之一，英文主页：http://nginx.net。俄罗斯的一些大网站已经使用它超过两年多了，一直表现不凡。Nginx的编译参数如下：[root@localhost]#./configure –prefix=/usr/local/server/nginx –with-openssl=/usr/include \–with-pcre=/usr/include/pcre/ –with-http_stub_status_module –without-http_memcached_module \–without-http_fastcgi_module –without-http_rewrite_module –without-http_map_module \–without-http_geo_module –without-http_autoindex_module在这里，需要说明一下，由于Nginx的配置文件中我想用到正则，所以需要 pcre 模块的支持。我已经安装了 pcre 及 pcre-devel 的rpm包，但是 Ngxin 并不能正确找到 .h/.so/.a/.la 文件，因此我稍微变通了一下：[root@localhost]#mkdir /usr/include/pcre/.libs/[root@localhost]#cp /usr/lib/libpcre.a /usr/include/pcre/.libs/libpcre.a[root@localhost]#cp /usr/lib/libpcre.a /usr/include/pcre/.libs/libpcre.la然后，修改 objs/Makefile 大概在908行的位置上，注释掉以下内容：./configure –disable-shared接下来，就可以正常执行 make 及 make install 了。2.) 修改配置文件 /usr/local/server/nginx/conf/nginx.conf以下是我的 nginx.conf 内容，仅供参考：#运行用户user nobody nobody;#启动进程worker_processes 2;#全局错误日志及PID文件error_log logs/error.log notice;pid logs/nginx.pid;#工作模式及连接数上限events {use epoll;worker_connections 1024;}#设定http服务器，利用它的反向代理功能提供负载均衡支持http {#设定mime类型include conf/mime.types;default_type application/octet-stream;#设定日志格式log_format main ‘$remote_addr – $remote_user [$time_local] ‘‘”$request” $status $bytes_sent ‘‘”$http_referer” “$http_user_agent” ‘‘”$gzip_ratio”‘;log_format download ‘$remote_addr – $remote_user [$time_local] ‘‘”$request” $status $bytes_sent ‘‘”$http_referer” “$http_user_agent” ‘‘”$http_range” “$sent_http_content_range”‘;#设定请求缓冲client_header_buffer_size 1k;large_client_header_buffers 4 4k;#开启gzip模块gzip on;gzip_min_length 1100;gzip_buffers 4 8k;gzip_types text/plain;output_buffers 1 32k;postpone_output 1460;#设定access logaccess_log logs/access.log main;client_header_timeout 3m;client_body_timeout 3m;send_timeout 3m;sendfile on;tcp_nopush on;tcp_nodelay on;keepalive_timeout 65;#设定负载均衡的服务器列表upstream mysvr {#weigth参数表示权值，权值越高被分配到的几率越大#本机上的Squid开启3128端口server 192.168.8.1:3128 weight=5;server 192.168.8.2:80 weight=1;server 192.168.8.3:80 weight=6;}#设定虚拟主机server {listen 80;server_name 192.168.8.1 www.enew.com.cn;charset gb2312;#设定本虚拟主机的访问日志access_log logs/www.enew.com.cn.access.log main;#如果访问 /img/*, /js/*, /css/* 资源，则直接取本地文件，不通过squid#如果这些文件较多，不推荐这种方式，因为通过squid的缓存效果更好location ~ ^/(img|js|css)/ {root /data3/Html;expires 24h;}#对 “/” 启用负载均衡location / {proxy_pass http://mysvr;proxy_redirect off;proxy_set_header Host $host;proxy_set_header X-Real-IP $remote_addr;proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;client_max_body_size 10m;client_body_buffer_size 128k;proxy_connect_timeout 90;proxy_send_timeout 90;proxy_read_timeout 90;proxy_buffer_size 4k;proxy_buffers 4 32k;proxy_busy_buffers_size 64k;proxy_temp_file_write_size 64k;}#设定查看Nginx状态的地址location /NginxStatus {stub_status on;access_log on;auth_basic “NginxStatus”;auth_basic_user_file conf/htpasswd;}}}备注：conf/htpasswd 文件的内容用 apache 提供的 htpasswd 工具来产生即可，内容大致如下：3.) 查看 Nginx 运行状态输入地址 http://192.168.8.1/NginxStatus/，输入验证帐号密码，即可看到类似如下内容：Active connections: 328server accepts handled requests9309 8982 28890Reading: 1 Writing: 3 Waiting: 324第一行表示目前活跃的连接数第三行的第三个数字表示Nginx运行到当前时间接受到的总请求数，如果快达到了上限，就需要加大上限值了。第四行是Nginx的队列状态安装MySQL、memcache1.) 安装MySQL，步骤如下：[root@localhost]#tar zxf mysql-standard-5.0.27-linux-i686.tar.gz -C /usr/local/server[root@localhost]#mv /usr/local/server/mysql-standard-5.0.27-linux-i686 /usr/local/server/mysql[root@localhost]#cd /usr/local/server/mysql[root@localhost]#./scripts/mysql_install_db –basedir=/usr/local/server/mysql \–datadir=/usr/local/server/mysql/data –user=nobody[root@localhost]#cp /usr/local/server/mysql/support-files/my-large.cnf \/usr/local/server/mysql/data/my.cnf2.) 修改 MySQL 配置，增加部分优化参数，如下：[root@localhost]#vi /usr/local/server/mysql/data/my.cnf主要内容如下：[mysqld]basedir = /usr/local/server/mysqldatadir = /usr/local/server/mysql/datauser = nobodyport = 3306socket = /tmp/mysql.sockwait_timeout = 30long_query_time=1#log-queries-not-using-indexes = TRUElog-slow-queries=/usr/local/server/mysql/slow.loglog-error = /usr/local/server/mysql/error.logexternal-locking = FALSEkey_buffer_size = 512Mback_log = 400table_cache = 512sort_buffer_size = 2Mjoin_buffer_size = 4Mread_buffer_size = 2Mread_rnd_buffer_size = 4Mmyisam_sort_buffer_size = 64Mthread_cache_size = 32query_cache_limit = 2Mquery_cache_size = 64Mthread_concurrency = 4thread_stack = 128Ktmp_table_size = 64Mbinlog_cache_size = 2Mmax_binlog_size = 128Mmax_binlog_cache_size = 512Mmax_relay_log_size = 128Mbulk_insert_buffer_size = 8Mmyisam_repair_threads = 1skip-bdb#如果不需要使用innodb就关闭该选项#skip-innodbinnodb_data_home_dir = /usr/local/server/mysql/data/innodb_data_file_path = ibdata1:256M;ibdata2:256M:autoextendinnodb_log_group_home_dir = /usr/local/server/mysql/data/innodb_log_arch_dir = /usr/local/server/mysql/data/innodb_buffer_pool_size = 512Minnodb_additional_mem_pool_size = 8Minnodb_log_file_size = 128Minnodb_log_buffer_size = 8Minnodb_lock_wait_timeout = 50innodb_flush_log_at_trx_commit = 2innodb_file_io_threads = 4innodb_thread_concurrency = 16innodb_log_files_in_group = 3以上配置参数请根据具体的需要稍作修改。运行以下命令即可启动 MySQL 服务器：/usr/local/server/mysql/bin/mysqld_safe \–defaults-file=/usr/local/server/mysql/data/my.cnf &由于 MySQL 不是安装在标准目录下，因此必须要修改 mysqld_safe 中的 my_print_defaults 文件所在位置，才能通过mysqld_safe 来启动 MySQL 服务器。3.) memcache + libevent 安装编译安装：[root@localhost]#cd libevent-1.2a[root@localhost]#./configure –prefix=/usr/ && make && make install[root@localhost]#cd ../memcached-1.2.0[root@localhost]#./configure –prefix=/usr/local/server/memcached –with-libevent=/usr/[root@localhost]#make && make install备注：如果 libevent 不是安装在 /usr 目录下，那么需要把 libevent-1.2a.so.1 拷贝/链接到 /usr/lib 中，否则memcached 无法正常加载。运行以下命令来启动 memcached：[root@localhost]#/usr/local/server/memcached/bin/memcached \-l 192.168.8.1 -d -p 10000 -u nobody -m 128表示用 daemon 的方式启动 memcached，监听在 192.168.8.1 的 10000 端口上，运行用户为 nobody，为其分配128MB 的内存。安装Apache、PHP、eAccelerator、php-memcache四、) 安装Apache、PHP、eAccelerator、php-memcache由于Apache2下的php静态方式编译十分麻烦，因此在这里采用动态模块(DSO)方式。1.) 安装Apache 2.2.3[root@localhost]#./configure –prefix=/usr/local/server/apache –disable-userdir –disable-actions \–disable-negotiation –disable-autoindex –disable-filter –disable-include –disable-status \–disable-asis –disable-auth –disable-authn-default –disable-authn-file –disable-authz-groupfile \–disable-authz-host –disable-authz-default –disable-authz-user –disable-userdir \–enable-expires –enable-module=so备注：在这里，取消了一些不必要的模块，如果你需要用到这些模块，那么请去掉部分参数。2.) 安装PHP 5.2.0[root@localhost]#./configure –prefix=/usr/local/server/php –with-mysql \–with-apxs2=/usr/local/server/apache/bin/apxs –with-freetype-dir=/usr/ –with-png-dir=/usr/ \–with-gd=/usr/ –with-jpeg-dir=/usr/ –with-zlib –enable-magic-quotes –with-iconv \–without-sqlite –without-pdo-sqlite –with-pdo-mysql –disable-dom –disable-simplexml \–enable-roxen-zts[root@localhost]#make && make install备注：如果不需要gd或者pdo等模块，请自行去掉。3.) 安装eAccelerator-0.9.5[root@localhost]#cd eAccelerator-0.9.5[root@localhost]#export PHP_PREFIX=/usr/local/server/php[root@localhost]#$PHP_PREFIX/bin/phpize[root@localhost]#./configure –enable-eaccelerator=shared –with-php-config=$PHP_PREFIX/bin/php-config[root@localhost]#make && make install4.) 安装memcache模块[root@localhost]#cd memcache-2.1.0[root@localhost]#export PHP_PREFIX=/usr/local/server/php[root@localhost]#$PHP_PREFIX/bin/phpize[root@localhost]#./configure –enable-eaccelerator=shared –with-php-config=$PHP_PREFIX/bin/php-config[root@localhost]#make && make install5.) 修改 php.ini 配置然后修改 php.ini，修改/加入类似以下内容：extension_dir = “/usr/local/server/php/lib/”extension=”eaccelerator.so”eaccelerator.shm_size=”32″ ;设定eaccelerator的共享内存为32MBeaccelerator.cache_dir=”/usr/local/server/eaccelerator”eaccelerator.enable=”1″eaccelerator.optimizer=”1″eaccelerator.check_mtime=”1″eaccelerator.debug=”0″eaccelerator.filter=”*.php”eaccelerator.shm_max=”0″eaccelerator.shm_ttl=”0″eaccelerator.shm_prune_period=”3600″eaccelerator.shm_only=”0″eaccelerator.compress=”1″eaccelerator.compress_level=”9″eaccelerator.log_file = “/usr/local/server/apache/logs/eaccelerator_log”eaccelerator.allowed_admin_path = “/usr/local/server/apache/htdocs/ea_admin”extension=”memcache.so”在这里，最好是在apache的配置中增加默认文件类型的cache机制，即利用apache的expires模块，新增类似如下几行：ExpiresActive OnExpiresByType text/html “access plus 10 minutes”ExpiresByType text/css “access plus 1 day”ExpiresByType image/jpg “access 1 month”ExpiresByType image/gif “access 1 month”ExpiresByType image/jpg “access 1 month”ExpiresByType application/x-shockwave-flash “access plus 3 day”这么设置是由于我的这些静态文件通常很少更新，因此我选择的是”access”规则，如果更新相对比较频繁，可以改用”modification”规则;或者也可以用”access”规则，但是在文件更新的时候，执行一下”touch”命令，把文件的时间刷新一下即可。 安装Squid五、) 安装Squid[root@localhost]#./configure –prefix=/usr/local/server/squid –enable-async-io=100 –disable-delay-pools –disable-mem-gen-trace –disable-useragent-log –enable-kill-parent-hack –disable-arp-acl –enable-epoll –disable-ident-lookups –enable-snmp –enable-large-cache-files –with-large-files[root@localhost]#make && make install或使用如下安装方法：[root@localhost]#yum install squid如果是2.6的内核，才能支持epoll的IO模式，旧版本的内核则只能选择poll或其他模式了;另外，记得带上支持大文件的选项，否则在accesslog等文件达到2G的时候就会报错。设定 squid 的配置大概如下内容：#设定缓存目录为 /var/cache1 和 /var/lib/squid，每次处理缓存大小为128MB，当缓存空间使用达到95%时#新的内容将取代旧的而不直接添加到目录中，直到空间又下降到90%才停止这一活动#/var/cache1 最大1024MB，/var/lib/squid 最大 5000MB，都是 16*256 级子目录cache_dir aufs /var/cache1 1024 16 256cache_dir aufs /var/lib/squid 5000 16 256cache_mem 128 MBcache_swap_low 90cache_swap_high 95#设置存储策略等maximum_object_size 4096 KBminimum_object_size 0 KBmaximum_object_size_in_memory 80 KBipcache_size 1024ipcache_low 90ipcache_high 95cache_replacement_policy lrumemory_replacement_policy lru#设置超时策略forward_timeout 20 secondsconnect_timeout 15 secondsread_timeout 3 minutesrequest_timeout 1 minutespersistent_request_timeout 15 secondsclient_lifetime 15 minutesshutdown_lifetime 5 secondsnegative_ttl 10 seconds#限制一个ip最大只能有16个连接acl OverConnLimit maxconn 16http_access deny OverConnLimit#限制baidu spider访问#acl AntiBaidu req_header User-Agent Baiduspider#http_access deny AntiBaidu#常规设置visible_hostname cache.enew.comcache_mgr webmaster@enew.comclient_persistent_connections offserver_persistent_connections oncache_effective_user nobodycache_effective_group nobodytcp_recv_bufsize 65535 byteshalf_closed_clients off#设定不缓存的规则hierarchy_stoplist cgi-binacl QUERY urlpath_regex cgi-bincache deny QUERY#不要相信ETag 因为有gzipacl apache rep_header Server ^Apachebroken_vary_encoding allow apache#设置access log，并且令其格式和apache的格式一样，方便awstats分析emulate_httpd_log onlogformat apache %>a %ui %un [%tl] “%rm %ru HTTP/%rv” %Hs %初始化和启动squid[root@localhost]#/usr/local/server/squid/sbin/squid -z[root@localhost]#/usr/local/server/squid/sbin/squid第一条命令是先初始化squid缓存哈希子目录，只需执行一次即可。六、后记六、后记一、)想要启用squid所需的改变想要更好的利用squid的cache功能，不是把它启用了就可以的，我们需要做以下几个调整：1、启用apache的 mod_expires 模块，修改 httpd.conf，加入以下内容：#expiresdefault “modification plus 2 weeks”expiresactiveonexpiresbytype text/html “access plus 10 minutes”expiresbytypeimage/gif “modification plus 1 month”expiresbytype image/jpeg “modificationplus 1 month”expiresbytype image/png “modification plus 1month”expiresbytype text/css “access plus 1 day”expiresbytypeapplication/x-shockwave-flash “access plus 3 day”以上配置的作用是规定各种类型文件的cache规则，对那些图片/flash等静态文件总是cache起来，可根据各自的需要做适当调整。2、修改 php.ini 配置，如下：session.cache_limiter = nocache以上配置的作用是默认取消php中的cache功能，避免不正常的cache产生。3、修改应用程序例如，有一个php程序页面static.php，它存放着某些查询数据库后的结果，并且数据更新并不频繁，于是，我们就可以考虑对其cache。只需在static.php中加入类似如下代码：header(‘Cache-Control: max-age=86400,must-revalidate’);header(‘Pragma:’);header(‘Last-Modified: ‘ .gmdate(‘D, d M Y H:i:s’) . ‘ GMT’ );header(“Expires: ” .gmdate (‘D, d M YH:i:s’, time() + ’86400′ ). ‘ GMT’);以上代码的意思是，输出一个http头部信息，让squid知道本页面默认缓存时长为一天。二、)squidclient简要介绍*取得squid运行状态信息： squidclient -p 80 mgr:info*取得squid内存使用情况： squidclient -p 80 mgr:mem*取得squid已经缓存的列表： squidclient -p 80 mgr:objects. use it carefully,it may crash*取得squid的磁盘使用情况： squidclient -p 80 mgr:diskd*强制更新某个url：squidclient -p 80 -m PURGE http://www.enew.com.cn/static.php*更多的请查看：squidclient-h 或者 squidclient -p 80 mgr:原文地址：http://blog.csdn.net/rushcc2006/archive/2009/11/11/4796892.aspx
又拍网是一个照片分享社区，从2005年6月至今积累了260万用户，1.1亿张照片，目前的日访问量为200多万。5年的发展历程里经历过许多起伏，也积累了一些经验，在这篇文章会介绍一些在技术上的积累。又拍网和大多数Web2.0站点一样，构建于大量开源软件之上，包括MySQL、PHP、nginx、Python、memcached、redis、Solr、Hadoop和RabbitMQ等等。又拍网的服务器端开发语言主要是PHP和Python，其中PHP用于编写Web逻辑（通过HTTP和用户直接打交道）， 而Python则主要用于开发内部服务和后台任务。在客户端则使用了大量的Javascript， 这里要感谢一下MooTools这个JS框架，它使得我们很享受前端开发过程。 另外，我们把图片处理过程从PHP进程里独立出来变成一个服务。这个服务基于nginx，但是是作为nginx的一个模块而开放REST API。图1：开发语言由于PHP的单线程模型，我们把耗时较久的运算和I/O操作从HTTP请求周期中分离出来， 交给由Python实现的任务进程来完成，以保证请求响应速度。这些任务主要包括：邮件发送、数据索引、数据聚合和好友动态推送（稍候会有介绍）等等。通常这些任务由用户触发，并且，用户的一个行为可能会触发多种任务的执行。 比如，用户上传了一张新的照片，我们需要更新索引，也需要向他的朋友推送一条新的动态。PHP通过消息队列（我们用的是RabbitMQ）来触发任务执行。图2：PHP和Python的协作数据库一向是网站架构中最具挑战性的，瓶颈通常出现在这里。又拍网的照片数据量很大，数据库也几度出现严重的压力问题。 因此，这里我主要介绍一下又拍网在分库设计这方面的一些尝试。分库设计和很多使用MySQL的2.0站点一样，又拍网的MySQL集群经历了从最初的一个主库一个从库、到一个主库多个从库、 然后到多个主库多个从库的一个发展过程。最初是由一台主库和一台从库组成，当时从库只用作备份和容灾，当主库出现故障时，从库就手动变成主库，一般情况下，从库不作读写操作（同步除外）。随着压力的增加，我们加上了memcached，当时只用其缓存单行数据。 但是，单行数据的缓存并不能很好地解决压力问题，因为单行数据的查询通常很快。所以我们把一些实时性要求不高的Query放到从库去执行。后面又通过添加多个从库来分流查询压力，不过随着数据量的增加，主库的写压力也越来越大。在参考了一些相关产品和其它网站的做法后，我们决定进行数据库拆分。也就是将数据存放到不同的数据库服务器中，一般可以按两个纬度来拆分数据:垂直拆分:是指按功能模块拆分，比如可以将群组相关表和照片相关表存放在不同的数据库中，这种方式多个数据库之间的表结构不同。水平拆分:而水平拆分是将同一个表的数据进行分块保存到不同的数据库中，这些数据库中的表结构完全相同。拆分方式一般都会先进行垂直拆分，因为这种方式拆分方式实现起来比较简单，根据表名访问不同的数据库就可以了。但是垂直拆分方式并不能彻底解决所有压力问题，另外，也要看应用类型是否合适这种拆分方式。如果合适的话，也能很好的起到分散数据库压力的作用。比如对于豆瓣我觉得比较适合采用垂直拆分， 因为豆瓣的各核心业务/模块（书籍、电影、音乐）相对独立，数据的增加速度也比较平稳。不同的是，又拍网的核心业务对象是用户上传的照片，而照片数据的增加速度随着用户量的增加越来越快。压力基本上都在照片表上，显然垂直拆分并不能从根本上解决我们的问题，所以，我们采用水平拆分的方式。拆分规则水平拆分实现起来相对复杂，我们要先确定一个拆分规则，也就是按什么条件将数据进行切分。 一般2.0网站都以用户为中心，数据基本都跟随用户，比如用户的照片、朋友和评论等等。因此一个比较自然的选择是根据用户来切分。每个用户都对应一个数据库，访问某个用户的数据时， 我们要先确定他/她所对应的数据库，然后连接到该数据库进行实际的数据读写。那么，怎么样对应用户和数据库呢？我们有这些选择：· 按算法对应最简单的算法是按用户ID的奇偶性来对应，将奇数ID的用户对应到数据库A，而偶数ID的用户则对应到数据库B。这个方法的最大问题是，只能分成两个库。另一个算法是按用户ID所在区间对应，比如ID在0-10000之间的用户对应到数据库A， ID在10000-20000这个范围的对应到数据库B，以此类推。按算法分实现起来比较方便，也比较高效，但是不能满足后续的伸缩性要求，如果需要增加数据库节点，必需调整算法或移动很大的数据集， 比较难做到在不停止服务的前提下进行扩充数据库节点。· 按索引/映射表对应这种方法是指建立一个索引表，保存每个用户的ID和数据库ID的对应关系，每次读写用户数据时先从这个表获取对应数据库。新用户注册后，在所有可用的数据库中随机挑选一个为其建立索引。这种方法比较灵活，有很好的伸缩性。一个缺点是增加了一次数据库访问，所以性能上没有按算法对应好。比较之后，我们采用的是索引表的方式，我们愿意为其灵活性损失一些性能，更何况我们还有memcached， 因为索引数据基本不会改变的缘故，缓存命中率非常高。所以能很大程度上减少了性能损失。图4：数据访问过程索引表的方式能够比较方便地添加数据库节点，在增加节点时，只要将其添加到可用数据库列表里即可。 当然如果需要平衡各个节点的压力的话，还是需要进行数据的迁移，但是这个时候的迁移是少量的，可以逐步进行。要迁移用户A的数据，首先要将其状态置为迁移数据中，这个状态的用户不能进行写操作，并在页面上进行提示。 然后将用户A的数据全部复制到新增加的节点上后，更新映射表，然后将用户A的状态置为正常，最后将原来对应的数据库上的数据删除。这个过程通常会在临晨进行，所以，所以很少会有用户碰到迁移数据中的情况。当然，有些数据是不属于某个用户的，比如系统消息、配置等等，我们把这些数据保存在一个全局库中。问题分库会给你在应用的开发和部署上都带来很多麻烦。· 不能执行跨库的关联查询如果我们需要查询的数据分布于不同的数据库，我们没办法通过JOIN的方式查询获得。比如要获得好友的最新照片，你不能保证所有好友的数据都在同一个数据库里。一个解决办法是通过多次查询，再进行聚合的方式。我们需要尽量避免类似的需求。有些需求可以通过保存多份数据来解决，比如User-A和User-B的数据库分别是DB-1和DB-2， 当User-A评论了User-B的照片时，我们会同时在DB-1和DB-2中保存这条评论信息，我们首先在DB-2中的photo_comments表中插入一条新的记录，然后在DB-1中的user_comments表中插入一条新的记录。这两个表的结构如下图所示。这样我们可以通过查询photo_comments表得到User-B的某张照片的所有评论， 也可以通过查询user_comments表获得User-A的所有评论。另外可以考虑使用全文检索工具来解决某些需求， 我们使用Solr来提供全站标签检索和照片搜索服务。图5：评论表结构· 不能保证数据的一致/完整性跨库的数据没有外键约束，也没有事务保证。比如上面的评论照片的例子， 很可能出现成功插入photo_comments表，但是插入user_comments表时却出错了。一个办法是在两个库上都开启事务，然后先插入photo_comments，再插入user_comments， 然后提交两个事务。这个办法也不能完全保证这个操作的原子性。· 所有查询必须提供数据库线索比如要查看一张照片，仅凭一个照片ID是不够的，还必须提供上传这张照片的用户的ID（也就是数据库线索），才能找到它实际的存放位置。因此，我们必须重新设计很多URL地址，而有些老的地址我们又必须保证其仍然有效。我们把照片地址改成/photos/{username}/{photo_id}/的形式，然后对于系统升级前上传的照片ID， 我们又增加一张映射表，保存photo_id和user_id的对应关系。当访问老的照片地址时，我们通过查询这张表获得用户信息, 然后再重定向到新的地址。· 自增ID如果要在节点数据库上使用自增字段，那么我们就不能保证全局唯一。这倒不是很严重的问题，但是当节点之间的数据发生关系时，就会使得问题变得比较麻烦。我们可以再来看看上面提到的评论的例子。如果photo_comments表中的comment_id的自增字段，当我们在DB-2.photo_comments表插入新的评论时， 得到一个新的comment_id，假如值为101，而User-A的ID为1，那么我们还需要在DB-1.user_comments表中插入(1, 101 …)。 User-A是个很活跃的用户，他又评论了User-C的照片，而User-C的数据库是DB-3。 很巧的是这条新评论的ID也是101，这种情况很用可能发生。那么我们又在DB-1.user_comments表中插入一行像这样(1, 101 …)的数据。 那么我们要怎么设置user_comments表的主键呢（标识一行数据）？可以不设啊，不幸的是有的时候（框架、缓存等原因）必需设置。那么可以以user_id、 comment_id和photo_id为组合主键，但是photo_id也有可能一样（的确很巧）。看来只能再加上photo_owner_id了， 但是这个结果又让我们实在有点无法接受，太复杂的组合键在写入时会带来一定的性能影响，这样的自然键看起来也很不自然。所以，我们放弃了在节点上使用自增字段，想办法让这些ID变成全局唯一。为此增加了一个专门用来生成ID的数据库，这个库中的表结构都很简单，只有一个自增字段id。 当我们要插入新的评论时，我们先在ID库的photo_comments表里插入一条空的记录，以获得一个唯一的评论ID。 当然这些逻辑都已经封装在我们的框架里了，对于开发人员是透明的。 为什么不用其它方案呢，比如一些支持incr操作的Key-Value数据库。我们还是比较放心把数据放在MySQL里。 另外，我们会定期清理ID库的数据，以保证获取新ID的效率。实现我们称前面提到的一个数据库节点为Shard，一个Shard由两个台物理服务器组成， 我们称它们为Node-A和Node-B，Node-A和Node-B之间是配置成Master-Master相互复制的。 虽然是Master-Master的部署方式，但是同一时间我们还是只使用其中一个，原因是复制的延迟问题， 当然在Web应用里，我们可以在用户会话里放置一个A或B来保证同一用户一次会话里只访问一个数据库， 这样可以避免一些延迟问题。但是我们的Python任务是没有任何状态的，不能保证和PHP应用读写相同的数据库。那么为什么不配置成Master-Slave呢？我们觉得只用一台太浪费了，所以我们在每台服务器上都创建多个逻辑数据库。 如下图所示，在Node-A和Node-B上我们都建立了shard_001和shard_002两个逻辑数据库， Node-A上的shard_001和Node-B上的shard_001组成一个Shard，而同一时间只有一个逻辑数据库处于Active状态。 这个时候如果需要访问Shard-001的数据时，我们连接的是Node-A上的shard_001， 而访问Shard-002的数据则是连接Node-B上的shard_002。以这种交叉的方式将压力分散到每台物理服务器上。 以Master-Master方式部署的另一个好处是，我们可以不停止服务的情况下进行表结构升级， 升级前先停止复制，升级Inactive的库，然后升级应用，再将已经升级好的数据库切换成Active状态， 原来的Active数据库切换成Inactive状态，然后升级它的表结构，最后恢复复制。 当然这个步骤不一定适合所有升级过程，如果表结构的更改会导致数据复制失败，那么还是需要停止服务再升级的。图6：数据库布局前面提到过添加服务器时，为了保证负载的平衡，我们需要迁移一部分数据到新的服务器上。为了避免短期内迁移的必要，我们在实际部署的时候，每台机器上部署了8个逻辑数据库， 添加服务器后，我们只要将这些逻辑数据库迁移到新服务器就可以了。最好是每次添加一倍的服务器， 然后将每台的1/2逻辑数据迁移到一台新服务器上，这样能很好的平衡负载。当然，最后到了每台上只有一个逻辑库时，迁移就无法避免了，不过那应该是比较久远的事情了。我们把分库逻辑都封装在我们的PHP框架里了，开发人员基本上不需要被这些繁琐的事情困扰。下面是使用我们的框架进行照片数据的读写的一些例子:array(‘type’ => ‘long’, ‘primary’ => true, ‘global_auto_increment’ => true),‘user_id’ => array(‘type’ => ‘long’),‘title’ => array(‘type’ => ’string’),‘posted_date’ => array(‘type’ => ‘date’),));$photo = $Photos->new_object(array(‘user_id’ => 1, ‘title’ => ‘Workforme’));$photo->insert();// 加载ID为10001的照片，注意第一个参数为用户ID$photo = $Photos->load(1, 10001);// 更改照片属性$photo->title = ‘Database Sharding’;$photo->update();// 删除照片$photo->delete();// 获取ID为1的用户在2010-06-01之后上传的照片$photos = $Photos->fetch(array(‘user_id’ => 1, ‘posted_date__gt’ => ‘2010-06-01′));?>首先要定义一个ShardedDBTable对象，所有的API都是通过这个对象开放。第一个参数是对象类型名称， 如果这个名称已经存在，那么将返回之前定义的对象。你也可以通过get_table(‘Photos’)这个函数来获取之前定义的Table对象。 第二个参数是对应的数据库表名，而第三个参数是数据库线索字段，你会发现在后面的所有API中全部需要指定这个字段的值。 第四个参数是字段定义，其中photo_id字段的global_auto_increment属性被置为true，这就是前面所说的全局自增ID， 只要指定了这个属性，框架会处理好ID的事情。如果我们要访问全局库中的数据，我们需要定义一个DBTable对象。array(‘type’ => ‘long’, ‘primary’ => true, ‘auto_increment’ => true),‘username’ => array(‘type’ => ’string’),));?>DBTable是ShardedDBTable的父类，除了定义时参数有些不同（DBTable不需要指定数据库线索字段），它们提供一样的API。缓存我们的框架提供了缓存功能，对开发人员是透明的。load(1, 10001);?>比如上面的方法调用，框架先尝试以Photos-1-10001为Key在缓存中查找，未找到的话再执行数据库查询并放入缓存。当更改照片属性或删除照片时，框架负责从缓存中删除该照片。这种单个对象的缓存实现起来比较简单。稍微麻烦的是像下面这样的列表查询结果的缓存。fetch(array(‘user_id’ => 1, ‘posted_date__gt’ => ‘2010-06-01′));?>我们把这个查询分成两步，第一步先查出符合条件的照片ID，然后再根据照片ID分别查找具体的照片信息。 这么做可以更好的利用缓存。第一个查询的缓存Key为Photos-list-{shard_key}-{md5(查询条件SQL语句)}， Value是照片ID列表（逗号间隔）。其中shard_key为user_id的值1。目前来看，列表缓存也不麻烦。 但是如果用户修改了某张照片的上传时间呢，这个时候缓存中的数据就不一定符合条件了。所以，我们需要一个机制来保证我们不会从缓存中得到过期的列表数据。我们为每张表设置了一个revision，当该表的数据发生变化时（调用insert/update/delete方法）， 我们就更新它的revision，所以我们把列表的缓存Key改为Photos-list-{shard_key}-{md5(查询条件SQL语句)}-{revision}， 这样我们就不会再得到过期列表了。revision信息也是存放在缓存里的，Key为Photos-revision。这样做看起来不错，但是好像列表缓存的利用率不会太高。因为我们是以整个数据类型的revision为缓存Key的后缀，显然这个revision更新的非常频繁，任何一个用户修改或上传了照片都会导致它的更新，哪怕那个用户根本不在我们要查询的Shard里。要隔离用户的动作对其他用户的影响，我们可以通过缩小revision的作用范围来达到这个目的。 所以revision的缓存Key变成Photos-{shard_key}-revision，这样的话当ID为1的用户修改了他的照片信息时， 只会更新Photos-1-revision这个Key所对应的revision。因为全局库没有shard_key，所以修改了全局库中的表的一行数据，还是会导致整个表的缓存失效。 但是大部分情况下，数据都是有区域范围的，比如我们的帮助论坛的主题帖子， 帖子属于主题。修改了其中一个主题的一个帖子，没必要使所有主题的帖子缓存都失效。 所以我们在DBTable上增加了一个叫isolate_key的属性。array(‘type’ => ‘long’, ‘primary’ => true),‘post_id’ => array(‘type’ => ‘long’, ‘primary’ => true, ‘auto_increment’ => true),‘author_id’ => array(‘type’ => ‘long’),‘content’ => array(‘type’ => ’string’),‘posted_at’ => array(‘type’ => ‘datetime’),‘modified_at’ => array(‘type’ => ‘datetime’),‘modified_by’ => array(‘type’ => ‘long’),), ‘topic_id’);?>注意构造函数的最后一个参数topic_id就是指以字段topic_id作为isolate_key，它的作用和shard_key一样用于隔离revision的作用范围。ShardedDBTable继承自DBTable，所以也可以指定isolate_key。 ShardedDBTable指定了isolate_key的话，能够更大幅度缩小revision的作用范围。 比如相册和照片的关联表yp_album_photos，当用户往他的其中一个相册里添加了新的照片时， 会导致其它相册的照片列表缓存也失效。如果我指定这张表的isolate_key为album_id的话， 我们就把这种影响限制在了本相册内。我们的缓存分为两级，第一级只是一个PHP数组，有效范围是Request。而第二级是memcached。这么做的原因是，很多数据在一个Request周期内需要加载多次，这样可以减少memcached的网络请求。另外我们的框架也会尽可能的发送memcached的gets命令来获取数据， 从而减少网络请求。总结这个架构使得我们在很长一段时间内都不必再为数据库压力所困扰。我们的设计很多地方参考了netlog和flickr的实现，因此非常感谢他们将一些实现细节发布出来。关于作者：周兆兆（Zola,不是你熟知的那个），又拍网架构师。6年IT从业经验，不太专注于某项技术，对很多技术都感兴趣。————————————————————作为国内最大的图片服务提供商之一，Yupoo! 的 Alexa 排名大约在 5300 左右。同时收集到的一些数据如下：带宽：4000M/S (参考)服务器数量：60 台左右Web服务器：Lighttpd, Apache, nginx应用服务器：Tomcat其他：Python, Java, MogileFS 、ImageMagick 等首先看一下网站的架构图：该架构图给出了很好的概览(点击可以查看在 Yupoo！ 上的大图和原图，请注意该图版权信息)。关于 Squid 与 TomcatSquid 与 Tomcat 似乎在 Web 2.0 站点的架构中较少看到。我首先是对 Squid 有点疑问，对此阿华的解释是”目前暂时还没找到效率比 Squid 高的缓存系统，原来命中率的确很差，后来在 Squid 前又装了层 Lighttpd, 基于 url 做 hash, 同一个图片始终会到同一台 squid 去，所以命中率彻底提高了”对于应用服务器层的 Tomcat，现在 Yupoo! 技术人员也在逐渐用其他轻量级的东西替代，而 YPWS/YPFS 现在已经用 Python 进行开发了。名次解释：YPWS–Yupoo Web Server YPWS 是用 Python开发的一个小型 Web 服务器，提供基本的 Web 服务外，可以增加针对用户、图片、外链网站显示的逻辑判断，可以安装于任何有空闲资源的服务器中，遇到性能瓶颈时方便横向扩展。YPFS–Yupoo File System 与 YPWS 类似，YPFS 也是基于这个 Web 服务器上开发的图片上传服务器。【Updated: 有网友留言质疑 Python 的效率，Yupoo 老大刘平阳在 del.icio.us 上写到 “YPWS用Python自己写的，每台机器每秒可以处理294个请求, 现在压力几乎都在10％以下”】图片处理层接下来的 Image Process Server 负责处理用户上传的图片。使用的软件包也是 ImageMagick，在上次存储升级的同时，对于锐化的比率也调整过了(我个人感觉，效果的确好了很多)。”Magickd“ 是图像处理的一个远程接口服务，可以安装在任何有空闲 CPU资源的机器上，类似 Memcached的服务方式。我们知道 Flickr 的缩略图功能原来是用 ImageMagick 软件包的，后来被雅虎收购后出于版权原因而不用了（?）；EXIF 与 IPTC Flicke 是用 Perl 抽取的，我是非常建议 Yupoo! 针对 EXIF 做些文章，这也是潜在产生受益的一个重点。图片存储层原来 Yupoo! 的存储采用了磁盘阵列柜，基于 NFS 方式的，随着数据量的增大，”Yupoo! 开发部从07年6月份就开始着手研究一套大容量的、能满足 Yupoo! 今后发展需要的、安全可靠的存储系统“，看来 Yupoo! 系统比较有信心，也是满怀期待的，毕竟这要支撑以 TB 计算的海量图片的存储和管理。我们知道，一张图片除了原图外，还有不同尺寸的，这些图片统一存储在 MogileFS 中。对于其他部分，常见的 Web 2.0 网站必须软件都能看到，如 MySQL、Memcached 、Lighttpd 等。Yupoo! 一方面采用不少相对比较成熟的开源软件，一方面也在自行开发定制适合自己的架构组件。这也是一个 Web 2.0 公司所必需要走的一个途径。来源：http://www.bopor.com/?p=652
LAMP（Linux-Apache-MySQL-PHP）网站架构是目前国际流行的Web框架， 该框架包括：Linux操作系统，Apache网络服务器，MySQL数据库，Perl、PHP或者Python编程语言，所有组成产品均是开源软件，是 国际上成熟的架构框架，很多流行的商业应用都是采取这个架构，和Java/J2EE架构相比，LAMP具有Web资源丰富、轻量、快速开发等特点，微软 的.NET架构相比，LAMP具有通用、跨平台、高性能、低价格的优势，因此LAMP无论是性能、质量还是价格都是企业搭建网站的首选平台。对于大流量、大并发量的网站系统架构来说，除了硬件上使用高性能的服务器、负载均衡、CDN等之外，在软件架构上需要重点关注下面几个环节：使用高性能 的操作系统（OS）、高性能的网页服务器（Web Server）、高性能的数据库（Databse）、高效率的编程语言等。下面我将从这几点对其一一讨论。操作系统Linux操作系统有很多个不同的发行版，如Red Hat Enterprise Linux、SUSE Linux Enterprice、Debian、Ubuntu、CentOS等，每一个发行版都有自己的特色，比如RHEL的稳定，Ubuntu的易用，基于稳定性 和性能的考虑，操作系统选择CentOS（Community ENTerprise Operating System）是一个理想的方案。CentOS（Community ENTerprise Operating System）是Linux发行版之一，是RHEL/Red Hat Enterprise Linux的精简免费版，和RHEL为同样的源代码，不过，RHEL和SUSE LE等企业版，提供的升级服务均是收费升级，无法免费在线升级，因此要求免费的高度稳定性的服务器可以用CentOS替代Red Hat Enterprise Linux使用。LAMP网站架构图Web服务器、缓存和PHP加速Apache是LAMP架构最核心的Web Server，开源、稳定、模块丰富是Apache的优势。但Apache的缺点是有些臃肿，内存和CPU开销大，性能上有损耗，不如一些轻量级的Web 服务器（例如nginx）高效，轻量级的Web服务器对于静态文件的响应能力来说远高于Apache服务器。Apache做为Web Server是负载PHP的最佳选择，如果流量很大的话，可以采用nginx来负载非PHP的Web请求。nginx是一个高性能的HTTP和反向代理服 务器，Nginx以它的稳定性、丰富的功能集、示例配置文件和低系统资源的消耗而闻名。Nginx不支持Windows，只能在包括Linux等环境下安 装，也不支持PHP和CGI等，但支持负载均衡和容错，可和Apache配合使用，是轻量级的HTTP服务器的首选。Web服务器的缓 存也有多种方案，Apache提供了自己的缓存模块，也可以使用外加的Squid模块进行缓存，这两种方式均可以有效的提高Apache的访问响应能力。 Squid Cache是一个Web缓存服务器，支持高效的缓存，可以作为网页服务器的前置cache服务器缓存相关请求来提高Web服务器的速度，把Squid放在 Apache的前端来缓存Web服务器生成的动态内容，而Web应用程序只需要适当地设置页面实效时间即可。如访问量巨大则可考虑使用memcache作 为分布式缓存。PHP的加速使用eAccelerator加速器，eAccelerator是一个自由开放源码PHP加速器，优化和动 态内容缓存，提高了性能PHP脚本的缓存性能，使得PHP脚本在编译的状态下，对服务器的开销几乎完全消除。它还有对脚本起优化作用，以加快其执行效率。 使PHP程序代码执效率能提高1-10倍。具体的解决方案有以下几种：1、squid + Apache + PHP + eAccelerator使用Apache负载PHP，使用squid进行缓存，html或图片的请求可以直接由squid返回给用户。很多大型网站都采用这种架构。2、nginx/Apache + PHP（fastcgi） + eAccelerator使用nginx或Apache负载PHP，PHP使用fastcgi方式运行，效率较高。3、nginx + Apache + PHP + eAccelerator此方案综合了nginx和Apache的优点，使用Apache负载PHP，nginx负责解析其他Web请求，使用nginx的rewrite模块，Apache端口不对外开放。数据库开源的数据库中，MySQL在性能、稳定性和功能上是首选，可以达到百万级别的数据存储，网站初期可以将MySQL和Web服务器放在一起，但是当访问 量达到一定规模后，应该将MySQL数据库从Web Server上独立出来，在单独的服务器上运行，同时保持Web Server和MySQL服务器的稳定连接。当数据库访问量达到更大的级别，可以考虑使用MySQL Cluster等数据库集群或者库表散列等解决方案。总的来说，LAMP架构的网站性能会远远优于Windows IIS + ASP + Access（例如月光博客）这样的网站，可以负载的访问量也非常大，国内的大量个人网站如果想要支撑大访问量，采用LAMP架构是一个不错的方案。综上所述，基于LAMP架构设计具有成本低廉、部署灵活、快速开发、安全稳定等特点，是Web网络应用和环境的优秀组合。原创文章如转载，请注明：转载自月光博客 [ http://www.williamlong.info/ ]本文链接地址：http://www.williamlong.info/archives/1908.html
在网站架构设计中，大家一定对 LAMP (Linux Apache Mysql Php) 不陌生。LAMP确实是一个非常优秀的架构，秉承着自由，开放，高效，易用的设计理念。但是，本文不打算探讨LAMP，网上有很多介绍LAMP的资料。这里，想给大家介绍另一个在LAMP上衍生出来的，以提升性能为主要目的的开源网站架构。1、选择高性能 OS首先，不难理解，任何一个server最底层的支撑还是OS，而OS的选择，主要包括 Unix, Windows server, Linux, BSD等等。其中，开源的OS，有Linux, BSD及部分unix。从目前使用情况来看，linux还是网站首选OS之一。但是，Linux由于其自由的特点，也给选择产生了一些不便 – 发行版太多。现有的主流版本包括 red hat(RHEL), ubuntu, 红旗, opensuse, debian等。其中，每一个发行版都有自己的特色，比如RHEL的稳定，ubuntu的易用，红旗的中文支持很棒等。但要以性能为主，又兼顾稳定，易用性，以上都不是最佳选择。这里推荐一个发行版，它是一个极限性能，加高度可定制，优化的 Linux – gentoo。gentoo的性能优化是从kernel源码编译就开始入手了，通过选择不同的源码包，可以适应于不同的应用场景。(不同内核介绍: http://imkenwu.javaeye.com/blog/168906 )举个经典的例子：国内，douban.com 在定制优化过的 gentoo 上跑的web服务器最高一天支撑了 2500 万pv。http://www.dbanotes.net/arch/douban_web_server.html这种流量，哪怕是提供纯静态的内容，也是很恐怖的。而支持这种大流量的，除了server本身，最关键的就是高度精简的OS了。所以，综上所述，高性能网站推荐使用可优化，定制的 gentoo 作为载体。2、 选择高性能 web serverApache是 LAMP 架构最核心的 web server， 开源，模块丰富，功能强大，稳定是它的绝对优势。在美国前100个网站中，有49%的使用apache。可见其影响力。但是，有利有弊，apache的致命缺陷，就是多于臃肿，强大的功能，一定会带来性能上的损耗。面对这种情形，在市场上，有一支异军突起，那就是更轻量级的 web server – lighty(lighttpd)。官方为它定义的口号是 fly light。它具有非常低的内存开销，cpu占用率低，效能好，以及丰富的模块支持等特点。这让他在短时间内占据了14%以上的市场份额。并且有越来越多的人开始选择使用lighty作为前端 web server。到这里为之，其实高性能 web server 非 lighty 莫属。但更棒的是，依靠 gentoo 的高度定制化，我们还可以进一步提升 lighty 的性能潜力-那就是定制 lighty。3、选择高性能 database数据库是任何网站走动态化内容展现及业务数据存储的保障。市面上的开源数据库主要有 mysql , postgresql , berkeley db, sqlite 等。其中，对比一下，mysql ： 多线程，多处理器，高性能，5.0以上支持事务，丰富数据类型和sql语法，跨平台。postgresql ： 面向对象，集成web，支持事务，使用进程，速度略慢于mysql.berkeley db ： 嵌入式，数据操作通过接口完成，跨语言。sqlite ： 与php集成，支持ACID特性，支持大并发量，库锁。从上面的对比中，不难看出，mysql 应该是性能，稳定性与功能性的综合之选。4、选择高性能 script language能与 lighty 结合的脚本语言，主要有 ruby, php, python, perl。方式主要是通过 fast-cgi 来访问。只从性能角度对比几种语言:（ http://www.timestretch.com/FractalBenchmark.html ）不难看出，python 是此次测试中，性能最好的脚本语言。动态处理方面有绝对优势。对比 php ， 前者，可以更快的渲染输出内容，并由经lighty， 高速flush缓存到浏览器。值得一提的是， douban.com 也是使用 python 作为应用服务器。总结一下，什么是 LLMP？LLMP 是 Linux Lighty Mysql Python 的组合，作为一种高性能的网站架构设计存在。什么是高性能的LLMP？LLMP并不意味着高性能，只是比其他架构，更有性能的提升潜力。高性能的LLMP，需要从系统，程序，硬件各个层面上协同进行的。FROM：http://www.javaeye.com/topic/174335
从集中到分布，解读网络视频IT架构变迁
作者：朱智力 来源：IT1682006年以视频网站为代表的网络视频行业迅速崛起，IPTV、视频分享网站、视频搜索网站、提供视频服务的互动社区、交友、播客等等新兴媒体发展迅猛。网络视频行业现已成为众多资本机构关注与投资的焦点。但是在网络视频行业发展前景一片大好的同时，一些运营问题也随之显现，步入2007年，资本机构对网络视频行业不再盲目狂热，对商业模式的创新和成本控制提出了更高的要求。今天，纯文字的表现形式已经不能满足用户对更丰富多彩的网络内容的需求，我们需要更丰富的数字出版物、图片、声音以及视频来表达和交流；同时，单向传播也不能满足用户对互动式网络体验的需求，我们需要更丰富灵活的交流与互动体验；IT架构必须能够承载和传播这些爆发式增长的非结构化的数据。同时，用户访问量的增长却从没有停止过，相对于传统应用，网络视频行业应用有着非常庞大的用户数量上涨空间，IT设施面临着强大的成本控制的压力。那么，我们究竟该如何应对网络视频行业的商业模式创新和成本控制的挑战呢？视频网站的典型应用架构我们将以网络视频行业中最具代表性的视频分享网站为例，通过某视频网站的存储环境改造案例来看看网络视频需要什么样的IT结构、分析什么样的存储系统能够更好的来支撑商业模式的创新，并合理的控制成本。视频网站的典型应用架构首先，我们来看看该视频网站的IT结构：如上图所示，分别由流媒体服务器、Web服务器、在线录制服务器、视频转换服务器、数据库服务器、管理服务器、图片服务器和其他服务器等一系列不同数量的服务器组成。这一架构在目前的视频网站中带有一定的典型性。但是由这几个部分组合起来的IT结构，如何才能支撑视频分享门户的竞争优势呢？满足交互性体验和服务压力以下我们将从应用需求和技术特点两方面来分析，该架构如何满足该视频网站的运营需求：1. 交互性体验方面：首先，为满足用户对交互体验的需要并保持自身的原创优势，需要为用户提供视频上传和在线录制视频的功能；其次，需要把不同格式的视频，转换成该网站统一的格式；最后，需要在上传后尽快发布以供播出。2. 服务压力方面：首先，不但需要应对已有的大数量的用户访问，而且将迎接持续的访问量增长；其次，当用户访问量增加时，需要保持良好的反映速度和响应时间；最后，必须面对清晰度日渐提高后，码流增大所带来的服务压力。在各个技术层面上，我们如何更好的满足上述诸多需要呢？1. 在编解码技术层面：编解码技术不断推陈出新，我们可以看到解码效果更好，编码压缩率更高的编解码方式等诸多方面均有良好进展。尤其是由中科院计算所牵头制定的AVS标准，是具有我国自主知识产权的新一代编解码标准，将促进我国网络视频行业的健康发展。2. 在媒体的传输层面：CDN技术已经比较成熟，P2P技术的发展也非常的迅速，虽然存在缺乏统一标准等问题，但无法掩盖P2P技术的锋芒。目前，已有不少的视频平台运营商采用了P2P技术。此外，CDN＋P2P的复合技术也有了比较好的发展。3. 在媒体的服务提供层面：服务器集群技术已经相当成熟：双机到多机的数据库集群、由DNS轮询或相关技术实现的Web服务器集群、由相关查询指向技术实现的流媒体服务器集群等都可以比较方便的实现。成熟的服务器集群技术可以实现按需增加相应应用服务器来应对业务需求，足以为网络视频行业提供良好的支撑。4. 在媒体资源存储方面：需要有大容量、高带宽、可共享的存储技术来支撑，而传统的存储结构和存储技术，却不能很好的满足视频网站的存储需求。那么视频网站在存储方面都有什么具体的要求，存储环境怎样才能够满足这些要求呢？下面我们通过一个实例来详细分析视频网站对存储环境的需求：集中式存储把鸡蛋放到一个篮子里传统存储的体系结构无非有两种：集中式和分布式。网络视频存储方案面临着集中式存储和分布式存储两种选择，两种结构各有优缺点，选择起来其实是比较困难的。本案中的视频网站的存储结构就经历了“集中–分布–分布式的集中存储”的循回式的变迁：该网站建立之初，采用了集中式的存储结构。某视频网站原有集中式存储系统很多网络视频的存储采用的大多类似于上图的、集中式的存储结构来存放所有媒体数据，通常为NAS架构。简单地说，就是一台大容量的文件服务器，而高端的NAS结构是由一个NAS头后面接SAS、SCSI或光纤盘阵。集中式存储的优点是比较明显的：1. 集中存储可实现服务的负载均衡，由于流媒体服务间的数据都是共享且统一的，当发生热点繁忙时，所有流媒体服务器都可为其提供服务，分减压力，而不像分布式的存储会出现热点繁忙，没有热点内容的存储出现空闲这种不均匀情况。2. 集中存储提高了存储资源的利用率。3. 集中的高Raid 级别保护且成本较低，分布式存储都实现Raid保护成本高昂。4. 集中的备份（快照）恢复，能方便的实现远程容灾。5. 集中存储方案管理复杂度相对较低，以管理Mount点为例：需管理Mount点的数量为16（M+N+F+W）个，即上图中的16根蓝线。6. 集中存储同时也是对流媒体服务器视频内容的集中管理。
从集中到分布，解读网络视频IT架构变迁(下)
作者：朱智力 来源：IT168从集中到分布，化解存储瓶颈接上篇：从集中到分布，解读网络视频IT架构变迁(下)。集中式存储已经拥有了诸多优势，那么为何这家视频网站最终却选择了其他的存储架构呢？该视频网站究竟在运营过程中遭遇到了什么样的阻碍呢？经过我们对整个网站存储结构的分析，原来，NAS头成为整个存储环境的瓶颈……从以下两幅图中，我们可以看到传统的集中存储方案中，存在如下问题： I/O瓶颈 容量扩展性差 性能不可扩展 专业高端NAS成本高昂 单点故障 NAS成为系统瓶颈传统集中式存储的瓶颈随着数据量的增加，存储压力也变得越来越集中，NAS已不足以支撑现有的应用，无法更好的应对未来的挑战。既而，该网站从集中式的存储方式转向了采用分布式的存储方式。分布式存储系统架构图中，每台服务器上都提供文件共享服务，由应用层来实现媒体资源数据在各个服务器集群之间的迁移，从而比较好的解决了集中存储的IO瓶颈问题，但是问题也随之而来。分布式的存储没有负载均衡，例如：发生热点的时候、部分流媒体服务器忙或部分闲置分布式存储利用相对较低率，重复数据大量存在，且份数多无法实现集中的高Raid 级别保护快照、备份、恢复、远程容灾比集中存储实现成本高需要在应用层对存储层过多关注。管理复杂度程几何级增长，整体系统维护工作越来越复杂、繁重。以管理Mount点为例：同样的服务器数，需管理Mount 点的数量为48 [M*(N+F)+W*N]个，即上图中的48根红线，远大于集中存储结构。这仅仅是Mount点一项，还不包括各个点存储数据的维护，在实际应用中相关的工作量是相当惊人，管理员疲于奔命。集中VS.分布？还是分布式的集中？既然传统的集中和分布都存在不同的问题，怎么样去解决？在给出答案之前，我们重新归纳前面分析的视频网站对存储的需求：1. 各种服务器集群之间有视频传递的需求，需要上传服务器、流媒体服务器、在线录制服务器和转换服务器之间的视频文件是互相可见的，翻译成存储的语言则需要文件级共享的存储。2. 各种应用服务器可能使用着不同的操作系统平台，都需要无差异的访问到存储空间，而翻译成存储的语言则需要跨平台共享的存储。3. 多台流媒体服务器之间的存储容量需要共享，从而提高存储空间的利用率。如采用传统SAN上面划分独立的存储空间，给每台服务器的类似做法显然是不可接受的，并且需要视频内容合理的分布在各个存储设备上，翻译成存储的语言则需要存储容量的负载均衡。4. 单台存储设备的存储速度始终是有限的，需要多个存储设备的聚合才能满足视频内容访问量的爆炸式的增长，翻译成存储的语言则需要多台存储设备间的存储速度的聚合，从而实现存储速度的负载均衡。5. 新增视频内容的不断添加会导致存储容量的不断扩大，在添加设备扩展容量的时，能够不影响原有系统，且平滑扩展，能够实现在线的扩展业务系统不停机，翻译成存储的语言则需要容量线性可扩展，能够实现在线扩容。6. 随着用户访问量增长和视频清晰度提高带来的带宽增长等诸多增长因素的影响，对存储带宽的增长需求，要求存储系统实现带宽随容量呈线性增长。7. 合理的成本控制是一个恒久的话题，需要存储系统的总体拥有成本随容量的扩展而合理的扩展，不能出现突变式的增长。8. 稳定性自然不用说，需要存储系统采用冗余结构以提高系统的稳定性。为了满足上述需求，当我们面对“集中VS.分布”这个艰难抉择的时候，技术的不断进步，涌现出：分布式的集中存储结构——集群存储技术，其核心技术是集群文件系统。集群存储系统满足视频行业服务需求目前，广为流行的集群文件系统的典型代表主要有： Google 的GFS （Google File System） 国内中科院研发的BWFS（Blue Whale File System） Panasas 的PanFS （PanFS File System） IBM 的 GPFS （General Parallel File System) CFS 的 Lustre （Lustre File System） 这五种集群文件系统各有特点和优势，一般而言，分布式集中存储相对于传统存储系统来说拥有如下优点：采用统一的全局命名空间，支持文件级共享，且采用分布式存储结构，能实现高聚合I/O带宽，并且跨Linux平台和Windows平台的文件共享，还能够线性扩展I/O带宽，拥有良好的系统负载平稳性，并能够动态扩展存储容量，实现成本可控。基于分布式结构的集中存储如上图所示：该架构能较好的满足网络视频对存储系统的需求。1. 文件共享、统一的全局命名空间——上传服务器和流媒体服务器之间的文件可见性；多台流媒体服务器间的存储容量共享2. 多台存储设备间的存储速度的聚合——流媒体服务器可用的存储速度负载均衡3. 跨平台共享（Windows/Linux）——支持不同平台的流媒体服务器、上传和其他服务器4. 容量线性可扩展——使流媒体服务能应对不断扩大的存储容量需求5. 带宽随着容量线性增长——使流媒体服务能应对不断扩大的用户访问量； 可以按需扩大流媒体服务器的数量而不用担心存储6. 成本需随着容量的扩展而扩展——良好的成本控制7. 全冗余结构——稳定的强壮的存储系统该视频网站最终选择基于BWFS集群文件系统的BWStor蓝鲸集群存储系统。BWFS是由我国中科院计算所工程中心自主研发的文件系统，并经由中科院中科储天公司产品化。中科院中科储天蓝鲸集群存储系统（BWStor）即采用BWFS文件系统为核心技术，是中国自主知识产权存储产品的代表之一。
这个架构基于squid、nginx和lvs等技术，从架构上对bbs进行全面优化和保护，有如下特点：1、高性能：所有的点击基本上全部由前端缓存负责，提供最快速的处理。2、高保障度：不需考虑应用程序稳定与否、程序语言是何种、数据库是何种，都能从架构上保证稳定。3、高可用性：对应用程序的修改达到最简化：在程序的某些地方加入清缓存的语句即可，当然还需要做页面静态化的工作和统计工作。这个架构的特点和一些流程的说明：1、主域名和图片域名分离域名分离可以使流量分离，缓存策略分离等等，好处诸多。bbs初期一定要做好规划，将图片用另外的域名独立服务，即使没有足够机器，域名也要先分开。另 外，图片服务器可以使用有别于主域名的另一个域名，一个好处是可以减少读取cookie对图片服务器的压力，另一个是提高安全性，避免cookie泄露。首先看图，这个图比较大：2、使用LVS作为前端、二级代理和数据库的访问入口使用LVS作为入口，比其他任何一种方式都来得更优质。首先LVS的负载能力很强，因为它工作在网络协议的第4层，使用虚拟ip技术，所以它本身并不担负 任何流量的处理，仅仅是一个封包转发的功能；第二，LVS的配置相对简单而且稳定，一般去调整的几率比较低，也减少了因人为等因素而出现故障；第 三，LVS可以处理任何端口的负载均衡，所以它基本可以做所有服务的负载均衡和容错。在这个架构中，除了处理http的80端口之外，LVS也处理了数据 库mysql的3306端口，在数据库这个应用中是采用的双机热备策略。3、使用nginx+squid作为最前端的缓存组合在这个架构中，是最能体现app_nginx_squid_nginx架构的优势的。在这个架构中的bbs运行在缓存上，用户每发布一张帖子，都需要使用 purge指令清除该帖子的缓存，如果是squid在最前端，那么每次发布一张帖子，都需要在所有的squid中调用purge指令，这样在机器比较多的 时候，purge将成为一个巨大的压力。所以在这里将nginx放在最前端并使用手工url_hash的方式分流，将经常需要purge的帖子页面和列表页面按一个url对应一台squid的策 略，分布到各台squid上，并提供了一台或一组backup的squid，个别squid出现异常时将自动使用backup的机器继续提供一段时间的服 务直到其正常。在这样的架构下，purge就不再是关键问题，因为一个url只会对应到一台机器上，所以purge的时候，后端app_server找到 对应的机器就可以了。可以看到在前端中还有一台nginx(purge)的机器，这台机器是专用于purge的，只要发送purge指令和需要清除的url到这台机器，就可以 找到相应的服务器并清除缓存了。另外，purge时还需要清理backup机器上的缓存，所以无论前端机器增加到多少，purge指令只会在2台机器上执 行，如果backup机器使用到2-3台，purge指令就会在3-4台机器上执行，仍然在可接受范围之内。nginx作为前端，另有的好处：1/使用nginx的日志统计点击量非常方便2/nginx也可作为缓存，一般可以直接负责favicon.ico和logo等固定的小图片4、基于nginx的中层代理nginx中层代理的优势，在：nginx和squid配合搭建的web服务器前端系统这篇文章中有解释。在这个架构中，假如后端的app_server上把帖子页和列表页直接生成了静态页面，那么使用中层代理再做一次url_hash，将可以解决后端 app_server的硬盘容量的压力，但是如果使用到url_hash的话，那做容错就相对麻烦了。所以建议不要采用生成静态页的方式，后端的压力一般 不会非常的大，所以没有必要生成静态页。假如前端squid的命中率实在太低下，造成大量穿透，可以考虑使用二级代理暂顶。5、基于LVS的数据库双机热备在这个架构中，因为大量的并发和访问量都由前端的缓存处理掉了，所以后端的mysql主要压力来自于数据的写入，所以压力并不是非常大，并且负载比较稳 定，一般不会随着访问量上升而提高过快，估计目前一台64位的机器，加满内存并使用高速的硬盘，前端负载数亿访问量时数据库都不会出现性能问题。在数据库 这方面应主要考虑故障恢复，因为数据库崩溃的话，按照一般使用备份恢复的做法，耗时很长而且难免丢失数据，是很棘手的问题。使用双机热备的方案，出现故障 时首先可由一台时刻同步着的备用数据库即刻充当主数据库，然后卸下的数据库可以有充分的时间对其进行维修，所以是个很安全有效的办法。当然，数据库的优化还是要细心做的，参考：mysql性能的检查和调优方法细心地调一遍，性能会好很多。6、图片服务器图片服务器我在这个架构中没有特别详细的介绍，在大型的bbs系统下，图片常常会出现容灾现象——图片数量严重超过了单台前端服务器容纳能力，导致前端服务器命中率低下。处理容灾问题也是非常棘手的，往后会有更详细的介绍。7、简单的点击量统计办法1/使用js的script标签访问另一（台）组服务器的空文件，然后定期向数据库更新2/在前端的nginx上直接开启日志功能，按需要统计点击量的链接规则进行记录，然后定期更新数据库FROM:http://sudone.com/archie/archi_bbs.html
Sql Server自身就提供了完善的备份机制，我们只要灵活运用，就能达到令人满意的效果。先了解一下：为何要做备份？数据备份是容灾的基础，是指为防止系统出现操作失误或系统故障导致数据丢失，而将全部或部分数据集合从应用主机的硬盘或阵列复制到其它的存储介质的过程。传统的数据备份主要是采用内置或外置的磁带机进行冷备份。但是这种方式只能防止操作失误等人为故障，而且其恢复时间也很长。随着技术的不断发展，数据的海量增加，不少的企业开始采用网络备份。网络备份一般通过专业的数据存储管理软件结合相应的硬件和存储设备来实现。方法一：SqlServer自动作业备份1、打开SQL Server Management Studio2、启动SQL Server代理3、点击作业->新建作业4、”常规”中输入作业的名称5、新建步骤，类型选T-SQL，在下面的命令中输入下面语句DECLARE @strPath NVARCHAR(200)set @strPath = convert(NVARCHAR(19),getdate(),120)set @strPath = REPLACE(@strPath, ‘:’ , ‘.’)set @strPath = ‘D:\bak\’ + ‘databasename’+@strPath + ‘.bak’BACKUP DATABASE [databasename] TO DISK = @strPath WITH NOINIT , NOUNLOAD , NOSKIP , STATS = 10, NOFORMAT（D:\bak\改为自己的备份路径，databasename修改为想备份的数据库的名称）6、添加计划，设置频率，时间等。确定，完成。方法二：SqlServer自动作业备份SQL2005的维护计划里面自带了备份数据库任务，但不会自动按日期命名，不方便，下面的方法是以存储过程来解决的。要用SQL2005的维护计划功能首先要确认SQL Server Agent服务是在启动状态，然后打开SQL Server Management Studio，展开对象资源管理器里的“管理”，右击维护计划选择新建维护计划，为维护计划命名，如：TestDB_Backup，选择默认的子计划 “Subplan_1 ”并点击上方的子计划设置按钮来设置子计划执行方式和时间：出现设置界面，设置每天3点执行一次，如图：确定后保存一下，然后新建一个查询页，开始创建存储过程，代码如下：USE [TestDB]GOSET ANSI_NULLS ONGOSET QUOTED_IDENTIFIER ONGOCREATE PROCEDURE [BackupDatabase](@FolderPath varchar(500))asDECLARE @FullPath varchar(1000)set @FullPath = @FolderPath+ ‘TestDB_’+convert(VARCHAR(4),year(getdate()))+right( ‘0′+convert(VARCHAR(2),MONTH(getdate())),2)+right(’0′+convert(VARCHAR(2),DAY(getdate())),2)+ ‘.bak’backup database [TestDB] to disk=@FullPath WITH INITreturnUSE [TestDB] GO SET ANSI_NULLS ON GO SET QUOTED_IDENTIFIER ON GO CREATE PROCEDURE [BackupDatabase](@FolderPath varchar(500)) as DECLARE @FullPath varchar(1000) set @FullPath = @FolderPath+ ‘TestDB_’+convert(VARCHAR(4),year(getdate()))+right( ‘0′+convert(VARCHAR(2),MONTH(getdate())),2)+right(’0′+convert(VARCHAR(2),DAY(getdate())),2)+ ‘.bak’ backup database [TestDB] to disk=@FullPath WITH INIT return设置完毕，切换回维护计划设置页面，在左边的工具箱中拖入一个“执行T-SQL语句任务”：双击拖入后的“执行T-SQL语句任务”，输入执行存储过程的语句来完成每天的自动备份工作，代码如下：use [TestDB]exec BackupDatabase ‘E:\SqlData\TestDB\’use [TestDB] exec BackupDatabase ‘E:\SqlData\TestDB\’确定后保存该维护计划即可SqlServer自动作业备份SQL2005的维护计划里面自带了备份数据库任务，但不会自动按日期命名，不方便，下面的方法是以存储过程来解决的。要用SQL2005的维护计划功能首先要确认SQL Server Agent服务是在启动状态，然后打开SQL Server Management Studio，展开对象资源管理器里的“管理”，右击维护计划选择新建维护计划，为维护计划命名，如：TestDB_Backup，选择默认的子计划 “Subplan_1 ”并点击上方的子计划设置按钮来设置子计划执行方式和时间：出现设置界面，设置每天3点执行一次，如图：确定后保存一下，然后新建一个查询页，开始创建存储过程，代码如下：USE [TestDB]GOSET ANSI_NULLS ONGOSET QUOTED_IDENTIFIER ONGOCREATE PROCEDURE [BackupDatabase](@FolderPath varchar(500))asDECLARE @FullPath varchar(1000)set @FullPath = @FolderPath+ ‘TestDB_’+convert(VARCHAR(4),year(getdate()))+right( ‘0′+convert(VARCHAR(2),MONTH(getdate())),2)+right(’0′+convert(VARCHAR(2),DAY(getdate())),2)+ ‘.bak’backup database [TestDB] to disk=@FullPath WITH INITreturnUSE [TestDB] GO SET ANSI_NULLS ON GO SET QUOTED_IDENTIFIER ON GO CREATE PROCEDURE [BackupDatabase](@FolderPath varchar(500)) as DECLARE @FullPath varchar(1000) set @FullPath = @FolderPath+ ‘TestDB_’+convert(VARCHAR(4),year(getdate()))+right( ‘0′+convert(VARCHAR(2),MONTH(getdate())),2)+right(’0′+convert(VARCHAR(2),DAY(getdate())),2)+ ‘.bak’ backup database [TestDB] to disk=@FullPath WITH INIT return设置完毕，切换回维护计划设置页面，在左边的工具箱中拖入一个“执行T-SQL语句任务”：双击拖入后的“执行T-SQL语句任务”，输入执行存储过程的语句来完成每天的自动备份工作，代码如下：use [TestDB]exec BackupDatabase ‘E:\SqlData\TestDB\’use [TestDB] exec BackupDatabase ‘E:\SqlData\TestDB\’确定后保存该维护计划即可
跟朋友聊天的时候，发现很多人对大型网站系统架构非常感兴趣，我也很感兴趣，经常会在家里2台笔记本和1台服务器组成的局域网环境里作些实验。我进入IT行业的时间，大约是97，98年吧，那时候PC客户端软件最为盛行，做软件开发是一份很体面也很喜欢的工作。我从Win3.1上的VC1.5开始一直到VC6.0，然后转为.Net开发，基本上都是从事客户端软件开发。本人的性格是危机意识向来严重，所以深感互联网必将盛行，传统软件必将走向没落，于是转向了WEB开发。记得以前去某Portal网站应聘的时候，主考官就问我：你认为客户端开发和互联网开发有什么不同。我当时的回答是：互联网开发比客户端软件开发简单多了，我再也不用考虑那么多的用户环境因素了，一点部署，何时何地都可用。很多年过去了，我再想起当初我的回答，依然觉得那个回答是正确的。就产品开发层面来讲，互联网开发确实简单多了。这里首先澄清一个概念，我所说的互联网开发并不是指所有的B/S应用，例如B/S方式的银行内部业务系统。我所说的互联网应用是指在互联网上服务于公众的应用。企业级的业务系统，它的特点是业务逻辑是比较复杂的，但用户一般不太大；互联网应用则相反，业务逻辑一般很简单，但面对的是海量用户。既然互联网应用开发的业务逻辑不复杂，但为什么大型网站都投入了那么多的技术人员呢？主要是因为运营的环境太复杂，这种复杂性形成的原因以下：1、公开性网站的服务是公开的，任何人都可以来访问，所以就会直接面对大量的不良用户，系统数据的安全面临很大的风险，一旦系统被攻入，结果将是灾难性的。2、访问量大访问量大，就意味着网站必须能够承受高并发大流量的考验，如果网站的服务能力和健壮性等达不到要求，你的系统就会被冲垮。3、用户体验用户体验要好，除了产品设计的因素之外，就要求访问网站的速度要快，具有高可用性，别用一会就挂。网站各子系统如何进行部署，如何提高系统的健壮性和高可用性，如何实现网站的安全，如何提高访问速度，如何进行负载均衡，甚至于采用什么的硬件设备，另外，网站发展的不同时期会可能会采用不同的架构，如何实现架构的平滑过渡，如何使目前的架构具有弹性，具备可扩展的能力，这都是大型网站必须解决的问题，也是小网站成长过程中迟早会遇到的问题。我后面的文章将会逐步就这个话题展开。网站机构包括网站的软件架构和系统架构两部分，软件架构主要是指子系统和逻辑层的划分结构；系统架构，一般是系统部署结构。系统架构师的知识体系比较庞杂，所谓的见多识广，多数是由运维工程师成长起来的，他们开发能力不强，编码不多，但动手能力很强，脚本编写非常熟练，经常会做各种类型的实验，密切跟踪最新技术最新产品的相关信息。当然，一个大型的网站，需要一个架构师团队，他们各自承担擅长领域的架构设计，比如安全架构、存储架构等等。我觉得一般的开发人员还是很难走上这条路的，这份工作需要经验，需要不断实践，但如果开发人员一旦走上了这条路，会有很大的发展，主要源于开发人员的思考习惯和技术的深度。我的这系列文章，开发人员可以作为参考，比如如何开发可分布式部署的系统，另外很多朋友都是身兼数职，从开发到实施，到部署全部包办。我个人深感精力有限，所以又特意找了几个朋友从Unix/Linux系统和Windows系统不同角度进行探索，以造福正在摸索中的朋友，有兴趣的朋友也可以参与。其实，这部分内容我一直在写，比如PHP深度探索系列，写了大量的关于apache的内容，我已经大体把apache代码阅读了一遍，很费时间，进度缓慢，但我想这有助于我们理解apache的配置和调优。在介绍网站架构之前，我们先介绍一些网站架构中最基础和常见的概念，以便更好的理解后面的有关负载均衡和分布式存储等技术。第一个，首先讲讲CDN。1、CDN是什么CDN（Content Delivery Network），就是内容发布网或者内容分发网，它的主要目的：通过在现有的Internet中增加一层新的网络架构，将网站的内容发布到最接近用户的网络边缘，使用户可以就近取得所需的内容，从而提高用户访问网站的响应速度，提升用户体验，同时能够分散访问压力，把本来用户集中访问分散到各地去。网站的内容提供商（比如新浪、搜狐、网易等等）使用CDN，就可以在宏观层解决一部分大流量、海量用户并发等令人头疼的问题。2、CDN的组成内容发布网(CDN)是一个经策略性部署的整体系统，包括分布式存储、负载均衡、网络请求的重定向和内容管理４个要件，而内容管理和全局的网络流量管理是CDN的核心所在。通过用户就近性和服务器负载的判断，CDN确保内容以一种极为高效的方式为用户的请求提供服务，达到用户所要求的服务距用户仅有”一跳”(Single Hop)之遥。我们通常的内容发布模式都是将网站数据放到一处，然后应对来自世界各地的访问，我们多数考虑的是软件部署架构，很少考虑网络硬件架构。与之形成对比的是，CDN则强调了网络在内容发布中的重要性。通过引入主动的内容管理层的和全局负载均衡，CDN从根本上区别于传统的内容发布模式。内容提供商承担了他们不该干也干不好的内容发布服务。3、互联网服务的产业链纵观整个宽带服务的价值链，内容提供商和用户位于整个价值链的两端，中间依靠网络服务提供商将其串接起来。随着互联网工业的成熟和商业模式的变革，在这条价值链上的角色越来越多也越来越细分，出现了内容运营商、托管服务提供商、骨干网络服务提供商、接入服务提供商等等。在这一条价值链上的每一个角色都要分工合作、各司其职才能为客户提供良好的服务，从而带来多赢的局面。从内容与网络的结合模式上看，内容的发布已经走过了ICP的内容（应用）服务器和IDC这两个阶段。IDC的热潮也催生了托管服务提供商这一角色。但是，IDC并不能解决内容的有效发布问题。内容位于网络的中心并不能解决骨干带宽的占用和建立IP网络上的流量秩序。因此将内容推到网络的边缘，为用户提供就近性的边缘服务，从而保证服务的质量和整个网络上的访问秩序就成了一种显而易见的选择，这就是CDN服务模式。CDN的建立解决了困扰内容运营商的内容”集中与分散”的两难选择，无疑对于构建良好的互联网价值链是有价值的，也是不可或缺的最优网站加速服务。4、CDN服务提供商ChinaCache是中国最大的CDN服务提供商，是不是唯一未可知也。要想成为CDN服务提供商，恐怕要摆平电信、网通、铁通等等运营商，这得需要什么样的能力和背景不得而知。它的服务节点在全球已经超过130个，其中国内节点超过80个，覆盖全国主要6大网络（所谓6线机房，就是这么来的）的主要省份，象各大门户网站，比如新浪、网易等等都是租用了他们的服务。所以，你无论是在南方，或者北方，还是在北美，访问这些门户网站，感觉速度都很快，最主要的原因之一就是CDN发挥了效果。一般小网站是用不起这服务的，所以慢点就慢点了吧，可以租用互联互通的6线机房，如果网络足够宽的话，用户也可以忍受。如果想继续提升用户体验的话，就需要做一些网站镜像，部署在具有代表性的几个大城市，比如华南可以部署在广州，华东可以部署在上海，华北可以部署在北京，不过内容镜像的过程，就需要自己去部署和维护。还有的网站，采用内容分割的方式，比如建立针对各地的分站，业务情况不同，可能部署的策略不同。CDN可以认为是基础网络建设的一种策略。前面介绍了cdn的一些原理和概念，以及提供cdn基础网络服务的途径。cdn看起来对于静态内容的，比如html,js,image是非常合适的，通过cdn的部署，用户只需要一跳就可以访问到网站的内容。那对于动态内容怎么办呢？我回答一下：动态内容按照存在形态可以分为三类。第一类：内容长时间不需变化，这类内容一般是通过网页静化技术，实现动态内容转换成静态内容，从而达到cdn部署，典型的就是内容类网站，比如新浪、搜狐、网易等等的内容发布系统cms，内容的增删改等管理工作被准实时同步到各个节点。第二类：内容可能会短时间内发生变动，但是最终会稳定。比如论坛、博客等应用，这类服务提供的内容按照一定的时间间隔，实现批量静化，当然也有实时静化，像Mop的大杂烩、网易社区就是使用了这样的策略。第三类：内容会实时变化，非常个性化。比如邮箱应用，这类服务提供的内容无法实现静化，只能通过实行分区域部署和负载均衡等手段进行优化。对于提供cdn服务的厂商来讲，静态内容的cdn自然没有问题，对于第三类服务，只能从通信链路层进行相应的优化。对于很多网站的伪静化，有的出于Seo的考虑，有的出于安全性的考虑，手段基本上是rewrite Url。它只不过是一种外在的表现形式，与Html静化是两回事，它依然是一种动态内容。1. 负载均衡的分类负载均衡技术在网站运营过程中应用非常普遍，技术也很成熟。负载均衡技术按照软硬件形式分为软均衡和硬均衡。软均衡就是基于软件技术的均衡，硬均衡是基于硬件技术的均衡；按照网络协议划分又分为四层均衡和七层均衡。四层均衡就是基于OSI网络层的数据均衡，七层均衡是基于OSI应用层的数据均衡。各种均衡方式在大型网站中均有采用，而且大多数情况下，是多种均衡方式的组合。2. DNS轮询均衡这种方式，算是比较独立的一种方式，不在上述划分之列，但使用比较广泛，一般用在网站最前端。你可以做个试验，在dos命令行中运行nslook命令。比如：nslookup www。163。com，你会看到命令给出了一堆解析后的IP地址。这些地址就是www.163.com这个域名绑定的多条A记录。我们从浏览器发起的访问请求http://www.163.com/，那么你输入的域名首先需要经过DNS服务器进行解析，Dns服务器的解析的过程就是按照A记录的顺序，依次分配IP地址。Dns轮询方式实现均衡就是利用这个原理，在一个域名下面绑定N个IP地址，访问请求被均衡到不同的设备。Dns轮询方式提供的IP地址，在大型网站中往往是一个集群的地址，可能是均衡交换机也可能是均衡服务器。对于小网站的话，挂接多台服务器也没有问题。DNS轮询均衡的优点：1、零成本：只是在Dns服务器上绑定几个A记录，域名注册商一般都提供；2、部署简单：就是在网络拓扑进行设备扩增，然后在Dns服务器上添加记录。DNS轮询均衡的缺点：1、流量分配不均：Dns解析过程其实环节很多，而且是一种层层缓存的机制，你的dns服务器虽然进行更新，但是客户机、以及网络上其它的dns服务器不会实时更新，所以流量很难保证100%的平均。目前，dns服务器都提供了多种手段可以调整dns轮询分配的策略，但是确实无法保证很完美的均衡。2、健康检查：Dns服务器中A记录地址中的某一台服务器宕机，DNS服务器是无法知道的，仍旧会将访问分配到此服务器。所以需要人员或者工具进行实时检测，在某台机器宕机之后，把备份机推上生产线，如果想要从A记录地址摘除某个地址，这个通知过程需要几个小时甚至更久才能扩散到所有的客户机。Dns轮询方式推到服务的最前端还是很有效的，它通过最原始的方式，把访问用户映射到不同的服务集群上。对于大型网站来讲，对外服务的IP地址是不可能经常变动的，而且后端的集群一旦宕掉，可以迅速推上冗余集群。再加上，一般都是经过CDN部署，服务被拆分到各个局部，所以在运营过程中不会产生太大的影响。3. OSI七层模型我们接下来讲讲七层均衡。要理解四七层均衡的原理，就先要回忆一下大学课本里学的网络七层模型(OSI)。OSI是一个开放性的通行系统互连参考模型，他是一个定义的非常好的协议规范。OSI模型有7层结构，每层都可以有几个子层。OSI七层模型是一个很好的理论模型，但是在实际应用中都做了裁剪。尤其是TCP/IP的盛行，把7层结构压成了4层，所以很多人都批评OSI七层模型过于复杂，但是作为一个完整的全面的网络模型，还是被大家非常认可的。OSI的7层从上到下分别是应用层、表示层、会话层、传输层、网络层、数据链路层、物理层。OSI 7层的功能描述：（1）应用层：与其他计算机进行通讯的一个应用，它是对应应用程序的通信服务的。例如，一个没有通信功能的字处理程序就不能执行通信的代码，从事字处理工作的程序员也不关心OSI的第7层。但是，如果添加了一个传输文件的选项，那么字处理器的程序员就需要实现OSI的第7层。示例：telnet，HTTP,FTP,WWW,NFS,SMTP等。（2）表示层：这一层的主要功能是定义数据格式及加密。例如，FTP允许你选择以二进制或ASII格式传输。如果选择二进制，那么发送方和接收方不改变文件的内容。如果选择ASII格式，发送方将把文本从发送方的字符集转换成标准的ASII后发送数据。在接收方将标准的ASII转换成接收方计算机的字符集。示例：加密，ASII等。（3）会话层：他定义了如何开始、控制和结束一个会话，包括对多个双向小时的控制和管理，以便在只完成连续消息的一部分时可以通知应用，从而使表示层看到的数据是连续的，在某些情况下，如果表示层收到了所有的数据，则用数据代表表示层。示例：RPC，SQL等。（4）传输层：这层的功能包括是否选择差错恢复协议还是无差错恢复协议，及在同一主机上对不同应用的数据流的输入进行复用，还包括对收到的顺序不对的数据包的重新排序功能。示例：TCP，UDP，SPX。（5）网络层：这层对端到端的包传输进行定义，他定义了能够标识所有结点的逻辑地址，还定义了路由实现的方式和学习的方式。为了适应最大传输单元长度小于包长度的传输介质，网络层还定义了如何将一个包分解成更小的包的分段方法。示例：IP,IPX等。（6）数据链路层：他定义了在单个链路上如何传输数据。这些协议与被讨论的歌种介质有关。示例：ATM，FDDI等。（7）物理层：OSI的物理层规范是有关传输介质的特性标准，这些规范通常也参考了其他组织制定的标准。连接头、针、针的使用、电流、电流、编码及光调制等都属于各种物理层规范中的内容。物理层常用多个规范完成对所有细节的定义。出处：51CTO http://developer.51cto.com/art/200903/115670_1.htm
有助于效率的类型选择1、使你的数据尽可能小最基本的优化之一是使你的数据(和索引)在磁盘上(并且在内存中)占据的空间尽可能小。这能给出巨大的改进，因为磁盘读入较快并且通常也用较少的主存储器。如果在更小的列上做索引，索引也占据较少的资源。你能用下面的技术使表的性能更好并且使存储空间最小：·尽可能地使用最有效(最小)的类型。MySQL有很多节省磁盘空间和内存的专业化类型。·如果可能使表更小，使用较小的整数类型。例如，MEDIUMINT经常比INT好一些。·如果可能，声明列为NOT NULL。它使任何事情更快而且你为每列节省一位。注意如果在你的应用程序中你确实需要NULL，你应该毫无疑问使用它，只是避免缺省地在所有列上有它。2、使用定长列，不使用可变长列这条准则对被经常修改，从而容易产生碎片的表来说特别重要。例如，应该选择 CHAR 列而不选择 VARCHAR 列。所要权衡的是使用定长列时，表所占用的空间更多，但如果能够承担这种空间的耗费，使用定长行将比使用可变长的行处理快得多。3、将列定义为 NOT NULL这样处理更快，所需空间更少。而且有时还能简化查询，因为不需要检查是否存在特例 NULL。4、考虑使用 ENUM 列如果有一个只含有限数目的特定值的列，那么应该考虑将其转换为 ENUM 列。ENUM 列的值可以更快地处理，因为它们在内部是以数值表示的。有关BLOB和TEXT类型1、使用BLOB和TEXT类型的优点用 BLOB 存储应用程序中包装或未包装的数据，有可能使原来需要几个检索操作才能完成的数据检索得以在单个检索操作中完成。而且还对存储标准表结构不易表示的数据或随时间变化的数据有帮助。2、使用BLOB和TEXT类型的可能弊端另一方面，BLOB 值也有自己的固有问题，特别是在进行大量的 DELETE 或 UPDATE 操作时更是如此。删除 BLOB 会在表中留下一个大空白，在以后将需用一个记录或可能是不同大小的多个记录来填充。除非有必要，否则应避免检索较大的 BLOB 或 TEXT 值。例如，除非肯定WHERE 子句能够将结果恰好限制在所想要的行上，否则 SELECT * 查询不是一个好办法。这样做可能会将非常大的 BLOB 值无目的地从网络上拖过来。这是存储在另一列中的 BLOB 标识信息很有用的另一种情形。可以搜索该列以确定想要的行，然后从限定的行中检索 BLOB 值。3、必要的准则对容易产生碎片的表使用 OPTIMIZE TABLE大量进行修改的表，特别是那些含有可变长列的表，容易产生碎片。碎片不好，因为它在存储表的磁盘块中产生不使用的空间。随着时间的增长，必须读取更 多的块才能取到有效的行，从而降低了性能。任意具有可变长行的表都存在这个问题，但这个问题对 BLOB 列更为突出，因为它们尺寸的变化非常大。经常使用 OPTIMIZE TABLE 有助于保持性能不下降。使用多列索引多列索引列有时很有用。一种技术是根据其他列建立一个散列值，并将其存储在一个独立的列中，然后可通过搜索散列值找到行。这只对精确匹配的查询有 效。(散列值对具有诸如“<”或“>=”这样的操作符的范围搜索没有用处)。在MySQL 3.23版及以上版本中，散列值可利用 MD5( ) 函数产生。散列索引对 BLOB 列特别有用。有一事要注意，在 MySQL 3.23.2 以前的版本中，不能索引 BLOB 类型。甚至是在 3.23.2 或更新的版本中，利用散列值作为标识值来查找 BLOB 值也比搜索 BLOB 列本身更快。将 BLOB 值隔离在一个独立的表中在某些情况下，将 BLOB 列从表中移出放入另一个副表可能具有一定的意义，条件是移出 BLOB 列后可将表转换为定长行格式。这样会减少主表中的碎片，而且能利用定长行的性能优势。使用ANALYSE过程检查表列如果使用的是 MySQL 3.23 或更新的版本，应该执行 PROCEDURE ANALYSE( )，查看它所提供的关于表中列的信息ANALYSE([max elements,[max memory]])它检验来自你的查询的结果并返回结果的分析。max elements(缺省256)是analyse将注意的每列不同值的最大数量。这被ANALYSE用来检查最佳的列类型是否应该是ENUM类型。max memory(缺省8192)是在analyse尝试寻找所有不同值的时候应该分配给每列的最大内存量。SELECT … FROM … WHERE … PROCEDURE ANALYSE([max elements,[max memory]])例如：mysql>SELECT * FROM student PROCEDURE ANALYSE();mysql>SELECT * FROM student PROCEDURE ANALYSE(16,256);相应输出中有一列是关于表中每列的最佳列类型的建议。第二个例子要求 PROCEDURE ANALYSE( ) 不要建议含有多于 16 个值或取多于 256 字节的 ENUM 类型(可根据需要更改这些值)。如果没有这样的限制，输出可能会很长;ENUM 的定义也会很难阅读。根据 PROCEDURE ANALYSE( ) 的输出，会发现可以对表进行更改以利用更有效的类型。如果希望更改值类型，使用 ALTER TABLE 语句即可。