调优您的 LAMP 应用程序的 5 种简单方法

简介: Linux、Apache、MySQL 和 PHP (LAMP) 架构是目前最流行的 web 服务器架构的之一。作者 John Mertic 审视了每个 LAMP 应用程序为实现最优性能而应当利用的 5 项内容。包括:使用操作码缓存、优化您的 PHP 设置、管理您的 require() 和 include()、优化您的数据库、有效缓存数据。

阅读全文→

smarteng 发布于 2011-03-01 13:09

Discuz!的Memcache缓存实现

前言:
在PHP+MySQL架构的站点中,本文重点从MySQL的角度去分析如何使Discuz!论坛(或者类似的PHP+MySQL架构的程序)应对大访问量。同时给出一些使用Memcache去减轻MySQL压力的建议。其中很多数据是个人测试的结果,如有不同意见,敬请留言告之。另外由于个人思维的问题,行文比较跳跃,特此声明!

系统分析:
单纯的从MySQL的角度出发,单台MySQL的数据库负载到每天上亿次的操作(每秒大概1100次MySQL操作,然后乘以86400)应该不是非常困难的事情。按照这个数据也就是说一个单MySQL服务器的论坛来说可以跑到2千万PV是不成问题的,我相信国内绝大部分的论坛都不可能做到每天2千万的 PV,但实际情况并不是如此。当论坛PV超过百万的时候一台WEB早已经不堪重负了。

就我手头的一些数据显示,目前的Discuz!论坛的基本服务器架构是前面Squid顶着,后面才是一台DB在撑着。这种架构中,web服务器压力增大可以通过并行增加服务器解决,而MySQL压力却无处释放,在不考虑MySQL官方服务的情况下,我们通过合理的利用Memcache是可以达到减轻 MySQL服务器负载的。

可能会有朋友说我们可以对数据表进行分表(注:此处分表是指通过PHP程序去分表,比如pw,dv的分表)处理,但是当前的情况是一台DB服务器已经不能支撑当前的数据处理了,通过PHP对MySQL进行的分表依然不能减轻MySQL的负载。(注:本段文字针对已经成型的系统,如果是独立开发的系统在架构前期就进行数据的同步分区还是不错的。)[break]

还可能有朋友会说利用MySQL的主从构架,如果你提出这个问题,我就很明确的告诉你,回去看看手册吧。在Mysql Master/Slave 模式中,Slave主要是来备份数据的,只有当Master出现故障时,Slave才会接过Master的服务,对外部请求进行处理,直到Master恢复正常。就是说:在Master/Slave中,要么是Master在服务,要么是Slave在服务,不会Master/Slave同时提供服务。使用MySQL主从依然不能有效的降低MySQL的负载。

或许你又会问我为什么不使用MySQL集群(MySQL Cluster),那可是白花花的银子啊,同等金钱的付出下,获得最大的收益才是王道。PS:说句题外话,MySQL手册中将MySQL集群解释为MySQL簇,不习惯。

其实在MySQL5.1中的MySQL分区(MySQL Partition)是个很好的东西,它允许根据可以设置为任意大小的规则,跨文件系统分配单个表的多个部分。实际上,表的不同部分在不同的位置被存储为单独的表。我认为这个才是当前情况下,最积极有效的降低MySQL负载的解决方法之一。但是遗憾的是,这种MySQL分区的方式我个人没有使用过的经历,也不见有相当充分的案例表明它是稳定的或者不稳定的。所以我还在徘徊中。如果你知道,请麻烦告之!有朋友说腾讯是在用MySQL分区,但是遗憾的是我没有得到确切的数据。

好了分析总结了这么多种降低MySQL负载的方式之后,在用户环境需求等特定条件下,我得出结论在当前情况下,缓解Discuz!论坛的MySQL负载比较有效的方法就是使用Memcache!

使用Memcache的理由:
1.Web Server(Lighttpd、Nginx据说都比Apache效率高好多,大家可以试用下)对CPU要求高,对内存要求低;而Memcached Server是对CPU要求低,对内存要求高,所以可以搭配使用。在对前端的Web Server上安装Memcached Server是可行的。
2.金钱金钱金钱,最少的付出,获得最大的收益。
3.简单简单简单,对于一个架构合理的系统来说,添加Memcache的支持可能只是一个批量处理文件的过程

Discuz!使用Memcache
1.在config.inc.php中增加
$memcachehost = '127.0.0.1';
$memcacheport = 11211;
$memcachelife = 60;

2.在include/common.inc.php中
$mem = new Memcache;
$mem->connect($memcachehost, $memcacheport);

3.修改include/db_mysql.class.php中的fetch_array、query这两个方法,并添加query_mysql方法,代码如下:
function fetch_array($query, $result_type = MYSQL_ASSOC) {
return is_resource($query) ? mysql_fetch_array($query, $result_type) : $query[0];
}

function query_memcache($sql, $type = '') {
global $mem,$memcachelife;

$key = md5($sql);
if(!($query = $mem->get($key))) {
$query = $this->query($sql, $type);
while($item  = $this->fetch_array($query)) {
$res[] = $item;
}
$query = $res;
$mem->set($key, $query , 0, $memcachelife);
}
return $query;
}

function query($sql, $type = '') {
global $debug, $discuz_starttime, $sqldebug, $sqlspenttimes;

$func = $type == 'UNBUFFERED' && @function_exists('mysql_unbuffered_query') ?
'mysql_unbuffered_query' : 'mysql_query';
if(!($query = $func($sql, $this->link)) && $type != 'SILENT') {
$this->halt('MySQL Query Error', $sql);
}

if(substr($sql, 0, 6) == 'SELECT') {
echo '<font color="red">Cache SQL</font>:<font color="green">'.$sql.'</font><br /><br />';
} else {
echo '<font color="red">Flash SQL</font>:<font color="green">'.$sql.'</font><br /><br />';
}

$this->querynum++;
return $query;
}

4.将需要使用Memcache缓存的SQL查询的代码由
$db->query(

修改为
$db->query_memcache(

注意并将
while($post = $db->fetch_array($query)) {

修改为
foreach($query as $post) {

没有while的$db->fetch_array可以不用修改。

下面代码有用得着的就拿去:
preg_replace("/while\([$](\w+)\s*\=\s*[$]db->fetch_array\([$]query\)\)/is", "foreach(\$query as \$\\1)", $file);

回头放出个小工具批量替换下就可以了。
在EditPlus中可以这样替换:while\([$](.*) = [$]db->fetch_array\([$]query\)\)替换为foreach($query as $\1)
5.完成了,测试吧!~
 


smarteng 发布于 2010-01-10 09:55

优化网站性能的14条规则 yahoo的14军规

前言:
对于一个高性能的Web站点,性能提升的2个主要突破点
.响应时间.
效率
当前集中考虑的主要是响应时间方面,平时我们关注比较多的是后台Web服务器端的优化,通过Yahoo的实际数据分析,80-90%的终端响应时间是由前端决定的,所以是前端的优化也是非常重要的,存在巨大的改善空间,本文集中讨论前端优化。8/2性能准则:80%的性能问题是由20%的原因引起的,我们集中精力在这20%的原因上来改善80%的性能。通常的经验告诉我们影响Web性能的几个主要因素由Cache的大小和有无,Http请求,静态和动态网页的分布,cookie的大小并行下等等,但是在具体的时间中到底如何去做,没有更多详细的探讨。从2004年起,yahoo专门成立了一个性能分析团队,他们通过开发实际工具和进行严格的数据分析,来改善他们那产品的性能。本文就是这些研究成果的最新总结,在提高网页速度方面Yahoo!的 Exceptional Performance团队提供了14种最佳实践,减少了Yahoo!25%-50%的响应时间,本文将详细介绍这些准则和背后的原理,从而使你可以更快地构建网页,减少响应时间,从前端到后端全面挖掘网页开发更大的潜能!到底如何优化一个网站,提高性能了?基本的原则是:

(1)优化网站资源,主要是图片

(2)优化脚本资源,例如JavaScript

(3)优化网页的样式表,例如CSS

核心思想是减少下载的数据量,尽可能使用少的HTTP请求,合并脚本和CSS表,使用合并的图片,尽量避免重定向和frames的使用。

来自Yahoo的14条准则[break]

由上述思想发散开发,总结起来yahoo的14条准则如下:

1.尽可能使用少的HTTP请求 Make fewer HTTP requests;

    这个是很重要的一条,具体措施是使用Image maps 和Inline Images;合并CSS和脚本代码。比如对于Image Maps

 

(服务器端)server-side

 

<a href="navbar.cgi"><img ismap src="imagemap.gif"></a>→ http://.../navbar.cgi?127,13

 

(客户端)client-side – preferred

 

<img usemap="#map1" border=0 src="/images/imagemap.gif">

 

<map name="map1"> <area shape="rect" coords="0,0,31,31" href="home.html" title="Home">

 

   …

 

</map>

 

(缺点是)drawbacks:

 

图片需要是连续的。

2.使用内容分发网络 Use a CDN;

 

在发布你的动态内容之前发布网站的静态内容,比如使用广泛的Akamai

 

3.增加一个期限头部 Add an Expires header

 

不仅仅是对图片设置,对于脚本和样式表同样需要设置,在Apache中具体的配置方法如下:

 

     ExpiresActive On

 

     ExpiresByType application/x-javascript "modification plus 2 years"

 

     ExpiresByType text/css "modification plus 5 years"

 

当你修改一个资源的时候,修改资源的名称(自动进行),给文件名称加一个时间戳,例如img_1385413733.png;

 

同时候将资源纳入版本控制系统中,比如使用CVS个SVN,例如img_1.2.png。

4.压缩组件Gzip components

 

当前90% 的浏览器都支持压缩,压缩不仅仅是指HTML,脚本,CSS和XML都可以压缩。

 

在Apache中修改下面的配置来支持压缩

 

Apache 2.x: mod_deflate

 

AddOutputFilterByType DEFLATE text/html text/css application/x-javascript

 

对于HTTP请求和响应,表现如下

 

HTTP request: Accept-Encoding: gzip, deflate

 

HTTP response: Content-Encoding: gzip Vary: Accept-Encoding

5.将CSS置为页面的顶部 Put CSS at the top

 

   主要原因是CSS样式表会阻塞HTML在IE中的显示(如果没有下载完的话)

 

6.将JS移植页面的底部 Move JS to the bottom

 

主要原因是脚本的执行会阻塞并行下载和阻塞其他元素在IE中的展示

 

7.避免使用CSS表达式 Avoid CSS expressions

 

比如下面的例子:

 

   width: expression(

 

         document.body.clientWidth < 600 ?

 

       “600px” : “auto” );

 

原因是由于鼠标的移动,按下键等操作事件会是的上述表达式执行多次

 

8. 是JS和CSS从外部包含 Make JS and CSS external

 

可以减少HTML文档的大小,同时增加脚本的重用,但是会增加请求数

 

但是HomePage是一个例外,在Homepage中使用Inline和 PostOnload效果不错,yahoo和goolgede

 

首页都这样处理了;

 

9.减少DNS的查找 Reduce DNS lookups

 

一个典型的DNS查询是20-120ms,DNS 查询的时间直接影响到并行下载的效率,尽量减少主机的域名数目。

 

常用浏览器的缓存时间设置如下:

 

IE

 

DnsCacheTimeout: 30 minutes

 

KeepAliveTimeout: 1 minute

 

ServerInfoTimeout: 2 minutes

 

Firefox

 

network.dnsCacheExpiration: 1 minute

 

network.dnsCacheEntries: 20

 

network.http.keep-alive.timeout: 5 minutes

 

Fasterfox: 1 hour, 512 entries, 30 seconds

 

10.精简JS Minify JS

 

这个主要涉及到JS的编码和优化

 

11.避免重定向 Avoid redirects

 

通常Web服务器返回的是3XX的状态码,常常是301和302,可以通过添加过期头来缓存重定向


smarteng 发布于 2009-11-22 22:28

40个技巧优化您的PHP代码

1.如果一个方法能被静态,那就声明他为静态的,速度可提高1/4;
2.echo的效率高于print,因为echo没有返回值,print返回一个整型;
3.在循环之前设置循环的最大次数,而非在在循环中;
4.销毁变量去释放内存,特别是大的数组;
5.避免使用像__get, __set, __autoload等魔术方法;
6.requiere_once()比较耗资源;
7.在includes和requires中使用绝对路径,这样在分析路径花的时间更少;
8.如果你需要得sexinsex到脚本执行时的时间,$_SERVER['REQUSET_TIME']优于time();
9.能使用字符处理函数的,尽量用他们,因为效率高于正则;//php100.com
10.str_replace字符替换比正则替换preg_replace快,但strtr比str_replace又快1/4;[break]
11.如果一个函数既能接受数组又能接受简单字符做为参数,例如字符替换,并且参数列表不是太长,可以考虑多用一些简洁的替换语句,一次只替换一个字符,而不是接受数组做为查找和替换参数。大事化小,1+1>2;
12.用@掩盖错误会降低脚本运行速度;
13.$row['id']比$row[id]速度快7倍,建议养成数组键加引号的习惯;
14.错误信息很有用;
15.在循环里别用函数,例如For($x=0; $x < count($array); $x), count()函数在外面先计算;
16.在方法里建立局部变量速度最快,97xxoo几乎和在方法里调用局部变量一样快;
17.建立一个全局变量要比局部变量要慢2倍;
18.建立一个对象属性(类里面的变量)例如($this->prop++)比局部变量要慢3倍;
19.建立一个未声明的局部变量要比一个初始化的局部变量慢9-10倍;
20.声明一个未被任何一个函数使用过的全局变量也会使性能降低(和声明相同数量的局部变量一样),PHP可能去检查这个全局变量是否存在;
21.方法的性能和在一个类里面定义的方法的数目没有关系,因为我添加10个或多个方法到测试的类里面(这些方法在测试方法的前后)后性能没什么差异;
22.在子类里方法的性能优于在基类中;//PHP100中文网
23.只调用一个参数并且函数体为空的函数运行花费的时间等于7-8次$localvar++运算,而一个类似的方法(类里的函数)运行等于大约15次$localvar++运算;
24.Surrounding your string by ‘ instead of ” will make things interpret a little faster since php looks for variables inside “…” but not inside ‘…’. Of course you can only do this when you don’t need to have variables in the string.
25.当输出字符串时用逗号代替点分割更快些。注意:这只对echo起作用,这个函数能接受一些字符串作为参数;
26.在apache服务器里一个php脚本页面比相应的HTML静态页面生成至少要多花2-10倍的时间,建议多用些静态HTML页面和少量的脚步;
27.除非你的安装了缓存,不然你的php脚本每次被访问都需要被重编译。建议安装个php缓存程序,这样通过去除一些重复的编译来很明显的提高你20-100%的性能;
28.建议用memcached,高性能的分布式内存对象缓存系统,提高动态网络应用程序性能,减轻数据库的负担;
29.使用ip2long()和long2ip()函数把IP地址转成整型存放进数据库而非字符型。这几乎能降低1/4的存储空间。同时可以很容易对地址进行排序和快速查找;
30.使用checkdnsrr()通过域名存在性来确认部分email地址的有效性,这个内置函数能保证每一个的域名对应一个IP地址;//php100.com
31.如果你在使用php5和mysql4.1以上的版本,考虑使用mysql_*的改良函数mysqli_*;
32.试着喜欢使用三元运算符(?:);
33.在你想在彻底重做你的项目前,看看PEAR有没有你需要的。PEAR是个巨大的资源库,很多php开发者都知道;
34.使用highlight_file()能自动打印一份很好格式化的页面源代码的副本; //php100.com
35. 使用error_reporting(0)函数来预防潜在的敏感信息显示给用户。理想的错误报告应该被完全禁用在php.ini文件里。可是如果你在用一个共享的虚拟主机,php.ini你不能修改,那么你最好添加error_reporting(0)函数,放在每个脚本文件的第一行(或用 require_once()来加载)这能有效的保护敏感的SQL查询和路径在出错时不被显示;
36.使用 gzcompress() 和gzuncompress()对容量大的字符串进行压缩(解压)在存进(取出)数据库时。这种内置的函数使用gzip算法能压缩到90%;
37.通过参数变量地址得引用来使一个函数有多个返回值。你可以在变量前加个“&”来表示按地址传递而非按值传递;
38.Fully understand “magic quotes” and the dangers of SQL injection. I’m hoping that most developers reading this are already familiar with SQL injection. However, I list it here because it’s absolutely critical to understand. If you’ve never heard the term before, spend the entire rest of the day googling and reading.//php100.com
39.使用strlen()因为要调用一些其他操作例如lowercase和hash表查询所以速度不是太好,我们可以用isset()来实现相似的功能,isset()速度优于strlen();
40.When incrementing or decrementing the value of the variable $i++ happens to be a tad slower then ++$i. //php100.com This is something PHP specific and does not apply to other languages, so don’t go modifying your C or Java code thinking it’ll suddenly become faster, it won’t. ++$i happens to be faster in PHP because instead of 4 opcodes used for $i++ you only need 3. Post incrementation actually causes in the creation of a temporary var that is then incremented. While pre-incrementation increases the original value directly. This is one of the optimization that opcode optimized like Zend’s PHP optimizer. It is a still a good idea to keep in mind since not all opcode optimizers perform this optimization and there are plenty of ISPs and servers running without an opcode optimizer.


smarteng 发布于 2009-11-18 09:16

针对Web服务器总体优化

一、文件系统
在fstab里加入noatime,如
#cat /etc/fstab
/dev/sda1          /home                  ext3    noatime,defaults        1 2
reboot或者重新mount生效


二、Tcp优化
在/etc/sysctl.conf里加入
net.ipv4.netfilter.ip_conntrack_tcp_timeout_syn_recv = 3
net.ipv4.tcp_syncookies = 1 #启用syncookies
net.ipv4.tcp_max_syn_backlog = 8192 #定义backlog队列容纳的最大半连接数
net.ipv4.tcp_fin_timeout = 30
net.ipv4.tcp_keepalive_time = 1800
net.ipv4.tcp_window_scaling = 0
net.ipv4.tcp_sack = 0
net.ipv4.tcp_timestamps = 0
老实说,对于sysctl.conf究竟能不能用起来还真不知道[break]


三、虚拟内存优化
/etc/sysctl.conf
vm.lower_zone_protection = 100
四、I/O调度器
在grub.conf的相应启动选项里加入elevator=deadline,如:
kernel /vmlinuz-2.6.6 ro root=/dev/sda6 elevator=deadline
这里用了Deadline的I/O调度器,它比系统默认的AnticipatoryI/O调度器更为小巧,在数据吞吐量非常大的数据库系统中表现得更有优势。


五、内核源代码参数修改
修改/usr/src/linux/include/linux/posix_types.h
#define __FD_SETSIZE 1024 65536
设置fd_set支持的最大数量
修改/usr/src/linux/include/linux/fs.h
#define INR_OPEN 1024 ?nbsp;65536
#define NR_FILE 8192 ?nbsp;65536
#define NR_RESERVED_FILES 10 ?nbsp;128
设置最大打开文件数量(TCP连接数量)
修改/usr/src/linux/include/net/tcp.h
#define TCP_TIMEWAIT_LEN (60*HZ) ?nbsp;1*HZ
#define TCP_SYNACK_RETRIES  5 ?nbsp;3
设置在backlog队列里的半连接的重试次数,每次都会花相应的时间,本质上
也是减少重试时间
make menuconfig中,去掉没用的选项,打开以下选项的开关:
High Memory Support (支持4GB以上内存)
Symmetric multi-processing support (支持多CPU)
TCP syncookie support (可以防DOS)
设置文件打开数等的其他方法(好处就是可以不重新编译内核)
在 /etc/init.d/sshd里加入(统一加在. /etc/rc.d/init.d/functions行后面)
ulimit -n 65535 >;/dev/null 2>;&1
ulimit -u 16384 >;/dev/null 2>;&1
重起sshd:nohup /etc/init.d/sshd restart &
BTW:其实limits.conf不好,因为他需要root su - user遗传给普通用户
如果直接远程登陆是没效果的
六、apache
1、安装:采用worker线程工作模式
./configure --prefix=/prefix
--enable-mods-shared=all
--enable-deflate
--with-mpm=worker
chmod +s /prefix/bin/httpd #允许普通用户也可以启动apache
2、配置:httpd.conf
Timeout 60
KeepAlive On
MaxKeepAliveRequests 150
KeepAliveTimeout  5
StartServers        3
MaxClients        3200  #支持3200个客户端哦
ServerLimit        32
MinSpareThreads    50
MaxSpareThreads    250
ThreadLimit  200
ThreadsPerChild    100
MaxRequestsPerChild 0
#上面的几个参数对机器要求比较高,如果是比较差的机器,每台机1000个连接最多了,你可能要重新设置参数UseCanonicalName Off
HostnameLookups Off
ServerSignature Off
 


smarteng 发布于 2009-10-18 17:03

MySQL索引分析和优化

一、什么是索引?

索引用来快速地寻找那些具有特定值的记录,所有MySQL索引都以B-树的形式保存。如果没有索引,执行查询时MySQL必须从第一个记录开始扫描整个表的所有记录,直至找到符合要求的记录。表里面的记录数量越多,这个操作的代价就越高。如果作为搜索条件的列上已经创建了索引,MySQL无需扫描任何记录即可迅速得到目标记录所在的位置。如果表有1000个记录,通过索引查找记录至少要比顺序扫描记录快100倍。

假设我们创建了一个名为people的表:[break]

CREATE TABLE people ( peopleid SMALLINT NOT NULL, name CHAR(50) NOT NULL );
 


然后,我们完全随机把1000个不同name值插入到people表。

对于索引中的每一项,MySQL在内部为它保存一个数据文件中实际记录所在位置的“指针”。因此,如果我们要查找name等于“Mike”记录的peopleid(SQL命令为“SELECT peopleid FROM people WHERE name=\'Mike\';”),MySQL能够在name的索引中查找“Mike”值,然后直接转到数据文件中相应的行,准确地返回该行的peopleid(999)。在这个过程中,MySQL只需处理一个行就可以返回结果。如果没有“name”列的索引,MySQL要扫描数据文件中的所有记录,即1000个记录!显然,需要MySQL处理的记录数量越少,则它完成任务的速度就越快。

二、索引的类型

  MySQL提供多种索引类型供选择:

 

普通索引
这是最基本的索引类型,而且它没有唯一性之类的限制。普通索引可以通过以下几种方式创建:
创建索引,例如CREATE INDEX <索引的名字> ON tablename (列的列表);
修改表,例如ALTER TABLE tablename ADD INDEX [索引的名字] (列的列表);
创建表的时候指定索引,例如CREATE TABLE tablename ( [...], INDEX [索引的名字] (列的列表) );
 

唯一性索引
这种索引和前面的“普通索引”基本相同,但有一个区别:索引列的所有值都只能出现一次,即必须唯一。唯一性索引可以用以下几种方式创建:
创建索引,例如CREATE UNIQUE INDEX <索引的名字> ON tablename (列的列表);
修改表,例如ALTER TABLE tablename ADD UNIQUE [索引的名字] (列的列表);
创建表的时候指定索引,例如CREATE TABLE tablename ( [...], UNIQUE [索引的名字] (列的列表) );
 

主键
主键是一种唯一性索引,但它必须指定为“PRIMARY KEY”。如果你曾经用过AUTO_INCREMENT类型的列,你可能已经熟悉主键之类的概念了。主键一般在创建表的时候指定,例如“CREATE TABLE tablename ( [...], PRIMARY KEY (列的列表) ); ”。但是,我们也可以通过修改表的方式加入主键,例如“ALTER TABLE tablename ADD PRIMARY KEY (列的列表); ”。每个表只能有一个主键。
 

全文索引
MySQL从3.23.23版开始支持全文索引和全文检索。在MySQL中,全文索引的索引类型为FULLTEXT。全文索引可以在VARCHAR或者TEXT类型的列上创建。它可以通过CREATE TABLE命令创建,也可以通过ALTER TABLE或CREATE INDEX命令创建。对于大规模的数据集,通过ALTER TABLE(或者CREATE INDEX)命令创建全文索引要比把记录插入带有全文索引的空表更快。本文下面的讨论不再涉及全文索引,要了解更多信息,请参见MySQL documentation。
三、单列索引与多列索引

索引可以是单列索引,也可以是多列索引。

这个数据片段中有四个名字为“Mikes”的人(其中两个姓Sullivans,两个姓McConnells),有两个年龄为17岁的人,还有一个名字与众不同的Joe Smith。

这个表的主要用途是根据指定的用户姓、名以及年龄返回相应的peopleid。例如,我们可能需要查找姓名为Mike Sullivan、年龄17岁用户的peopleid(SQL命令为SELECT peopleid FROM people WHERE firstname=\'Mike\' AND lastname=\'Sullivan\' AND age=17;)。由于我们不想让MySQL每次执行查询就去扫描整个表,这里需要考虑运用索引。

首先,我们可以考虑在单个列上创建索引,比如firstname、lastname或者age列。如果我们创建firstname列的索引(ALTER TABLE people ADD INDEX firstname (firstname);),MySQL将通过这个索引迅速把搜索范围限制到那些firstname=\'Mike\'的记录,然后再在这个“中间结果集”上进行其他条件的搜索:它首先排除那些lastname不等于“Sullivan”的记录,然后排除那些age不等于17的记录。当记录满足所有搜索条件之后,MySQL就返回最终的搜索结果。

由于建立了firstname列的索引,与执行表的完全扫描相比,MySQL的效率提高了很多,但我们要求MySQL扫描的记录数量仍旧远远超过了实际所需要的。虽然我们可以删除firstname列上的索引,再创建lastname或者age列的索引,但总地看来,不论在哪个列上创建索引搜索效率仍旧相似。

为了提高搜索效率,我们需要考虑运用多列索引。如果为firstname、lastname和age这三个列创建一个多列索引,MySQL只需一次检索就能够找出正确的结果!下面是创建这个多列索引的SQL命令:

 

 
ALTER TABLE people ADD INDEX fname_lname_age (firstname,lastname,age);
 


由于索引文件以B-树格式保存,MySQL能够立即转到合适的firstname,然后再转到合适的lastname,最后转到合适的age。在没有扫描数据文件任何一个记录的情况下,MySQL就正确地找出了搜索的目标记录!

那么,如果在firstname、lastname、age这三个列上分别创建单列索引,效果是否和创建一个firstname、lastname、age的多列索引一样呢?答案是否定的,两者完全不同。当我们执行查询的时候,MySQL只能使用一个索引。如果你有三个单列的索引,MySQL会试图选择一个限制最严格的索引。但是,即使是限制最严格的单列索引,它的限制能力也肯定远远低于firstname、lastname、age这三个列上的多列索引。

四、最左前缀

多列索引还有另外一个优点,它通过称为最左前缀(Leftmost Prefixing)的概念体现出来。继续考虑前面的例子,现在我们有一个firstname、lastname、age列上的多列索引,我们称这个索引为fname_lname_age。当搜索条件是以下各种列的组合时,MySQL将使用fname_lname_age索引:

 

firstname,lastname,age
firstname,lastname
firstname
从另一方面理解,它相当于我们创建了(firstname,lastname,age)、(firstname,lastname)以及(firstname)这些列组合上的索引。下面这些查询都能够使用这个fname_lname_age索引:

 

找到有关该命令的更多说明。下面是一个例子:
 

 
 table type possible_keys key key_len ref rows Extra
people ref fname_lname_age fname_lname_age 102 const,const,const 1 Where used


下面我们就来看看这个EXPLAIN分析结果的含义。

 

table:这是表的名字。
type:连接操作的类型。下面是MySQL文档关于ref连接类型的说明:

“对于每一种与另一个表中记录的组合,MySQL将从当前的表读取所有带有匹配索引值的记录。如果连接操作只使用键的最左前缀,或者如果键不是UNIQUE或PRIMARY KEY类型(换句话说,如果连接操作不能根据键值选择出唯一行),则MySQL使用ref连接类型。如果连接操作所用的键只匹配少量的记录,则ref是一种好的连接类型。”

在本例中,由于索引不是UNIQUE类型,ref是我们能够得到的最好连接类型。

如果EXPLAIN显示连接类型是“ALL”,而且你并不想从表里面选择出大多数记录,那么MySQL的操作效率将非常低,因为它要扫描整个表。你可以加入更多的索引来解决这个问题。预知更多信息,请参见MySQL的手册说明。
 
possible_keys:
可能可以利用的索引的名字。这里的索引名字是创建索引时指定的索引昵称;如果索引没有昵称,则默认显示的是索引中第一个列的名字(在本例中,它是“firstname”)。默认索引名字的含义往往不是很明显。
 
Key:
它显示了MySQL实际使用的索引的名字。如果它为空(或NULL),则MySQL不使用索引。
 
key_len:
索引中被使用部分的长度,以字节计。在本例中,key_len是102,其中firstname占50字节,lastname占50字节,age占2字节。如果MySQL只使用索引中的firstname部分,则key_len将是50。
 
ref:
它显示的是列的名字(或单词“const”),MySQL将根据这些列来选择行。在本例中,MySQL根据三个常量选择行。
 
rows:
MySQL所认为的它在找到正确的结果之前必须扫描的记录数。显然,这里最理想的数字就是1。
 
Extra:
这里可能出现许多不同的选项,其中大多数将对查询产生负面影响。在本例中,MySQL只是提醒我们它将用WHERE子句限制搜索结果集。
 
七、索引的缺点

到目前为止,我们讨论的都是索引的优点。事实上,索引也是有缺点的。

首先,索引要占用磁盘空间。通常情况下,这个问题不是很突出。但是,如果你创建每一种可能列组合的索引,索引文件体积的增长速度将远远超过数据文件。如果你有一个很大的表,索引文件的大小可能达到操作系统允许的最大文件限制。

第二,对于需要写入数据的操作,比如DELETE、UPDATE以及INSERT操作,索引会降低它们的速度。这是因为MySQL不仅要把改动数据写入数据文件,而且它还要把这些改动写入索引文件。

【结束语】在大型数据库中,索引是提高速度的一个关键因素。不管表的结构是多么简单,一次500000行的表扫描操作无论如何不会快。如果你的网站上也有这种大规模的表,那么你确实应该花些时间去分析可以采用哪些索引,并考虑是否可以改写查询以优化应用。要了解更多信息,请参见MySQL manual。另外注意,本文假定你所使用的MySQL是3.23版,部分查询不能在3.22版MySQL上执行。
 膘叔指导:

1、经常被用来做搜索的,并段字段的值重复量不多的,做索引
2、经常被其他表用来做外键关联的,做索引


smarteng 发布于 2009-10-14 12:24