Yun's profile无尽的探索PhotosBlogLists Tools Help
Photo 1 of 21

Yun Fang

无尽的探索

从混沌到有序
June 19

jQuery form提交时中文乱码 [转]

关键字: ajax post 中文 乱码

在使用jsp开始时,一不小心就会出现中文乱码的情况。通常可以使用如下方法解决:
第一种解决方法:
从jsp到servlet统一使用utf-8编码.全部使用utf-8编码能省去很多麻烦,但一点不足是utf-8编码对汉字是使用3-4个字节,会加大网络传输量。
第二种方法:
1.jsp页面使用GBK
2.使用servlet过滤器设置request.setCharacterEncoding("GBK");google一下就能找到很多怎么使用过滤器转换编码。
以上两种方法能解决大部分的乱码问题,特别是第一种方法,能解决使用ajax提交时的中文乱码问题。如果采用第二种方法,那么在使用ajax提交表单时仍然会有中文乱码。这是因为ajax方式提交时js使用的是utf-8的编码,过滤器使用gbk进行转码就不正确了,应该使用utf-8进行转码。要解决这个问题,网上也有很多个版本,其中一个是我曾经采用的在客户端使用encodeURI,然后再在服务器端进行URLDecoder.decode,这种方案在偶尔处理一下中文是可行的,但是如果页面有大量数据录入,那么这种方案是不可行的。有没有好的解决方法呢?在经过新一轮的google之后,还是找到了方法。参考http://www.javaeye.com/topic/157698?page=1。原理就是根据httpheader中的内容来区分是ajax方式请求还是普通的请求。在jquery1.2.6中,ajaxSettings默认设置contentType为"application/x-www-form-urlencoded",在ajax方法中设置xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest");,我们只要在servlet中根据这两个参数值来判断是不是通过xmlhttprequest发起的请求。这里值得一提的是jquery在提交form时对参数进行了encodeURIComponent调用,参见param方法

Java代码 复制代码

  1. jQuery.each( a, function(){   
  2.                 s.push( encodeURIComponent(this.name) + "=" + encodeURIComponent( this.value ) );   
  3.             }); 
jQuery.each( a, function(){
				s.push( encodeURIComponent(this.name) + "=" + encodeURIComponent( this.value ) );
			});

所以传给servlet的就是utf-8编码了,那么我们在过滤器中就必须使用utf-8进行转码。
对其中的过滤器做了下修正,在IE下和chrome下,request.getContentType()的值为"application/x-www-form-urlencoded",但是在firefox下,这个值为"application/x-www-form-urlencoded; charset=UTF-8",不是很明白ff为什么会是这个值。
filter代码如下:

Java代码 复制代码

  1. package com.ajax.demo.action;   
  2. import java.io.IOException;   
  3. import javax.servlet.Filter;   
  4. import javax.servlet.FilterChain;   
  5. import javax.servlet.FilterConfig;   
  6. import javax.servlet.ServletException;   
  7. import javax.servlet.ServletRequest;   
  8. import javax.servlet.ServletResponse;   
  9. import javax.servlet.http.HttpServletRequest;   
  10. import javax.servlet.http.HttpServletResponse;   
  11. public class AjaxPostFilter implements Filter {   
  12. private static final String IE_CONTENT_TYPE = "application/x-www-form-urlencoded";   
  13. private static final String FF_AJAX_CONTENT_TYPE = "application/x-www-form-urlencoded; charset=UTF-8";   
  14. private static final String XMLHTTP_REQUEST = "XMLHttpRequest";   
  15. private static final String AJAX_CHARACTER_ENCODING_UTF8 = "UTF-8";   
  16. public void destroy() {   
  17. // TODO Auto-generated method stub
  18.     }   
  19. public void doFilter(ServletRequest servletRequest,   
  20.             ServletResponse servletResponse, FilterChain filterChain)   
  21. throws IOException, ServletException {   
  22.         HttpServletRequest request = (HttpServletRequest) servletRequest;   
  23.         HttpServletResponse response = (HttpServletResponse) servletResponse;   
  24.         String requestedWith = request.getHeader("x-requested-with");   
  25.         String type = request.getContentType();   
  26. if (XMLHTTP_REQUEST.equals(requestedWith)&& (FF_AJAX_CONTENT_TYPE.equals(type)   
  27.                 ||IE_CONTENT_TYPE.equals(type))) {   
  28.             request.setCharacterEncoding(AJAX_CHARACTER_ENCODING_UTF8);   
  29.             response.setCharacterEncoding(AJAX_CHARACTER_ENCODING_UTF8);   
  30. //          request.getParameterMap();
  31.         }   
  32.         filterChain.doFilter(request, response);   
  33.     }   
  34. public void init(FilterConfig arg0) throws ServletException {   
  35. // TODO Auto-generated method stub
  36.     }   
package com.ajax.demo.action;

import java.io.IOException;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class AjaxPostFilter implements Filter {

	private static final String IE_CONTENT_TYPE = "application/x-www-form-urlencoded";
	private static final String FF_AJAX_CONTENT_TYPE = "application/x-www-form-urlencoded; charset=UTF-8";
	private static final String XMLHTTP_REQUEST = "XMLHttpRequest";
	private static final String AJAX_CHARACTER_ENCODING_UTF8 = "UTF-8";

	public void destroy() {
		// TODO Auto-generated method stub

	}

	public void doFilter(ServletRequest servletRequest,
			ServletResponse servletResponse, FilterChain filterChain)
			throws IOException, ServletException {
		HttpServletRequest request = (HttpServletRequest) servletRequest;
		HttpServletResponse response = (HttpServletResponse) servletResponse;
		String requestedWith = request.getHeader("x-requested-with");
		String type = request.getContentType();
		if (XMLHTTP_REQUEST.equals(requestedWith)&& (FF_AJAX_CONTENT_TYPE.equals(type)
				||IE_CONTENT_TYPE.equals(type))) {
			request.setCharacterEncoding(AJAX_CHARACTER_ENCODING_UTF8);
			response.setCharacterEncoding(AJAX_CHARACTER_ENCODING_UTF8);
//			request.getParameterMap();
		}
		filterChain.doFilter(request, response);

	}

	public void init(FilterConfig arg0) throws ServletException {
		// TODO Auto-generated method stub

	}

}


web.xml配置,我用的是struts

Java代码 复制代码

  1. <filter>   
  2.         <filter-name>ajaxEncodeFilter</filter-name>   
  3.         <filter-class>com.ajax.demo.action.AjaxPostFilter</filter-class>   
  4.     </filter>   
  5.     <filter-mapping>   
  6.         <filter-name>ajaxEncodeFilter</filter-name>   
  7.         <url-pattern>*.do</url-pattern>   
  8.     </filter-mapping>   
  9.     <filter-mapping>   
  10.         <filter-name>ajaxEncodeFilter</filter-name>   
  11.         <url-pattern>*.jsp</url-pattern>   
  12.     </filter-mapping> 
<filter>
		<filter-name>ajaxEncodeFilter</filter-name>
		<filter-class>com.ajax.demo.action.AjaxPostFilter</filter-class>
	</filter>
	<filter-mapping>
		<filter-name>ajaxEncodeFilter</filter-name>
		<url-pattern>*.do</url-pattern>
	</filter-mapping>
	<filter-mapping>
		<filter-name>ajaxEncodeFilter</filter-name>
		<url-pattern>*.jsp</url-pattern>
	</filter-mapping>

这个filter应该在你的EncodeFilter之后,RoyMax说要在之前,我试了下是不行的。
经过这样设置之后,jsp使用gbk采用ajax提交就不会存在中文乱码了。
在使用chrome进行测试时,还发现chrome的一个奇怪问题,对于返回的结果,使用jquery.ajax处理

Java代码 复制代码

  1. success:function showResponse(responseText, statusText)  {    
  2. //这里name为input text id,如果"aa"在前,那么name的值会改为aa+返回的结果
  3.                     $('#name').val("aa"+responseText);   
  4. //如果改成$('#name').val(responseText+"aa"),那么name的值仍然是//responseText,“aa”没有加到后面去,不知道是什么原因
success:function showResponse(responseText, statusText)  { 
//这里name为input text id,如果"aa"在前,那么name的值会改为aa+返回的结果
    				$('#name').val("aa"+responseText);
//如果改成$('#name').val(responseText+"aa"),那么name的值仍然是//responseText,“aa”没有加到后面去,不知道是什么原因
}
 
参考:http://jasin2008.javaeye.com/blog/312854

February 15

又见情人节

二月的北京,春寒料峭。

清晨,还听见屋外呼呼地吹着大风。我和老婆伸着腰,抖擞精神,起床来。岳母张老师已经准备好了丰盛的早餐:咖喱排骨,凉拌紫白菜丝,凉拌榨菜丝,还有煮元宵。我和老婆都来不及洗漱就被喊上桌,先吃饭了。

感谢岳母准备好这么丰富营养的早餐,我们吃得饱饱的。然后洗漱完毕,收拾好行李,动身从昌平往城区赶了。还在车上,老婆就和我商量今天日子怎么安排?

今天可不是一般的日子,是情人节,认识老婆以来的过的第二个情人节。

早早的日子里,老婆就咕噜着要礼物,说如果没有礼物,就要把我啃成胡萝卜。这是可爱的、应该的事情啊,但买礼物也是件头疼的事情。记得去年情人节,也是在仓促之间,给当时的还是女朋友身份的老婆,买了一盒德芙巧克力,老婆珍视地放在床头,放了许久才开封逐一地吃掉。

又见情人节。怎么过?礼物是什么?老婆告诉我,给我的礼物她已经准备好了。我给老婆礼物了?还是个秘密。我只是坦言告诉老婆,给她的礼物需要她自己亲自去挑选。于是建议我们先去看电影,然后五道口去选礼物。

时光如沙漏中的沙一点点流逝,早晨还在昌平,到达知春里的时候已经是正午时分了。告诉妈妈我们不吃午饭了,径直乘车386去中关村,然后走路去美嘉电影城。在中关村大街上,随处可见一对对的情侣手牵着手,也时不时的看到街头花贩向路上的情侣兜售玫瑰。来到电影城,里面人头攒动,来看来电影的情侣很多。在三楼售票厅,买票的人已经排成一条长龙一直排到楼梯间。和老婆商量,决定不看电影了,回家去看下载的《马达加斯加2》。在此之前,还是去五道口,给老婆挑选礼物。

乘车731来到五道口,下了车,我拉着老婆来到宝岛眼镜店,这才告诉老婆,我要为她挑选一副太阳镜作为情人节礼物。老婆欣喜不已。总算我这番心思没有白费,还好没有被老婆啃成胡萝卜。店里的太阳镜样式倒是不少,老婆初初地挑来选去的,没有特别满意的。我们又出去逛了逛其它的三家眼镜店,都没挑到合适的,最后还是回到宝岛眼镜店,挑中了一款粉红色大眼镜的太阳镜。另外宝岛眼镜做优惠买一赠一的活动,如是还挑了另一副墨绿色的运动型太阳镜。老婆兴奋不已,回来的路上都戴着粉红色太阳镜。后来,我还给老婆买了一束三朵的含有一朵蓝色妖姬的玫瑰花。这个情人节总算是有个好的交代了。

老婆在路上说,她希望天天都是情人节。是啊老婆,我也希望天天都是情人节,这样我们能够天天这样愉快的逛街开心地购物。

二月000

November 14

Linux下修改网卡IP和网关

修改ifcfg-eth0文件内容:

[root@yourmachine ~]# vi /etc/sysconfig/network-scripts/ifcfg-eth0

DEVICE=eth0
ONBOOT=yes
BOOTPROTO=static
IPADDR=10.130.40.69
NETMASK=255.255.255.0
GATEWAY=10.130.40.1

修改resolv.conf:

[root@yourmachine ~]# vi /etc/resolv.conf

nameserver 10.130.1.1
nameserver 10.130.1.9
nameserver 202.106.196.115

重新启动网络配置
/etc/init.d/network restart

September 12

Linux下安装Apache httpd + Subversion服务配置

先决条件

为了让你的版本库使用HTTP网络,你基本上需要两个包里的四个部分。你需要Apache httpd2.0和包括的mod_dav DAV模块,Subversion和与之一同分发的mod_dav_svn文件系统提供者模块,如果你有了这些组件,网络化你的版本库将非常简单,如:

  • 配置好httpd 2.0,并且使用mod_dav启动,

  • 为mod_dav安装mod_dav_svn插件,它会使用Subversion的库访问版本库,并且

  • 配置你的httpd.conf来输出(或者说暴露)版本库。

你可以通过从源代码编译httpd和Subversion来完成前两个项目,也可以通过你的系统上的已经编译好的二进制包来安装。最新的使用Apache HTTP的Subversion的编译方法和Apache的配置方式可以看Subversion源代码树根目录的INSTALL文件。

 
基本的 Apache 配置

一旦你安装了必须的组件,剩下的工作就是在httpd.conf里配置Apache,使用LoadModule来加载mod_dav_svn模块,这个指示必须先与其它Subversion相关的其它配置出现,如果你的Apache使用缺省布局安装,你的mod_dav_svn模块一定在Apache安装目录(通常是在/usr/local/apache2)的modules子目录,LoadModule指示的语法很简单,影射一个名字到它的共享库的物理位置:

LoadModule dav_svn_module     modules/mod_dav_svn.so

注意,如果mod_dav是作为共享对象编译(而不是静态链接到httpd程序),你需要为它使用LoadModule语句,一定确定它在mod_dav_svn之前:

LoadModule dav_module         modules/mod_dav.so
LoadModule dav_svn_module     modules/mod_dav_svn.so
基本HTTP认证

最简单的客户端认证方式是通过HTTP基本认证机制,简单的使用用户名和密码来验证一个用户所自称的身份,Apache提供了一个htpasswd工具来管理可接受的用户名和密码,这些就是你希望赋予Subversion特别权限的用户,让我们给Sally和Harry赋予提交权限,首先,我们需要添加他们到密码文件。

$ ### First time: use -c to create the file
$ ### Use -m to use MD5 encryption of the password, which is more secure
$ htpasswd -cm /etc/svn-auth-file harry
New password: *****
Re-type new password: *****
Adding password for user harry
$ htpasswd -m /etc/svn-auth-file sally
New password: *******
Re-type new password: *******
Adding password for user sally
$

下一步,你需要在httpd.confLocation区里添加一些指示来告诉Apache如何来使用这些密码文件,AuthType指示指定系统使用的认证类型,这种情况下,我们需要指定Basic认证系统,AuthName是你提供给认证域一个任意名称,大多数浏览器会在向用户询问名称和密码的弹出窗口里显示这个名称,最终,使用AuthUserFile指示来指定使用htpasswd创建的密码文件的位置。

添加完这三个指示,你的<Location>区块一定像这个样子:

<Location /svn>
  DAV svn
  SVNParentPath /usr/local/svn
  AuthType Basic
  AuthName "Subversion repository"
  AuthUserFile /etc/svn-auth-file
</Location>

这个<Location>区块还没有结束,还不能做任何有用的事情,它只是告诉Apache当需要授权时,要去向Subversion客户端索要用户名和密码。我们这里遗漏的,是一些告诉Apache什么样客户端需要授权的指示。哪里需要授权,Apache就会在哪里要求认证,最简单的方式是保护所有的请求,添加Require valid-user来告诉Apache任何请求需要认证的用户:

<Location /svn>
  DAV svn
  SVNParentPath /usr/local/svn
  AuthType Basic
  AuthName "Subversion repository"
  AuthUserFile /etc/svn-auth-file
  Require valid-user
</Location>
授权选项

此刻,你已经配置了认证,但是没有配置授权,Apache可以要求用户认证并且确定身份,但是并没有说明这个身份的怎样允许和限制,这个部分描述了两种控制访问版本库的策略。

整体访问控制

最简单的访问控制形式是授权特定用户为只读版本库访问或者是读/写访问版本库。

你可以通过在<Location>区块添加Require valid-user指示来限制所有的版本库操作,使用我们前面的例子,这意味着只有客户端只可以是harry或者sally,而且他们必须提供正确的用户名及对应密码,这样允许对Subversion版本库做任何事:

<Location /svn>
  DAV svn
  SVNParentPath /usr/local/svn

  # how to authenticate a user
  AuthType Basic
  AuthName "Subversion repository"
  AuthUserFile /path/to/users/file

  # only authenticated users may access the repository
  Require valid-user
</Location>

有时候,你不需要这样严密,举个例子,Subversion自己在http://svn.collab.net/repos/svn的源代码允许全世界的人执行版本库的只读操作(例如检出我们的工作拷贝和使用浏览器浏览版本库),但是限定只有认证用户可以执行写操作。为了执行特定的限制,你可以使用LimitLimitExcept配置指示,就像Location指示,这个区块有开始和结束标签,你需要在<Location>中添加这个指示。

LimitLimitExcept中使用的参数是可以被这个区块影响的HTTP请求类型,举个例子,如果你希望禁止所有的版本库访问,只是保留当前支持的只读操作,你可以使用LimitExcept指示,并且使用GETPROPFINDOPTIONSREPORT请求类型参数,然后前面提到过的Require valid-user指示将会在<LimitExcept>区块中而不是在<Location>区块。

<Location /svn>
  DAV svn
  SVNParentPath /usr/local/svn

  # how to authenticate a user
  AuthType Basic
  AuthName "Subversion repository"
  AuthUserFile /path/to/users/file

  # For any operations other than these, require an authenticated user.
  <LimitExcept GET PROPFIND OPTIONS REPORT>
    Require valid-user
  </LimitExcept>
</Location>

这里只是一些简单的例子,想看关于Apache访问控制Require指示的更深入信息,可以查看Apache文档中的教程集http://httpd.apache.org/docs-2.0/misc/tutorials.html中的Security部分。

每目录访问控制

也可以使用Apache的httpd模块mod_authz_svn更加细致的设置访问权限,这个模块收集客户端传递过来的不同的晦涩的URL信息,询问mod_dav_svn来解码,然后根据在配置文件定义的访问政策来裁决请求。

如果你从源代码创建Subversion,mod_authz_svn会自动附加到mod_dav_svn,许多二进制分发版本也会自动安装,为了验证它是安装正确,确定它是在httpd.confLoadModule指示中的mod_dav_svn后面:

LoadModule dav_module         modules/mod_dav.so
LoadModule dav_svn_module     modules/mod_dav_svn.so
LoadModule authz_svn_module   modules/mod_authz_svn.so

为了激活这个模块,你需要配置你的Location区块的AuthzSVNAccessFile指示,指定保存路径中的版本库访问政策的文件。(一会儿我们将会讨论这个文件的格式。)

Apache非常的灵活,你可以从三种模式里选择一种来配置你的区块,作为开始,你选择一种基本的配置模式。(下面的例子非常简单;见Apache自己的文档中的认证和授权选项来查看更多的细节。)

最简单的区块是允许任何人可以访问,在这个场景里,Apache决不会发送认证请求,所有的用户作为“匿名”对待。

例 6.1. 匿名访问的配置实例。

<Location /repos>
  DAV svn
  SVNParentPath /usr/local/svn

  # our access control policy
  AuthzSVNAccessFile /path/to/access/file
</Location>
          

在另一个极端,你可以配置为拒绝所有人的认证,所有客户端必须提供证明自己身份的证书,你通过Require valid-user指示来阻止无条件的认证,并且定义一种认证的手段。

例 6.2. 一个认证访问的配置实例。

<Location /repos>
  DAV svn
  SVNParentPath /usr/local/svn

  # our access control policy
  AuthzSVNAccessFile /path/to/access/file

  # only authenticated users may access the repository
  Require valid-user

  # how to authenticate a user
  AuthType Basic
  AuthName "Subversion repository"
  AuthUserFile /path/to/users/file
</Location>

Apache和svnserve都可以给用户赋予(或拒绝)访问许可,通常是对整个版本库:一个用户可以读版本库(或不),而且他可以写版本库(或不)。如果可能,也可以定义细粒度的访问规则。一组用户可以有版本库的一个目录的读写权限,但是没有其它的;另一个目录可以是只对一少部分用户可读。

两种服务器都使用同样的文件格式描述路径为基础的规则,如果是Apache,需要加载mod_authz_svn模块,然后添加AuthzSVNAccessFile指示(在文件httpd.conf中)指明你的规则文件。(完全解释可以看“每目录访问控制”一节。)如果你在使用svnserve,你需要让你的authz-db变量(在svnserve.conf中)指向规则文件。

当你的服务器知道去查找规则文件时,就是需要定义规则的时候了。

访问文件的语法与svnserve.conf和运行中配置文件非常相似,以(#)开头的行会被忽略,在它的简单形式里,每一小节命名一个版本库和一个里面的路径,认证用户名是在每个小节中的选项名,每个选项的值描述了用户访问版本库的级别:r(只读)或者rw(读写),如果用户没有提到,访问是不允许的。

具体一点:这个小节的名称是[repos-name:path]或者[path]的形式,如果你使用SVNParentPath指示,指定版本库的名字是很重要的,如果你漏掉了他们,[/some/dir]部分就会与/some/dir的所有版本库匹配,如果你使用SVNPath指示,因此在你的小节中只是定义路径也很好—毕竟只有一个版本库。

[calc:/branches/calc/bug-142]
harry = rw
sally = r

在第一个例子里,用户harrycalc版本库中/branches/calc/bug-142具备完全的读写权利,但是用户sally只有读权利,任何其他用户禁止访问这个目录。

当然,访问控制是父目录传递给子目录的,这意味着我们可以为Sally指定一个子目录的不同访问策略:

[calc:/branches/calc/bug-142]
harry = rw
sally = r

# give sally write access only to the 'testing' subdir
[calc:/branches/calc/bug-142/testing]
sally = rw

现在Sally可以读取分支的testing子目录,但对其他部分还是只可以读,同时,Harry对整个分支还继续有完全的读写权限。

也可以通过继承规则明确的的拒绝某人的访问,只需要设置用户名参数为空:

[calc:/branches/calc/bug-142]
harry = rw
sally = r

[calc:/branches/calc/bug-142/secret]
harry =

在这个例子里,Harry对bug-142目录树有完全的读写权限,但是对其中的secret子目录没有任何访问权利。

需要记住的是最详细的的路径会被匹配,服务器首先找到匹配自己的目录,然后父目录,然后父目录的父目录,就这样继续下去,更具体的路径控制会覆盖所有继承下来的访问控制。

缺省情况下,没有人对版本库有任何访问,这意味着如果你已经从一个空文件开始,你会希望给所有用户对版本库根目录具备读权限,你可以使用星号(*)实现,用来代表“所有用户”:

[/]
* = r

这是一个普通的设置;注意在小节名中没有提到版本库名称,这让所有版本库对所有的用户可读。当所有用户对版本库有了读权利,你可以赋予特定用户对特定子目录的rw权限。

星号(*)参数需要在这里详细强调:这是匹配匿名用户的唯一模式,如果你已经配置了你的Location区块允许匿名和认证用户的混合访问,所有用户作为Apache匿名用户开始访问,mod_authz_svn会在要访问路径的定义中查找*值;如果找不到,Apache就会要求真实的客户端认证。

访问文件也允许你定义一组的用户,很像Unix的/etc/group文件:

[groups]
calc-developers = harry, sally, joe
paint-developers = frank, sally, jane
everyone = harry, sally, joe, frank, sally, jane

组可以被赋予通用户一样的访问权限,使用“at”(@)前缀来加以区别:

[calc:/projects/calc]
@calc-developers = rw

[paint:/projects/paint]
@paint-developers = rw
jane = r

组中也可以定义为包含其它的组:

[groups]
calc-developers = harry, sally, joe
paint-developers = frank, sally, jane
everyone = @calc-developers, @paint-developers
 

参考:

September 02

MySQL优化实例

在Apache, PHP, MySQL的体系架构中,MySQL对于性能的影响最大,也是关键的核心部分。对于Discuz!论坛程序也是如此,MySQL的设置是否合理优化,直接影响到论坛的速度和承载量!同时,MySQL也是优化难度最大的一个部分,不但需要理解一些MySQL专业知识,同时还需要长时间的观察统计并且根据经验进行判断,然后设置合理的参数。

下面我们了解一下MySQL优化的一些基础,MySQL的优化我分为两个部分,一是服务器物理硬件的优化;二是MySQL自身(my.cnf)的优化。

(1) 服务器硬件对MySQL性能的影响

a) 磁盘寻道能力(磁盘I/O),以目前高转速SCSI硬盘(7200转/秒)为例,这种硬盘理论上每秒寻道7200次,这是物理特性决定的,没有办法改变。MySQL每秒钟都在进行大量、复杂的查询操作,对磁盘的读写量可想而知。所以,通常认为磁盘I/O是制约MySQL性能的最大因素之一,对于日均访问量在100万PV以上的Discuz!论坛,由于磁盘I/O的制约,MySQL的性能会非常低下!解决这一制约因素可以考虑以下几种解决方案:

使用RAID-0+1磁盘阵列,注意不要尝试使用RAID-5,MySQL在RAID-5磁盘阵列上的效率不会像你期待的那样快; 抛弃传统的硬盘,使用速度更快的闪存式存储设备。经过Discuz!公司技术工程的测试,使用闪存式存储设备可比传统硬盘速度高出6-10倍左右。

b) CPU 对于MySQL应用,推荐使用S.M.P.架构的多路对称CPU,例如:可以使用两颗Intel Xeon 3.6GHz的CPU。

c) 物理内存对于一台使用MySQL的Database Server来说,服务器内存建议不要小于2GB,推荐使用4GB以上的物理内存。

(2) MySQL自身因素当解决了上述服务器硬件制约因素后,让我们看看MySQL自身的优化是如何操作的。对MySQL自身的优化主要是对其配置文件my.cnf中的各项参数进行优化调整。下面我们介绍一些对性能影响较大的参数。

由于my.cnf文件的优化设置是与服务器硬件配置息息相关的,因而我们指定一个假想的服务器硬件环境:

CPU: 2颗Intel Xeon 2.4GHz

内存: 4GB DDR

硬盘: SCSI 73GB

下面,我们根据以上硬件配置结合一份已经优化好的my.cnf进行说明:

# vi /etc/my.cnf

以下只列出my.cnf文件中[mysqld]段落中的内容,其他段落内容对MySQL运行性能影响甚微,因而姑且忽略。

  [mysqld]
  port = 3306
  serverid = 1 
  socket = /tmp/mysql.sock
  skip-locking
  # 避免MySQL的外部锁定,减少出错几率增强稳定性。
  skip-name-resolve

禁止MySQL对外部连接进行DNS解析,使用这一选项可以消除MySQL进行DNS解析的时间。但需要注意,如果开启该选项,则所有远程主机连接授权都要使用IP地址方式,否则MySQL将无法正常处理连接请求!

back_log = 384

指定MySQL可能的连接数量。当MySQL主线程在很短的时间内接收到非常多的连接请求,该参数生效,主线程花费很短的时间检查连接并且启动一个新线程。

back_log参数的值指出在MySQL暂时停止响应新请求之前的短时间内多少个请求可以被存在堆栈中。 如果系统在一个短时间内有很多连接,则需要增大该参数的值,该参数值指定到来的TCP/IP连接的侦听队列的大小。不同的操作系统在这个队列大小上有它自己的限制。

试图设定back_log高于你的操作系统的限制将是无效的。默认值为50。对于Linux系统推荐设置为小于512的整数。

     key_buffer_size = 256M
  # key_buffer_size指定用于索引的缓冲区大小,增加它可得到更好的索引处理性能。
对于内存在4GB左右的服务器该参数可设置为256M或384M。
注意:该参数值设置的过大反而会是服务器整体效率降低! max_allowed_packet = 4M thread_stack = 256K table_cache = 128K sort_buffer_size = 6M

查询排序时所能使用的缓冲区大小。注意:该参数对应的分配内存是每连接独占!如果有100个连接,那么实际分配的总共排序缓冲区大小为100 × 6 = 600MB。所以,对于内存在4GB左右的服务器推荐设置为6-8M。

 read_buffer_size = 4M

读查询操作所能使用的缓冲区大小。和sort_buffer_size一样,该参数对应的分配内存也是每连接独享!

join_buffer_size = 8M

联合查询操作所能使用的缓冲区大小,和sort_buffer_size一样,该参数对应的分配内存也是每连接独享!

     myisam_sort_buffer_size = 64M
    table_cache = 512
    thread_cache_size = 64
    query_cache_size = 64M

指定MySQL查询缓冲区的大小。可以通过在MySQL控制台执行以下命令观察:

 # > SHOW VARIABLES LIKE '%query_cache%';
 # > SHOW STATUS LIKE 'Qcache%';
 # 如果Qcache_lowmem_prunes的值非常大,则表明经常出现缓冲不够的情况;

如果Qcache_hits的值非常大,则表明查询缓冲使用非常频繁,如果该值较小反而会影响效率,那么可以考虑不用查询缓冲;Qcache_free_blocks,如果该值非常大,则表明缓冲区中碎片很多。

     tmp_table_size = 256M
    max_connections = 768

指定MySQL允许的最大连接进程数。如果在访问论坛时经常出现Too Many Connections的错误提 示,则需要增大该参数值。

     max_connect_errors = 10000000
    wait_timeout = 10

指定一个请求的最大连接时间,对于4GB左右内存的服务器可以设置为5-10。

     thread_concurrency = 8

该参数取值为服务器逻辑CPU数量×2,在本例中,服务器有2颗物理CPU,而每颗物理CPU又支持H.T超线程,所以实际取值为4 × 2 = 8

    skip-networking

开启该选项可以彻底关闭MySQL的TCP/IP连接方式,如果WEB服务器是以远程连接的方式访问MySQL数据库服务器则不要开启该选项!否则将无法正常连接!

 

参考:http://kb.discuz.net/index.php?title=MySQL%E4%BC%98%E5%8C%96%E5%AE%9E%E4%BE%8B