分类目录归档:服务器

linux中php-fpm进程数优化与分析详解(转)

下面本文章来给各位同学介绍关于linux中php-fpm进程数优化与分析详解,有兴趣的同学不防进入参考一下下呀。

在几个不是很繁忙的线上服务器发现php-fpm进程数达到500多,且存在一些运行时间长达几个月的进程。
进行了以下排查,以确定:phpfpm运行是否有问题,是否需要重启。
查看这些进程是否被正常启动

由于php-fpm.log中,会以notice级别打印worker进程的启动和回收时间,故可以通过以下语句检查有哪些php没有被记录到(ps axuf可以查看到进程的父子关系):

$cd /path/to/php-fpm.log
$for word in `ps axu | grep php | perl -ne 'chomp; @tmp=split " +", $_; print $tmp[1]."n";'` ; do  if grep $word php-fpm.log >/dev/null ; then echo -n ''; else echo $word;  fi ; done

执行结果,发现有173个进程未被记录,一部分有可能是在php-fpm父进程启动时,直接fork出来的,所以未记录。但还剩余几十个,我也不知道啥原因了。
确认进程是否存活
通过root帐号登录上去,strace -p <pid>,其中pid是上面173个进程之一。发现正常滚动,并能看到对php方法的调用,故确定该进程存活,且可以提供服务。
为什么有这么多进程存在呢?
虽然对服务没有影响,但这么多进程存在,是否正常呢?
首先得看下php-fpm.conf:

pm = dynamic
pm.max_children = 8192
pm.start_servers = 128
pm.min_spare_servers = 128
pm.max_spare_servers = 1024
pm.max_requests = 500000

由于我们的空闲进程配置为128 ~ 1024,而max_request配置为50w,并且之前机器流量不大,所以可能很长一段时间,进程都不会因为处理完了max_request个请求、或者超过了spare_servers个数 而被回收。
而一个php-fpm进程占用12m左右非共享内存,根据机器配置,cpu、内存配置来看,支持1k个php-fpm进程是足够的。
约定几个目录

/usr/local/php/sbin/php-fpm
/usr/local/php/etc/php-fpm.conf
/usr/local/php/etc/php.ini

一,php-fpm的启动参数

#测试php-fpm配置
/usr/local/php/sbin/php-fpm -t
/usr/local/php/sbin/php-fpm -c /usr/local/php/etc/php.ini -y /usr/local/php/etc/php-fpm.conf -t
 
#启动php-fpm
/usr/local/php/sbin/php-fpm
/usr/local/php/sbin/php-fpm -c /usr/local/php/etc/php.ini -y /usr/local/php/etc/php-fpm.conf
 
#关闭php-fpm
kill -INT `cat /usr/local/php/var/run/php-fpm.pid`
 
#重启php-fpm
kill -USR2 `cat /usr/local/php/var/run/php-fpm.pid`

二,php-fpm.conf重要参数详解

pid = run/php-fpm.pid
#pid设置,默认在安装目录中的var/run/php-fpm.pid,建议开启
 
error_log = log/php-fpm.log
#错误日志,默认在安装目录中的var/log/php-fpm.log
 
log_level = notice
#错误级别. 可用级别为: alert(必须立即处理), error(错误情况), warning(警告情况), notice(一般重要信息), debug(调试信息). 默认: notice.
 
emergency_restart_threshold = 60
emergency_restart_interval = 60s
#表示在emergency_restart_interval所设值内出现SIGSEGV或者SIGBUS错误的php-cgi进程数如果超过 emergency_restart_threshold个,php-fpm就会优雅重启。这两个选项一般保持默认值。
 
process_control_timeout = 0
#设置子进程接受主进程复用信号的超时时间. 可用单位: s(秒), m(分), h(小时), 或者 d(天) 默认单位: s(秒). 默认值: 0.
 
daemonize = yes
#后台执行fpm,默认值为yes,如果为了调试可以改为no。在FPM中,可以使用不同的设置来运行多个进程池。 这些设置可以针对每个进程池单独设置。
 
listen = 127.0.0.1:9000
#fpm监听端口,即nginx中php处理的地址,一般默认值即可。可用格式为: 'ip:port', 'port', '/path/to/unix/socket'. 每个进程池都需要设置.
 
listen.backlog = -1
#backlog数,-1表示无限制,由操作系统决定,此行注释掉就行。backlog含义参考:http://www.3gyou.cc/?p=41
 
listen.allowed_clients = 127.0.0.1
#允许访问FastCGI进程的IP,设置any为不限制IP,如果要设置其他主机的nginx也能访问这台FPM进程,listen处要设置成本地可被访问的IP。默认值是any。每个地址是用逗号分隔. 如果没有设置或者为空,则允许任何服务器请求连接
 
listen.owner = www
listen.group = www
listen.mode = 0666
#unix socket设置选项,如果使用tcp方式访问,这里注释即可。
 
user = www
group = www
#启动进程的帐户和组
 
pm = dynamic #对于专用服务器,pm可以设置为static。
#如何控制子进程,选项有static和dynamic。如果选择static,则由pm.max_children指定固定的子进程数。如果选择dynamic,则由下开参数决定:
pm.max_children #,子进程最大数
pm.start_servers #,启动时的进程数
pm.min_spare_servers #,保证空闲进程数最小值,如果空闲进程小于此值,则创建新的子进程
pm.max_spare_servers #,保证空闲进程数最大值,如果空闲进程大于此值,此进行清理
 
pm.max_requests = 1000
#设置每个子进程重生之前服务的请求数. 对于可能存在内存泄漏的第三方模块来说是非常有用的. 如果设置为 '0' 则一直接受请求. 等同于 PHP_FCGI_MAX_REQUESTS 环境变量. 默认值: 0.
 
pm.status_path = /status
#FPM状态页面的网址. 如果没有设置, 则无法访问状态页面. 默认值: none. munin监控会使用到
 
ping.path = /ping
#FPM监控页面的ping网址. 如果没有设置, 则无法访问ping页面. 该页面用于外部检测FPM是否存活并且可以响应请求. 请注意必须以斜线开头 (/)。
 
ping.response = pong
#用于定义ping请求的返回相应. 返回为 HTTP 200 的 text/plain 格式文本. 默认值: pong.
 
request_terminate_timeout = 0
#设置单个请求的超时中止时间. 该选项可能会对php.ini设置中的'max_execution_time'因为某些特殊原因没有中止运行的脚本有用. 设置为 '0' 表示 'Off'.当经常出现502错误时可以尝试更改此选项。
 
request_slowlog_timeout = 10s
#当一个请求该设置的超时时间后,就会将对应的PHP调用堆栈信息完整写入到慢日志中. 设置为 '0' 表示 'Off'
 
slowlog = log/$pool.log.slow
#慢请求的记录日志,配合request_slowlog_timeout使用
 
rlimit_files = 1024
#设置文件打开描述符的rlimit限制. 默认值: 系统定义值默认可打开句柄是1024,可使用 ulimit -n查看,ulimit -n 2048修改。
 
rlimit_core = 0
#设置核心rlimit最大限制值. 可用值: 'unlimited' 、0或者正整数. 默认值: 系统定义值.
 
chroot =
#启动时的Chroot目录. 所定义的目录需要是绝对路径. 如果没有设置, 则chroot不被使用.
 
chdir =
#设置启动目录,启动时会自动Chdir到该目录. 所定义的目录需要是绝对路径. 默认值: 当前目录,或者/目录(chroot时)
 
catch_workers_output = yes
#重定向运行过程中的stdout和stderr到主要的错误日志文件中. 如果没有设置, stdout 和 stderr 将会根据FastCGI的规则被重定向到 /dev/null . 默认值: 空.

三,常见错误及解决办法整理

1,request_terminate_timeout引起的资源问题
request_terminate_timeout的值如果设置为0或者过长的时间,可能会引起file_get_contents的资源问题。
如果file_get_contents请求的远程资源如果反应过慢,file_get_contents就会一直卡在那里不会超时。我们知道php.ini 里面max_execution_time 可以设置 PHP 脚本的最大执行时间,但是,在 php-cgi(php-fpm) 中,该参数不会起效。真正能够控制 PHP 脚本最大执行时间的是 php-fpm.conf 配置文件中的request_terminate_timeout参数。
request_terminate_timeout默认值为 0 秒,也就是说,PHP 脚本会一直执行下去。这样,当所有的 php-cgi 进程都卡在 file_get_contents() 函数时,这台 Nginx+PHP 的 WebServer 已经无法再处理新的 PHP 请求了,Nginx 将给用户返回“502 Bad Gateway”。修改该参数,设置一个 PHP 脚本最大执行时间是必要的,但是,治标不治本。例如改成 30s,如果发生 file_get_contents() 获取网页内容较慢的情况,这就意味着 150 个 php-cgi 进程,每秒钟只能处理 5 个请求,WebServer 同样很难避免”502 Bad Gateway”。解决办法是request_terminate_timeout设置为10s或者一个合理的值,或者给file_get_contents加一个超时参数。

$ctx = stream_context_create(array(
    'http' => array(
        'timeout' => 10    //设置一个超时时间,单位为秒
    )
));
 
file_get_contents($str, 0, $ctx);

2,max_requests参数配置不当,可能会引起间歇性502错误:

pm.max_requests = 1000

设置每个子进程重生之前服务的请求数. 对于可能存在内存泄漏的第三方模块来说是非常有用的. 如果设置为 ’0′ 则一直接受请求. 等同于 PHP_FCGI_MAX_REQUESTS 环境变量. 默认值: 0.
这段配置的意思是,当一个 PHP-CGI 进程处理的请求数累积到 500 个后,自动重启该进程。
但是为什么要重启进程呢?
一般在项目中,我们多多少少都会用到一些 PHP 的第三方库,这些第三方库经常存在内存泄漏问题,如果不定期重启 PHP-CGI 进程,势必造成内存使用量不断增长。因此 PHP-FPM 作为 PHP-CGI 的管理器,提供了这么一项监控功能,对请求达到指定次数的 PHP-CGI 进程进行重启,保证内存使用量不增长。
正是因为这个机制,在高并发的站点中,经常导致 502 错误,我猜测原因是 PHP-FPM 对从 NGINX 过来的请求队列没处理好。不过我目前用的还是 PHP 5.3.2,不知道在 PHP 5.3.3 中是否还存在这个问题。
目前我们的解决方法是,把这个值尽量设置大些,尽可能减少 PHP-CGI 重新 SPAWN 的次数,同时也能提高总体性能。在我们自己实际的生产环境中发现,内存泄漏并不明显,因此我们将这个值设置得非常大(204800)。大家要根据自己的实际情况设置这个值,不能盲目地加大。
话说回来,这套机制目的只为保证 PHP-CGI 不过分地占用内存,为何不通过检测内存的方式来处理呢?我非常认同高春辉所说的,通过设置进程的峰值内在占用量来重启 PHP-CGI 进程,会是更好的一个解决方案。

3,php-fpm的慢日志,debug及异常排查神器:

request_slowlog_timeout设置一个超时的参数,slowlog设置慢日志的存放位置
1
tail -f /var/log/www.slow.log

上面的命令即可看到执行过慢的php过程。
大家可以看到经常出现的网络读取超过、Mysql查询过慢的问题,根据提示信息再排查问题就有很明确的方向了。
启动参数及重要配置

php-fpm优化

1、php-fpm优化参数介绍
他们分别是:pm、pm.max_children、pm.start_servers、pm.min_spare_servers、pm.max_spare_servers。
pm:表示使用那种方式,有两个值可以选择,就是static(静态)或者dynamic(动态)。
在更老一些的版本中,dynamic被称作apache-like。这个要注意看配置文件的说明。
下面4个参数的意思分别为:
pm.max_children:静态方式下开启的php-fpm进程数量
pm.start_servers:动态方式下的起始php-fpm进程数量
pm.min_spare_servers:动态方式下的最小php-fpm进程数
pm.max_spare_servers:动态方式下的最大php-fpm进程数量
区别:
如果dm设置为 static,那么其实只有pm.max_children这个参数生效。系统会开启设置数量的php-fpm进程。
如果dm设置为 dynamic,那么pm.max_children参数失效,后面3个参数生效。
系统会在php-fpm运行开始 的时候启动pm.start_servers个php-fpm进程,
然后根据系统的需求动态在pm.min_spare_servers和pm.max_spare_servers之间调整php-fpm进程数


2、服务器具体配置
        对于我们的服务器,选择哪种执行方式比较好呢?事实上,跟Apache一样,运行的PHP程序在执行完成后,或多或少会有内存泄露的问题。
这也是为什么开始的时候一个php-fpm进程只占用3M左右内存,运行一段时间后就会上升到20-30M的原因了。
        对于内存大的服务器(比如8G以上)来说,指定静态的max_children实际上更为妥当,因为这样不需要进行额外的进程数目控制,会提高效率。
因为频繁开关php-fpm进程也会有时滞,所以内存够大的情况下开静态效果会更好。数量也可以根据 内存/30M 得到,比如8GB内存可以设置为100,
那么php-fpm耗费的内存就能控制在 2G-3G的样子。如果内存稍微小点,比如1G,那么指定静态的进程数量更加有利于服务器的稳定。
这样可以保证php-fpm只获取够用的内存,将不多的内存分配给其他应用去使用,会使系统的运行更加畅通。
        对于小内存的服务器来说,比如256M内存的VPS,即使按照一个20M的内存量来算,10个php-cgi进程就将耗掉200M内存,那系统的崩溃就应该很正常了。
因此应该尽量地控制php-fpm进程的数量,大体明确其他应用占用的内存后,给它指定一个静态的小数量,会让系统更加平稳一些。或者使用动态方式,
因为动态方式会结束掉多余的进程,可以回收释放一些内存,所以推荐在内存较少的服务器或VPS上使用。具体最大数量根据 内存/20M 得到。
    比如说512M的VPS,建议pm.max_spare_servers设置为20。至于pm.min_spare_servers,则建议根据服务器的负载情况来设置,比如服务器上只是部署php环境的话,比较合适的值在5~10之间。


本服务器配置
1、服务器基本信息:
硬盘:数据盘30G、系统盘20G
内存:1.5G
CPU:双核
系统:CentOS 6.3 64位
带宽:独享2M
2、部署的应用
Git、SVN、Apache、Tomcat、PHP、Nginx、Mysql、JDK
3、优化后的参数

pm = dynamic
pm.start_servers = 5
pm.min_spare_servers = 2
pm.max_spare_servers = 8

php5.3.3以后php-fpm进程管理方式

PHP-FPM提供了更好的PHP进程管理方式,可以有效控制内存和进程、可以平滑重载PHP配置,比spawn-fcgi具有更多优点,所以被PHP官方收录了。在php5.3.3 以后./configure的时候带 –enable-fpm参数即可开启PHP-FPM。
使用PHP-FPM来控制PHP-CGI的FastCGI进程

/usr/local/php/sbin/php-fpm{start|stop|quit|restart|reload|logrotate}
  --start 启动php的fastcgi进程
  --stop 强制终止php的fastcgi进程
  --quit 平滑终止php的fastcgi进程
  --restart 重启php的fastcgi进程
  --reload 重新平滑加载php的php.ini
  --logrotate 重新启用log文件

php-fpm目前主要又两个分支,分别对应于php-5.3.3以前的版本和php-5.3.3以后的版本。在5.2.x的版本中,php-fpm.conf使用的是xml格式,而在新的5.3.3以后的版本中,则是php.ini一样的配置风格。对于5.5.3以后的版本中存在两种php-fpm进程的管理方式——static和dynamic。具体/usr/local/php/conf/php-fpm.conf.default中的配置如下:

; Choose how the process manager will control the number of child processes.
; Possible Values:
;   static  - a fixed number (pm.max_children) of child processes;
;   dynamic - the number of child processes are set dynamically based on the
;             following directives. With this process management, there will be
;             always at least 1 children.
;             pm.max_children      - the maximum number of children that can
;                                    be alive at the same time.
;             pm.start_servers     - the number of children created on startup.
;             pm.min_spare_servers - the minimum number of children in 'idle'
;                                    state (waiting to process). If the number
;                                    of 'idle' processes is less than this
;                                    number then some children will be created.
;             pm.max_spare_servers - the maximum number of children in 'idle'
;                                    state (waiting to process). If the number
;                                    of 'idle' processes is greater than this
;                                    number then some children will be killed.
;  ondemand - no children are created at startup. Children will be forked when
;             new requests will connect. The following parameter are used:
;             pm.max_children           - the maximum number of children that
;                                         can be alive at the same time.
;             pm.process_idle_timeout   - The number of seconds after which
;                                         an idle process will be killed.
; Note: This value is mandatory.
pm = dynamic

从上面可以看到默认是启用的动态管理方式。而php-fpm进程数是动态的,最开始是pm.start_servers指定的数量,如果请求较多,则会自动增加,保证空闲的进程数不小于pm.min_spare_servers,如果进程数较多,也会进行相应清理,保证多余的进程数不多于pm.max_spare_servers。在上面的配置文件中也可以看到其中涉及到四个参数的设置,其作用分别如下:
    pm.max_children:静态方式下开启的php-fpm进程数量。
    pm.start_servers:动态方式下的起始php-fpm进程数量。
    pm.min_spare_servers:动态方式下的最小php-fpm进程数量。
    pm.max_spare_servers:动态方式下的最大php-fpm进程数量。
如果dm设置为static,那么其实只有pm.max_children这个参数生效。系统会开启设置数量的php-fpm进程。
如果dm设置为dynamic,那么pm.max_children参数失效,后面3个参数生效。系统会在php-fpm运行开始的时候启动pm.start_servers个php-fpm进程,然后根据系统的需求动态在pm.min_spare_servers和pm.max_spare_servers之间调整php-fpm进程数。
那么,对于我们的服务器,选择哪种执行方式比较好呢?事实上,跟Apache一样,运行的PHP程序在执行完成后,或多或少会有内存泄露的问题。这也是为什么开始的时候一个php-fpm进程只占用3M左右内存,运行一段时间后就会上升到20-30M的原因了。
     对于内存大的服务器(比如8G以上)来说,指定静态的max_children实际上更为妥当,因为这样不需要进行额外的进程数目控制,会提高效率。因为频繁开关php-fpm进程也会有时滞,所以内存够大的情况下开静态效果会更好。比如8GB内存可以设置为100,那么php-fpm耗费的内存就能控制在 2G-3G的样子。,如果数据库和web应用在不同的服务器上,该机器只跑web应用,大可开到300左右。如果内存稍微小点,比如1G那么指定静态的进程数量更加有利于服务器的稳定。这样可以保证php-fpm只获取够用的内存,将不多的内存分配给其他应用去使用,会使系统的运行更加畅通。
      对于小内存的服务器来说,比如256M内存的VPS,即使按照一个20M的内存量来算,10个php-cgi进程就将耗掉200M内存,那系统的崩溃就应该很正常了。因此应该尽量地控制php-fpm进程的数量,大体明确其他应用占用的内存后,给它指定一个静态的小数量,会让系统更加平稳一些。或者使用动态方式,因为动态方式会结束掉多余的进程,可以回收释放一些内存,所以推荐在内存较少的服务器或VPS上使用。比如说512M的VPS,建议pm.max_spare_servers设置为20。至于pm.min_spare_servers,则建议根据服务器的负载情况来设置,比较合适的值在5~10之间。

Linux NFS服务器的安装与配置

一、NFS服务简介

  NFS 是Network File System的缩写,即网络文件系统。一种使用于分散式文件系统的协定,由Sun公司开发,于1984年向外公布。功能是通过网络让不同的机器、不同的操作系统能够彼此分享个别的数据,让应用程序在客户端通过网络访问位于服务器磁盘中的数据,是在类Unix系统间实现磁盘文件共享的一种方法。

  NFS 的基本原则是“容许不同的客户端及服务端通过一组RPC分享相同的文件系统”,它是独立于操作系统,容许不同硬件及操作系统的系统共同进行文件的分享。

  NFS在文件传送或信息传送过程中依赖于RPC协议。RPC,远程过程调用 (Remote Procedure Call) 是能使客户端执行其他系统中程序的一种机制。NFS本身是没有提供信息传输的协议和功能的,但NFS却能让我们通过网络进行资料的分享,这是因为NFS使用了一些其它的传输协议。而这些传输协议用到这个RPC功能的。可以说NFS本身就是使用RPC的一个程序。或者说NFS也是一个RPC SERVER。所以只要用到NFS的地方都要启动RPC服务,不论是NFS SERVER或者NFS CLIENT。这样SERVER和CLIENT才能通过RPC来实现PROGRAM PORT的对应。可以这么理解RPC和NFS的关系:NFS是一个文件系统,而RPC是负责负责信息的传输。

二、系统环境

系统平台:CentOS release 5.6 (Final)

NFS Server IP:192.168.1.108

防火墙已关闭/iptables: Firewall is not running.

SELINUX=disabled

三、安装NFS服务

NFS的安装是非常简单的,只需要两个软件包即可,而且在通常情况下,是作为系统的默认包安装的。

  • nfs-utils-* :包括基本的NFS命令与监控程序 
  • portmap-* :支持安全NFS RPC服务的连接

1、查看系统是否已安装NFS

系统默认已安装了nfs-utils portmap 两个软件包。

2、如果当前系统中没有安装NFS所需的软件包,需要手工进行安装。nfs-utils 和portmap 两个包的安装文件在系统光盘中都会有。

# mount /dev/cdrom /mnt/cdrom/
# cd /mnt/cdrom/CentOS/
# rpm -ivh portmap-4.0-65.2.2.1.i386.rpm 
# rpm -ivh nfs-utils-1.0.9-50.el5.i386.rpm
# rpm -q nfs-utils portmap

四、NFS系统守护进程

  • nfsd:它是基本的NFS守护进程,主要功能是管理客户端是否能够登录服务器;
  • mountd:它是RPC安装守护进程,主要功能是管理NFS的文件系统。当客户端顺利通过nfsd登录NFS服务器后,在使用NFS服务所提供的文件前,还必须通过文件使用权限的验证。它会读取NFS的配置文件/etc/exports来对比客户端权限。
  • portmap:主要功能是进行端口映射工作。当客户端尝试连接并使用RPC服务器提供的服务(如NFS服务)时,portmap会将所管理的与服务对应的端口提供给客户端,从而使客户可以通过该端口向服务器请求服务。

五、NFS服务器的配置

NFS服务器的配置相对比较简单,只需要在相应的配置文件中进行设置,然后启动NFS服务器即可。

NFS的常用目录

/etc/exports                           NFS服务的主要配置文件
/usr/sbin/exportfs                   NFS服务的管理命令
/usr/sbin/showmount              客户端的查看命令
/var/lib/nfs/etab                      记录NFS分享出来的目录的完整权限设定值
/var/lib/nfs/xtab                      记录曾经登录过的客户端信息

NFS服务的配置文件为 /etc/exports,这个文件是NFS的主要配置文件,不过系统并没有默认值,所以这个文件不一定会存在,可能要使用vim手动建立,然后在文件里面写入配置内容。

/etc/exports文件内容格式:

<输出目录> [客户端1 选项(访问权限,用户映射,其他)] [客户端2 选项(访问权限,用户映射,其他)]

a. 输出目录:

输出目录是指NFS系统中需要共享给客户机使用的目录;

b. 客户端:

客户端是指网络中可以访问这个NFS输出目录的计算机

客户端常用的指定方式

  • 指定ip地址的主机:192.168.0.200
  • 指定子网中的所有主机:192.168.0.0/24 192.168.0.0/255.255.255.0
  • 指定域名的主机:david.bsmart.cn
  • 指定域中的所有主机:*.bsmart.cn
  • 所有主机:*

c. 选项:

选项用来设置输出目录的访问权限、用户映射等。

NFS主要有3类选项:

访问权限选项

  • 设置输出目录只读:ro
  • 设置输出目录读写:rw

用户映射选项

  • all_squash:将远程访问的所有普通用户及所属组都映射为匿名用户或用户组(nfsnobody);
  • no_all_squash:与all_squash取反(默认设置);
  • root_squash:将root用户及所属组都映射为匿名用户或用户组(默认设置);
  • no_root_squash:与rootsquash取反;
  • anonuid=xxx:将远程访问的所有用户都映射为匿名用户,并指定该用户为本地用户(UID=xxx);
  • anongid=xxx:将远程访问的所有用户组都映射为匿名用户组账户,并指定该匿名用户组账户为本地用户组账户(GID=xxx);

其它选项

  • secure:限制客户端只能从小于1024的tcp/ip端口连接nfs服务器(默认设置);
  • insecure:允许客户端从大于1024的tcp/ip端口连接服务器;
  • sync:将数据同步写入内存缓冲区与磁盘中,效率低,但可以保证数据的一致性;
  • async:将数据先保存在内存缓冲区中,必要时才写入磁盘;
  • wdelay:检查是否有相关的写操作,如果有则将这些写操作一起执行,这样可以提高效率(默认设置);
  • no_wdelay:若有写操作则立即执行,应与sync配合使用;
  • subtree:若输出目录是一个子目录,则nfs服务器将检查其父目录的权限(默认设置);
  • no_subtree:即使输出目录是一个子目录,nfs服务器也不检查其父目录的权限,这样可以提高效率;

六、NFS服务器的启动与停止

在对exports文件进行了正确的配置后,就可以启动NFS服务器了。

1、启动NFS服务器

为了使NFS服务器能正常工作,需要启动portmap和nfs两个服务,并且portmap一定要先于nfs启动。

# service portmap start
# service nfs start

2、查询NFS服务器状态

# service portmap status
# service nfs status  

3、停止NFS服务器

要停止NFS运行时,需要先停止nfs服务再停止portmap服务,对于系统中有其他服务(如NIS)需要使用时,不需要停止portmap服务

# service nfs stop
# service portmap stop

4、设置NFS服务器的自动启动状态

对于实际的应用系统,每次启动LINUX系统后都手工启动nfs服务器是不现实的,需要设置系统在指定的运行级别自动启动portmap和nfs服务。

# chkconfig --list portmap
# chkconfig --list nfs

设置portmap和nfs服务在系统运行级别3和5自动启动。

# chkconfig --level 35 portmap on
# chkconfig --level 35 nfs on

七、实例

1、将NFS Server 的/home/david/ 共享给192.168.1.0/24网段,权限读写。

服务器端文件详细如下:

# vi /etc/exports

/home/david 192.168.1.0/24(rw)

2、重启portmap 和nfs 服务

# service portmap restart
# service nfs restart
# exportfs

3、服务器端使用showmount命令查询NFS的共享状态

# showmount -e    //默认查看自己共享的服务,前提是要DNS能解析自己,不然容易报错

# showmount -a    //显示已经与客户端连接上的目录信息

4、客户端使用showmount命令查询NFS的共享状态

# showmount -e NFS服务器IP

5、客户端挂载NFS服务器中的共享目录

命令格式

# mount NFS服务器IP:共享目录 本地挂载点目录

# mount 192.168.1.108:/home/david/ /tmp/david/

# mount |grep nfs

挂载成功。

查看文件是否和服务器端一致。

6、NFS的共享权限和访问控制

现在我们在/tmp/david/ 里面建立一个文件,看看权限是什么

# touch 20130103

这里出现Permission denied,是因为NFS 服务器端共享的目录本身的写权限没有开放给其他用户,在服务器端打开该权限。

# chmod 777 -R /home/david/

再次在客户端/tmp/david/ 里面建立一个文件

我用root 用户建立的文件,变成了nfsnobody 用户。

NFS有很多默认的参数,打开/var/lib/nfs/etab 查看分享出来的/home/david/ 完整权限设定值。

# cat /var/lib/nfs/etab

默认就有sync,wdelay,hide 等等,no_root_squash 是让root保持权限,root_squash 是把root映射成nobody,no_all_squash 不让所有用户保持在挂载目录中的权限。所以,root建立的文件所有者是nfsnobody。

下面我们使用普通用户挂载、写入文件测试。

# su – david

$ cd /tmp/david/

$ touch 2013david

普通用户写入文件时就是自己的名字,这也就保证了服务器的安全性。

  关于权限的分析

  1. 客户端连接时候,对普通用户的检查

    a. 如果明确设定了普通用户被压缩的身份,那么此时客户端用户的身份转换为指定用户;

    b. 如果NFS server上面有同名用户,那么此时客户端登录账户的身份转换为NFS server上面的同名用户;

    c. 如果没有明确指定,也没有同名用户,那么此时 用户身份被压缩成nfsnobody;

  2. 客户端连接的时候,对root的检查

    a. 如果设置no_root_squash,那么此时root用户的身份被压缩为NFS server上面的root;

    b. 如果设置了all_squash、anonuid、anongid,此时root 身份被压缩为指定用户;

    c. 如果没有明确指定,此时root用户被压缩为nfsnobody;

    d. 如果同时指定no_root_squash与all_squash 用户将被压缩为 nfsnobody,如果设置了anonuid、anongid将被压缩到所指定的用户与组;

7、卸载已挂载的NFS共享目录

# umount /tmp/david/

八、启动自动挂载nfs文件系统

格式:

<server>:</remote/export> </local/directory> nfs < options> 0 0

# vi /etc/fstab

保存退出,重启系统。

查看/home/david 有没有自动挂载。

自动挂载成功。

九、相关命令

1、exportfs

如果我们在启动了NFS之后又修改了/etc/exports,是不是还要重新启动nfs呢?这个时候我们就可以用exportfs 命令来使改动立刻生效,该命令格式如下:

  # exportfs [-aruv]

  -a 全部挂载或卸载 /etc/exports中的内容 


  -r 重新读取/etc/exports 中的信息 ,并同步更新/etc/exports、/var/lib/nfs/xtab


  -u 卸载单一目录(和-a一起使用为卸载所有/etc/exports文件中的目录)


  -v 在export的时候,将详细的信息输出到屏幕上。

具体例子: 


  # exportfs -au 卸载所有共享目录


  # exportfs -rv 重新共享所有目录并输出详细信息

2、nfsstat

查看NFS的运行状态,对于调整NFS的运行有很大帮助。

3、rpcinfo

查看rpc执行信息,可以用于检测rpc运行情况的工具,利用rpcinfo -p 可以查看出RPC开启的端口所提供的程序有哪些。

 

4、showmount

  -a 显示已经于客户端连接上的目录信息


  -e IP或者hostname 显示此IP地址分享出来的目录

5、netstat

可以查看出nfs服务开启的端口,其中nfs 开启的是2049,portmap 开启的是111,其余则是rpc开启的。

最后注意两点,虽然通过权限设置可以让普通用户访问,但是挂载的时候默认情况下只有root可以去挂载,普通用户可以执行sudo。

NFS server 关机的时候一点要确保NFS服务关闭,没有客户端处于连接状态!通过showmount -a 可以查看,如果有的话用kill killall pkill 来结束,(-9 强制结束)

转自:http://www.cnblogs.com/mchina/archive/2013/01/03/2840040.html

apache虚拟主机



//虚拟主机的配置
也谈apache本地虚拟主机测试环境的搭建

最近的一个系统要求必须在网站根目录下运行,因为生成静态页啥的,URL处理非常繁琐,真正的上线运行就不用担心那么多的问题,那肯定是根目录cool,而我本地的开发环境AMP的根目录下已经遍地狼藉,实在不能再往里头填东西,不然,找文件又得找半天,甚至最大的问题是到时候怎么完整的导出整个网站,不多目录也不少一个文件。唉!~有些愁眉不展。。。咦!强大的apache不是告诉我们可以虚拟主机的吗?我本地搞个不就成了。于是想起小猪写的一篇文章,挖他博客,不难就找到了,照做之,我将本地的域名指定为mydown,我apache的端口是8080,重启apache后,遗憾的事情还是发生了,我输入http://mydown:8080/和http://localhost:8080/指向的同一个站点,这就意味着我原来的资源全部不能访问,哦~
千万不能!继续找资料,查apache手册,总算弄明白怎么整了。
第一步和小猪一样,在C:WINDOWSsystem32driversetchosts的文件中加上一条

127.0.0.1 mydown

然后打开apache的配置文件httpd.conf,翻到最后,加上下面这些

NameVirtualHost *

<VirtualHost *>    
   DocumentRoot D:/myserver/wwwroot
   ServerName localhost:8080
</VirtualHost>

<VirtualHost *>    
   DocumentRoot D:/myserver/wwwroot/mydown
   ServerName mydown:8080
</VirtualHost>

意思就是不同的域名指向不同的目录,重启apache,大功告成~,嘿嘿!今儿就挖了小猪的墙角一回。至于每句的意思,如果您不懂,我还是建议您查查手册了。


如何完成Apache虚拟主机设置?


实现Apache虚拟主机方法一:

开启虚拟主机配置文件


对httpd.conf进行设置:


1.注释以下三行
#ServerAdmin
#ServerName
#DocumentRoot


2.去掉mod_proxy.so和mod_proxy_ajp.so的注释


3.#Virtual hosts

#Include conf/extra/httpd-vhosts.conf (查找这行,把前面的#去掉)
Include /conf/extra/httpd-vhosts.conf


4.打开 /conf/extra/httpd-vhosts.conf

#192.168.1.24为本地Ip
NameVirtualHost 192.168.1.24:80

# php项目

<VirtualHost 192.168.1.24:80>

<Directory “D:/phproot/phpmyadmin”>

DirectoryIndex index.php

</Directory>

ServerAdmin baibiao @gmail.com

ServerName email.sinoepiboly.com

#ServerAlias email.sinoepiboly.com

DocumentRoot D:/phproot/phpmyadmin
ErrorLog “logs/dummy-host-www.nianw-error.log”
CustomLog “logs/dummy-host-www.nianw-access.log” common

</VirtualHost>

其中

Errorlog:是本域名的错误日志

CustomLog:是本域名的访问日志

==================================================================


方法二:

多二级域名主机开发环境设置说明

第一步 DNS解析实现


找到本机的host文件,一般在 C:WINNTsystem32driversetc,在文件结尾添加:


127.0.0.1 localhost

127.0.0.1 www.com.cn

127.0.0.1 home.com.cn

127.0.0.1 mail.com.cn

127.0.0.1 music.com.cn

127.0.0.1 browseusers.com.cn

127.0.0.1 search.com.cn

127.0.0.1 invite.com.cn

127.0.0.1 rank.com.cn

127.0.0.1 blog.com.cn

127.0.0.1 favorites.com.cn

127.0.0.1 forum.com.cn

127.0.0.1 groups.com.cn

127.0.0.1 events.com.cn

127.0.0.1 classifieds.com.cn

127.0.0.1 signup.com.cn

192.168.6.153 i.com.cn

192.168.6.153 x.com.cn


其中

192.168.6.2 i.com.cn

192.168.6.2 x.com.cn

技术人员不作修改,是美工页面专用的


第二步:apache 设置

打开httpd.conf,以music.com.cn为例,其他栏目类似添加,在文件最后加入:

1. 保证
Listen 80

2. 打开注释
NameVirtualHost *:80

3. 在末尾添加
<VirtualHost *:80>
ServerAdmin [email protected]
DocumentRoot “C:/Program Files/Apache Group/Apache2/htdocs/espace/music”
ServerName music.com.cn
</VirtualHost>
<VirtualHost *:80>
ServerAdmin [email protected]
DocumentRoot “C:/Program Files/Apache Group/Apache2/htdocs/espace/blog”
ServerName blog.com.cn
</VirtualHost>

重启即可,Apache虚拟主机设置完成。

windows下配置nginx+php环境



 刚看到nginx这个词,我很好奇它的读法(engine x),我的直译是“引擎x”,一般引“擎代”表了性能,而“x”大多出现是表示“xtras(额外的效果)”,那么整个词的意思就是类似“极致效果”,“额外性能”。当然这里不是要来唠嗑,以上是题外话。


  nginx相较于我们熟悉的apache、IIS的优势,就我浅入浅出的了解,在于“反向代理”和“负载均衡”。因此考虑到能够为Web服务器节省资源,它可以代替apache来提供Web服务。那么上正题了,nginx有这么多优势,那在windows下如何来配置nginx+php环境?网上看到还是那么多转载来转载去的文章。这里就我配置的过程,来介绍一下:


1、首先需要准备的应用程序包。


  nginx:nginx/Windows-1.0.4


  php:php-5.2.16-nts-Win32-VC6-x86.zip (nginx下php是以FastCGI的方式运行,所以我们下载非线程安全也就是nts的php包)


  (还会用到)RunHiddenConsole:RunHiddenConsole.zip


2、安装与配置。


 1)php的安装与配置。


  直接解压下载好的php包,到D盘wnmp目录(D:wnmp),这里把解压出来的文件夹重命名成php5。进入文件夹修改php.ini-recommended文件为php.ini,并用Editplus或者Notepad++打开来。找到

extension_dir = "./ext"


更改为

extension_dir = "D:/wnmp/php5/ext"


往下看,再找到

;extension=php_mysql.dll ;extension=php_mysqli.dll


前面指定了php的ext路径后,只要把需要的扩展包前面所对应的“;”去掉,就可以了。这里打开php_mysql.dll和php_mysqli.dll,让php支持mysql。当然不要忘掉很重要的一步就是,把php5目录下的libmysql.dll文件复制到C:Windows目录下,也可以在系统变量里面指定路径,当然这里我选择了更为方便的方法^_^。


到这里,php已经可以支持mysql了。


  接下来我们来配置php,让php能够与nginx结合。找到


;cgi.fix_pathinfo=1


我们去掉这里的封号。

cgi.fix_pathinfo=1


这一步非常重要,这里是php的CGI的设置。


 2)nginx的安装与配置。


  把下载好的nginx-1.0.4的包同样解压到D盘的wnmp目录下,并重命名为nginx。接下来,我们来配置nginx,让它能够和php协同工作。进入nginx的conf目录,打开nginx的配置文件nginx.conf,找到

location / { root html;      #这里是站点的根目录 index index.html index.htm; }


root  html;改为root   D:/wnmp/www;


再往下,找到


复制代码

# pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000 # #location ~ .php$ { # root html; # fastcgi_pass 127.0.0.1:9000; # fastcgi_index index.php; # fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name; # include fastcgi_params; #}

复制代码


先将前面的“#”去掉,同样将root  html;改为root   D:/wnmp/www;。再把标记为红色的/scripts改为“$document_root”,这里的“$document_root”就是指前面“root”所指的站点路径,这是改完后的:

复制代码

# pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000 # location ~ .php$ { root D:/wnmp/www; fastcgi_pass 127.0.0.1:9000; fastcgi_index index.php; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; include fastcgi_params; }

复制代码


保存配置文件,就可以了。


  nginx+php的环境就初步配置好了,来跑跑看。我们可以输入命令 


来启动php,并手动启动nginx,当然也可以利用脚本来实现。


  首先把下载好的RunHiddenConsole.zip包解压到nginx目录内,RunHiddenConsole.exe的作用是在执行完命令行脚本后可以自动关闭脚本,而从脚本中开启的进程不被关闭。然后来创建脚本,命名为“start_nginx.bat”,我们在Notepad++里来编辑它

复制代码

@echo off REM Windows 下无效 REM set PHP_FCGI_CHILDREN=5 REM 每个进程处理的最大请求数,或设置为 Windows 环境变量 set PHP_FCGI_MAX_REQUESTS=1000 echo Starting PHP FastCGI... RunHiddenConsole D:/wnmp/php5/php-cgi.exe -b 127.0.0.1:9000 -c D:/wnmp/php5/php.ini echo Starting nginx... RunHiddenConsole D:/wnmp/nginx/nginx.exe -p D:/wnmp/nginx

复制代码


再另外创建一个名为stop_nginx.bat的脚本用来关闭nginx

@echo off echo Stopping nginx...   taskkill /F /IM nginx.exe > nul echo Stopping PHP FastCGI... taskkill /F /IM php-cgi.exe > nul exit


做好后,是这样的



这样,我们的服务脚本也都创建完毕了。双击start_nginx.bat看看进程管理器是不是有两个nginx.exe的进程和一个php-cgi.exe的进程呢?



这样nginx服务就启动了,而且php也以fastCGI的方式运行了。


到站点目录下,新建一个phpinfo.php的文件,在里面编辑

<?php phpinfo(); ?>


保存后,打开浏览器输入“http://localhost/phpinfo.php”,如果看到



就说明,nginx+php的环境已经配置好了,呵呵~


转自:http://www.cnblogs.com/huayangmeng/archive/2011/06/15/2081337.html

Memcached 集群架构方面的问题


转自:http://kb.cnblogs.com/page/69074/

这里收集了经常被问到的关于memcached的问题

  集群架构方面的问题

  memcached是怎么工作的?

  Memcached的神奇来自两阶段哈希(two-stage hash)。Memcached就像一个巨大的、存储了很多<key,value>对的哈希表。通过key,可以存储或查询任意的数据。

  客户端可以把数据存储在多台memcached上。当查询数据时,客户端首先参考节点列表计算出key的哈希值(阶段一哈希),进而选中一个节点;客户端将请求发送给选中的节点,然后memcached节点通过一个内部的哈希算法(阶段二哈希),查找真正的数据(item)。

  举个列子,假设有3个客户端1, 2, 3,3台memcached A, B, C:

  Client 1想把数据”barbaz”以key “foo”存储。Client 1首先参考节点列表(A, B, C),计算key “foo”的哈希值,假设memcached B被选中。接着,Client 1直接connect到memcached B,通过key “foo”把数据”barbaz”存储进去。  Client 2使用与Client 1相同的客户端库(意味着阶段一的哈希算法相同),也拥有同样的memcached列表(A, B, C)。

  于是,经过相同的哈希计算(阶段一),Client 2计算出key “foo”在memcached B上,然后它直接请求memcached B,得到数据”barbaz”。

  各种客户端在memcached中数据的存储形式是不同的(perl Storable, php serialize, java hibernate, JSON等)。一些客户端实现的哈希算法也不一样。但是,memcached服务器端的行为总是一致的。

  最后,从实现的角度看,memcached是一个非阻塞的、基于事件的服务器程序。这种架构可以很好地解决C10K problem ,并具有极佳的可扩展性。

  可以参考A Story of Caching ,这篇文章简单解释了客户端与memcached是如何交互的。

  memcached最大的优势是什么?

  请仔细阅读上面的问题(即memcached是如何工作的)。Memcached最大的好处就是它带来了极佳的水平可扩展性,特别是在一个巨大的系统中。由于客户端自己做了一次哈希,那么我们很容易增加大量memcached到集群中。memcached之间没有相互通信,因此不会增加 memcached的负载;没有多播协议,不会网络通信量爆炸(implode)。memcached的集群很好用。内存不够了?增加几台memcached吧;CPU不够用了?再增加几台吧;有多余的内存?在增加几台吧,不要浪费了。

  基于memcached的基本原则,可以相当轻松地构建出不同类型的缓存架构。除了这篇FAQ,在其他地方很容易找到详细资料的。

  看看下面的几个问题吧,它们在memcached、服务器的local cache和MySQL的query cache之间做了比较。这几个问题会让您有更全面的认识。

  memcached和MySQL的query cache相比,有什么优缺点?

  把memcached引入应用中,还是需要不少工作量的。MySQL有个使用方便的query cache,可以自动地缓存SQL查询的结果,被缓存的SQL查询可以被反复地快速执行。Memcached与之相比,怎么样呢?MySQL的query cache是集中式的,连接到该query cache的MySQL服务器都会受益。

  • 当您修改表时,MySQL的query cache会立刻被刷新(flush)。存储一个memcached item只需要很少的时间,但是当写操作很频繁时,MySQL的query cache会经常让所有缓存数据都失效。
  • 在多核CPU上,MySQL的query cache会遇到扩展问题(scalability issues)。在多核CPU上,query cache会增加一个全局锁(global lock), 由于需要刷新更多的缓存数据,速度会变得更慢。
  • 在MySQL的query cache中,我们是不能存储任意的数据的(只能是SQL查询结果)。而利用memcached,我们可以搭建出各种高效的缓存。比如,可以执行多个独立的查询,构建出一个用户对象(user object),然后将用户对象缓存到memcached中。而query cache是SQL语句级别的,不可能做到这一点。在小的网站中,query cache会有所帮助,但随着网站规模的增加,query cache的弊将大于利。
  • query cache能够利用的内存容量受到MySQL服务器空闲内存空间的限制。给数据库服务器增加更多的内存来缓存数据,固然是很好的。但是,有了memcached,只要您有空闲的内存,都可以用来增加memcached集群的规模,然后您就可以缓存更多的数据。

  memcached和服务器的local cache(比如PHP的APC、mmap文件等)相比,有什么优缺点?

  首先,local cache有许多与上面(query cache)相同的问题。local cache能够利用的内存容量受到(单台)服务器空闲内存空间的限制。不过,local cache有一点比memcached和query cache都要好,那就是它不但可以存储任意的数据,而且没有网络存取的延迟。

  • local cache的数据查询更快。考虑把highly common的数据放在local cache中吧。如果每个页面都需要加载一些数量较少的数据,考虑把它们放在local cached吧。
  • local cache缺少集体失效(group invalidation)的特性。在memcached集群中,删除或更新一个key会让所有的观察者觉察到。但是在local cache中, 我们只能通知所有的服务器刷新cache(很慢,不具扩展性),或者仅仅依赖缓存超时失效机制。
  • local cache面临着严重的内存限制,这一点上面已经提到。

  memcached的cache机制是怎样的?

  Memcached主要的cache机制是LRU(最近最少用)算法+超时失效。当您存数据到memcached中,可以指定该数据在缓存中可以呆多久Which is forever, or some time in the future。如果memcached的内存不够用了,过期的slabs会优先被替换,接着就轮到最老的未被使用的slabs。

  memcached如何实现冗余机制?


  不实现!我们对这个问题感到很惊讶。Memcached应该是应用的缓存层。它的设计本身就不带有任何冗余机制。如果一个memcached节点失去了所有数据,您应该可以从数据源(比如数据库)再次获取到数据。您应该特别注意,您的应用应该可以容忍节点的失效。不要写一些糟糕的查询代码,寄希望于memcached来保证一切!如果您担心节点失效会大大加重数据库的负担,那么您可以采取一些办法。比如您可以增加更多的节点(来减少丢失一个节点的影响),热备节点(在其他节点down了的时候接管IP),等等。

  memcached如何处理容错的?


  不处理!:) 在memcached节点失效的情况下,集群没有必要做任何容错处理。如果发生了节点失效,应对的措施完全取决于用户。节点失效时,下面列出几种方案供您选择:

  • 忽略它! 在失效节点被恢复或替换之前,还有很多其他节点可以应对节点失效带来的影响。
  • 把失效的节点从节点列表中移除。做这个操作千万要小心!在默认情况下(余数式哈希算法),客户端添加或移除节点,会导致所有的缓存数据不可用!因为哈希参照的节点列表变化了,大部分key会因为哈希值的改变而被映射到(与原来)不同的节点上。
  • 启动热备节点,接管失效节点所占用的IP。这样可以防止哈希紊乱(hashing chaos)。
  • 如果希望添加和移除节点,而不影响原先的哈希结果,可以使用一致性哈希算法(consistent hashing)。您可以百度一下一致性哈希算法。支持一致性哈希的客户端已经很成熟,而且被广泛使用。去尝试一下吧!
  • 两次哈希(reshing)。当客户端存取数据时,如果发现一个节点down了,就再做一次哈希(哈希算法与前一次不同),重新选择另一个节点(需要注意的时,客户端并没有把down的节点从节点列表中移除,下次还是有可能先哈希到它)。如果某个节点时好时坏,两次哈希的方法就有风险了,好的节点和坏的节点上都可能存在脏数据(stale data)。

  如何将memcached中item批量导入导出?

  您不应该这样做!Memcached是一个非阻塞的服务器。任何可能导致memcached暂停或瞬时拒绝服务的操作都应该值得深思熟虑。向memcached中批量导入数据往往不是您真正想要的!想象看,如果缓存数据在导出导入之间发生了变化,您就需要处理脏数据了;如果缓存数据在导出导入之间过期了,您又怎么处理这些数据呢?

  因此,批量导出导入数据并不像您想象中的那么有用。不过在一个场景倒是很有用。如果您有大量的从不变化的数据,并且希望缓存很快热(warm)起来,批量导入缓存数据是很有帮助的。虽然这个场景并不典型,但却经常发生,因此我们会考虑在将来实现批量导出导入的功能。

  Steven Grimm,一如既往地,,在邮件列表中给出了另一个很好的例子:http://lists.danga.com/pipermail/memcached/2007-July/004802.html

  但是我确实需要把memcached中的item批量导出导入,怎么办??

  好吧好吧。如果您需要批量导出导入,最可能的原因一般是重新生成缓存数据需要消耗很长的时间,或者数据库坏了让您饱受痛苦。

  如果一个memcached节点down了让您很痛苦,那么您还会陷入其他很多麻烦。您的系统太脆弱了。您需要做一些优化工作。比如处理”惊群”问题(比如 memcached节点都失效了,反复的查询让您的数据库不堪重负…这个问题在FAQ的其他提到过),或者优化不好的查询。记住,Memcached 并不是您逃避优化查询的借口。

  如果您的麻烦仅仅是重新生成缓存数据需要消耗很长时间(15秒到超过5分钟),您可以考虑重新使用数据库。这里给出一些提示:

  • 使用MogileFS(或者CouchDB等类似的软件)在存储item。把item计算出来并dump到磁盘上。MogileFS可以很方便地覆写item,并提供快速地访问。您甚至可以把MogileFS中的item缓存在memcached中,这样可以加快读取速度。 MogileFS+Memcached的组合可以加快缓存不命中时的响应速度,提高网站的可用性。
  • 重新使用MySQL。MySQL的InnoDB主键查询的速度非常快。如果大部分缓存数据都可以放到VARCHAR字段中,那么主键查询的性能将更好。从memcached中按key查询几乎等价于MySQL的主键查询:将key 哈希到64-bit的整数,然后将数据存储到MySQL中。您可以把原始(不做哈希)的key存储都普通的字段中,然后建立二级索引来加快查询…key被动地失效,批量删除失效的key,等等。

  上面的方法都可以引入memcached,在重启memcached的时候仍然提供很好的性能。由于您不需要当心”hot”的item被memcached LRU算法突然淘汰,用户再也不用花几分钟来等待重新生成缓存数据(当缓存数据突然从内存中消失时),因此上面的方法可以全面提高性能。

  关于这些方法的细节,详见博客:http://dormando.livejournal.com/495593.html

  memcached是如何做身份验证的?


  没有身份认证机制!memcached是运行在应用下层的软件(身份验证应该是应用上层的职责)。memcached的客户端和服务器端之所以是轻量级的,部分原因就是完全没有实现身份验证机制。这样,memcached可以很快地创建新连接,服务器端也无需任何配置。

  如果您希望限制访问,您可以使用防火墙,或者让memcached监听unix domain socket。

  memcached的多线程是什么?如何使用它们?


  线程就是定律(threads rule)!在Steven Grimm和Facebook的努力下,memcached 1.2及更高版本拥有了多线程模式。多线程模式允许memcached能够充分利用多个CPU,并在CPU之间共享所有的缓存数据。memcached使用一种简单的锁机制来保证数据更新操作的互斥。相比在同一个物理机器上运行多个memcached实例,这种方式能够更有效地处理multi gets。

  如果您的系统负载并不重,也许您不需要启用多线程工作模式。如果您在运行一个拥有大规模硬件的、庞大的网站,您将会看到多线程的好处。

  更多信息请参见:http://code.sixapart.com/svn/memcached/trunk/server/doc/threads.txt

  简单地总结一下:命令解析(memcached在这里花了大部分时间)可以运行在多线程模式下。memcached内部对数据的操作是基于很多全局锁的(因此这部分工作不是多线程的)。未来对多线程模式的改进,将移除大量的全局锁,提高memcached在负载极高的场景下的性能。

  memcached能接受的key的最大长度是多少?


  key的最大长度是250个字符。需要注意的是,250是memcached服务器端内部的限制,如果您使用的客户端支持”key的前缀”或类似特性,那么key(前缀+原始key)的最大长度是可以超过250个字符的。我们推荐使用使用较短的key,因为可以节省内存和带宽。

  memcached对item的过期时间有什么限制?


  过期时间最大可以达到30天。memcached把传入的过期时间(时间段)解释成时间点后,一旦到了这个时间点,memcached就把item置为失效状态。这是一个简单但obscure的机制。

  memcached最大能存储多大的单个item?

  1MB。如果你的数据大于1MB,可以考虑在客户端压缩或拆分到多个key中。

  为什么单个item的大小被限制在1M byte之内?


  啊…这是一个大家经常问的问题!

  简单的回答:因为内存分配器的算法就是这样的。

  详细的回答:Memcached的内存存储引擎(引擎将来可插拔…),使用slabs来管理内存。内存被分成大小不等的slabs chunks(先分成大小相等的slabs,然后每个slab被分成大小相等chunks,不同slab的chunk大小是不相等的)。chunk的大小依次从一个最小数开始,按某个因子增长,直到达到最大的可能值。

  如果最小值为400B,最大值是1MB,因子是1.20,各个slab的chunk的大小依次是:slab1 – 400B slab2 – 480B slab3 – 576B …

  slab中chunk越大,它和前面的slab之间的间隙就越大。因此,最大值越大,内存利用率越低。Memcached必须为每个slab预先分配内存,因此如果设置了较小的因子和较大的最大值,会需要更多的内存。

  还有其他原因使得您不要这样向memcached中存取很大的数据…不要尝试把巨大的网页放到mencached中。把这样大的数据结构load和unpack到内存中需要花费很长的时间,从而导致您的网站性能反而不好。

  如果您确实需要存储大于1MB的数据,你可以修改slabs.c:POWER_BLOCK的值,然后重新编译memcached;或者使用低效的malloc/free。其他的建议包括数据库、MogileFS等。

  我可以在不同的memcached节点上使用大小不等的缓存空间吗?这么做之后,memcached能够更有效地使用内存吗?


  Memcache客户端仅根据哈希算法来决定将某个key存储在哪个节点上,而不考虑节点的内存大小。因此,您可以在不同的节点上使用大小不等的缓存。但是一般都是这样做的:拥有较多内存的节点上可以运行多个memcached实例,每个实例使用的内存跟其他节点上的实例相同。

  什么是二进制协议,我该关注吗?

  关于二进制最好的信息当然是二进制协议规范:http://code.google.com/p/memcached/wiki/MemcacheBinaryProtocol

  二进制协议尝试为端提供一个更有效的、可靠的协议,减少客户端/服务器端因处理协议而产生的CPU时间。

根据Facebook的测试,解析ASCII协议是memcached中消耗CPU时间最多的环节。所以,我们为什么不改进ASCII协议呢?

  在这个邮件列表的thread中可以找到一些旧的信息:http://lists.danga.com/pipermail/memcached/2007-July/004636.html

  memcached的内存分配器是如何工作的?为什么不适用malloc/free!?为何要使用slabs?


  实际上,这是一个编译时选项。默认会使用内部的slab分配器。您确实确实应该使用内建的slab分配器。最早的时候,memcached只使用malloc/free来管理内存。然而,这种方式不能与OS的内存管理以前很好地工作。反复地malloc/free造成了内存碎片,OS最终花费大量的时间去查找连续的内存块来满足malloc的请求,而不是运行memcached进程。如果您不同意,当然可以使用malloc!只是不要在邮件列表中抱怨啊:)

  slab分配器就是为了解决这个问题而生的。内存被分配并划分成chunks,一直被重复使用。因为内存被划分成大小不等的slabs,如果item的大小与被选择存放它的slab不是很合适的话,就会浪费一些内存。Steven Grimm正在这方面已经做出了有效的改进。

  邮件列表中有一些关于slab的改进(power of n 还是 power of 2)和权衡方案:http://lists.danga.com/pipermail/memcached/2006-May/002163.html

http://lists.danga.com/pipermail/memcached/2007-March/003753.html

  如果您想使用malloc/free,看看它们工作地怎么样,您可以在构建过程中定义USE_SYSTEM_MALLOC。这个特性没有经过很好的测试,所以太不可能得到开发者的支持。

  更多信息:http://code.sixapart.com/svn/memcached/trunk/server/doc/memory_management.txt

  memcached是原子的吗?


  当然!好吧,让我们来明确一下:

  所有的被发送到memcached的单个命令是完全原子的。如果您针对同一份数据同时发送了一个set命令和一个get命令,它们不会影响对方。它们将被串行化、先后执行。即使在多线程模式,所有的命令都是原子的,除非程序有bug:)

  命令序列不是原子的。如果您通过get命令获取了一个item,修改了它,然后想把它set回memcached,我们不保证这个item没有被其他进程(process,未必是操作系统中的进程)操作过。在并发的情况下,您也可能覆写了一个被其他进程set的item。

  memcached 1.2.5以及更高版本,提供了gets和cas命令,它们可以解决上面的问题。如果您使用gets命令查询某个key的item,memcached会给您返回该item当前值的唯一标识。如果您覆写了这个item并想把它写回到memcached中,您可以通过cas命令把那个唯一标识一起发送给memcached。如果该item存放在memcached中的唯一标识与您提供的一致,您的写操作将会成功。如果另一个进程在这期间也修改了这个item,那么该item存放在memcached中的唯一标识将会改变,您的写操作就会失败。

  通常,基于memcached中item的值来修改item,是一件棘手的事情。除非您很清楚自己在做什么,否则请不要做这样的事情。