数据库做为当今系统的数据中心,不仅给程序提供了方便,也给开发提供了统一的选择。数据库是一个独立的数据系统,主要是读写数据并保存到指定的文件中,对外提供了一个很灵活的命令操作端囗。而如今的数据访问量不但的增加(多的每天过亿独立访问者),数据记录量也在增加(多的过亿条),而我们的硬件数据传输在一定的程度上没有跟上,导致数据在读取时存在着延时。数据库中的数据是一条一条的保存,而读取时一般是一条一条的判断达到筛选的条件,就好比如说是程序中的while一样,如果我们在程序种用while循环个一万次就可以看到延时(当然数据库内肯定使用特殊的方式循环记录,但还是跑不了循环判断的过程,只是循环的方式要快)。如今MYSQL数据库一般在高档服务器中十万条内记录可不特意建立索引来优化。如果数据过百万时我们还不建立索引,那么服务器读取速度会大大降低,不断会增加客户浏览页面等待时间,访问量大时还会使服务器崩溃。网上也有很多的统计数据说明如果一个页面的等待时间超12秒99%的浏览者会关闭这个页面,从而流失客户。
当然如果你够有钱可以购买高级的服务器来支持你的数据库操作能力,当然即使再好的硬件如果在软件上没在做好优化,它的性能也不会好到哪里去,相反再好的软件优化如果没有硬件的支持,也等于白搭。
对于数据库来说软件优化主要是减少内部不必要的循环判断来筛选,硬件的优化主要是提高整机的硬件访问执行速度减少循环判断时间。当然很大的程度上讲只能在软件上进行优化,理想化中希望整个系统达到最佳执行状态才是一个合格的系统,不过这也只是理想化的,实际中没有绝对的最佳状态,系统在运行中会有很多的因素影响到它的执行速度,我们也可以通过获取服务器端开始执行程序到返回结果的执行时间,可以看到每次的时间基本不一样,所以在软件上的优化只是在最大程序上减少执行时间,只有做这样才能让硬件可以执行更多的请求。
这里只说下软件方面的优化,当然不能做到所有的优化方式,后期也会在工作中添加,如果你看完后有更好的想法可以给我留言,我会尽快修正。
软件优化一般有两类:一类是只修改程序的运行方式减少不必要的重复操作,一类是以牺牲硬件资源来减少程序运行时间。当然在数据库中这两类都可以使用到。比如建立索引就是牺牲硬件资源来换取程序的执行时间,查询取一条记录时加一个limit 1就是只修改程序的运行方式,等。
下面是我收集的一些优化方法。如果有不当之外还请不吝指正。
一,建表时要根据表的用途选择好存储引擎,字符集,字段数据类型。
一般常用的存储引擎有:
InnoBD 这类引擎支持事务,行级锁机制,外键约束特殊功能,但查询速度比MyISAM稍慢,尤其在使用count(*)时,在实际应用中不在这类引擎中使用全表count(*)尤其记录上十万条时,其会进行全表查询,很慢。这类表主要是针对使用它的特殊功能时才会用到,比如要使用到事务。
MyISAM 这类引擎不支持事务,等级锁机制,外键约束功能。查询速度比InnoDB稍快,在count(*)更是快到InnoDB没有办法比。这类引擎又分三种类型:静态,动态,压缩。当建立的表中没有变长字段(VARCHAR,xxxTEXT,xxxBLOB等)时系统自动选择静态类型,这种类型字段长度不变所有的记录保存空间一样大,所以查询速度高,数据受损时,恢复工作相对好做。当建立的表中包含了变长字段(VARCHAR,xxxTEXT,xxxBLOB等)时系统自动选择为动态类型,这种类型字段长度不一所有的记录保存空间大小也不一,而且多次修改后会产生碎片,相对于静态类型来说,这种类型的查询速度会低,而且长时间修改记录会添加很多的碎片,浪费空间和降低查询速度,必需定时使用(optimize table 表名)或优化工具进行碎片整理来优化表。压缩类型是要借助myisamchk工具把前两种类型进行压缩,压缩后不能修改数据,只是减少了数据占用空间,每次读取时还得进行解压,所以查询速度相对要低,只是针对一些旧数据保存用。
MyISAM Merge 这类引擎是MyISAM的变种,可以合并几个相同MyISAM的表为一个虚表。常用于日志和数据仓库。
MEMORY 这种引擎数据表只存在于内存中,它使用散列索引,所以数据的读取速度非常的快,因为数据保存在内存中,所以一般用于临时表(如保存session数据)
archive 这种表只支持select,insert语句,而且不支持索引,常应用于日志记录和聚合分析方面。
在创建一张表时,我们就得开始考虑优化,根据不同的要求来创建表选择不同的引擎(当然引擎有很多种,实际中根据不同的需求选择合适的存储引擎),引擎会影响到后期的查询,修改,删除,插入,和恢复。字段数据类型选用是体现你对这个表是否负责。要做到,能使用定长数据类型不用变长,能用小的数据类型不用大的数据类型,能使用数字类型不使用字符串型,有三个到五个状态的使用枚举类型。当然在选用这些类型完全看你对这些数据的敏感度,往往选择不当会造成数据被遗漏或转换,比如说你选择了char(10)的长度,但后期插入的记录超了10个长度,那么10个后面的字符就会被遗漏。如果你使用了tinyint的短整型,插入了一个400会导致数据过大而转换为最大值255 等,还有最好给字段添加一个默认值和not null因为字段为null时影响到索引。字符集一般选用UTF8或gbk,当然如果没有中文内容可以选用其它字符集。
二,建立索引,改善查询速度。
索引只是为了提高数据库查询速度,如果索引建立不当不仅会影响插入修改甚至影响删除的速度(因为索引在建立后会产生索引数据并写文件,当对数据增删改操作时也会去修改对应索引数据)。一般在建表时就会建立部分索引,索引主要是针对SQL查询中的筛选条件字段,索引可以说是给指定的字段列内容进行一个分类,然后类分的越小所筛选时查找的时间越短,当然这些分类记录会保存到硬盘中,从而增加了硬盘占用空间,这种做法主要是针对大数据量才会起到明显的作用。
程序不像人可以自主的分类查找,程序所有的执行过程都是人为设置好的,也只有这样才能够了解程序的执行过程,从而分析优化。在MYSQL中索引使用了二叉树的结构,如上图,左边的是没有使用索引,那么在取记录时,程序只能一个一个的去找,100000多的记录找到位置在55000的记录,那最少程序要循环判断55000次,明显的程序执行效率不高;而右边是建立了索引的表,如果查找位置在55000的记录时程序就可以先判断55000在1~50000或50000~100000哪个范围内,很明显是在50000~100000内,那么程序又可以再判断到55000在50000~75000之间,程序就可以在50000到75000之间进行循环判断,只要5000次就可以找到,加上前面的几次也就5002次,执行效率可想而知(当然这只是简单模拟,实际数据库的索引不只是这样简单的分类)。
索引在大数据量的数据库中是必须了解的优化知识。
MYSQL的索引分为:主键索引,唯一索引,普通索引,全文索引;前面的三个索引是针对定长空间,全文索引是针对大文本变长空间。在MYSQL中并不是看到筛选条件就建立索引就可以进行优化,内部查询机制只采用满足一定条件的索引,所以索引建立不当,直接影响到增删改查的速度。
由于要实际应用中SQL查询大部分不是简单的查询,大部分用到了连接查询,子查询,联合查询等,所以在建立索引时要知道哪些筛选条件字段建立索引后不会被采用。下面是收集部分索引建立要素:
索引列尽可能不包含NULL值。只要列中包含NULL值是不会在索引数据中;复合索引中某一列包含NULL的值,那该列在复合索引中是无效的。所以在建立索引的字段中必须要添加not null来明确说明这个列里的值不能为NULL,当然这个值如果可以不填时可以设置默认defaule 默认值。
字符串类列尽可能的使用短索引。如果要建立索引的列是字符串类(不论定长还是变长的),当前面的几个字符在整列中将近唯一时,就可以使用短索引,这种索引可以减少索引数据量,同时可以提高索引速度。这类索引只要在指定的字段后面加上一个()中间写上指定的前面字符长度就可以。
排序索引尽可能不要与WHERE索引同时分开建立。如果SQL中有WHERE并且建立了索引,那么就不给order建立索引,因为MYSQL一般只使用一个索引进行查询,当然如果不用order可以完成查询要求的最好不要排序;尽量不要使用多个列排序,如果需要把这些多个排序字段建立一个复合索引。
like语句索引。like是一种模糊查询,很多的地方会使用到,如果采用"%string%" 的形式是不会使用到索引,只有在"string%"时才会使用到索引,所以查询中尽可能使用后面的查询方式,否则这个字段没有必要建立索引(如果有其它查询筛选另论)。
尽可能缩小筛选后的范围。MYSQL数据库往往有时在筛选后范围过大就放弃采用索引,所以在筛选前尽量缩小筛选范围。所以在筛选是尽量不要使用not in,<>,!=等条件,除非这类的范围很小。
虽然通过explain分析在changes大于27时没有使用索引,而在大于30时使用了range索引样式,说明大于30时筛选范围要小于大于27,但到底多少范围才能采用索引,个人评估最好在1/3以内(需要的记录或不需要的记录匀算)。但后面的两个图片中说明了两种count()获取总数,一个时间差很大,一个相差却相反。当然使用count(*)时使用的时间比count(id)快很多,这也是使用count()的一些技巧,当有where时count(*)是最快的,而如果没有where那count(*)与count(id)时间是相等的,这个问题在使用中一定要注意。最好全部使用count(*)(当然这个不要在大数据量的InnoDB引擎类的表中使用)。
过多筛选条件建立复合索引。MYSQL索引机制在每个表中只会使用一个索引,所以当条件字段加排序字段或多个筛选字段时,如果分别建立索引,最后只会采用最后建立索引的字段。所以当没有特殊要求下可以给这些条件字段加排序字段建立一个复合索引,不仅提高速度还能减少索引数量。但在复合索引中的条件字段尽量不要出现> ,>=,<,<=类的筛选,否则复合索引可能不被采用,主要是因为这些判断会扩大筛选范围,当有的字段出现了类似筛选条件可以不把该字段添加到复合索引中。复合索引建立的字段顺序一定要同SQL中的条件字段加排序字段相同。
常用EXPLAIN来分析建立的索引。索引建立后不等于万事大吉,往往在后期的运营中会随着数据的增加,系统更新等变动后,使原来建立的索引没有用,有的甚至影响到系统一正常运行。所以在后期的维护中,定期的了解你的索引状态是必要的,一般在的这类变动较大时或系统变慢时就得找到对应的页面取出里面使用的SQL进行explain分析,修正不合理的索引。如果可以在程序操作每个查询SQL时用PHP程序或其它操作MYSQL的程序中添加一个explain分析语句并且判断结果,把不合理的SQL记录下来,这样一来后期的维护会更加快捷,不过这个分析周期一定要注意,如今就有部分PHP的框架使用了这一思想(如:TYPO3内容管理系统CMS)。当然可以开启数据库的慢查询日志,这个相对方便,只要定时去查看下日志内容即可。
三,SQL语句的优化。
很多时候数据库的优化不能光靠索引,有好多的时候我们可以使用SQL语句本身来优化。
如果取的记录条数有限制可以添加一个limit,这样可以大大的减少筛选范围。
group by分组时默认MYSQL会自动进行排序,当不需要排序时可以使用order by null,禁止排序影响查询速度,当然如果分组项数过少是也没有什么影响。
简化where条件。有的时候where条件复杂,但最好去简化它。
尽量不要在SQL中使用一些动态函数,如时间函数now(),sysdate(),curdate(),curtime(),current_time(),current_date(),随机函数rand()等;这类函数会影响到数据库使用查询缓存。