前言: 程序员
大部分人都一致认为一个数据库应用系统(这里的数据库应用系统概指全部使用数据库的系统)的性能瓶颈最容易出如今数据的操做方面,而数据库应用系统的大部分数据操做都是经过数据库管理软件所提供的相关接口来完成的。因此数据库管理软件也就很天然的成为了数据库应用系统的性能瓶颈所在,这是当前业界比较广泛的一个见解。但咱们的应用系统的性能瓶颈真的彻底是由于数据库管理软件和数据库主机自身形成的吗?咱们将经过本章的内容来进行一个较为深刻的分析,让你们了解到一个数据库应用系统的性能到底与哪些地方有关,让你们寻找出各自应用系统的出现性能问题的根本缘由,而尽量清楚的知道该如何去优化本身的应用系统。 数据库
考虑到本书的数据库对象是MySQL,而MySQL最多的使用场景是WEB应用,那么咱们就以一个WEB应用系统为例,逐个分析其系统构成,结合笔者在大型互联网公司从事DBA工做多年的经验总结,分析出数据库应用系统中各个环境对性能的影响。 编程
应用系统中的每个功能在设计初衷确定都是出于为用户提供某种服务,或者知足用户的某种需求,可是,并非每个功能在最后都能很成功,甚至有些功能的推出可能在整个系统中是多此一举。不只没有为用户提升任何体验度,也没有为用户改进多少功能易用性,反而在整个系统中成为一个累赘,带来资源的浪费。 安全
不合理需求形成资源投入产出比太低 性能优化
需求是否合理不少时候可能并非很容易界定,尤为是做为技术人员来讲,可能更难以肯定一个需求的合理性。即便指出,也不必定会被产品经历们承认。那做为技术人员的咱们怎么来证实一个需求是否合理呢? 服务器
第1、每次产品经理们提出新的项目(或者功能需求)的时候,应该要求他们同时给出该项目的预期收益的量化指标,以备项目上前后统计评估投入产出比率; 网络
第2、在每次项目进行过程当中,应该详细记录全部的资源投入,包括人力投入,硬件设施的投入,以及其余任何项目相关的资源投入; 架构
第3、项目(或者功能需求)上线以后应该及时经过手机相关数据统计出项目的实际收益值,以便计算投入产出比率的时候使用; 并发
第4、技术部门应该尽量推进设计出一个项目(或者功能需求)的投入产出比率的计算规则。在项目上线一段时间以后,经过项目实际收益的统计数据和项目的投入资源量,计算出整个项目的实际投入产出值,并公布给全部参与项目的部门知晓,同时存放以备后查。 框架
有了实际的投入产出比率,咱们就能够和项目立项之初产品经理们的预期投入产出比率作出比较,断定出这个项目作的是否值得。并且当积累了较多的项目投入产出比率以后,咱们能够根据历史数据分析出一个项目合理的投入产出比率应该是多少。这样,在项目立项之初,咱们就能够断定出产品经理们的预期投入产出比率是否合理,项目是否真的有进行的必要。
有了实际的投入产出比率以后,咱们还能够拿出数据给老板们看,让他知道功能并非越多越好,让他知道有些功能是应该撤下来的,即便撤下该功能可能须要投入很多资源。
实际上,通常来讲,在产品开发及运营部门内部都会作上面所说的这些事情的。但不少时候可能更多只是一种形式化的过程。在有些比较规范的公司可能也完成了上面的大部分流程,可是要么数据不公开,要么公开给其余部门的数据存在必定的误差,不具有真实性。
为何会这样?其实就一个缘由,就是部门之间的利益冲突及业绩冲突问题。产品经理们老是但愿尽量的让用户以为本身设计的产品功能齐全,让老板以为本身作了不少事情。可是历来都不会去关心由于作一个功能所带来的成本投入,或者说是不会特别的关心这一点。并且不少时候他们也并不能太理解技术方面带来的复杂度给产品自己带来的负面影响。
这里咱们就拿一个看上去很简单的功能来分析一下。
需求:一个论坛帖子总量的统计附加要求:实时更新
在不少人看来,这个功能很是容易实现,不就是执行一条SELECT COUNT(*)的Query就能够获得结果了么?是的,确实只须要如此简单的一个Query就能够获得结果。可是,若是咱们采用不是MyISAM存储引擎,而是使用的Innodb的存储引擎,那么你们能够试想一下,若是存放帖子的表中已经有上千万的帖子的时候,执行这条Query语句须要多少成本?恐怕再好的硬件设备,恐怕都不可能在10秒以内完成一次查询吧。若是咱们的访问量再大一点,还有人以为这是一件简单的事情么?
既然这样查询不行,那咱们是否是该专门为这个功能建一个表,就只有一个字段,一条记录,就存放这个统计量,每次有新的帖子产生的时候,都将这个值增长1,这样咱们每次都只须要查询这个表就能够获得结果了,这个效率确定可以知足要求了。确实,查询效率确定可以知足要求,但是若是咱们的系统帖子产生很快,在高峰时期可能每秒就有几十甚至上百个帖子新增操做的时候,恐怕这个统计表又要成为你们的噩梦了。要么由于并发的问题形成统计结果的不许确,要么由于锁资源争用严重形成总体性能的大幅度降低。
其实这里问题的焦点不该该是实现这个功能的技术细节,而是在于这个功能的附加要求"实时更新"上面。当一个论坛的帖子数量很大了以后,到底有多少人会关注这个统计数据是不是实时变化的?有多少人在意这个数据在短期内的不精确性?我想恐怕不会有人会傻傻的盯着这个统计数字并追究当本身发了一个帖子而后回头刷新页面发现这个统计数字没有加1吧?即便明明白白的告诉用户这个统计数据是每过多长时间段更新一次,那有怎样?难道会有不少用户就此很不爽么?
只要去掉了这个"实时更新"的附加条件,咱们就能够很是容易的实现这个功能了。就像以前所提到的那样,经过建立一个统计表,而后经过一个定时任务每隔必定时间段去更新一次里面的统计值,这样既能够解决统计值查询的效率问题,又能够保证不影响新发贴的效率,一箭双雕。
实际上,在咱们应用的系统中还有不少不少相似的功能点能够优化。如某些场合的列表页面参与列表的数据量达到一个数量级以后,彻底能够不用准确的显示这个列表总共有多少条信息,总共分了多少页,而只须要一个大概的估计值或者一个时间段以前的统计值。这样就省略了咱们的分页程序须要在分之前实时COUNT出知足条件的记录数。
其实,在不少应用系统中,实时和准实时,精确与基本准确,在不少地方所带来的性能消耗多是几个性能的差异。在系统性能优化中,应该尽可能分析出那些能够不实时和不彻底精确的地方,做出一些相应的调整,可能会给你们带来意想不到的巨大性能提高。
无用功能堆积使系统过分复杂影响总体性能
不少时候,为系统增长某个功能可能并不须要花费太多的成本,而要想将一个已经运行了一段时间的功能从原有系统中撤下来倒是很是困难的。
首先,对于开发部门,可能要从新整理不少的代码,找出可能存在与增长该功能所编写的代码有交集的其余功能点,删除没有关联的代码,修改有关联的代码;
其次,对于测试部门,因为功能的变更,必需要回归测试全部相关的功能点是否正常。可能因为界定困难,不得不将回归范围扩展到很大,测试工做量也很大。
最后,全部与撤除下线某个功能相关的工做参与者来讲,又没法带来任何实质性的收益,而偏偏相反是,带来的只多是风险。
因为上面的这几个因素,可能不多有公司可以有很完善的项目(或者功能)下线机制,也不多有公司能作到及时将系统中某些不合适的功能下线。因此,咱们所面对的应用系统可能老是愈来愈复杂,愈来愈庞大,短时间内的复杂可能并没有太大问题,可是随着时间的积累,咱们所面对的系统就会变得极其臃肿。不只维护困难,性能也会愈来愈差。尤为是有些并不合理的功能,在设计之初或者是刚上线的时候因为数据量较小,带来不了多少性能损耗。可随着时间的推移,数据库中的数据量愈来愈大,数据检索愈来愈困难,对真个系统带来的资源消耗也就愈来愈大。
并且,因为系统复杂度的不断增长,给后续其余功能的开发带来实现的复杂度,可能不少原本很简单的功能,由于系统的复杂而不得不增长不少的逻辑判断,形成系统应用程序的计算量不断增长,自己性能就会受到影响。而若是这些逻辑判断还须要与数据库交互经过持久化的数据来完成的话,所带来的性能损失就更大,对整个系统的性能影响也就更大了。
一个WEB应用系统,天然离不开Web应用程序(Web App)和应用程序服务器(App Server)。App Server咱们能控制的内容很少,大多都是使用已经久经考验的成熟产品,你们能作的也就只是经过一些简单的参数设置调整来进行调优,不作细究。而Web App大部分都是各自公司根据业务需求自行开发,可控性要好不少。因此咱们从Web应用程序着手分析一个应用程序架构的不一样设计对整个系统性能的影响将会更合适。
上一节中商业需求告诉了咱们一个系统应该有什么不该该有什么,系统架构则则决定了咱们系统的构建环境。就像修建一栋房子同样,在清楚了这栋房子的用途以后,会先有建筑设计师来画出一章基本的造型图,而后还须要结构设计师为咱们设计出结构图。系统架构设计的过程就和结构工程好似设计结构图同样,须要为整个系统搭建出一个尽量最优的框架,让整个系统可以有一个稳定高效的结构体系让咱们实现各类商业需求。
谈到应用系统架构的设计,可能有人的内心会开始嘀咕,一个DBA有什么资格谈论人家架构师(或者程序员)所设计的架构?其实你们彻底没有必要这样去考虑,咱们谈论架构只是分析各类情形下的性能消耗区别,仅仅是根据本身的专业特长来针对相应架构给出咱们的建议及意见,并非要批判架构总体的好坏,更不是为了推翻某个架构。并且咱们所考虑的架构大多数时候也只是数据层面相关的架构。咱们数据库中存放的数据都是适合在数据库中存放的吗?
对于有些开发人员来讲,数据库就是一个操做最方便的万能存储中心,但愿什么数据都存放在数据库中,不管是须要持久化的数据,仍是临时存放的过程数据,不管是普通的纯文本格式的字符数据,仍是多媒体的二进制数据,都喜欢所有塞如数据库中。由于对于应用服务器来讲,数据库不少时候都是一个集中式的存储环境,不像应用服务器那样可能有不少台;并且数据库有专门的 DBA去帮忙维护,而不像应用服务器不少时候还须要开发人员去作一些维护;还有一点很关键的就是数据库的操做很是简单统一,不像文件操做或者其余类型的存储方式那么复杂。
其实我我的认为,如今的不少数据库为咱们提供了太多的功能,反而让不少并非太了解数据库的人错误的使用了数据库的不少并非太擅长或者对性能影响很大的功能,最后却所有怪罪到数据库身上。
实际上,如下几类数据都是不适合在数据库中存放的:
将二进制多媒体数据存放在数据库中,一个问题是数据库空间资源耗用很是严重,另外一个问题是这些数据的存储很消耗数据库主机的CPU资源。这种数据主要包括图片,音频、视频和其余一些相关的二进制文件。这些数据的处理本不是数据的优点,若是咱们硬要将他们塞入数据库,确定会形成数据库的处理资源消耗严重。
咱们都知道,数据库为了保证事务的安全性(支持事务的存储引擎)以及可恢复性,都是须要记录全部变动的日志信息的。而流水队列数据的用途就决定了存放这种数据的表中的数据会不断的被INSERT,UPDATE和DELETE,而每个操做都会生成与之对应的日志信息。在MySQL中,若是是支持事务的存储引擎,这个日志的产生量更是要翻倍。而若是咱们经过一些成熟的第三方队列软件来实现这个Queue数据的处理功能,性能将会成倍的提高。
对于5.0.3以前的MySQL版本,VARCHAR类型的数据最长只能存放255个字节,若是须要存储更长的文本数据到一个字段,咱们就必须使用TEXT类型(最大可存放64KB)的字段,甚至是更大的
LONGTEXT类型(最大4GB)。而TEXT类型数据的处理性能要远比VARCHAR类型数据的处理性能低下不少。从5.0.3版本开始,VARCHAR类型的最大长度被调整到64KB了,可是当实际数据小于255 Bytes的时候,实际存储空间和实际的数据长度同样,可一旦长度超过255 Bytes以后,所占用的存储空间就是实际数据长度的两倍。
因此,超大文本数据存放在数据库中不只会带来性能低下的问题,还会带来空间占用的浪费问题。
是否合理的利用了应用层Cache机制?
对于Web应用,活跃数据的数据量老是不会特别的大,有些活跃数据更是不多变化。对于这类数据,咱们是否有必要每次须要的时候都到数据库中去查询呢?若是咱们可以将变化相对较少的部分活跃数据经过应用层的Cache机制Cache到内存中,对性能的提高确定是成数量级的,并且因为是活跃数据,对系统总体的性能影响也会很大。
固然,经过Cache机制成功的案例数不胜数,可是失败的案例也一样并很多见。如何合理的经过
Cache技术让系统性能获得较大的提高也不是经过寥寥几笔就能说明的清楚,这里我仅根据以往的经验列举一下什么样的数据适合经过Cache技术来提升系统性能:
因为这些配置信息变更的频率很是低,访问几率又很高,因此很是适合存使用Cache;
咱们的数据层实现都是最精简的吗?
从以往的经验来看,一个合理的数据存取实现和一个拙劣的实现相比,在性能方面的差别常常会超出一个甚至几个数量级。咱们先来分析一个很是简单且常常会遇到相似状况的示例:
在咱们的示例网站系统中,如今要实现每一个用户查看各自相册列表(假设每一个列表显示10张相片)的时候,可以在相片名称后面显示该相片的留言数量。这个需求你们认为应该如何实现呢?我想90%的开发开发工程师会经过以下两步来实现该需求:
WHERE photh_id = ?" 来获得每张相册的回复数量而后再瓶装展示对象。
此外可能还有部分人想到了以下的方案:
咱们来对以上两个方案作一下简单的比较:
BY操做,比第一种解决方案增长了了排序分组操做;
咱们先从以上6点来作一个性能消耗的分析:
综合上面的这6点比较,咱们能够很容易得出结论,从总体资源消耗来看,第二中方案会远远优于第一种解决方案。而在实际开发过程当中,咱们的程序员却不多选用。主要缘由其实有两个,一个是第二种方案在程序代码实现方面可能会比第一种方案略为复杂,尤为是在当前编程环境中面向对象思想的普及,开发工程师可能会更习惯于以对象为中心的思考方式来解决问题。还有一个缘由就是咱们的程序员可能对SQL语句的使用并非特别的熟悉,并不必定可以想到第二条SQL语句所实现的功能。对于第一个缘由,咱们可能只能经过增强开发工程师的性能优化意识来让你们可以自觉纠正,而第二个缘由的解决就正是须要咱们出马的时候了。 SQL语句正是咱们的专长,按期对开发工程师进行一些相应的数据库知识包括SQL语句方面的优化培训,可能会给你们带来意想不到的收获的。这里咱们还仅仅只是经过一个很长见的简单示例来讲明数据层架构实现的区别对总体性能的影响,实际上能够简单的归结为过渡依赖嵌套循环的使用或者说是过渡弱化SQL语句的功能形成性能消耗过多的实例。后面我将进一步分析一下更多的由于架构实现差别所带来的性能消耗差别。
过分依赖数据库SQL语句的功能形成数据库操做效率低下
前面的案例是开发工程师过渡弱化SQL语句的功能形成的资源浪费案例,而这里咱们再来分析一个彻底相反的案例:在群组简介页面须要显示群名称和简介,每一个群成员的nick_name,以及群主的我的签名信息。
需求中所需信息存放在如下四个表中:user,user_profile,groups,user_group 咱们先看看最简单的实现方法,一条SQL语句搞定全部事情:
SELECT name,description,user_type,nick_name,sign
FROM groups,user_group,user ,user_profile
WHERE groups.id = ?
AND groups.id = user_group.group_id
AND user_group.user_id = user.id
AND user_profile.user_id = user.id
固然咱们也能够经过以下稍微复杂一点的方法分两步搞定:
首先取得全部须要展现的group的相关信息和全部群组员的nick_name信息和组员类别:
SELECT name,description,user_type,nick_name
FROM groups,user_group,user
WHERE groups.id = ?
AND groups.id = user_group.group_id
AND user_group.user_id = user.id
而后在程序中经过上面结果集中的user_type找到群主的user_id再到user_profile表中取得群主的签名信息:
SELECT sign FROM user_profile WHERE user_id = ?
你们应该可以看出二者的区别吧,两种解决方案最大的区别在于交互次数和 SQL复杂度。而带来的实际影响是第一种解决方案对user_profile表有没必要要的访问(非群主的profile信息),形成IO访问的直接增长在20%左右。而你们都知道,IO操做在数据库应用系统中是很是昂贵的资源。尤为是当这个功能的PV较大的时候,第一种方案形成的IO损失是至关大的。
重复执行相同的SQL形成资源浪费
这个问题实际上是每一个人都很是清楚也彻底认同的一个问题,可是在应用系统开发过程当中,仍然会常有这样的现象存在。究其缘由,主要仍是开发工程师思惟中面向对象的概念太过深刻,以及为了减小本身代码开发的逻辑和对程序接口过分依赖所形成的。
我曾经在一个性能优化项目中遇到过一个案例,某个功能页面一侧是"分组"列表,是一列"分组"的名字。页面主要内容则是该"分组"的全部"项目"列表。每一个"项目"以名称(或者图标)显示,同时还有一个SEO相关的需求就是每一个"项目"名称的连接地址中是须要有"分组"的名称的。因此在"项目"列表的每一个 "项目"的展现内容中就须要获得该项目所属的组的名称。按照开发工程师开发思路,很是容易产生取得全部"项目"结果集并映射成相应对象以后,再从对象集中获取"项目"所属组的标识字段,而后循环到"分组"表中取得须要的"组名"。而后再将拼装成展现对象。
看到这里,我想你们应该已经知道这里存在的一个最大的问题就是屡次重复执行了彻底相同的 SQL 获得彻底相同的内容。同时还犯了前面第一个案例中所犯的错误。或许你们看到以后会不相信有这样的案例存在,我能够很是确定的告诉你们,事实就是这样。同时也请你们若是有条件的话,好好Review本身所在的系统的代码,很是有可能一样存在上面相似的情形。
还有部分解决方案要远优于上面的作法,那就是不循环去取了,而是经过Join一次完成,也就是解决了第一个案例所描述的性能问题。可是又误入了相似于第二个案例所描述的陷阱中了,由于实际上他只须要一次查询就能够获得全部"项目"所属的"分组"的名称(全部项目都是同一个组的)。
固然,也有部分解决方案也避免了第二个案例的问题,分为两条SQL,两步完成了这个需求。这样在性能上面基本上也将近是数量级的提高了。
可是这就是性能最优的解决方案了么?不是的,咱们甚至能够连一次都不须要访问就得到所须要的
"分组"名称。首先,侧栏中的"分组"列表是须要有名称的,咱们为何不能直接利用到呢?
固然,可能有些系统的架构决定了侧栏和主要内容显示区来源于不一样的模板(或者其余结构),那么咱们也彻底能够经过在进入这个功能页面的连接请求中经过参数传入咱们须要的"分组"名称。这样咱们就能够彻底不须要根据"项目"相关信息去数据库获取所属"分组"的信息,就能够完成相应需求了。固然,是否须要经过请求参数来节省最后的这一次访问,可能会根据这个功能页面的PV来决定,若是访问并非很是频繁,那么这个节省可能并非很明显,而应用系统的复杂度却有所增长,并且程序看上去可能也会不够优雅,可是若是访问很是频繁的场景中,所节省的资源仍是比较可观的。
上面还仅仅只是列举了咱们平时比较常见的一些实现差别对性能所带来的影响,除了这些实现方面所带来的问题以外,应用系统的总体架构实现设计对系统性能的影响可能会更严重。下面大概列举了一些较为常见的架构设计实现不当带来的性能问题和资源浪费状况。
以上仅仅是一些比较常见的症结,在各类不一样的应用环境中确定还会有不少不一样的性能问题,可能须要你们经过仔细的数据分析和对系统的充分了解才能找到,可是一旦找到症结所在,经过相应的优化措施,所带来的收益也是至关可观的。
前面一节咱们介绍了应用系统的实现差别对数据库应用系统总体性能的影响,这一节咱们将分析SQL 语句的差别对系统性能的影响。
我想对于各位读者来讲,确定都清楚SQL语句的优劣是对性能有影响的,可是到底有多大影响可能每一个人都会有不一样的体会,每一个SQL语句在优化以前和优化以后的性能差别也是各不相同,因此对于性能差别到底有多大这个问题咱们咱们这里就不作详细分析了。咱们重点分析实现一样功能的不一样 SQL语句在性能方面会产生较大的差别的根本缘由,并经过一个较为典型的示例来对咱们的分析作出相应的验证。
为何返回彻底相同结果集的不一样SQL语句,在执行性能方面存在差别呢?这里咱们先从SQL语句在数据库中执行并获取所需数据这个过程来作一个大概的分析了。
当MySQL Server的链接线程接收到Client端发送过来的SQL请求以后,会通过一系列的分解
Parse,进行相应的分析。而后,MySQL会经过查询优化器模块(Optimizer)根据该SQL所设涉及到的数据表的相关统计信息进行计算分析,而后再得出一个MySQL认为最合理最优化的数据访问方式,也就是咱们常说的"执行计划",而后再根据所获得的执行计划经过调用存储引擎借口来获取相应数据。而后再将存储引擎返回的数据进行相关处理,并以 Client端所要求的格式做为结果集返回给Client端的应用程序。
注:这里所说的统计数据,是咱们经过ANALYZE TABLE命令通知MySQL对表的相关数据作分析以后所得到到的一些数据统计量。这些统计数据对MySQL优化器而言是很是重要的,优化器所生成的执行计划的好坏,主要就是由这些统计数据所决定的。实际上,在其余一些数据库管理软件中也有相似相应的统计数据。
咱们都知道,在数据库管理软件中,最大的性能瓶颈就是在于磁盘IO,也就是数据的存取操做上面。而对于同一份数据,当咱们以不一样方式去寻找其中的某一点内容的时候,所须要读取的数据量可能会有天壤之别,所消耗的资源也天然是区别甚大。因此,当咱们须要从数据库中查询某个数据的时候,所消耗资源的多少主要就取决于数据库以一个什么样的数据读取方式来完成咱们的查询请求,也就是取决于SQL语句的执行计划。
对于惟一一个SQL语句来讲,通过MySQL Parse以后分解的结构都是固定的,只要统计信息稳定,其执行计划基本上都是比较固定的。而不一样写法的SQL语句,通过MySQL Parse以后分解的结构结构就可能彻底不一样,即便优化器使用彻底同样的统计信息来进行优化,最后所得出的执行计划也可能彻底不同。而执行计划又是决定一个SQL语句最终的资源消耗量的主要因素。因此,实现功能彻底同样的SQL语句,在性能上面可能会有差异巨大的性能消耗。固然,若是功能同样,并且通过 MySQL的优化器优化以后的执行计划也彻底一致的不一样SQL语句在资源消耗方面可能就相差很小了。固然这里所指的消耗主要是IO资源的消耗,并不包括 CPU的消耗。下面咱们将经过一两个具体的示例来分析写法不同而功能彻底相同的两条 SQL的在性能方面的差
异。
示例一
需求:取出某个group(假设id为100)下的用户编号(id),用户昵称(nick_name)、用户性别(sexuality)、用户签名( sign)和用户生日( birthday),并按照加入组的时间
(user_group.gmt_create)来进行倒序排列,取出前20个。
解决方案1、
SELECT id,nick_name
FROM user,user_group
WHERE user_group.group_id = 1 and user_group.user_id = user.id
limit 100,20;
解决方案2、
SELECT user.id,user.nick_name
FROM (
SELECT user_id
FROM user_group
WHERE user_group.group_id = 1 ORDER BY gmt_create desc limit 100,20) t,user WHERE t.user_id = user.id;
咱们先来看看执行计划:
sky@localhost : example 10:32:13> explain
-> SELECT id,nick_name
-> FROM user,user_group
-> WHERE user_group.group_id = 1
-> and user_group.user_id = user.id
-> ORDER BY user_group.gmt_create desc
-> limit 100,20\G
*************************** 1. row *************************** id: 1 select_type: SIMPLE table: user_group
type: ref possible_keys: user_group_uid_gid_ind,user_group_gid_ind key: user_group_gid_ind
key_len: 4 ref: const rows: 31156 Extra: Using where; Using filesort
*************************** 2. row *************************** id: 1
select_type: SIMPLE table: user type: eq_ref
possible_keys: PRIMARY key: PRIMARY
key_len: 4 ref: example.user_group.user_id
rows: 1 Extra:
sky@localhost : example 10:32:20> explain
-> SELECT user.id,user.nick_name
-> FROM (
-> SELECT user_id
-> FROM user_group
-> WHERE user_group.group_id = 1
-> ORDER BY gmt_create desc
-> limit 100,20) t,user
-> WHERE t.user_id = user.id\G
*************************** 1. row *************************** id: 1
select_type: PRIMARY table: <derived2>
type: ALL
possible_keys: NULL key: NULL key_len: NULL ref: NULL
rows: 20 Extra:
*************************** 2. row *************************** id: 1
select_type: PRIMARY table: user type: eq_ref
possible_keys: PRIMARY key: PRIMARY
key_len: 4 ref: t.user_id
rows: 1 Extra: *************************** 3. row *************************** id: 2 select_type: DERIVED table: user_group
type: ref possible_keys: user_group_gid_ind
key: user_group_gid_ind
key_len: 4 ref: const rows: 31156
Extra: Using filesort
执行计划对比分析:解决方案一中的执行计划显示MySQL在对两个参与Join的表都利用到了索引,user_group表利用了
user_group_gid_ind 索引(key: user_group_gid_ind),user 表利用到了主键索引(key: PRIMARY),在参与Join前MySQL经过Where过滤后的结果集与user表进行Join,最后经过排序取出 Join后结果的"limit 100,20"条结果返回。
解决方案二的SQL语句利用到了子查询,因此执行计划会稍微复杂一些,首先能够看到两个表都和解决方案1同样都利用到了索引(所使用的索引也彻底同样),执行计划显示该子查询以user_group为驱动,也就是先经过 user_group进行过滤并立刻进行这一论的结果集排序,也就取得了 SQL中的 "limit 100,20"条结果,而后与user表进行Join,获得相应的数据。这里可能有人会怀疑在自查询中从user_group表所取得与user表参与 Join的记录条数并非20条,而是整个group_id=1的全部结果。那么清你们看看该执行计划中的第一行,该行内容就充分说明了在外层查询中的全部的20条记录所有被返回。
经过比较两个解决方案的执行计划,咱们能够看到第一中解决方案中须要和user表参与Join的记录数MySQL经过统计数据估算出来是31156,也就是经过user_group表返回的全部知足group_id=1的记录数(系统中的实际数据是20000)。而第二种解决方案的执行计划中,user 表参与Join的数据就只有20 条,二者相差很大,经过本节最初的分析,咱们认为第二中解决方案应该明显优于第一种解决方案。
下面咱们经过对比两个解决觉方案的SQL实际执行的profile详细信息,来验证咱们上面的判断。因为SQL语句执行所消耗的最大两部分资源就是IO和CPU,因此这里为了节约篇幅,仅列出BLOCK IO和CPU 两项profile信息(Query Profiler 的详细介绍将在后面章节中独立介绍):先打开profiling功能,而后分别执行两个解决方案的SQL语句:
sky@localhost : example 10:46:43> set profiling = 1; Query OK, 0 rows affected (0.00 sec)
sky@localhost : example 10:46:50> SELECT id,nick_name
-> FROM user,user_group
-> WHERE user_group.group_id = 1
-> and user_group.user_id = user.id
-> ORDER BY user_group.gmt_create desc
-> limit 100,20;
+--------+-----------+
| id | nick_name |
+--------+-----------+
| 990101 | 990101 |
| |
| 990102 | 990102 |
| |
| 990103 | 990103 |
| |
| 990104 | 990104 |
| |
| 990105 | 990105 |
| |
| 990106 | 990106 |
| |
| 990107 | 990107 |
| |
| 990108 | 990108 |
| |
| 990109 | 990109 |
| |
| 990110 | 990110 |
| |
| 990111 | 990111 |
| |
| 990112 | 990112 |
| |
| 990113 | 990113 |
| |
| 990114 | 990114 |
| |
| 990115 | 990115 |
| |
| 990116 | 990116 |
| |
| 990117 | 990117 |
| |
| 990118 | 990118 |
| |
| 990119 | 990119 |
| |
| 990120 | 990120 |
| |
+--------+-----------+ 20 rows in set (1.02 sec)
sky@localhost : example 10:46:58> SELECT user.id,user.nick_name
-> FROM (
-> SELECT user_id
-> FROM user_group
-> WHERE user_group.group_id = 1
-> ORDER BY gmt_create desc
-> limit 100,20) t,user
-> WHERE t.user_id = user.id;
+--------+-----------+
| id | nick_name |
+--------+-----------+
990101 | 990101 |
| |
|
990102 | 990102 |
| |
|
990103 | 990103 |
| |
|
990104 | 990104 |
| |
|
990105 | 990105 |
| |
|
990106 | 990106 |
| |
|
| 990107 | 990107 |
| |
|
| 990108 | 990108 |
| |
|
| 990109 | 990109 |
| |
|
| 990110 | 990110 |
| |
|
| 990111 | 990111 |
| |
|
| 990112 | 990112 |
| |
|
| 990113 | 990113 |
| |
|
| 990114 | 990114 |
| |
|
| 990115 | 990115 |
| |
|
| 990116 | 990116 |
| |
|
| 990117 | 990117 |
| |
|
| 990118 | 990118 |
| |
|
| 990119 | 990119 |
| |
|
| 990120 | 990120 |
| |
+--------+-----------+ 20 rows in set (0.96 sec)
查看系统中的profile信息,刚刚执行的两个SQL语句的执行profile信息已经记录下来了:
sky@localhost : example 10:47:07> show profiles\G
*************************** 1. row ***************************
Query_ID: 1
Duration: 1.02367600
Query: SELECT id,nick_name
FROM user,user_group
WHERE user_group.group_id = 1 and user_group.user_id = user.id ORDER BY user_group.gmt_create desc limit 100,20
*************************** 2. row ***************************
Query_ID: 2
Duration: 0.96327800
Query: SELECT user.id,user.nick_name
FROM (
SELECT user_id
FROM user_group
WHERE user_group.group_id = 1 ORDER BY gmt_create desc limit 100,20) t,user
WHERE t.user_id = user.id 2 rows in set (0.00 sec) sky@localhost : example 10:47:34> SHOW profile CPU,BLOCK IO io FOR query 1; +--------------------+----------+-----------+------------+--------------+---------------+
| Status | Duration | CPU_user | CPU_system | Block_ops_in | Block_ops_out |
+--------------------+----------+-----------+------------+--------------+---------------+
| (initialization) |
| 0.000068 | 0 | 0 |
| |
0 | |
0 | |
| Opening tables |
| 0.000015 | 0 | 0 |
| |
0 | |
0 | |
| System lock |
| 0.000006 | 0 | 0 |
| |
0 | |
0 | |
| Table lock |
| 0.000009 | 0 | 0 |
| |
0 | |
0 | |
| init |
| 0.000026 | 0 | 0 |
| |
0 | |
0 | |
| optimizing |
| 0.000014 | 0 | 0 |
| |
0 | |
0 | |
| statistics |
| 0.000068 | 0 | 0 |
| |
0 | |
0 | |
| preparing |
| 0.000019 | 0 | 0 |
| |
0 | |
0 | |
| executing |
| 0.000004 | 0 | 0 |
| |
0 | |
0 | |
| Sorting result |
| 1.03614 | 0.5600349 | 0.428027 |
| |
0 | |
15632 | |
| Sending data |
| 0.071047 | 0 | 0.004 |
| |
88 | |
0 | |
| end |
| 0.000012 | 0 | 0 |
| |
0 | |
0 | |
| query end |
| 0.000006 | 0 | 0 |
| |
0 | |
0 | |
| freeing items |
| 0.000012 | 0 | 0 |
| |
0 | |
0 | |
| closing tables |
| 0.000007 | 0 | 0 |
| |
0 | |
0 | |
| logging slow query | 0.000003 | 0 | 0 |
| |
0 | |
0 | |
+--------------------+----------+-----------+------------+--------------+---------------+
16 rows in set (0.00 sec)
sky@localhost : example 10:47:40> SHOW profile CPU,BLOCK IO io FOR query 2;
+--------------------+----------+----------+------------+--------------+---------------+
| Status | Duration | CPU_user | CPU_system | Block_ops_in | Block_ops_out |
+--------------------+----------+----------+------------+--------------+---------------+
| (initialization) |
| 0.000087 | 0 | 0 |
| |
0 | |
0 | |
||
| Opening tables |
| 0.000018 | 0 | 0 |
| |
0 | |
0 | |
||
| System lock |
| 0.000007 | 0 | 0 |
| |
0 | |
0 | |
||
| Table lock |
| 0.000059 | 0 | 0 |
| |
0 | |
0 | |
||
| optimizing |
| 0.00001 | 0 | 0 |
| |
0 | |
0 | |
||
| statistics |
| 0.000068 | 0 | 0 |
| |
0 | |
0 | |
||
| preparing |
| 0.000017 | 0 | 0 |
| |
0 | |
0 | |
||
| executing |
| 0.000004 | 0 | 0 |
| |
0 | |
0 | |
||
| Sorting result |
| 0.928184 | 0.572035 | 0.352022 |
| |
0 | |
32 | |
||
| Sending data |
| 0.000112 | 0 | 0 |
| |
0 | |
0 | |
||
| init |
| 0.000025 | 0 | 0 |
| |
0 | |
0 | |
||
| optimizing |
| 0.000012 | 0 | 0 |
| |
0 | |
0 | |
||
statistics |
| 0.000025 | 0 | 0 |
| |
0 | |
0 | |
||
preparing |
| 0.000013 | 0 | 0 |
| |
0 | |
0 | |
||
executing |
| 0.000004 | 0 | 0 |
| |
0 | |
0 | |
||
Sending data |
| 0.000241 | 0 | 0 |
| |
0 | |
0 | |
||
end |
| 0.000005 | 0 | 0 |
| |
0 | |
0 | |
||
query end |
| 0.000006 | 0 | 0 |
| |
0 | |
0 | |
||
| freeing items | 0.000015 | 0 |
| 0 |
| |
0 | |
0 | |
||
| closing tables | 0.000004 | 0 |
| 0 |
| |
0 | |
0 | |
||
| removing tmp table | 0.000019 | 0 |
| 0 |
| |
0 | |
0 | |
||
| closing tables | 0.000005 | 0 |
| 0 |
| |
0 | |
0 | |
||
| logging slow query | 0.000004 | 0 |
| 0 |
| |
0 | |
0 | |
+--------------------+----------+----------+------------+--------------+---------------+
咱们先看看两条SQL执行中的IO消耗,二者区别就在于"Sorting result",咱们回顾一下前面执行计划的对比,两个解决方案的排序过滤数据的时机不同,排序后须要取得的数据量一个是20000,一个是20,正好和这里的profile信息吻合,第一种解决方案的
"Sorting result"的IO值是第二种解决方案的将近500倍。
而后再来看看CPU消耗,全部消耗中,消耗最大的也是"Sorting result"这一项,第一个消耗多出的原因和上面IO消耗差别是同样的。
结论:
经过上面两条功能彻底相同的SQL语句的执行计划分析,以及经过实际执行后的 profile数据的验证,都证实了第二种解决方案优于第一种解决方案。同时经过后者的实际验证,也再次证实了咱们前面所作的执行计划基本决定了SQL语句性能。
前面两节中,咱们已经分析了在一个数据库应用系统的软环境中应用系统的架构实现和系统中与数据库交互的SQL语句对系统性能的影响。在这一节咱们再分析一下系统的数据模型设计实现对系统的性能影响,更通俗一点就是数据库的Schema设计对系统性能的影响。
在不少人看来,数据库Schema设计是一件很是简单的事情,就大致按照系统设计时候的相关实体对象对应成一个一个的表格基本上就能够了。而后为了在功能上作到尽量容易扩展,再根据数据库范式规则进行调整,作到第三范式或者第四范式,基本就算完事了。
数据库Schema设计真的有如上面所说的这么简单么?能够很是确定的告诉你们,数据库Schema设计所须要作的事情远远不止如此。若是您以前的数据库Schema设计一直都是这么作的,那么在该设计应用于正式环境以后,极可能带来很是大的性能代价。
因为在后面的"MySQL 数据库应用系统设计"中的"系统架构最优化"这一节中会介较为详细的从性能优化的角度来分析如何如何设计数据库Schema,因此这里暂时先不介绍如何来设计性能优异的数据库Schema结构,仅仅经过一个实际的示例来展现Schema结构的不同在性能方面所带来的差别。
需求概述:一个简单的讨论区系统,须要有用户,用户组,组讨论区这三部分基本功能简要分析:一、须要存放用户数据的表;
原始方案一:分别用用四个表来存放用户,分组,用户与组关系以及各组的讨论帖子的信息如
下:
user 用户表:
+-------------+---------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------------+---------------+------+-----+---------+-------+
| id |
| int(11) |
| NO |
| |
| 0 |
| |
| |
| nick_name |
| varchar(32) |
| NO |
| |
| NULL |
| |
| |
| password |
| char(64) |
| YES |
| |
| NULL |
| |
| |
| varchar(32) |
| NO |
| |
| NULL |
| |
| |
|
| status |
| varchar(16) |
| NO |
| |
| NULL |
| |
| |
| sexuality |
| char(1) |
| NO |
| |
| NULL |
| |
| |
| msn |
| varchar(32) |
| YES |
| |
| NULL |
| |
| |
| sign |
| varchar(64) |
| YES |
| |
| NULL |
| |
| |
| birthday |
| date |
| YES |
| |
| NULL |
| |
| |
| hobby |
| varchar(64) |
| YES |
| |
| NULL |
| |
| |
| location |
| varchar(64) |
| YES |
| |
| NULL |
| |
| |
| description | varchar(1024) | YES |
| |
| NULL |
| |
| |
+-------------+---------------+------+-----+---------+-------+
groups 分组表:
+--------------+---------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+--------------+---------------+------+-----+---------+-------+
| id | int(11) | NO |
| |
| NULL |
| |
| |
| gmt_create | datetime | NO |
| |
| NULL |
| |
| |
| gmt_modified | datetime | NO |
| |
| NULL |
| |
| |
| name | varchar(32) | NO |
| |
| NULL |
| |
| |
| status | varchar(16) | NO |
| |
| NULL |
| |
| |
| description | varchar(1024) | YES |
| |
| NULL |
| |
| |
+--------------+---------------+------+-----+---------+-------+
user_group 关系表:
+--------------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+--------------+-------------+------+-----+---------+-------+
user_id | int(11) | NO |
| MUL | NULL |
| |
| |
group_id | int(11) | NO |
| MUL | NULL |
| |
| |
user_type | int(11) | NO |
| | NULL |
| |
| |
gmt_create | datetime | NO |
| | NULL |
| |
| |
gmt_modified | datetime | NO |
| | NULL |
| |
| |
status | varchar(16) | NO |
| | NULL |
| |
| |
+--------------+-------------+------+-----+---------+-------+
group_message 讨论组帖子表:
+--------------+--------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+--------------+--------------+------+-----+---------+-------+
| id | int(11) | NO |
| |
| NULL |
| |
| |
| gmt_create | datetime | NO |
| |
| NULL |
| |
| |
| gmt_modified | datetime | NO |
| |
| NULL |
| |
| |
| group_id | int(11) | NO |
| |
| NULL |
| |
| |
| user_id | int(11) | NO |
| |
| NULL |
| |
| |
| subject | varchar(128) | NO |
| |
| NULL |
| |
| |
| content | text | YES |
| |
| NULL |
| |
| |
+--------------+--------------+------+-----+---------+-------+
优化后方案二: user 用户表:
+-------------+---------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------------+---------------+------+-----+---------+-------+
| id |
| int(11) |
| NO |
| |
| 0 |
| |
| |
| nick_name |
| varchar(32) |
| NO |
| |
| NULL |
| |
| |
| password |
| char(64) |
| YES |
| |
| NULL |
| |
| |
| varchar(32) |
| NO |
| |
| NULL |
| |
| |
|
| status |
| varchar(16) |
| NO |
| |
| NULL |
| |
| |
+-------------+---------------+------+-----+---------+-------+
user_profile 用户属性表(记录与user一一对应):
+-------------+---------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------------+---------------+------+-----+---------+-------+
| sexuality |
| char(1) |
| NO |
| |
| NULL |
| |
| |
| msn |
| varchar(32) |
| YES |
| |
| NULL |
| |
| |
| sign |
| varchar(64) |
| YES |
| |
| NULL |
| |
| |
| birthday |
| date |
| YES |
| |
| NULL |
| |
| |
| hobby |
| varchar(64) |
| YES |
| |
| NULL |
| |
| |
| location |
| varchar(64) |
| YES |
| |
| NULL |
| |
| |
| description | varchar(1024) | YES |
| |
| NULL |
| |
| |
+-------------+---------------+------+-----+---------+-------+
groups 和 user_group这两个表和方案一彻底同样
group_message 讨论组帖子表:
+--------------+--------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+--------------+--------------+------+-----+---------+-------+
| id | int(11) |
| NO |
| |
| NULL |
| |
| |
| gmt_create | datetime |
| NO |
| |
| NULL |
| |
| |
| gmt_modified | datetime |
| NO |
| |
| NULL |
| |
| |
| group_id | int(11) |
| NO |
| |
| NULL |
| |
| |
| user_id | int(11) |
| NO |
| |
| NULL |
| |
| |
| author | varchar(32) |
| NO |
| |
| NULL |
| |
| |
| subject | varchar(128) | NO |
| |
| NULL |
| |
| |
+--------------+--------------+------+-----+---------+-------+
group_message_content 帖子内容表(记录与group_message一一对应):
+--------------+---------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+--------------+---------+------+-----+---------+-------+
| group_msg_id | int(11) | NO | | NULL | |
| content | text | NO | | NULL | |
+--------------+---------+------+-----+---------+-------+
咱们先来比较一下两个解决方案所设计的Schema的区别。区别主要体如今两点,一个区别是在
group_message表中增长了author字段来存放发帖做者的昵称,与user表的nick_name相对应,另一个就是第二个解决方案将user表和group_message表都分拆成了两个表,关系分别都是一一对应。
方案二看上去比方案一要更复杂一些,首先是表的数量多了2个,而后是在group_message中冗余存
放了做者昵称。咱们试想一下,一个讨论区系统,访问最多的页面会是什么?我想你们都会很清楚是帖子标题列表页面。而帖子标题列表页面最主要的信息就是都是来自 group_message表中,同时帖子标题后面的做者通常都是经过用户名成(昵称)来展现。按照第一种解决方案来设计的 Schema,咱们就须要执行相似以下这样的SQL语句来获得数据:
SELECT t.id, t.subject,user.id, u.nick_name
FROM (
SELECT id, user_id, subject FROM group_message
WHERE group_id = ?
ORDER BY gmt_modified DESC LIMIT 20
) t, user u
WHERE t.user_id = u.id
可是第二中解决方案所须要执行的SQL就会简单不少,以下:
SELECT t.id, t.subject, t.user_id, t.author FROM group_message
WHERE group_id = ?
ORDER BY gmt_modified DESC LIMIT 20
两个SQL相比较,你们都能很明显的看出谁优谁劣了,第一个是须要读取两个表的数据进行 Join,与第二个SQL相比性能差距很大,尤为是若是第一个再写的差一点,性能更是很是糟糕,二者所带来的资源消耗就更相差玄虚了。
不只仅如此,因为第一个方案中的group_message表中还包含一个大字段"content",该字段所存放的信息要占整个表的绝大部分存储空间,但在这条系统中执行最频繁的 SQL之一中是彻底不须要该字段所存放信息的,可是因为这个SQL又没办法作到不访问group_message表的数据,因此第一条SQL在数据读取过程当中会须要读取大量没有任何意义的数据。
在系统中用户数据的读取也是比较频繁的,可是大多数地方所须要的用户数据都只是用户的几个基本属性,如用户的id,昵称,密码,状态,邮箱等,因此将用户表的这几个属性单独分离出来后,也会让大量的SQL语句在运行的时候减小数据的检索量,从而提升性能。
可能有人会以为,在咱们将一个表分红两个表的时候,咱们若是要访问被分拆出去的信息的时候,性能不是就会变差了吗?是的,对于那些须要访问如user的sign,msn等原来只须要一个表就能够完成的SQL来讲,如今都须要两条SQL来完成,性能确实会 有所下降,可是因为两个表都是一对一的关联关系,关联字段的过滤性也很是高,并且这样的查询需求在整个系统中所占有的比例也并不高,因此这里所带来的性能损失实际上要远远小于在其余SQL上所节省出来的资源,因此彻底没必要为此担忧
在本章以前的全部部分都是介绍的整个系统中的软件环境对系统性能的影响,这一节咱们将从系统硬件环境来分析对数据库系统的影响,并从数据库服务器主机的角度来作一些针对性的优化建议。
任何一个系统的硬件环境都会对起性能起到很是关键的做用,这一点我想每一位读者朋友都是很是清楚的。而数据库应用系统环境中,因为数据库自身的特色和在系统中的角色决定了他在整个系统中是最难以扩展的部分。因此在大多数环境下,数据库服务器主机(或者主机集群)的性能在很大程度上决定了整个应用系统的性能。
既然咱们的数据库主机资源如此重要,确定不少读者朋友会但愿知道,数据库服务器主机的各部分硬件到底谁最重要,各部分对总体性能的影响各自所占的比例是多少,以便可以根据这些比例选取合适的主机机型做为数据库主机。可是我只能很遗憾的告诉你们,没有任何一个定律或者法则能够很准确的给出这个答案。
固然,你们也没必要太沮丧。虽然没有哪一个法则能够准确的知道咱们到底该如何选配一个主机的各部分硬件,可是根据应用类型的不一样,整体上仍是有一个能够大体遵循的原则能够参考的。
首先,数据库主机是存取数据的地方,那么其IO操做天然不会少,因此数据库主机的IO性能确定是须要最优先考虑的一个因素,这一点无论是什么类型的数据库应用都是适用的。不过,这里的IO性能并不只仅只是指物理的磁盘IO,而是主机的总体IO性能,是主机整个IO系统的整体IO性能。而IO性能自己又能够分为两类,一类是每秒可提供的IO访问次数,也就是咱们常说的IOPS数量,还有一种就是每秒的IO总流量,也就是咱们常说的IO吞吐量。在主机中决定 IO性能部件主要由磁盘和内存所决定,固然也包括各类与IO相关的板卡。其次,因为数据库主机和普通的应用程序服务器相比,资源要相对集中不少,单台主机上所须要进行的计算量天然也就比较多,因此数据库主机的CPU处理能力也不能忽视。
最后,因为数据库负责数据的存储,与各应用程序的交互中传递的数据量比其余各种服务器都要多,因此数据库主机的网络设备的性能也可能会成为系统的瓶颈。
因为上面这三类部件是影响数据库主机性能的最主要因素,其余部件成为性能瓶颈的概率要小不少,因此后面咱们经过对各类类型的应用作一个简单的分析,再针对性的给出这三类部件的基本选型建议。
一、典型OLTP应用系统
对于各类数据库系统环境中你们最多见的OLTP系统,其特色是并发量大,总体数据量比较多,但每次访问的数据比较少,且访问的数据比较离散,活跃数据占整体数据的比例不是太大。对于这类系统的数据库其实是最难维护,最难以优化的,对主机总体性能要求也是最高的。由于他不只访问量很高,数据量也不小。
针对上面的这些特色和分析,咱们能够对OLTP的得出一个大体的方向。
虽然系统整体数据量较大,可是系统活跃数据在数据总量中所占的比例不大,那么咱们能够经过扩大内存容量来尽量多的将活跃数据cache到内存中;
虽然IO访问很是频繁,可是每次访问的数据量较少且很离散,那么咱们对磁盘存储的要求是IOPS表现要很好,吞吐量是次要因素;并发量很高,CPU每秒所要处理的请求天然也就不少,因此CPU处理能力须要比较强劲;
虽然与客户端的每次交互的数据量并非特别大,可是网络交互很是频繁,因此主机与客户端交互的网络设备对流量能力也要求不能太弱。
二、典型OLAP应用系统
用于数据分析的OLAP系统的主要特色就是数据量很是大,并发访问很少,但每次访问所须要检索的数据量都比较多,并且数据访问相对较为集中,没有太明显的活跃数据概念。
基于OLAP系统的各类特色和相应的分析,针对OLAP系统硬件优化的大体策略以下:数据量很是大,因此磁盘存储系统的单位容量须要尽可能大一些;
单次访问数据量较大,并且访问数据比较集中,那么对IO系统的性能要求是须要有尽量大的每秒
IO吞吐量,因此应该选用每秒吞吐量尽量大的磁盘;
虽然IO性能要求也比较高,可是并发请求较少,因此CPU处理能力较难成为性能瓶颈,因此CPU处理能力没有太苛刻的要求;
虽然每次请求的访问量很大,可是执行过程当中的数据大都不会返回给客户端,最终返回给客户端的数据量都较小,因此和客户端交互的网络设备要求并非过高;
此外,因为OLAP系统因为其每次运算过程较长,能够很好的并行化,因此通常的OLAP系统都是由多台主机构成的一个集群,而集群中主机与主机之间的数据交互量通常来讲都是很是大的,因此在集群中主机之间的网络设备要求很高。
三、除了以上两个典型应用以外,还有一类比较特殊的应用系统,他们的数据量不是特别大,可是访问请求及其频繁,并且大部分是读请求。可能每秒须要提供上万甚至几万次请求,每次请求都很是简单,可能大部分都只有一条或者几条比较小的记录返回,就好比基于数据库的 DNS服务就是这样类型的服务。
虽然数据量小,可是访问极其频繁,因此能够经过较大的内存来cache住大部分的数据,这可以保证很是高的命中率,磁盘IO量比较小,因此磁盘也不须要特别高性能的;并发请求很是频繁,比须要较强的CPU处理能力才能处理;
虽然应用与数据库交互量很是大,可是每次交互数据较少,整体流量虽然也会较大,可是通常来讲普通的千兆网卡已经足够了。
在不少人看来,性能的根本决定因素是硬件性能的好坏。但实际上,硬件性能只能在某些阶段对系统性能产生根本性影响。当咱们的CPU处理能力足够的多,IO系统的处理能力足够强的时候,若是咱们的应用架构和业务实现不够优化,一个原本很简单的实现非得绕不少个弯子来回交互屡次,那再强的硬件也没有用,由于来回的交互老是须要消耗时间。尤为是有些业务逻辑设计不是特别合理的应用,数据库Schema设计的不够合理,一个任务在系统中又被分拆成不少个步骤,每一个步骤都使用了很是复杂的
Query语句。笔者曾经就遇到过这样一个系统,该系统是购买的某知名厂商的一个项目管理软件。该系统最初运行在一台Dell2950的PC Server上面,使用者一直抱怨系统响应很慢,但我从服务器上面的状态来看系统并繁忙(系统并发不是太大)。后来使用者强烈要求经过更换硬件设施来提高系统性能,虽然我一直反对,但最后在管理层的要求下,更换成了一台Sun的S880小型机,主机CPU的处理能力至少是原来机器的3倍以上,存储系统也从原来使用本地磁盘换成使用EMC的中断存储CX300。可在试用阶段,发现系统总体性能没有任何的提高,最终仍是取消了更换硬件的计划。
因此,在应用系统的硬件配置方面,咱们应该要以一个理性的眼光来看待,只有合适的才是最好的。并非说硬件资源越好,系统性能就必定会越好。并且,硬件系统自己老是有一个扩展极限的,若是咱们一味的但愿经过升级硬件性能来解决系统的性能问题,那么总有一天将会遇到没法逾越的瓶颈。
到那时候,就算有再多的钱去砸也无济于事了。
虽然本章是以影响MySQL Server性能的相关因素来展开分析,但实际上不少内容都对于大多数数据库应用系统适用。数据库管理软件仅仅是实现了数据库应用系统中的数据存取操做,和数据的持久化。数据库应用系统的优化真正能带来最大收益的就是商业需求和系统架构及业务实现的优化,而后是数据库Schema设计的优化,而后才是Query语句的优化,最后才是数据库管理软件自身的一些优化。经过笔者的经验,在整个系统的性能优化中,若是按照百分比来划分上面几个层面的优化带来的性能收益,能够得出大概以下的数据:需求和架构及业务实现优化:55%
Query语句的优化:30% 数据库自身的优化:15%
不少时候,你们看到数据库应用系统中性能瓶颈出如今数据库方面,就但愿经过数据库的优化来解决问题,但无论DBA对数据库多们了解,对Query语句的优化多么精通,最终仍是很难解决整个系统的性能问题。缘由就在于并无真正找到根本的症结所在。
因此,数据库应用系统的优化,其实是一个须要多方面配合,多方面优化的才能产生根本性改善的事情。简单来讲,能够经过下面三句话来简单的归纳数据库应用系统的性能优化:商业需求合理化,系统架构最优化,逻辑实现精简化,硬件设施理性化。