MySQL性能优化
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了MySQL性能优化相关的知识,希望对你有一定的参考价值。
1 存储引擎的选择
InnoDB和MyISAM是许多人在使用mysql时最常用的两个表类型,这两个表类型各有优劣,视具体应用而定。基本的差别为:MyISAM类型不支持事务处理等高级处理,而InnoDB类型支持。MyISAM类型的表强调的是性能,其执行数度比InnoDB类型更快,但是不提供事务支持,而InnoDB提供事务支持以及外部键等高级数据库功能。
1.1 InnoDB和MyISAM的差别
◆1.InnoDB不支持FULLTEXT类型的索引。
◆2.InnoDB 中不保存表的具体行数,也就是说,执行select count(*) from table时,InnoDB要扫描一遍整个表来计算有多少行,但是MyISAM只要简单的读出保存好的行数即可。注意的是,当count(*)语句包含 where条件时,两种表的操作是一样的。
◆3.对于AUTO_INCREMENT类型的字段,InnoDB中必须包含只有该字段的索引,但是在MyISAM表中,可以和其他字段一起建立联合索引。
◆4.DELETE FROM table时,InnoDB不会重新建立表,而是一行一行的删除。
◆5.LOAD TABLE FROM MASTER操作对InnoDB是不起作用的,解决方法是首先把InnoDB表改成MyISAM表,导入数据后再改成InnoDB表,但是对于使用的额外的InnoDB特性(例如外键)的表不适用。
另外,InnoDB表的行锁也不是绝对的,假如在执行一个SQL语句时MySQL不能确定要扫描的范围,InnoDB表同样会锁全表,例如update table set num=1 where name like “%aaa%”
1.2 InnoDB和MyISAM的选择
两种类型最主要的差别就是Innodb 支持事务处理与外键和行级锁。而MyISAM不支持.所以MyISAM往往就容易被人认为只适合在小项目中使用。
作为使用MySQL的用户角度出发,Innodb和MyISAM都是比较喜欢的,如果数据库平台要达到需求:99.9%的稳定性,方便的扩展性和高可用性来说的话,MyISAM绝对是首选。
原因如下:
1、平台上承载的大部分项目是读多写少的项目,而MyISAM的读性能是比Innodb强不少的。
2、MyISAM的索引和数据是分开的,并且索引是有压缩的,内存使用率就对应提高了不少。能加载更多索引,而Innodb是索引和数据是紧密捆绑的,没有使用压缩从而会造成Innodb比MyISAM体积庞大不小。
3、经常隔1,2个月就会发生应用开发人员不小心update一个表where写的范围不对,导致这个表没法正常用了,这个时候MyISAM的优越性就体现出来了,随便从当天拷贝的压缩包取出对应表的文件,随便放到一个数据库目录下,然后dump成sql再导回到主库,并把对应的binlog补上。如果是Innodb,恐怕不可能有这么快速度,别和我说让Innodb定期用导出xxx.sql机制备份,因为最小的一个数据库实例的数据量基本都是几十G大小。
4、从接触的应用逻辑来说,select count(*) 和order by 是最频繁的,大概能占了整个sql总语句的60%以上的操作,而这种操作Innodb其实也是会锁表的,很多人以为Innodb是行级锁,那个只是where对它主键是有效,非主键的都会锁全表的。
5、还有就是经常有很多应用部门需要我给他们定期某些表的数据,MyISAM的话很方便,只要发给他们对应那表的frm.MYD,MYI的文件,让他们自己在对应版本的数据库启动就行,而Innodb就需要导出xxx.sql了,因为光给别人文件,受字典数据文件的影响,对方是无法使用的。
6、如果和MyISAM比insert写操作的话,Innodb还达不到MyISAM的写性能,如果是针对基于索引的update操作,虽然MyISAM可能会逊色Innodb,但是那么高并发的写,从库能否追的上也是一个问题,还不如通过多实例分库分表架构来解决。
7、如果是用MyISAM的话,merge引擎可以大大加快应用部门的开发速度,他们只要对这个merge表做一些select count(*)操作,非常适合大项目总量约几亿的rows某一类型(如日志,调查统计)的业务表。
当然Innodb也不是绝对不用,用事务的项目就用Innodb的。另外,可能有人会说你MyISAM无法抗太多写操作,但是可以通过架构来弥补。
2 参数配置
2.1 连接
连接通常来自Web服务器,下面列出了一些与连接有关的参数,以及该如何设置它们。
1、max_connections
这是Web服务器允许的最大连接数,记住每个连接都要使用会话内存。
2、max_packet_allowed
最大数据包大小,通常等于你需要在一个大块中返回的最大数据集的大小,如果你在使用远程mysqldump,那它的值需要更大。
3、aborted_connects
检查系统状态的计数器,确定其没有增长,如果数量增长说明客户端连接时遇到了错误。
4、thread_cache_size
入站连接会在MySQL中创建一个新的线程,因为MySQL中打开和关闭连接都很廉价,速度也快,它就没有象其它数据库,如Oracle那么多持续连接了,但线程预先创建并不会节约时间,这就是为什么要MySQL线程缓存的原因了。
如果在增长请密切注意创建的线程,让你的线程缓存更大,对于2550或100的thread_cache_size,内存占用也不多。
2.2 查询缓存
MySQL查询缓存可以跳过SQL解析优化查询等阶段,直接返回缓存结果给用户。缓存存在一个hash表中,通过查询SQL,查询数据库,客户端协议等作为key.在判断是否命中前,MySQL不会解析SQL,而是直接使用SQL去查询缓存,SQL任何字符上的不同,如空格,注释,都会导致缓存不命中.
如果查询中有不确定数据,例如CURRENT_DATE()和NOW()函数,那么查询完毕后则不会被缓存.所以,包含不确定数据的查询是肯定不会找到可用缓存的。
工作流程
1. 服务器接收SQL,以SQL和一些其他条件为key查找缓存表(额外性能消耗)
2. 如果找到了缓存,则直接返回缓存(性能提升)
3. 如果没有找到缓存,则执行SQL查询,包括原来的SQL解析,优化等.
4. 执行完SQL查询结果以后,将SQL查询结果存入缓存表(额外性能消耗)
缓存失效
当某个表正在写入数据,则这个表的缓存(命中检查,缓存写入等)将会处于失效状态.在Innodb中,如果某个事务修改了表,则这个表的缓存在事务提交前都会处于失效状态,在这个事务提交前,这个表的相关查询都无法被缓存。
缓存的内存管理
缓存会在内存中开辟一块内存(query_cache_size)来维护缓存数据,其中有大概40K的空间是用来维护缓存的元数据的,例如空间内存,数据表和查询结果的映射,SQL和查询结果的映射等.
MySQL将这个大内存块分为小的内存块(query_cache_min_res_unit),每个小块中存储自身的类型,大小和查询结果数据,还有指向前后内存块的指针.
MySQL需要设置单个小存储块的大小,在SQL查询开始(还未得到结果)时就去申请一块空间,所以即使你的缓存数据没有达到这个大小,也需要用这个大小的数据块去存(这点跟Linux文件系统的Block一样).如果结果超出这个内存块的大小,则需要再去申请一个内存块.当查询完成发现申请的内存块有富余,则会将富余的空间释放掉,这就会造成内存碎片问题。
缓存配置参数
1. query_cache_type: 是否打开缓存
1) OFF: 关闭
2) ON: 总是打开
3) DEMAND: 只有明确写了SQL_CACHE的查询才会吸入缓存
2. query_cache_size: 缓存使用的总内存空间大小,单位是字节,这个值必须是1024的整数倍,否则MySQL实际分配可能跟这个数值不同(感觉这个应该跟文件系统的blcok大小有关)
3. query_cache_min_res_unit: 分配内存块时的最小单位大小
4. query_cache_limit: MySQL能够缓存的最大结果,如果超出,则增加 Qcache_not_cached的值,并删除查询结果
5. query_cache_wlock_invalidate: 如果某个数据表被锁住,是否仍然从缓存中返回数据,默认是OFF,表示仍然可以返回
2.3 临时表
内存速度是相当快的,因此我们希望所有的排序操作都在内存中进行,我们可以通过调整查询让结果集更小以实现内存排序,或将变量设置得更大。
tmp_table_size
max_heap_table_size
无论何时在MySQL中创建临时表,它都会使用这两个变量的最小值作为临界值,除了在磁盘上构建临时表外,还会创建许多会话,这些会话会抢占有 限制的资源,因此最好是调整查询而不是将这些参数设置得更高,同时,需要注意的是有BLOB或TEXT字段类型的表将直接写入磁盘。
2.4 会话内存
MySQL中每个会话都有其自己的内存,这个内存就是分配给SQL查询的内存,因此你想让它变得尽可能大以满足需要。但你不得不平衡同一时间数 据库内一致性会话的数量。这里显得有点黑色艺术的是MySQL是按需分配缓存的,因此,你不能只添加它们并乘以会话的数量,这样估算下来比MySQL典型 的使用要大得多。
最佳做法是启动MySQL,连接所有会话,然后继续关注顶级会话的VIRT列,mysqld行的数目通常保持相对稳定,这就是实际的内存 总用量,减去所有的静态MySQL内存区域,就得到了实际的所有会话内存,然后除以会话的数量就得到平均值。
1、read_buffer_size
缓存连续扫描的块,这个缓存是跨存储引擎的,不只是MyISAM表。
2、sort_buffer_size
执行排序缓存区的大小,最好将其设置为1M-2M,然后在会话中设置,为一个特定的查询设置更高的值。
3、join_buffer_size
执行联合查询分配的缓存区大小,将其设置为1M-2M大小,然后在每个会话中再单独按需设置。
4、read_rnd_buffer_size
用于排序和order by操作,最好将其设置为1M,然后在会话中可以将其作为一个会话变量设置为更大的值。
2.5 慢查询日志
慢速查询日志是MySQL很有用的一个特性。
1、log_slow_queries
MySQL参数中log_slow_queries参数在my.cnf文件中设置它,将其设置为on,默认情况下,MySQL会将文件放到数据目录,文件以“主机名-slow.log”的形式命名,但你在设置这个选项的时候也可以为其指定一个名字。
2、long_query_time
默认值是10秒,你可以动态设置它,值从1到将其设置为on,如果数据库启动了,默认情况下,日志将关闭。截至5.1.21和安装了 Google补丁的版本,这个选项可以以微秒设置,这是一个了不起的功能,因为一旦你消除了所有查询时间超过1秒的查询,说明调整非常成功,这样可以帮助 你在问题变大之前消除问题SQL。
3、log_queries_not_using_indexes
开启这个选项是个不错的主意,它真实地记录了返回所有行的查询。
2.6 小结
我们介绍了MySQL参数的五大类设置,平时我们一般都很少碰它们,在进行MySQL性能调优和故障诊断时这些参数还是非常有用的。
MySQL中的缓存查询包括两个解析查询计划,以及返回的数据集,如果基础表数据或结构有变化,将会使查询缓存中的项目无效。
1、query_cache_min_res_unit
MySQL参数中query_cache_min_res_unit查询缓存中的块是以这个大小进行分配的,使用下面的公式计算查询缓存的平均大小,根据计算结果设置这个变量,MySQL就会更有效地使用查询缓存,缓存更多的查询,减少内存的浪费。
2、query_cache_size
这个参数设置查询缓存的总大小。
3、query_cache_limit
这个参数告诉MySQL丢掉大于这个大小的查询,一般大型查询还是比较少见的,如运行一个批处理执行一个大型报表的统计,因此那些大型结果集不应该填满查询缓存。
3 查询语句优化
Explain用于显示当前sql语句的执行情况,包括调用了索引,所以我们可以根据这个语句给表加需要的索引。
explain 显示内容的时候最重要的是keys这个字段,这个字段表示该查询中用到了那个索引,如果没有用到索引的话就显示Null,这时候我们根据要求增加合适的索引,如果用到了索引就显示用到的索引。
下面只说两个explain列出的三个重要的参数:
Type 表连接类型 依次是从最好的到最差的
system > const > eq_ref > ref > fulltext > ref_or_null > index_merge > unique_subquery > index_subquery > range > index > ALL
一般来说,得保证查询至少达到range级别,最好能达到ref。
Const该类型用在 表中最多只有一行匹配的记录,它在一开始查询的时候就被读取出来。并且用到了primary或者unique的时候。(也就是说只要用到了primary或者unique索引的并且匹配的记录只有一条的,所用的类型就是const)这里通篇以ecshop中的ecs_goods表为例
Explain select * from ecs_goods where goods_id = 10这种情况用的类型是const因为其中goods_id是主键 primary key
Explain select * from ecs_goods where goods_id < 10 这种用的就不是const
Explain select * from ecs_goods where add_time = 123445324 这种情况用的类型也是const,因为add_time是unique索引
Eq_ref 适用于有表连接的sql语句,并且只能从表中读取传一条语句,并且用到的索引必须是primary_key或者unique
Ref 该表中所有匹配的记录都会被读取出来,并且用到的索引不能是primary或者unique,这是最普通最常用的一种类型
All 这种情况非常糟糕,是因为表中没有建立索引,所以要对该表做全部扫描
Possible_keys 指的是该sql语句在搜索表时,坑内会使用哪个索引,但可能不会使用。如果该参数为空,则表示没有索引被用到。这种情况就可以检查where子句中那些字段适合加索引以提高查询性能。
Key 表示在查询中实际用到的索引,如果当前没有任何索引被用到则该参数为null,这时候同上,查看where字句中那些字段适合加索引来提高查询性能。
好的索引非常重要,好的索引让查询有良好的访问类型并且只检查需要的行。但是添加索引并不意味着mysql会访问并且返回同样的行。
Extra 包含不适合在其他列中显示但十分重要的额外信息。如果是using temporary或者using filesort,则表示有需要改进的地方。
以上是关于MySQL性能优化的主要内容,如果未能解决你的问题,请参考以下文章