您的位置:

首页 >

5060官方网址大全 >

redhat 5.4下安装MYSQL全过程 >

redhat 5.4下安装MYSQL全过程

2016-06-02 08:02:37

分类:5060官方网址大全

前一段时间购买了阿里云的主机.因为自己需要的是用JAVA环境而不是用php+apache这种一键安装的东西,所以没有下载阿里云官网上说的sh.zip.况且里面也没有gzip这个解压包.所以果断自己来安装自己想要的东西 了. 购买的机器配置是: 操作系统:Red Hat Enterprise Linux Server 5.4 64位。 CPU:1核 内存:512M 硬盘空间:20G 带宽:1Mbps。 安装MYSQL数据需要用到以下三个包: MySQL-server-5.5.32-1.rhel5.x86_64.rpm MySQL-devel-5.5.32-1.rhel5.x86_64.rpm MySQL-client-5.5.32-1.rhel5.x86_64.rpm 首先,我们可以通过cd /home 目录下.其次,需要把我们需要的三个包下载下来. 因为这台主机上是有了wget这条命名的,所以我们可以直接通过下面三条命令来把我们需要的三个包下载下来 [[email protected] home]# wget http://cdn.mysql.com/Downloads/MySQL-5.5/MySQL-server-5.5.32-1.rhel5.x86_64.rpm [[email protected] home]# wget http://cdn.mysql.com/Downloads/MySQL-5.5/MySQL-devel-5.5.32-1.rhel5.x86_64.rpm [[email protected] home]# wget http://cdn.mysql.com/Downloads/MySQL-5.5/MySQL-client-5.5.32-1.rhel5.x86_64.rpm 接着开始安装MYSQL-SERVER 输入命令: rpm -ivh MySQL-server-5.5.32-1.rhel5.x86_64.rpm 过了一会发现控制台出现如下的提示. error: Failed dependencies: libaio.so.1()(64bit) is needed by MySQL-server-5.5.32-1.rhel5.x86_64 libaio.so.1(LIBAIO_0.1)(64bit) is needed by MySQL-server-5.5.32-1.rhel5.x86_64 libaio.so.1(LIBAIO_0.4)(64bit) is needed by MySQL-server-5.5.32-1.rhel5.x86_64 这个是因为这里少了一个里面的库,我试过在网上找了一个下载,然后又出现各种各样的问题.于是直接使用另外一种方式来解决这个问题了 就是通过修改yum来实现新的依赖库下载,输入rpm -qa|grep yum命令.显示结果如下 [[email protected] home]# rpm -qa|grep yum yum-3.2.22-20.el5 yum-metadata-parser-1.1.2-3.el5 再按照如下步骤把包下载下来 [[email protected] home]# cd /etc/yum yum/ yum.conf yum.repos.d/ [[email protected] home]# cd /etc/yum yum/ yum.conf yum.repos.d/ [[email protected] home]# cd /etc/yum.repos.d/ [[email protected] yum.repos.d]# ls epel.repo epel-testing.repo rhel-debuginfo.repo [[email protected] yum.repos.d]# wget http://www.linuxidc.com/files/2011/05/06/CentOS-Base.repo 完成这个CentOS-Base.repo的下载后.输入yum makecache 此时会出现一大堆信息,在此期间一直输入yes操作就可以了.呵呵,看下面的结果 Installed: libaio.x86_64 0:0.3.106-5 Complete! 这时可以安装上面三个包了,回到home目录先. [[email protected] yum.repos.d]# cd /home/ [[email protected] home]# ls glibc-2.16.0-8.ram1.x86_64.rpm MySQL-client-5.5.32-1.rhel5.x86_64.rpm perl-DBD-MySQL-4.022-1.el5.rfx.x86_64.rpm libaio-0.3.105-2.x86_64.rpm MySQL-devel-5.5.32-1.rhel5.x86_64.rpm perl-DBI-1.52-1.fc6.i386.rpm libaio-0.3.107-10.el6.x86_64.rpm MySQL-server-5.5.32-1.rhel5.x86_64.rpm rpm-4.9.1.3-6.fc17.i686.rpm [[email protected] home]# rpm -ivh MySQL-server-5.5.32-1.rhel5.x86_64.rpm 这里已经装上mysql-server了 继续安装其他的两个包.如下面. [[email protected] yum.repos.d]# cd /home/ [[email protected] home]# ls glibc-2.16.0-8.ram1.x86_64.rpm MySQL-client-5.5.32-1.rhel5.x86_64.rpm perl-DBD-MySQL-4.022-1.el5.rfx.x86_64.rpm libaio-0.3.105-2.x86_64.rpm MySQL-devel-5.5.32-1.rhel5.x86_64.rpm perl-DBI-1.52-1.fc6.i386.rpm libaio-0.3.107-10.el6.x86_64.rpm MySQL-server-5.5.32-1.rhel5.x86_64.rpm rpm-4.9.1.3-6.fc17.i686.rpm [[email protected] home]# rpm -ivh MySQL-server-5.5.32-1.rhel5.x86_64.rpm 此时装完三个包,可以启动MYSQL了 [[email protected] home]# service mysql start 进入MYSQL管理 [[email protected] home]# mysql -u root 此时因为没有设置密码,所以为空的 按回车就行了 然后进入后开始设置密码了 SET PASSWORD = PASSWORD('hello'); 注意:

fristPinyin : 此函数是将一个中文字符串的第一个汉字转成拼音字母 (例如:"中国人"->Z)复制代码 代码如下:CREATE FUNCTION `fristPinyin`(P_NAME VARCHAR(255)) RETURNS varchar(255) CHARSET utf8BEGIN    DECLARE V_RETURN VARCHAR(255);    SET V_RETURN = ELT(INTERVAL(CONV(HEX(left(CONVERT(P_NAME USING gbk),1)),16,10),         0xB0A1,0xB0C5,0xB2C1,0xB4EE,0xB6EA,0xB7A2,0xB8C1,0xB9FE,0xBBF7,         0xBFA6,0xC0AC,0xC2E8,0xC4C3,0xC5B6,0xC5BE,0xC6DA,0xC8BB,        0xC8F6,0xCBFA,0xCDDA,0xCEF4,0xD1B9,0xD4D1),        'A','B','C','D','E','F','G','H','J','K','L','M','N','O','P','Q','R','S','T','W','X','Y','Z');    RETURN V_RETURN;ENDpinyin :此函数是将一个中文字符串对应拼音母的每个相连 (例如:"中国人"->ZGR)复制代码 代码如下:CREATE FUNCTION `pinyin`(P_NAME VARCHAR(255)) RETURNS varchar(255) CHARSET utf8BEGIN    DECLARE V_COMPARE VARCHAR(255);    DECLARE V_RETURN VARCHAR(255);    DECLARE I INT;    SET I = 1;    SET V_RETURN = '';    while I < LENGTH(P_NAME) do        SET V_COMPARE = SUBSTR(P_NAME, I, 1);        IF (V_COMPARE != '') THEN            #SET V_RETURN = CONCAT(V_RETURN, ',', V_COMPARE);            SET V_RETURN = CONCAT(V_RETURN, fristPinyin(V_COMPARE));            #SET V_RETURN = fristPinyin(V_COMPARE);        END IF;        SET I = I + 1;    end while;    IF (ISNULL(V_RETURN) or V_RETURN = '') THEN        SET V_RETURN = P_NAME;    END IF;    RETURN V_RETURN;END示例:复制代码 代码如下:mysql> select p.province, fristPinyin(p.province),

1 减少HTTP请求数量 (Minimize HTTP Requests) tag:content80%的用户响应时间被花费在前端,而这其中的绝大多数时间是用于下载页面中的图片、样式表、脚本以及Flash这些组件。减少这些组件的数量就可以减少展示页面所需的请求数,而这是提高网页响应速度的关键。朴素的页面设计当然是减少组件的一种途径,但有没有能兼顾丰富的页面内容和快速的响应速度的方法呢?下面就是一些不错的技巧,能在提供丰富的页面展现的同时,减少Http请求数量:合并文件,通过把所有脚本置于一个脚本文件里或者把所有样式表放于一个样式表文件中,从而减少Http请求的数量。当不同页面需要应用不同的脚本或样式时,合并这些文件会是一个很大的挑战,不过在发布网站时进行这种合并,将是提高网站响应速度的重要一步。 CSS Sprites是减少图片请求的首选方案。把所有的背景图片合并到一张图中,使用CSS的background-image 和background-position 属性去控制展现恰当的图片区域。 Image maps把多张图片组合成为一张图片。图片的总大小是不变的,但减少Http请求数会提高页面的响应速度。Image maps只能用于图片在网页中相邻的情况,比如导航条。制定image maps中的图片坐标是个很麻烦的过程,而且容易出错。同时给导航使用image maps也并不易读,所以并不推荐使用。 内联图片使用data: URL scheme 把图片数据嵌入页面,但这会增加Html文档的大小。把内联图片合并到你被缓存的的样式表中是一个能既减少HTTP请求数又不会增加页面大小的方法。但目前并不是所有的主流浏览器都支持内联图片。 减少页面的Http请求数量是第一步,而且对于提高用户初次访问体验是最重要的一步。正如在 Tenni Theurer的blog中的Browser Cache Usage - Exposed!里描述的,每天,有 40%-60%的访客并没有你的网站的缓存文件。提高这些初次访客的访问速度是提高用户体验的关键。 2 使用内容分布式网络 (Use a Content Delivery Network)tag:server用户连接你的网站服务器的速度影响响应的快慢。把你的网站布置在多台分布于不同地域的服务器上,会让用户觉得你的页面加载速度更快。那么我们应该从哪里开始呢?不要一开始就把重新设计你的网站使其能够适应分布式结构作为实现网站地域分布的第一步。根据你的网站的复杂程度不同,更新网站结构的过程也许会包含诸如同步会话状态、在服务器间复制数据库等一系列复杂的步骤。这样你提高用户访问速度的目的反而会被更新网站架构的工作耽误。记住,用户80-90%的访问时间被花费在下载页面中的图片、样式表、脚本、Flash这些组件上。这是网站展示的黄金法则。那么与其重新设计网站的结构,不如先实现这些静态组件的分布。这不仅仅可以大幅减少响应时间,而且由于内容分布式网络(content delivery networks)的存在,这将是个很简单的工作。内容分布式网络(CDN)是一系列分布在不同地域的服务器的集合,能够更有效的给用户发送信息。它会根据一种衡量网域距离的方法,选取为某个用户发送数据的服务器。比如,到达用户最少跳或者最快相应速度的服务器会被选中。一些大型Internet公司拥有他们自己的CDN,但使用公用的CDN服务提供商更为划算,比如 Akamai Technologies, Mirror Image Internet, 或者Limelight Networks。对于刚起步的公司和个人网站来说,CDN服务的花费也许会偏高。但当你的目标用户越来越多而且趋于国际化,用CDN来降低响应时间就很必要了。在Yahoo!,把静态的内容从自己的网络服务器移到CDN提高了用户20%甚至更多的访问速度。转向CDN会是一个只需要相对简单的代码更新的工作,而且那将会显著的提高你的网站访问速度。 3 给头部添加一个失效期或者Cache-Control (Add an Expires or a Cache-Control Header)tag:server这条法则包含两方面:    * 对于静态组件:把头部的缓存期设为某个遥远的未来,使其能够“永不过期”。    * 对于动态组件:使用适当的Cache-Control头部帮助浏览器执行特定的请求。网页设计越来越丰富,页面里包含了越来越多的脚本、样式表、图片和Flash。页面的初次访问者也许会发送多个HTTP请求,但通过给头部添加失效期,你可以使那些组件被缓存。这会避免下次浏览页面时的不必要的HTTP请求。给图片文件的头部设置失效时间更为常用,但包括脚本文件、样式表和 Flash之类的所有组件的头部都应该被设置失效时间。浏览器(还有代理服务器)使用缓存以减少HTTP请求的数量和大小,提高网页的加载速度。服务器在HTTP相应中通过头部中的过期时间告知客户端一个组件可以被缓存多久。下面是一个far future的过期头部,告诉浏览器这个响应直到2010年4月15日才会过期:Expires: Thu, 15 Apr 2010 20:00:00 GMT如果你使用的是Apache服务器,使用ExpiresDefault 指令会设置一个相对于当前时间的过期时间。这里有一个通过ExpiresDefault 指令把过期时间设为请求时间之后10年的例子:ExpiresDefault "access plus 10 years"记住,如果你使用了far future过期头部,你必须在组件更新时更换它的名字。在Yahoo!我们通常在建设网站的过程中执行这样的步骤:组件的名字里包含了它的版本,比如:yahoo_2.0.6.js。使用一个far future过期头部只会在用户已经访问你的网站之后起作用。它不会影响一个没有缓存的初次访问者的HTTP请求数量。所以这一切的效果取决于多少用户访问页面时有一份预缓存(一份"预缓存"中已经包含了页面的所有组件)。我们对此在Yahoo!做过测试,发现拥有预缓存的用户在 75-85%。给头部添加far future失效期,可以增加浏览器缓存的组件数量并重复用于随后的页面浏览而不需要通过用户的网络发送哪怕一个字节。 4 Gzip压缩组件(Gzip Components)tag:server前台工程师的决策能够显著的减少在网络上传输 HTTP请求和响应花费的时间。确实,终端用户的带宽速度、Internet服务提供商和连接交换机的服务器这些因素都是开发小组所不能控制的。但还有一些其它因素会影响响应的时间,比如压缩文件,就会减少HTTP响应的大小从而减少响应的时间。 从HTTP/1.1开始,Web客户端就被设定为支持HTTP请求的头部中Accept-Encoding指定的压缩格式:Accept-Encoding: gzip, deflate当服务器检测到请求头部中的这一代吗,它就会使用客户端提供的方法列表中的一个来压缩响应内容。而服务器通过响应头部中的Content- Encoding来告知客户端它所使用的压缩方式:Content-Encoding: gzipGzip是当前最常用也是最有效的压缩方式,GNU项目开发了这一方法并且符合RFC 1952标准。另外一种你可能见过的压缩格式是deflate,但它没有那么有效和常用。使用gzip压缩通常会减少70%的响应大小。当前浏览器中大约90%的Internet通讯传输声明支持gzip。如果你使用Apache服务器,配置gzip的模块取决于服务器的版本:Apache 1.3 使用mod_gzip ,而Apache 2.x 使用mod_deflate。浏览器和代理会有一些已知的问题,可能导致浏览器的预期内容和获得的实际压缩内容不匹配。幸运的是,这种情况随着旧浏览器的使用者减少而减少。 Apache的模块可以通过自动添加适当的变化响应文件头来解决这些问题。服务器会根据文件类型选择gzip压缩的内容,但一般情况下,服务器选择压缩的内容会过于局限。大部分网站会压缩它们的Html文档,而压缩脚本和样式表也是值得一做的,但很多网站并没有这样做,事实上,压缩在包括 XML和JSON在内的任何文本响应都是值得的。图片和PDF文件不应该被gzip压缩,因为它们已经是被压缩了的文件,gzip它们不仅浪费CPU甚至还有增大文件大小的可能。Gzip尽可能多的文件类型是减少页面大小从而提高用户体验的一个简单的方法。 5 把样式表放在前面(Put Stylesheets at the Top)tag:css在研究Yahoo!的性能时,我们发现把样式表挪到文档的头部可以让页面的加载显得更快。因为把样式表放在头部可以让页面逐步呈现。关心网站性能的前台工程师通常希望页面能够逐步加载;即,我们希望浏览器能够把已经获得的内容尽快展现。这对于内容很多的页面以及网络连接较慢的用户尤为重要。给予用户视觉上的反馈(比如进度提示)的重要性,已经经过了很详尽的论证。而对于我们来说,Html 页面本身就可以作为进度提示!当浏览器逐步加载页面时,头部、导航条、顶部的logo等等这些都可以作为对正在等待页面的用户的可视的反馈。而这会从整体上提高用户体验。把样式表放在文档的最后,会导致包括IE在内的大部分浏览器不进行逐步呈现。浏览器为了避免当样式改变时重绘元素而中止呈现。用户会十分无聊的看到一个空白的页面。Html规范明确规定样式表应该被包含在页面的HEAD中:“和A不同,LINK只能在文档的HEAD部位出现,但它可以出现多次。”空白的屏幕或者由于没有应用样式而引起的内容的闪现都不值得去尝试。最好的方法是遵循HTML规范,把样式表放在文档的HEAD部位。 6 把脚本放在最后(Put Scripts at the Bottom)tag:javascript脚本可能会堵塞并发的下载。HTTP/1.1规范建议浏览器在每个域名下只进行两个并发下载。如果你把图片放在多个域名下,可以实现多于两个的并发下载。当脚本被下载时,即使使用不同的域名。浏览器也不会进行任何其它的下载。有些情况下把脚本放到底部并不太容易。比如,脚本使用了document.write 来添加部分页面中的内容,就不能放到页面中更后面的位置。还可能有作用域的问题。很多情况下,还有一些变通的方法。通常的建议是使用延迟脚本。DEFER属性表明脚本不包含document.write,而且提示浏览器继续展现。不幸的是,Firefox不支持DEFER属性。IE中,脚本可以被延迟,但并不如你期望的那么久。如果一个脚本可以被延迟,那么它也可以被放在页面的底部。这会让你的页面加载的更快。 7 不使用CSS表达式 (Avoid CSS Expressions)tag:css CSS表达式是一种有力的(同时也很危险的)动态设置CSS属性的方法。从IE5开始支持CSS表达式。比如,使用CSS表达式可以实现背景颜色每小时变换的效果。background-color: expression_r( (new Date()).getHours()%2 ? "#B8D4FF" : "#F08A00" );如上所示,表达式方法采用了 Javascript的表达。CSS属性则被设为Javascript表达式的结果。其它的浏览器会忽略CSS表达式,所以对于设置专属IE的属性以便在不同浏览器间能有一致的体验是有用的。、而CSS表达式的问题是它比大多数人期望的执行次数更频繁。表达式不仅仅在页面展现和重新设置大小的时候执行,在页面滚动,甚至用户在页面上挪动鼠标时都会执行。给CSS表达式添加一个计数器可以跟踪CSS在什么时候和怎样执行。在页面上移动鼠标可以轻易的产生一万次以上的执行。使用一次性的表达式是减少CSS表达式的执行次数的一个方法,当表达式第一次执行时,CSS表达式会被一个确定的值代替。如果在页面生命周期中,样式属性必须动态的设定,使用事件处理替代CSS表达式是一个可选的方法。如果必须使用CSS表达式,要记得它们会执行上千次并影响页面的性能。 8 使用外部的JavaScript和CSS (Make JavaScript and CSS External)tag:javascript,css 很多性能规则都是解决怎样处理独立的组件的问题的。但是,考虑这些之前,你应该先考虑一个更基本的问题:JavaScript和CSS应该被放于外部的文件,还是内联在页面里?在实际应用中使用外部的文件往往产生更快的页面,因为浏览器会缓存JavaScript和CSS文件。而内联在页面里的JavaScript和CSS会在每次请求页面时下载。这会减少所需的HTTP请求数,但增大HTML文档的体积。而另一方面,如果放在外部文件里的JavaScript和CSS被浏览器缓存,则既不用增加HTTP请求的数量,HTML文档的体积也会减少。关键的问题是,外部的JavaScript和CSS的组件被缓存的频率和HTML文档被请求的次数相关。虽然很难去量化,但可以被用很多指标衡量。如果你的网站的用户在每个会话中浏览了很多网页而且很多页面重用了相同的JavaSctipt和样式表,缓存外部文件是有很大潜在的好处的。很多网站都符合这样的指标。对于这些网站来说,最好的解决方案是把JavaScript和CSS发布为单独的文件。唯一的例外,对于主页,内联的文件更好一些,例如 Yahoo!'s front page 和 My Yahoo!。主页在每个会话中只有很少浏览(也许只有一次),你会发现内联的 JavaScript和CSS会让终端用户的响应更快。对于有很多页面浏览量的首页来说,有很多能平衡内联文件所提供的HTTP请求减少的效果与外部文件缓存获得的好处的技巧。一种这样的技巧就是把JavaScript和CSS内联在说夜里,但在页面完成加载时动态下载外部文件。随后的页面会调用浏览器中已经缓存的外部文件。 9 减少DNS的查询 (Reduce DNS Lookups)tag:content 正如电话簿使人名和他们的电话号码相对应,域名系统(DNS)能够使域名和IP地址相对应。当你在浏览器中键入http://www.yahoo.com,浏览器链接的DNS解析器会返回服务器的 IP地址。域名解析会耗费一些时间,DNS查找给定域名的IP地址一般会耗费20-120毫秒。在DNS查找结束前,浏览器不会从目标域名那里下载任何东西。DNS查询会被缓存以便优化性能。会有一个专门的缓存服务器进行缓存,用户的ISP或者本地网络会维护它,但独立用户的电脑里也会有缓存。DNS信息存在于操作系统的DNS缓存里(微软Windows操作系统里的“DNS客户服务”)。大部分浏览器有它们自己的缓存,与操作系统的缓存相独立。当浏览器在自己的缓存里保存了DNS的记录,它不会向操作系统发出请求记录的要求。IE默认缓存DNS查询30分钟,在注册表的DnsCacheTimeout的键值中设定。Firefox则缓存DNS查询一分钟,在配置network.dnsCacheExpiration 中设定。(Fasterfox 将它变为一小时。)当客户端的DNS缓存被清空(包括浏览器和操作系统的缓存),DNS查询的数量等同于网页中单独的域名的数量。包括页面中的链接,图片,脚本文件,样式表,Flash对象等。减少不同域名的数量则会减少DNS查询的数量。减少不同域名的数量可能减少页面并行的下载数量。减少 DNS查询缩短了响应时间,但减少了并行下载数也许会增加响应时间。我的建议是将组件分布在两到四个域名之间。这能很好的折中减少DNS查询提高的速度和维持较高水平的并行下载的效果。  10 缩小JavaScript和CSS (Minify JavaScript and CSS)tag:javascript,css 缩小是指从代码中删除不必要的字母,减少文件体积从而提高加载速度。缩减代码时需要移除所有的注释,以及不需要的空白(空格,新行和tab)。这样处理JavaScript之后,会由于下载文件的体积被减少而提高响应的性能。两个常用的缩减JavaScript代码的工具是JSMin 和YUI Compressor。YUI compressor也可以压缩CSS。代码混淆是另一个可用于源代码的优化方案。它比压缩更为复杂,而且在混淆的过程中更容易产生 Bug。纵观U.S.的前十大网站,压缩获得了21%的体积减小而代码混淆达到了25%。虽然代码混淆的压缩程度更高,但压缩JavaScript的风险更小。不仅仅要压缩外部的脚本和样式表,内敛的<script>和<style>部分也可以而且应当被压缩。即使你gzip了你的脚本和样式,压缩它们仍然能减少5%以上的体积。随着JavaScript和CSS的应用和体积的增加,压缩你的代码获得的收益也会越来越多。  11 避免重定向 (Avoid Redirects)tag:content重定向结束于 301或302状态码。这里有一个301响应的HTTP头的例子:      HTTP/1.1 301 Moved Permanently      Location: http://example.com/newuri      Content-Type: text/html浏览器会自动把用户转向Location域中指明的Url地址。HTTP头里包含了重定向所需的所有信息。响应的主体一般是空的。301或者302响应都不会被实际缓存,除非添加额外的头部,比如 Expires或者Cache- Control指明了它应该被缓存。meta refresh标签和JavaScript也可以将用户重定向到不同的URL,但如果你必须执行重定向,最好的方法是使用标准的3XX HTTP状态代码,以便使后退按钮工作正常。需要谨记的是,重定向降低了用户体验。在用户和HTML文档之间插入的重定向延误了页面的呈现和组件下载,因为它们都不可能在获得HTML文档之前开始。 一种最浪费性能的重定向频繁发生而网络开发者们却往往没有意识到,那就是当地址中应当有一个左斜线(/)却没有的时候。比如,访问http://astrology.yahoo.com/astrology会导致一个301效应并重定向到http://astrology.yahoo.com/astrology/(注意这里加了一个左斜线)。在Apache中,这可以使用mod_rewrite,或者在Apache事件处理中使用DirectorySlash指令来修补。 使用重定向的另一个常见场景是连接旧网站和新网站。还包括连接网站的不同部分,或者在不同情况下(比如依据浏览器的类型,用户的类型等)重定向用户。使用重定向来连接两个网站很简单而且需要很少的额外代码。虽然在这些情况下使用重定向减少了开发者的麻烦,但却降低了用户体验。如果两部分在同一个服务器上,可以使用Alias 和rewrite来替代重定向。如果域名变更引起了重定向,可以创建一个CNAME(一种可以创建一个别名使一个域名指向另一个的DNS记录)结合 Alias 或者mod_rewrite来替代重定向。 12 移除重复的脚本 (Remove Duplicate Scripts)tag:javascript 在同一个页面中包含两个相同的脚本文件降低了性能。这并不如你想象的那么罕见。在对美国十大网站中的检查中,发现它们中的两个包含了重复的脚本。有两个主要因素增加了一个页面包含两个相同脚本的几率——团队的大小和脚本的数量。当脚本被重复包含时,由于增加了不必要的HTTP请求和JavaScript的执行,影响了性能。不必要的HTTP请求在IE中存在,而Firefox终没有。在IE中,如果一个外部脚本被包含了两次而且没有被缓存,在页面加载的过程中会产生两次HTTP请求。即使脚本被缓存了,当用户重载页面时,多余的HTTP请求也会发生。产生多余的HTTP请求的同时,多次执行脚本也会浪费时间。在Firefox和IE中,无论是否被缓存,脚本都会被重复执行。 避免脚本被意外加载两次的一个方法是在你的模板系统中执行一个脚本管理模块。通常的方式是在HTML页面中使用SCRIPT标签来添加一个脚本:<script type="text/javascript" src="menu_1.0.17.js"></script>HP 中,可以选择创建一个叫做insertScript的方法:<?php insertScript("menu.js") ?>这个函数不仅仅能防止脚本被重复加载多次,还可以解决脚本的其他问题,比如独立性检测以及为脚本添加版本号码以应对far future Expires头部。 13 设定ETags (Configure ETags)tag:server 实体标签(ETags)是服务器和浏览器用于确定浏览器中缓存的组件和服务器中的是否对应的一种机制。("entity"是组件的另一种说法:图片、脚本、样式表等等)添加 ETags用于辨别组件提供了比单纯利用“最后修改时间”更为灵活的机制。ETag是一个唯一标识组件的特定版本的字符串。它的唯一格式规范是字符串必须被引号引用。来源服务器使用ETag响应头来设定一个组件的ETag:      HTTP/1.1 200 OK      Last-Modified: Tue, 12 Dec 2006 03:03:59 GMT      ETag: "10c24bc-4ab-457e1c1f"      Content-Length: 12195 当浏览器晚些时候需要检测一个组件时,它使用If-None-Match 头部把ETag传回来源服务器。如果ETag匹配了,会返回一个304状态码,在这个例子里它会减少12195个字节的响应:      GET /i/yahoo.gif HTTP/1.1      Host: us.yimg.com      If-Modified-Since: Tue, 12 Dec 2006 03:03:59 GMT      If-None-Match: "10c24bc-4ab-457e1c1f"      HTTP/1.1 304 Not Modified ETag的问题是它们往往在网站的一个服务器中被设为唯一的,当浏览器从一个服务器得到了组件并在稍后试图到另一个服务器验证时,ETag会不匹配,而这在使用多个服务器来处理请求的网站中是很常见的。默认情况下,Apache和IIS在ETag中嵌入的数据戏剧性的减少了应用多台服务器的网站的ETag验证成功几率。 Apache1.3和2.新版本的ETag格式是inode-size- timestamp。虽然一个给定的文件在多台服务器中处于同一个目录,而且有同样的大小,权限,时间戳,但它的inode在不同服务器中是不同的。 IIS5.0和6.0有同样的问题。IIS中ETag的格式是Filetimestamp:ChangeNumber。 ChangeNumber用来跟踪IIS配置的改变次数。一个网站的所有IIS不可能有相同的ChangeNumber。 这导致的结果是,Apache和IIS对完全相同的组件产生的ETag在不同服务器间不能匹配。如果ETags不匹配,用户不会得到小而快的304响应,而是一个普通的200响应和组件的所有数据。如果你把你的网站放在了一个服务器里,这不会是一个问题。但如果你的网站有多台服务器,而且你使用了Apache和IIS 默认的ETag配置,你的用户会访问页面的速度会变慢,你的服务器加载的程度更高,消耗了更大的带宽,代理服务器不能有效的缓存你的内容。即使你的组件有一个ar future Expires头部,当用户重载或者刷新页面时,依然会发送一个GET请求。 如果你不打算利用ETags提供的灵活的验证模式,你最好把ETag统统移除。Last-Modified头部的验证方式给予组件的时间戳。移除ETag 同时减少响应和随后的请求中的HTTP头部大小。这篇微软的支持文档描述了怎样在IIS中移除 ETags。在Apache中,你只要在Apache配置文件中添加如下一行:     FileETag none 14 让Ajax可以缓存 (Make Ajax Cacheable)tag:contentAjax 的好处之一是它能给用户提供瞬间的响应,因为它从服务端异步请求数据。但Ajax不能保证用户在等候那些异步的JavaScript和XML响应返回时什么都不做。在应用程序中,用户是否继续等待取决于Ajax怎样应用。比如,在一个基于Web的Email客户端用户会等候Ajax返回他们所搜索的邮件信息。记住异步并不代表“即刻”。为了提高性能,优化Ajax响应很重要。提高Ajax性能最重要的方式是使响应缓存,正如“添加一个过期期限和缓存控制头”这一节讨论的。这些原则同样适用于Ajax。     * Gzip组件    * 减少DNS查询    * 压缩JavaScript    * 避免重定向    * 设定ETag 我们看一个例子。一个Web2,0的邮件客户端可能会用Ajax自动下载用户地址簿。如果用户从上次使用邮件网站以来没有修改她的地址簿,那么如果Ajax响应使用了长期失效时间或者缓存控制头部,地址簿就可以从缓存中读取出来。浏览器必须被告知“使用之前的缓存地址簿”而不是“请求一个新的地址簿”。可以在地址簿Ajax的URL中添加一个标识用户最后一次修改地址簿的时间戳,比如,&t=1190241612。如果地址簿从最后一次下载后没有被更改,时间戳将相同而地址簿将会从浏览器的缓存中得到来替代额外的HTTP传输。如果用户更改了她的地址簿,时间戳会保证新的URL不会和缓存中的匹配,而且浏览器会请求会请求更新的地址簿记录。 即使你的Ajax响应时动态创建的,而且只适用于一个用户,它们依然会被缓存。这样做会让你的Web2.0应用程序更快。 15 更早的刷新缓冲区 (Flush the Buffer Early)tag:server当用户请求一个页面,服务端会花费200至500毫秒的时间组合HTML页面。在这期间,浏览器会静静等待数据到来。PHP中有flush()函数,它允许你向浏览器发送部分就绪的HTML响应,这样浏览器可以在服务器处理余下的HTML页面时去获取组件。这样的好处主要在忙碌的后台和轻松的前台间可以看到。 在HEAD后面是放置刷新操作的好地方,因为头部的HTML代码更容易产生,而且可以让你放置任何CSS和JavaScript文件,以便浏览器在后台工作依然进行时并行开始获取组件。例子:... <!-- css, js --></head><?php flush(); ?><body>... <!-- content -->Yahoo! search先行研究并且进行了真人测试证明了使用这项技术的好处。 16 在Ajax请求中使用GET方法 (Use GET for AJAX Requests)tag:serverYahoo! Mail 团队发现进行XMLHttpRequest的时候,POST方法在浏览器中分两步执行:先发送头部,然后发送数据。所以最好使用只发送一个TCP包(除非你有很多的cookie)的GET方法。IE中URL的最大长度是2000,所以如果你发送超过 2000的数据就不能使用GET方法。 一个有趣的现象是,POST方法并不像GET那样实际发送数据(而Get则名副其实)。基于 HTTP规范,GET方法意味着取回数据,所以当你只是请求数据时使用GET方法更为有意义(从语义上来说),而在发送需要储存在服务器端的数据时则相反使用POST。17 后加载组件 (Post-load Components)tag:content 你可以仔细端详下你的页面然后自问:“什么是在页面初始化时必须的?”那么其余的内容和组件可以放在后面。 JavaScript是理想的用来分割onload事件之前和之后的选择。例如你有执行拖放、下拉和动画的JavaScript代码和菜单,它们可以稍后加载,因为用户在初始呈现之后才会在页面上拖动元素。其他的可以被后加载的地方包括隐藏的内容(当用户做某项操作才会展现的内容)和被折叠的图片。 可以帮助你的工具有: YUI Image Loader能帮助你延缓加载折叠的图片,而且YUI Get utility 能够很简单的包装运行中的JS和CSS。比如,打开Firebug的网络选项卡去查看Yahoo! Home Page。 当性能指标和其它网站开发的好的实践一致时是不错的。渐进增强的观念告诉我们当支持JavaScript时,会提高用户体验,但你必须确保在没有JavaScript时页面也能工作。所以当你确保页面工作正常时,你会通过延后加载的那些更花哨的脚本比如拖放和动画,来增强你的页面。 18 预先加载组件 (Preload Components)tag:content 预加载看起来和后加载原则是个矛盾,但它其实是为了另外一个目的。预加载组件让你可以利用浏览器的空闲时间来加载之后需要的组件(比如图片,样式表和脚本)。这样当用户浏览下一个页面的时候,大部分组件都已经在缓存里了而页面会加载的更快。 有几种预加载的类型:     * 无条件预加载-当原本内容加载完成时,立刻开始获取一些额外的组件。比如到google.com看下一个sprite图片怎样被在onload事件后请求的。在google.com的首页里并没有用到sprite图片,但被用在接下来的结果页面里。    * 有条件的预加载-根据用户的行为来猜测用户什么时候到达下个页面然后据此预加载。在search.yahoo.com上,你可以看到额外的组件在你在输入框中输入时是怎样被加载的。    * 有预期的加载-当登录重新设计的网站时进行加载。你通常会在重新设计网站后听到:“新网站很酷,但它比以前的要慢”。这个问题的部分原因是用户访问旧网站时有所有的缓存,而对于新的来说,缓存是空的。你可以通过在登录重新设计的网站前预加载一些组件来缓解这方面的影响。你的旧网站可以用浏览器空闲的时间来请求新网站中用到的脚本和图片。 19 减小DOM元素的数量 (Reduce the Number of DOM Elements)tag:content 复杂的页面意味着更多的字节需要被下载而且也意味着在JavaScript中遍历DOM更慢。比如你在页面中添加一个事件,让它在500或者 5000个DOM元素中循环,它们的效率是不同的。 更多的DOM元素表明有些标签需要被改良而并不一定需要移除实际内容。你是否为了布局而使用繁琐的网一样的表格?你是否只是为了弥补一些布局的问题而使用了更多的div标签?也许还有更好和更符合语义的标签可以使用。  YUI CSS utilities可以很好的帮助进行布局:grid.css 可以帮助你进行所有的布局,front.css 和 reset.css 可以帮助你去除浏览器默认的格式。这是你开始重新审视你的标签的机会,比如只在语义符合时使用div标签,而不是用它来另起一行。 DOM 元素的数量很好检测,只要在Firebug的控制台里输入:document.getElementsByTagName_r('*').length 那么多少DOM元素算多呢?查看下类似的使用较好的标签的页面。比如Yahoo! Home Page是一个很丰富的页面但只有700以下的DOM元素(HTML 标签)。 20 分域部署部件:Split Components Across Domainstag:内容将部件分割能使你获得最大的并行下载效率。但你同时需要注意不使用多于2~4个域名,以避免DNS查询导致的问题。例如,你可以将HTML内容和动态的组建放于 www.example.org域名下,将静态组件分别放于static1.example.org和static2.example.org之下。 查看Tenni Theurer和Patty Chi的"Maximizing Parallel Downloads in the Carpool Lane"获取更多关于并行下载的信息。 21 减少Iframe的数量 Minimize the Number of iframestag:内容 Iframes 能够使HTML文档被插入进父级文档中。首先需要明确iframe的工作方式,才能有效的利用这一形式。 <iframe> 的优点:    * 对于第三方内容,比如广告,能够在不影响父级设计的情况下快捷插入。    * 提供安全沙箱,不影响父级。    * 能够并行下载脚本。 <iframe> 的缺点:    * 即使是空的也会有消耗。    * 会锁住页面的onload事件。    * 不支持语义表达。 22 避免404错误 No 404stag:内容 一个获得没用的404响应的HTTP请求对于宝贵的HTTP请求资源来说是完全不必要的,而且这样还会减慢用户的体验。 有的网站提供了有帮助的404错误信息,比如"你是否在寻找……?",这样虽然能提高用户体验,但同样浪费了服务端资源(比如数据库)。尤其不妙的是在请求一个外部的Javascript脚本文件失败时获得的一个404错误,因为这样不但会堵塞并行的下载,而且浏览器会尝试分析404响应的内容,来辨识它是否是有用的Javascript代码。 23 减少Cookie的大小 Reduce Cookie Sizetag:cookie 有多种理由让我们应用HTTP cookie,比如身份验证,或者个性化设置。Cookie中的信息在服务端和浏览器间被放在HTTP头中交换。尽量减少cookie的体积对减少用户获得响应的时间十分重要。 查看Tenni Theurer和Patty Chi的"When the Cookie Crumbles"获取更多信息。    * 去除不必要的 cookie。    * 尽量减少cookie的大小。    * 留心将cookie设置在适当的域名下,避免影响到其他网站。    * 设置适当的过期时间。一个较早的过期时间或者不设置过期时间会更快的删除cookie,从而减少用户的响应时间。 24 为部件使用没有cookie的域名 Use Cookie-free Domains for Componentstag:cookie 当浏览其请求一个静态图片并一同发送cookie时,服务器并不需要这些cookie。这样只是毫无益处的创建了多余的网络流量。应当保证静态的部件在请求时没有携带cookie,所以需要把你的静态部件放在另一个子域名下。 如果你的域名是www.example.org,你可以将你的静态部件放在static.example.org中。如果你把cookie设置在顶级域名example.org上而不是 www.example.org,那么所有发送至static.example.org的请求会包括那些cookie。在这种情况下,你可以买一个全新的没有cookie的域名来放置你的静态部件。Yahoo!使用了yimg.com,YouTube试用了ytimg.com,Amazon使用的是 images-amazon.com。 将静态部件放于没有cookie的域名下的另一个好处是一些代理服务器会拒绝缓存有cookie 的部件。于此相关的一点是,如果你怀疑你应该为你的首页使用example.org还是www.example.org,考虑cookie的赢下。省略 www会让你不得不把cookie写到*.example.org下,所以考虑性能,最好使用www.子域名,然后把cookie写到这个子域名下。 25 减少DOM的读取 Minimize DOM Accesstag:javascript 利用Javascript读取 DOM元素很慢,所以为了获得响应更快的页面,你应该:    * 缓存被读取的元素的引用。    * 脱机更新节点然后把它们加回到树结构中。    * 避免利用Javascript定位布局。 26 开发灵巧的事件处理程序 Develop Smart Event Handlerstag:javascript 如果有太多的事件处理逻辑部署在DOM树的不同元素上,它们的频繁执行会拖慢页面的响应速度。而使用事件委托是一个好的解决方法。如果在一个Div中有10个按钮,与其在每个按钮上都放一个事件处理程序,步入只在Div上放一个事件处理程序。事件会冒泡上溯,这样你就会捕获这一事件,并找出是哪个按钮发起的它。同样,你并不需要等待onload事件来启动一些操作DOM树的程序。你只需要保证你需要操作的元素可用就可以了。你不需要等待所有的图片下载完毕,你应当使用DOMContentLoaded事件来替代onload事件,当然,目前并不是所有浏览器都支持这一事件,你可以使用YUI Event组件,其中包含了一个onAvailable函数。查看Julien Lecomte的"High Performance Ajax Applications"获取更多信息。 27 选择<link>而不是@import Choose <link> over @importtag:css 前面提到把CSS应当放在最顶端来提供预显。在IE中,放在页面底部的@import和<link>效果是一样的,所以最好不要用它。 28 不使用过滤器 Avoid Filterstag:css IE专有的AlphaImageLoader过滤器是为了解决半透明真色PNG图片在IE7之前的版本中显示的问题。这个过滤器会在图片下载时堵塞住展示。而且它会消耗内存并影响每个元素而不仅仅是每张图片,所以这个过滤器的问题很多。 所以最好在IE中完全不使用AlphaImageLoader过滤器,而使用渐淡的 PNG8图片。如果你明确需要AlphaImageLoader,请使用hack _filter,从而不影响到你的IE7+的用户。 29 优化图片 Optimize Imagestag:images 当设计师制作好网站的图片后,还有些事情应该是你在把这些图片上传到服务器之前做的。    * 你可以检查GIF图片中的调色板是否和图片中的色彩数一致。使用imagemagick来帮助你方便的检查:      identify -verbose image.gif      如果你看到一个4色的图片却有一个256色的调色板,那么还有很大的空间来做性能优化。    * 试试把GIF转换成PNG是否会节省空间,这是常有的事情。由于浏览器支持的限制,开发者往往怀疑是否该使用PNG,但这是过去的事情了。唯一的问题是真色的PNG图片的半透明问题,但同样,GIF不是真色的而且也不支持丰富的透明效果。所以GIF可以做的,PNG或者PNG8同样可以做到(除了动画)。一条简单的imagemagick语句就可以提供可用的PNG:      convert image.gif image.png      “我们强调的是,给PNG一个机会!”    * 使用pngcrush或者任何的PNG优化工具,例如:      pngcrush image.png -rem alla -reduce -brute result.png    * 使用 jpegtran处理JPEG图片。这个工具会无损操作JPEG图片,比如旋转,而且可以用来优化图片,比如去除图片中的注释和其他无用的信息(比如 EXIF信息)。      jpegtran -copy none -optimize -perfect src.jpg dest.jpg 30 优化CSS精灵 Optimize CSS Spritestag:images    * 横向布局Sprite中的图片往往比纵向布局会减少文件大小。    * 在一个Sprite中组合相近的颜色会降低颜色的数量,从而达到适合PNG8的低于256色的标准。    * “对移动设备友好”,不在Sprite里留下大的空隙。这并不十分影响文件的大小,但会减少客户端代理将图片解压为像素映射的内存消耗,100*100的图片是一万像素,而1000*1000则是一百万像素。 31 不要在HTML中缩放图片 Don't Scale Images in HTMLtag:images 不要使用大小超过需要的图片,即使你能够在HTML中设置它的属性。如果你需要<img width="100" height="100" src="mycat.jpg" alt="My Cat" />那么就使用恰好100*100px的图片,而不是使用缩放后的500*500的图片。 32 使用小的可缓存的Favicon.ico Make favicon.ico Small and Cacheabletag:images favicon.icon是放在服务器根目录的一个图片,它是个麻烦却不得不处理,因为即使你不关心,浏览器依然会请求这张图片,所以最好不要提供一个404的错误。而且由于它是在同一服务器下的,Cookie也会随着每次请求一并发送。这张图片同样干扰下载队列,比如在IE中,当你在onload 事件中请求额外的组件时,favicon会在这些额外组件之前下载。 所以为了减少favicon.ico的不利影响,我们应当:    * 使用小图片,小于1k为佳。    * 设置你认为合适的过期时间(因为你如果更新了图片,你并不能修改它的名字)。你可以设置过期为未来的几个月。你可以借鉴下你当前的 favicon.ico的最后更新时间来作为设置依据。Imagemagick 能够帮助你创建小图片。 33 保证组件大小小于25K Keep Components under 25Ktag:mobile 这一限制是因为iPone不会缓存大于25K的组件,注意这里指的是未压缩前的大小。这就是为什么缩小大小很重要,因为单单gzip并不足够。 查看Wayne Shea和Tenni Theurer的"Performance Research, Part 5: iPhone Cacheability - Making it Stick"获取更多信息。 34 把组件打包进多部分文档中 Pack Components into a Multipart Documenttag:mobile 把组件打包进多部分文档类似一封包含有附件的邮件,它能让你通过一个HTTP请求获取多个组件(记住HTTP请求是很昂贵的)。当你使用这一技术,请先检查客户端是否支持它(iPone不支持)。

redis-stat是一个用ruby写成的监控redis的程序,基于info命令获取信息,而不是通过monitor获取信息一、安装ruby复制代码 代码如下:yum install -y ruby ruby-devel rubygemsapt-get install rubygems ruby.. 由于国内网络原因,导致 rubygems.org 存放在 Amazon S3 上面的资源文件间歇性连接失败。所以你会与遇到 gem install rack 或 bundle install 的时候半天没有响应,所以需要修改rubygems为淘宝镜像。二、替换rubygems复制代码 代码如下:gem sources --add https://ruby.taobao.org/ --remove http://rubygems.org/gem sources -l 三、安装redis-stat复制代码 代码如下:git clone https://github.com/junegunn/redis-stat.gitgem install redis-stat 会提示Building native extensions. This could take a while...Building native extensions. This could take a while...Building native extensions. This could take a while...ERROR: Error installing redis-stat:redis-stat requires daemons (~> 1.1.9, runtime)四、卸载daemons复制代码 代码如下:gem uninstall daemonsgem install daemonsgem install redis-stat装redis-stat同时会安装daemons-1.1.9,如果系统已安装daemons其他版本,需再次卸载ERROR: Error installing redis-stat:redis-stat requires daemons (~> 1.1.9, runtime)复制代码 代码如下:gem uninstall daemonsSelect gem to uninstall:1. daemons-1.1.92. daemons-1.2.33. All versions> 2Successfully uninstalled daemons-1.2.3五、安装SystemTimer复制代码 代码如下:gem install SystemTimer然后就可以启动redis-stat了复制代码 代码如下:redis-stat --verbose --server= ...: ...:redis-stat 110.160.4.71:6379 110.47.90.168:6379 1 10补充:redis-statredis-stat是实时监控Redis实例的状态,包括overview、vmstat、vmpage、ondisk-size、latency,具体信息如下:复制代码 代码如下:$ ./redis-stat helpUsage: redis-stat <type> ... options ...Statistic types:复制代码 代码如下:overview (default) Print general information about a Redis instance.vmstat Print information about Redis VM activity.vmpage Try to guess the best vm-page-size for your dataset.ondisk-size Stats and graphs about values len once stored on disk.latency Measure Redis server latency.Options:复制代码 代码如下:host <hostname> Server hostname (default 127.0.0.1)port <hostname> Server port (default 6379)delay <milliseconds> Delay between requests (default: 1000 ms, 1 second).samplesize <keys> Number of keys to sample for 'vmpage' stat.logscale User power-of-two logarithmic scale in graphs.一个简单的测试例子:复制代码 代码如下:$ ./redis-stat host 127.0.0.1 port 6379------- data ------ --------------------- load -------------------- - child -keys mem clients blocked requests connections 29602 3.45M 1 0 30090 (+0) 157 29602 3.45M 1 0 30091 (+1) 157 29602 3.45M 1 0 30092 (+1) 157 29602 3.45M 1 0 30093 (+1) 157 29602 3.45M 1 0 30094 (+1) 157 29602 3.45M 1 0 30095 (+1) 157 29602 3.45M 1 0 30096 (+1) 157 29602 3.45M 1 0 30097 (+1) 157 29602 3.45M 1 0 30098 (+1) 157......查看Redis实例的实时的latency也非常有用:复制代码 代码如下:$ ./redis-stat latency host 127.0.0.1 port 6379 1: 0.23 ms2: 0.67 ms3: 0.21 ms4: 0.23 ms5: 0.23 ms6: 0.16 ms7:

(一)深入浅出理解索引结构   实际上,您可以把索引理解为一种特殊的目录。微软的SQL SERVER提供了两种索引:聚集索引(clustered index,也称聚类索引、簇集索引)和非聚集索引(nonclustered index,也称非聚类索引、非簇集索引)。下面,我们举例来说明一下聚集索引和非聚集索引的区别:  其实,我们的汉语字典的正文本身就是一个聚集索引。比如,我们要查“安”字,就会很自然地翻开字典的前几页,因为“安”的拼音是“an”,而按照拼音排序汉字的字典是以英文字母“a”开头并以“z”结尾的,那么“安”字就自然地排在字典的前部。如果您翻完了所有以“a”开头的部分仍然找不到这个字,那么就说明您的字典中没有这个字;同样的,如果查“张”字,那您也会将您的字典翻到最后部分,因为“张”的拼音是“zhang”。也就是说,字典的正文部分本身就是一个目录,您不需要再去查其他目录来找到您需要找的内容。  我们把这种正文内容本身就是一种按照一定规则排列的目录称为“聚集索引”。  如果您认识某个字,您可以快速地从自动中查到这个字。但您也可能会遇到您不认识的字,不知道它的发音,这时候,您就不能按照刚才的方法找到您要查的字,而需要去根据“偏旁部首”查到您要找的字,然后根据这个字后的页码直接翻到某页来找到您要找的字。但您结合“部首目录”和“检字表”而查到的字的排序并不是真正的正文的排序方法,比如您查“张”字,我们可以看到在查部首之后的检字表中“张”的页码是672页,检字表中“张”的上面是“驰”字,但页码却是63页,“张”的下面是“弩”字,页面是390页。很显然,这些字并不是真正的分别位于“张”字的上下方,现在您看到的连续的“驰、张、弩”三字实际上就是他们在非聚集索引中的排序,是字典正文中的字在非聚集索引中的映射。我们可以通过这种方式来找到您所需要的字,但它需要两个过程,先找到目录中的结果,然后再翻到您所需要的页码。  我们把这种目录纯粹是目录,正文纯粹是正文的排序方式称为“非聚集索引”。通过以上例子,我们可以理解到什么是“聚集索引”和“非聚集索引”。  进一步引申一下,我们可以很容易的理解:每个表只能有一个聚集索引,因为目录只能按照一种方法进行排序。 (二)何时使用聚集索引或非聚集索引 下面的表总结了何时使用聚集索引或非聚集索引(很重要)。 动作描述使用聚集索引使用非聚集索引列经常被分组排序应应返回某范围内的数据应不应一个或极少不同值不应不应小数目的不同值应不应大数目的不同值不应应频繁更新的列不应应外键列应应主键列应应频繁修改索引列不应应  事实上,我们可以通过前面聚集索引和非聚集索引的定义的例子来理解上表。如:返回某范围内的数据一项。比如您的某个表有一个时间列,恰好您把聚合索引建立在了该列,这时您查询2004年1月1日至2004年10月1日之间的全部数据时,这个速度就将是很快的,因为您的这本字典正文是按日期进行排序的,聚类索引只需要找到要检索的所有数据中的开头和结尾数据即可;而不像非聚集索引,必须先查到目录中查到每一项数据对应的页码,然后再根据页码查到具体内容。(三)结合实际,谈索引使用的误区   理论的目的是应用。虽然我们刚才列出了何时应使用聚集索引或非聚集索引,但在实践中以上规则却很容易被忽视或不能根据实际情况进行综合分析。下面我们将根据在实践中遇到的实际问题来谈一下索引使用的误区,以便于大家掌握索引建立的方法。 1、主键就是聚集索引   这种想法笔者认为是极端错误的,是对聚集索引的一种浪费。虽然SQL SERVER默认是在主键上建立聚集索引的。  通常,我们会在每个表中都建立一个ID列,以区分每条数据,并且这个ID列是自动增大的,步长一般为1。我们的这个办公自动化的实例中的列Gid就是如此。此时,如果我们将这个列设为主键,SQL SERVER会将此列默认为聚集索引。这样做有好处,就是可以让您的数据在数据库中按照ID进行物理排序,但笔者认为这样做意义不大。  显而易见,聚集索引的优势是很明显的,而每个表中只能有一个聚集索引的规则,这使得聚集索引变得更加珍贵。  从我们前面谈到的聚集索引的定义我们可以看出,使用聚集索引的最大好处就是能够根据查询要求,迅速缩小查询范围,避免全表扫描。在实际应用中,因为ID号是自动生成的,我们并不知道每条记录的ID号,所以我们很难在实践中用ID号来进行查询。这就使让ID号这个主键作为聚集索引成为一种资源浪费。其次,让每个ID号都不同的字段作为聚集索引也不符合“大数目的不同值情况下不应建立聚合索引”规则;当然,这种情况只是针对用户经常修改记录内容,特别是索引项的时候会负作用,但对于查询速度并没有影响。  在办公自动化系统中,无论是系统首页显示的需要用户签收的文件、会议还是用户进行文件查询等任何情况下进行数据查询都离不开字段的是“日期”还有用户本身的“用户名”。  通常,办公自动化的首页会显示每个用户尚未签收的文件或会议。虽然我们的where语句可以仅仅限制当前用户尚未签收的情况,但如果您的系统已建立了很长时间,并且数据量很大,那么,每次每个用户打开首页的时候都进行一次全表扫描,这样做意义是不大的,绝大多数的用户1个月前的文件都已经浏览过了,这样做只能徒增数据库的开销而已。事实上,我们完全可以让用户打开系统首页时,数据库仅仅查询这个用户近3个月来未阅览的文件,通过“日期”这个字段来限制表扫描,提高查询速度。如果您的办公自动化系统已经建立的2年,那么您的首页显示速度理论上将是原来速度8倍,甚至更快。  在这里之所以提到“理论上”三字,是因为如果您的聚集索引还是盲目地建在ID这个主键上时,您的查询速度是没有这么高的,即使您在“日期”这个字段上建立的索引(非聚合索引)。下面我们就来看一下在1000万条数据量的情况下各种查询的速度表现(3个月内的数据为25万条): (1)仅在主键上建立聚集索引,并且不划分时间段: Select gid,fariqi,neibuyonghu,title from tgongwen用时:128470毫秒(即:128秒) (2)在主键上建立聚集索引,在fariq上建立非聚集索引: select gid,fariqi,neibuyonghu,title from Tgongwenwhere fariqi> dateadd(day,-90,getdate())用时:53763毫秒(54秒) (3)将聚合索引建立在日期列(fariqi)上: select gid,fariqi,neibuyonghu,title from Tgongwenwhere fariqi> dateadd(day,-90,getdate())用时:2423毫秒(2秒)   虽然每条语句提取出来的都是25万条数据,各种情况的差异却是巨大的,特别是将聚集索引建立在日期列时的差异。事实上,如果您的数据库真的有1000万容量的话,把主键建立在ID列上,就像以上的第1、2种情况,在网页上的表现就是超时,根本就无法显示。这也是我摒弃ID列作为聚集索引的一个最重要的因素。  得出以上速度的方法是:在各个select语句前加:declare @d datetime  set @d=getdate()  并在select语句后加:  select [语句执行花费时间(毫秒)]=datediff(ms,@d,getdate()) 2、只要建立索引就能显著提高查询速度   事实上,我们可以发现上面的例子中,第2、3条语句完全相同,且建立索引的字段也相同;不同的仅是前者在fariqi字段上建立的是非聚合索引,后者在此字段上建立的是聚合索引,但查询速度却有着天壤之别。所以,并非是在任何字段上简单地建立索引就能提高查询速度。  从建表的语句中,我们可以看到这个有着1000万数据的表中fariqi字段有5003个不同记录。在此字段上建立聚合索引是再合适不过了。在现实中,我们每天都会发几个文件,这几个文件的发文日期就相同,这完全符合建立聚集索引要求的:“既不能绝大多数都相同,又不能只有极少数相同”的规则。由此看来,我们建立“适当”的聚合索引对于我们提高查询速度是非常重要的。 3、把所有需要提高查询速度的字段都加进聚集索引,以提高查询速度   上面已经谈到:在进行数据查询时都离不开字段的是“日期”还有用户本身的“用户名”。既然这两个字段都是如此的重要,我们可以把他们合并起来,建立一个复合索引(compound index)。  很多人认为只要把任何字段加进聚集索引,就能提高查询速度,也有人感到迷惑:如果把复合的聚集索引字段分开查询,那么查询速度会减慢吗?带着这个问题,我们来看一下以下的查询速度(结果集都是25万条数据):(日期列fariqi首先排在复合聚集索引的起始列,用户名neibuyonghu排在后列) (1)select gid,fariqi,neibuyonghu,title from Tgongwen where fariqi>'2004-5-5' 查询速度:2513毫秒(2)select gid,fariqi,neibuyonghu,title from Tgongwen where fariqi>'2004-5-5' and neibuyonghu='办公室'查询速度:2516毫秒(3)select gid,fariqi,neibuyonghu,title from Tgongwen where neibuyonghu='办公室'查询速度:60280毫秒   从以上试验中,我们可以看到如果仅用聚集索引的起始列作为查询条件和同时用到复合聚集索引的全部列的查询速度是几乎一样的,甚至比用上全部的复合索引列还要略快(在查询结果集数目一样的情况下);而如果仅用复合聚集索引的非起始列作为查询条件的话,这个索引是不起任何作用的。当然,语句1、2的查询速度一样是因为查询的条目数一样,如果复合索引的所有列都用上,而且查询结果少的话,这样就会形成“索引覆盖”,因而性能可以达到最优。同时,请记住:无论您是否经常使用聚合索引的其他列,但其前导列一定要是使用最频繁的列。 (四)其他书上没有的索引使用经验总结 1、用聚合索引比用不是聚合索引的主键速度快   下面是实例语句:(都是提取25万条数据) select gid,fariqi,neibuyonghu,reader,title from Tgongwen where fariqi='2004-9-16'使用时间:3326毫秒select gid,fariqi,neibuyonghu,reader,title from Tgongwen where gid<=250000使用时间:4470毫秒   这里,用聚合索引比用不是聚合索引的主键速度快了近1/4。 2、用聚合索引比用一般的主键作order by时速度快,特别是在小数据量情况下 select gid,fariqi,neibuyonghu,reader,title from Tgongwen order by fariqi用时:12936select gid,fariqi,neibuyonghu,reader,title from Tgongwen order by gid用时:18843   这里,用聚合索引比用一般的主键作order by时,速度快了3/10。事实上,如果数据量很小的话,用聚集索引作为排序列要比使用非聚集索引速度快得明显的多;而数据量如果很大的话,如10万以上,则二者的速度差别不明显。 3、使用聚合索引内的时间段,搜索时间会按数据占整个数据表的百分比成比例减少,而无论聚合索引使用了多少个 select gid,fariqi,neibuyonghu,reader,title from Tgongwen where fariqi>'2004-1-1'用时:6343毫秒(提取100万条) select gid,fariqi,neibuyonghu,reader,title from Tgongwen where fariqi>'2004-6-6'用时:3170毫秒(提取50万条)select gid,fariqi,neibuyonghu,reader,title from Tgongwen where fariqi='2004-9-16'用时:3326毫秒(和上句的结果一模一样。如果采集的数量一样,那么用大于号和等于号是一样的)select gid,fariqi,neibuyonghu,reader,title from Tgongwen where fariqi>'2004-1-1' and fariqi<'2004-6-6'用时:3280毫秒 4 、日期列不会因为有分秒的输入而减慢查询速度   下面的例子中,共有100万条数据,2004年1月1日以后的数据有50万条,但只有两个不同的日期,日期精确到日;之前有数据50万条,有5000个不同的日期,日期精确到秒。 select gid,fariqi,neibuyonghu,reader,title from Tgongwen where fariqi>'2004-1-1' order by fariqi用时:6390毫秒select gid,fariqi,neibuyonghu,reader,title from Tgongwen where fariqi<'2004-1-1' order by fariqi用时:6453毫秒 (五)其他注意事项   “水可载舟,亦可覆舟”,索引也一样。索引有助于提高检索性能,但过多或不当的索引也会导致系统低效。因为用户在表中每加进一个索引,数据库就要做更多的工作。过多的索引甚至会导致索引碎片。  所以说,我们要建立一个“适当”的索引体系,特别是对聚合索引的创建,更应精益求精,以使您的数据库能得到高性能的发挥。  当然,在实践中,作为一个尽职的数据库管理员,您还要多测试一些方案,找出哪种方案效率最高、最为有效。 二、改善SQL语句   很多人不知道SQL语句在SQL SERVER中是如何执行的,他们担心自己所写的SQL语句会被SQL SERVER误解。比如:select * from table1 where name='zhangsan' and tID > 10000  和执行:select * from table1 where tID > 10000 and name='zhangsan'  一些人不知道以上两条语句的执行效率是否一样,因为如果简单的从语句先后上看,这两个语句的确是不一样,如果tID是一个聚合索引,那么后一句仅仅从表的10000条以后的记录中查找就行了;而前一句则要先从全表中查找看有几个name='zhangsan'的,而后再根据限制条件条件tID>10000来提出查询结果。  事实上,这样的担心是不必要的。SQL SERVER中有一个“查询分析优化器”,它可以计算出where子句中的搜索条件并确定哪个索引能缩小表扫描的搜索空间,也就是说,它能实现自动优化。  虽然查询优化器可以根据where子句自动的进行查询优化,但大家仍然有必要了解一下“查询优化器”的工作原理,如非这样,有时查询优化器就会不按照您的本意进行快速查询。  在查询分析阶段,查询优化器查看查询的每个阶段并决定限制需要扫描的数据量是否有用。如果一个阶段可以被用作一个扫描参数(SARG),那么就称之为可优化的,并且可以利用索引快速获得所需数据。  SARG的定义:用于限制搜索的一个操作,因为它通常是指一个特定的匹配,一个值得范围内的匹配或者两个以上条件的AND连接。形式如下: 列名 操作符 <常数 或 变量>或<常数 或 变量> 操作符列名   列名可以出现在操作符的一边,而常数或变量出现在操作符的另一边。如: Name='张三'价格>50005000<价格Name='张三' and 价格>5000   如果一个表达式不能满足SARG的形式,那它就无法限制搜索的范围了,也就是SQL SERVER必须对每一行都判断它是否满足WHERE子句中的所有条件。所以一个索引对于不满足SARG形式的表达式来说是无用的。   介绍完SARG后,我们来总结一下使用SARG以及在实践中遇到的和某些资料上结论不同的经验: 1、Like语句是否属于SARG取决于所使用的通配符的类型   如:name like ‘张%' ,这就属于SARG  而:name like ‘%张',就不属于SARG。  原因是通配符%在字符串的开通使得索引无法使用。 2、or 会引起全表扫描   Name='张三' and 价格>5000 符号SARG,而:Name='张三' or 价格>5000 则不符合SARG。使用or会引起全表扫描。 3、非操作符、函数引起的不满足SARG形式的语句   不满足SARG形式的语句最典型的情况就是包括非操作符的语句,如:NOT、!=、<>、!<、!>、NOT EXISTS、NOT IN、NOT LIKE等,另外还有函数。下面就是几个不满足SARG形式的例子: ABS(价格)<5000Name like ‘%三' 有些表达式,如: WHERE 价格*2>5000 SQL SERVER也会认为是SARG,SQL SERVER会将此式转化为: WHERE 价格>2500/2   但我们不推荐这样使用,因为有时SQL SERVER不能保证这种转化与原始表达式是完全等价的。 4、IN 的作用相当与OR 语句: Select * from table1 where tid in (2,3) 和 Select * from table1 where tid=2 or tid=3 是一样的,都会引起全表扫描,如果tid上有索引,其索引也会失效。 5、尽量少用NOT 6、exists 和 in 的执行效率是一样的   很多资料上都显示说,exists要比in的执行效率要高,同时应尽可能的用not exists来代替not in。但事实上,我试验了一下,发现二者无论是前面带不带not,二者之间的执行效率都是一样的。因为涉及子查询,我们试验这次用SQL SERVER自带的pubs数据库。运行前我们可以把SQL SERVER的statistics I/O状态打开。 (1)select title,price from titles where title_id in (select title_id from sales where qty>30)   该句的执行结果为:表 'sales'。扫描计数 18,逻辑读 56 次,物理读 0 次,预读 0 次。表 'titles'。扫描计数 1,逻辑读 2 次,物理读 0 次,预读 0 次。    (2)select title,price from titles where exists (select * from sales where sales.title_id=titles.title_id and qty>30)   第二句的执行结果为:表 'sales'。扫描计数 18,逻辑读 56 次,物理读 0 次,预读 0 次。表 'titles'。扫描计数 1,逻辑读 2 次,物理读 0 次,预读 0 次。  我们从此可以看到用exists和用in的执行效率是一样的。 7、用函数charindex()和前面加通配符%的LIKE执行效率一样   前面,我们谈到,如果在LIKE前面加上通配符%,那么将会引起全表扫描,所以其执行效率是低下的。但有的资料介绍说,用函数charindex()来代替LIKE速度会有大的提升,经我试验,发现这种说明也是错误的: select gid,title,fariqi,reader from tgongwen where charindex('刑侦支队',reader)>0 and fariqi>'2004-5-5'  用时:7秒,另外:扫描计数 4,逻辑读 7155 次,物理读 0 次,预读 0 次。 select gid,title,fariqi,reader from tgongwen where reader like '%' + '刑侦支队' + '%' and fariqi>'2004-5-5'  用时:7秒,另外:扫描计数 4,逻辑读 7155 次,物理读 0 次,预读 0 次。 8、union并不绝对比or的执行效率高   我们前面已经谈到了在where子句中使用or会引起全表扫描,一般的,我所见过的资料都是推荐这里用union来代替or。事实证明,这种说法对于大部分都是适用的。 select gid,fariqi,neibuyonghu,reader,title from Tgongwen where fariqi='2004-9-16' or gid>9990000  用时:68秒。扫描计数 1,逻辑读 404008 次,物理读 283 次,预读 392163 次。select gid,fariqi,neibuyonghu,reader,title from Tgongwen where fariqi='2004-9-16' unionselect gid,fariqi,neibuyonghu,reader,title from Tgongwen where gid>9990000  用时:9秒。扫描计数 8,逻辑读 67489 次,物理读 216 次,预读 7499 次。  看来,用union在通常情况下比用or的效率要高的多。  但经过试验,笔者发现如果or两边的查询列是一样的话,那么用union则反倒和用or的执行速度差很多,虽然这里union扫描的是索引,而or扫描的是全表。 select gid,fariqi,neibuyonghu,reader,title from Tgongwen where fariqi='2004-9-16' or fariqi='2004-2-5'  用时:6423毫秒。扫描计数 2,逻辑读 14726 次,物理读 1 次,预读 7176 次。select gid,fariqi,neibuyonghu,reader,title from Tgongwen where fariqi='2004-9-16' unionselect gid,fariqi,neibuyonghu,reader,title from Tgongwen where  fariqi='2004-2-5'  用时:11640毫秒。扫描计数 8,逻辑读 14806 次,物理读 108 次,预读 1144 次。 9、字段提取要按照“需多少、提多少”的原则,避免“select *” 我们来做一个试验: select top 10000 gid,fariqi,reader,title from tgongwen order by gid desc  用时:4673毫秒select top 10000 gid,fariqi,title from tgongwen order by gid desc  用时:1376毫秒select top 10000 gid,fariqi from tgongwen order by gid desc  用时:80毫秒   由此看来,我们每少提取一个字段,数据的提取速度就会有相应的提升。提升的速度还要看您舍弃的字段的大小来判断。 10、count(*)不比count(字段)慢   某些资料上说:用*会统计所有列,显然要比一个世界的列名效率低。这种说法其实是没有根据的。我们来看: select count(*) from Tgongwen用时:1500毫秒select count(gid) from Tgongwen 用时:1483毫秒select count(fariqi) from Tgongwen用时:3140毫秒select count(title) from Tgongwen用时:52050毫秒   从以上可以看出,如果用count(*)和用count(主键)的速度是相当的,而count(*)却比其他任何除主键以外的字段汇总速度要快,而且字段越长,汇总的速度就越慢。我想,如果用count(*), SQL SERVER可能会自动查找最小字段来汇总的。当然,如果您直接写count(主键)将会来的更直接些。 11、order by按聚集索引列排序效率最高   我们来看:(gid是主键,fariqi是聚合索引列) select top 10000 gid,fariqi,reader,title from tgongwen用时:196 毫秒。 扫描计数 1,逻辑读 289 次,物理读 1 次,预读 1527 次。select top 10000 gid,fariqi,reader,title from tgongwen order by gid asc用时:4720毫秒。 扫描计数 1,逻辑读 41956 次,物理读 0 次,预读 1287 次。select top 10000 gid,fariqi,reader,title from tgongwen order by gid desc用时:4736毫秒。 扫描计数 1,逻辑读 55350 次,物理读 10 次,预读 775 次。select top 10000 gid,fariqi,reader,title from tgongwen order by fariqi asc用时:173毫秒。 扫描计数 1,逻辑读 290 次,物理读 0 次,预读 0 次。select top 10000 gid,fariqi,reader,title from tgongwen order by fariqi desc用时:156毫秒。 扫描计数 1,逻辑读 289 次,物理读 0 次,预读 0 次。   从以上我们可以看出,不排序的速度以及逻辑读次数都是和“order by 聚集索引列” 的速度是相当的,但这些都比“order by 非聚集索引列”的查询速度是快得多的。  同时,按照某个字段进行排序的时候,无论是正序还是倒序,速度是基本相当的。 12、高效的TOP   事实上,在查询和提取超大容量的数据集时,影响数据库响应时间的最大因素不是数据查找,而是物理的I/0操作。如: select top 10 * from (select top 10000 gid,fariqi,title from tgongwenwhere neibuyonghu='办公室'order by gid desc) as aorder by gid asc   这条语句,从理论上讲,整条语句的执行时间应该比子句的执行时间长,但事实相反。因为,子句执行后返回的是10000条记录,而整条语句仅返回10条语句,所以影响数据库响应时间最大的因素是物理I/O操作。而限制物理I/O操作此处的最有效方法之一就是使用TOP关键词了。TOP关键词是SQL SERVER中经过系统优化过的一个用来提取前几条或前几个百分比数据的词。经笔者在实践中的应用,发现TOP确实很好用,效率也很高。但这个词在另外一个大型数据库ORACLE中却没有,这不能说不是一个遗憾,虽然在ORACLE中可以用其他方法(如:rownumber)来解决。在以后的关于“实现千万级数据的分页显示存储过程”的讨论中,我们就将用到TOP这个关键词。   到此为止,我们上面讨论了如何实现从大容量的数据库中快速地查询出您所需要的数据方法。当然,我们介绍的这些方法都是“软”方法,在实践中,我们还要考虑各种“硬”因素,如:网络性能、服务器的性能、操作系统的性能,甚至网卡、交换机等。 三、实现小数据量和海量数据的通用分页显示存储过程   建立一个web 应用,分页浏览功能必不可少。这个问题是数据库处理中十分常见的问题。经典的数据分页方法是:ADO 纪录集分页法,也就是利用ADO自带的分页功能(利用游标)来实现分页。但这种分页方法仅适用于较小数据量的情形,因为游标本身有缺点:游标是存放在内存中,很费内存。游标一建立,就将相关的记录锁住,直到取消游标。游标提供了对特定集合中逐行扫描的手段,一般使用游标来逐行遍历数据,根据取出数据条件的不同进行不同的操作。而对于多表和大表中定义的游标(大的数据集合)循环很容易使程序进入一个漫长的等待甚至死机。  更重要的是,对于非常大的数据模型而言,分页检索时,如果按照传统的每次都加载整个数据源的方法是非常浪费资源的。现在流行的分页方法一般是检索页面大小的块区的数据,而非检索所有的数据,然后单步执行当前行。  最早较好地实现这种根据页面大小和页码来提取数据的方法大概就是“俄罗斯存储过程”。这个存储过程用了游标,由于游标的局限性,所以这个方法并没有得到大家的普遍认可。  后来,网上有人改造了此存储过程,下面的存储过程就是结合我们的办公自动化实例写的分页存储过程: CREATE procedure pagination1(@pagesize int,  --页面大小,如每页存储20条记录@pageindex int   --当前页码)asset nocount onbegindeclare @indextable table(id int identity(1,1),nid int)  --定义表变量declare @PageLowerBound int  --定义此页的底码declare @PageUpperBound int  --定义此页的顶码set @PageLowerBound=(@pageindex-1)*@pagesizeset @[email protected][email protected] rowcount @PageUpperBoundinsert into @indextable(nid) select gid from TGongwen where fariqi >dateadd(day,-365,getdate()) order by fariqi descselect O.gid,O.mid,O.title,O.fadanwei,O.fariqi from TGongwen O,@indextable t where O.gid=t.nidand t.id>@PageLowerBound and [email protected] order by t.idendset nocount off   以上存储过程运用了SQL SERVER的最新技术――表变量。应该说这个存储过程也是一个非常优秀的分页存储过程。当然,在这个过程中,您也可以把其中的表变量写成临时表:CREATE TABLE #Temp。但很明显,在SQL SERVER中,用临时表是没有用表变量快的。所以笔者刚开始使用这个存储过程时,感觉非常的不错,速度也比原来的ADO的好。但后来,我又发现了比此方法更好的方法。  笔者曾在网上看到了一篇小短文《从数据表中取出第n条到第m条的记录的方法》,全文如下:  从publish 表中取出第 n 条到第 m 条的记录: SELECT TOP m-n+1 * FROM publish WHERE (id NOT IN     (SELECT TOP n-1 id      FROM publish))  id 为publish 表的关键字    我当时看到这篇文章的时候,真的是精神为之一振,觉得思路非常得好。等到后来,我在作办公自动化系统(ASP.NET+ C#+SQL SERVER)的时候,忽然想起了这篇文章,我想如果把这个语句改造一下,这就可能是一个非常好的分页存储过程。于是我就满网上找这篇文章,没想到,文章还没找到,却找到了一篇根据此语句写的一个分页存储过程,这个存储过程也是目前较为流行的一种分页存储过程,我很后悔没有争先把这段文字改造成存储过程: CREATE PROCEDURE pagination2( @SQL nVARCHAR(4000),    --不带排序语句的SQL语句 @Page int,              --页码 @RecsPerPage int,       --每页容纳的记录数 @ID VARCHAR(255),       --需要排序的不重复的ID号 @Sort VARCHAR(255)      --排序字段及规则)ASDECLARE @Str nVARCHAR(4000)SET @Str='SELECT   TOP '+CAST(@RecsPerPage AS VARCHAR(20))+' * FROM ('[email protected]+') T WHERE T.'[email protected]+'NOT IN (SELECT   TOP '+CAST((@RecsPerPage*(@Page-1)) AS VARCHAR(20))+' '[email protected]+' FROM ('[email protected]+') T9 ORDER BY '[email protected]+') ORDER BY '[email protected] @StrEXEC sp_ExecuteSql @StrGO其实,以上语句可以简化为:SELECT TOP 页大小 *FROM Table1WHERE (ID NOT IN          (SELECT TOP 页大小*页数 id         FROM 表         ORDER BY id))ORDER BY ID   但这个存储过程有一个致命的缺点,就是它含有NOT IN字样。虽然我可以把它改造为: SELECT TOP 页大小 *FROM Table1WHERE not exists(select * from (select top (页大小*页数) * from table1 order by id) b where b.id=a.id )order by id   即,用not exists来代替not in,但我们前面已经谈过了,二者的执行效率实际上是没有区别的。  既便如此,用TOP 结合NOT IN的这个方法还是比用游标要来得快一些。  虽然用not exists并不能挽救上个存储过程的效率,但使用SQL SERVER中的TOP关键字却是一个非常明智的选择。因为分页优化的最终目的就是避免产生过大的记录集,而我们在前面也已经提到了TOP的优势,通过TOP 即可实现对数据量的控制。  在分页算法中,影响我们查询速度的关键因素有两点:TOP和NOT IN。TOP可以提高我们的查询速度,而NOT IN会减慢我们的查询速度,所以要提高我们整个分页算法的速度,就要彻底改造NOT IN,同其他方法来替代它。  我们知道,几乎任何字段,我们都可以通过max(字段)或min(字段)来提取某个字段中的最大或最小值,所以如果这个字段不重复,那么就可以利用这些不重复的字段的max或min作为分水岭,使其成为分页算法中分开每页的参照物。在这里,我们可以用操作符“>”或“<”号来完成这个使命,使查询语句符合SARG形式。如: Select top 10 * from table1 where id>200 于是就有了如下分页方案: select top 页大小 *from table1 where id>      (select max (id) from       (select top ((页码-1)*页大小) id from table1 order by id) as T       )       order by id   在选择即不重复值,又容易分辨大小的列时,我们通常会选择主键。下表列出了笔者用有着1000万数据的办公自动化系统中的表,在以GID(GID是主键,但并不是聚集索引。)为排序列、提取gid,fariqi,title字段,分别以第1、10、100、500、1000、1万、10万、25万、50万页为例,测试以上三种分页方案的执行速度:(单位:毫秒) 页  码方案1方案2方案3160307610461663100107672013050054012943831000171104702501万24796450014010万3832642283155325万28140128720233050万1216861278467168  从上表中,我们可以看出,三种存储过程在执行100页以下的分页命令时,都是可以信任的,速度都很好。但第一种方案在执行分页1000页以上后,速度就降了下来。第二种方案大约是在执行分页1万页以上后速度开始降了下来。而第三种方案却始终没有大的降势,后劲仍然很足。  在确定了第三种分页方案后,我们可以据此写一个存储过程。大家知道SQL SERVER的存储过程是事先编译好的SQL语句,它的执行效率要比通过WEB页面传来的SQL语句的执行效率要高。下面的存储过程不仅含有分页方案,还会根据页面传来的参数来确定是否进行数据总数统计。 -- 获取指定页的数据CREATE PROCEDURE [email protected]   varchar(255),       -- 表名@strGetFields varchar(1000) = '*',  -- 需要返回的列 @fldName varchar(255)='',      -- 排序的字段名@PageSize   int = 10,          -- 页尺寸@PageIndex  int = 1,           -- 页码@doCount  bit = 0,   -- 返回记录总数, 非 0 值则返回@OrderType bit = 0,  -- 设置排序类型, 非 0 值则降序@strWhere  varchar(1500) = ''  -- 查询条件 (注意: 不要加 where)ASdeclare @strSQL   varchar(5000)       -- 主语句declare @strTmp   varchar(110)        -- 临时变量declare @strOrder varchar(400)        -- 排序类型 if @doCount != 0  begin    if @strWhere !=''    set @strSQL = "select count(*) as Total from [" + @tblName + "] where "[email protected]    else    set @strSQL = "select count(*) as Total from [" + @tblName + "]"end  --以上代码的意思是如果@doCount传递过来的不是0,就执行总数统计。以下的所有代码都是@doCount为0的情况elsebegin if @OrderType != 0begin    set @strTmp = "<(select min"set @strOrder = " order by [" + @fldName +"] desc"--如果@OrderType不是0,就执行降序,这句很重要!endelsebegin    set @strTmp = ">(select max"    set @strOrder = " order by [" + @fldName +"] asc"end if @PageIndex = 1begin    if @strWhere != ''       set @strSQL = "select top " + str(@PageSize) +" "[email protected]+ "  from [" + @tblName + "] where " + @strWhere + " " + @strOrder     else     set @strSQL = "select top " + str(@PageSize) +" "[email protected]+ "  from ["+ @tblName + "] "+ @strOrder--如果是第一页就执行以上代码,这样会加快执行速度endelsebegin--以下代码赋予了@strSQL以真正执行的SQL代码set @strSQL = "select top " + str(@PageSize) +" "[email protected]+ "  from ["    + @tblName + "] where [" + @fldName + "]" + @strTmp + "(["+ @fldName + "]) from (select top " + str((@PageIndex-1)*@PageSize) + " ["+ @fldName + "] from [" + @tblName + "]" + @strOrder + ") as tblTmp)"+ @strOrder if @strWhere != ''    set @strSQL = "select top " + str(@PageSize) +" "[email protected]+ "  from ["        + @tblName + "] where [" + @fldName + "]" + @strTmp + "(["        + @fldName + "]) from (select top " + str((@PageIndex-1)*@PageSize) + " ["        + @fldName + "] from [" + @tblName + "] where " + @strWhere + " "        + @strOrder + ") as tblTmp) and " + @strWhere + " " + @strOrderend end   exec (@strSQL)GO   上面的这个存储过程是一个通用的存储过程,其注释已写在其中了。  在大数据量的情况下,特别是在查询最后几页的时候,查询时间一般不会超过9秒;而用其他存储过程,在实践中就会导致超时,所以这个存储过程非常适用于大容量数据库的查询。  笔者希望能够通过对以上存储过程的解析,能给大家带来一定的启示,并给工作带来一定的效率提升,同时希望同行提出更优秀的实时数据分页算法。 四、聚集索引的重要性和如何选择聚集索引   在上一节的标题中,笔者写的是:实现小数据量和海量数据的通用分页显示存储过程。这是因为在将本存储过程应用于“办公自动化”系统的实践中时,笔者发现这第三种存储过程在小数据量的情况下,有如下现象:  1、分页速度一般维持在1秒和3秒之间。  2、在查询最后一页时,速度一般为5秒至8秒,哪怕分页总数只有3页或30万页。  虽然在超大容量情况下,这个分页的实现过程是很快的,但在分前几页时,这个1-3秒的速度比起第一种甚至没有经过优化的分页方法速度还要慢,借用户的话说就是“还没有ACCESS数据库速度快”,这个认识足以导致用户放弃使用您开发的系统。  笔者就此分析了一下,原来产生这种现象的症结是如此的简单,但又如此的重要:排序的字段不是聚集索引!  本篇文章的题目是:“查询优化及分页算法方案”。笔者只所以把“查询优化”和“分页算法”这两个联系不是很大的论题放在一起,就是因为二者都需要一个非常重要的东西――聚集索引。  在前面的讨论中我们已经提到了,聚集索引有两个最大的优势:  1、以最快的速度缩小查询范围。  2、以最快的速度进行字段排序。  第1条多用在查询优化时,而第2条多用在进行分页时的数据排序。  而聚集索引在每个表内又只能建立一个,这使得聚集索引显得更加的重要。聚集索引的挑选可以说是实现“查询优化”和“高效分页”的最关键因素。  但要既使聚集索引列既符合查询列的需要,又符合排序列的需要,这通常是一个矛盾。  笔者前面“索引”的讨论中,将fariqi,即用户发文日期作为了聚集索引的起始列,日期的精确度为“日”。这种作法的优点,前面已经提到了,在进行划时间段的快速查询中,比用ID主键列有很大的优势。  但在分页时,由于这个聚集索引列存在着重复记录,所以无法使用max或min来最为分页的参照物,进而无法实现更为高效的排序。而如果将ID主键列作为聚集索引,那么聚集索引除了用以排序之外,没有任何用处,实际上是浪费了聚集索引这个宝贵的资源。  为解决这个矛盾,笔者后来又添加了一个日期列,其默认值为getdate()。用户在写入记录时,这个列自动写入当时的时间,时间精确到毫秒。即使这样,为了避免可能性很小的重合,还要在此列上创建UNIQUE约束。将此日期列作为聚集索引列。  有了这个时间型聚集索引列之后,用户就既可以用这个列查找用户在插入数据时的某个时间段的查询,又可以作为唯一列来实现max或min,成为分页算法的参照物。  经过这样的优化,笔者发现,无论是大数据量的情况下还是小数据量的情况下,分页速度一般都是几十毫秒,甚至0毫秒。而用日期段缩小范围的查询速度比原来也没有任何迟钝。  聚集索引是如此的重要和珍贵,所以笔者总结了一下,一定要将聚集索引建立在:  1、您最频繁使用的、用以缩小查询范围的字段上;  2、您最频繁使用的、需要排序的字段上。 结束语:  本篇文章汇集了笔者近段在使用数据库方面的心得,是在做“办公自动化”系统时实践经验的积累。希望这篇文章不仅能够给大家的工作带来一定的帮助,也希望能让大家能够体会到分析问题的方法;最重要的是,希望这篇文章能够抛砖引玉,掀起大家的学习和讨论的兴趣,以共同促进,共同为公安科技强警事业和金盾工程做出自己最大的努力。  最后需要说明的是,在试验中,我发现用户在进行大数据量查询的时候,对数据库速度影响最大的不是内存大小,而是CPU。在我的P4 2.4机器上试验的时候,查看“资源管理器”,CPU经常出现持续到100%的现象,而内存用量却并没有改变或者说没有大的改变。即使在我们的HP ML 350 G3服务器上试验时,CPU峰值也能达到90%,一般持续在70%左右。  本文的试验数据都是来自我们的HP ML 350服务器。服务器配置:双Inter Xeon 超线程 CPU 2.4G,内存1G,操作系统Windows Server 2003 Enterprise Edition,数据库SQL Server 2000 SP3。

焦点访谈

最新最热的文章

更多 >

COPYRIGHT (©) 2017 Copyright ©2017 5060网址大全 网站地图

联系我们

827570882

扫描二维码分享到微信