1. 简介
在Web应用程序体系架构中,数据持久层(一般是一个关系数据库)是关键的核心部分,它对系统的性能有很是重要的影响。MySQL是目前使用最多的开源数据库,可是MySQL数据库的默认设置性能很是的差,仅仅是一个玩具数据库。所以在产品中使用MySQL数据库必须进行必要的优化。
优化是一个复杂的任务,本文描述MySQL相关的数据库设计和查询优化,服务器端优化,存储引擎优化。
2. 数据库设计和查询优化
在MySQL Server性能调优中,首先要考虑的就是Database Schema设计,这一点是很是重要的。一个糟糕的Schema设计即便在性能调优的MySQL Server上运行,也会表现出不好的性能;和Schema类似,查询语句的设计也会影响MySQL的性能,应该避免写出低效的SQL查询。这一节将详细讨论这两方面的优化。
2.1 Schema Design
Schema的优化取决于将要运行什么样的query,不一样的query会有不一样的Schema优化方案。2.2节将介绍Query Design的优化。Schema设计一样受到预期数据集大小的影响。Schema设计时主要考虑:标准化,数据类型,索引。
2.1.1 标准化
标准化是在数据库中组织数据的过程。其中包括,根据设计规则建立表并在这些表间创建关系;经过取消冗余度与不一致相关性,该设计规则能够同时保护数据并提升数据的灵活性。一般数据库标准化是让数据库设计符合某一级别的范式,一般知足第三范式便可。也有第四范式(也称为 Boyce Codd范式,BCNF))与第五范式存在,可是在实际设计中不多考虑。忽视这些规则可能使得数据库的设计不太完美,但这不该影响功能。
标准化的特色:java
1) 全部的“对象”都在它本身的table中,没有冗余。
2) 数据库一般由E-R图生成。
3) 简洁,更新属性一般只须要更新不多的记录。
4) Join操做比较耗时。
5) Select,sort优化措施比较少。
6) 适用于OLTP应用。
非标准化的特色:mysql
1) 在一张表中存储不少数据,数据冗余。
2) 更新数据开销很大,更新一个属性可能会更新不少表,不少记录。
3) 在删除数据是有可能丢失数据。
4) Select,order有不少优化的选择。
5) 适用于DSS应用。
标准化和非标准化都有各自的优缺点,一般在一个数据库设计中能够混合使用,一部分表格标准化,一部分表格保留一些冗余数据:c++
1) 对OLTP使用标准化,对DSS使用非标准化
2) 使用物化视图。MySQL不直接支持该数据库特性,可是能够用MyISAM表代替。
3) 冗余一些数据在表格中,例如将ref_id和name存在同一张表中。可是要注意更新问题。
4) 对于一些简单的对象,直接使用value做为建。例如IP address等
5) Reference by PRIMARY/UNIQUE KEY。MySQL能够优化这种操做,例如:
java 代码sql
select city_name数据库
from city,state缓存
where state_id=state.id and state.code=‘CA’” converted to “select city_name from city where state_id=12安全
2.1.2 数据类型
最基本的优化之一就是使表在磁盘上占据的空间尽量小。这能带来性能很是大的提高,由于数据小,磁盘读入较快,而且在查询过程当中表内容被处理所占用的内存更少。同时,在更小的列上建索引,索引也会占用更少的资源。
可使用下面的技术可使表的性能更好而且使存储空间最小:服务器
1) 使用正确合适的类型,不要将数字存储为字符串。
2) 尽量地使用最有效(最小)的数据类型。MySQL有不少节省磁盘空间和内存的专业化类型。
3) 尽量使用较小的整数类型使表更小。例如,MEDIUMINT常常比INT好一些,由于MEDIUMINT列使用的空间要少25%。
4) 若是可能,声明列为NOT NULL。它使任何事情更快并且每列能够节省一位。注意若是在应用程序中确实须要NULL,应该毫无疑问使用它,只是避免 默认地在全部列上有它。
5) 对于MyISAM表,若是没有任何变长列(VARCHAR、TEXT或BLOB列),使用固定尺寸的记录格式。这比较快可是不幸地可能会浪费一些空间。即便你已经用CREATE选项让VARCHAR列ROW_FORMAT=fixed,也能够提示想使用固定长度的行。
6) 使用sample character set,例如latin1。尽可能少使用utf-8,由于utf-8占用的空间是latin1的3倍。能够在不须要使用utf-8的字段上面使用latin1,例如mail,url等。
2.1.3 索引
全部MySQL列类型能够被索引。对相关列使用索引是提升SELECT操做性能的最佳途径。使用索引应该注意如下几点:架构
1) MySQL只会使用前缀,例如key(a, b) …where b=5 将使用不到索引。
2) 要选择性的使用索引。在变化不多的列上使用索引并非很好,例如性别列。
3) 在Unique列上定义Unique index。
4) 避免创建使用不到的索引。
5) 在Btree index中(InnoDB使用Btree),能够在须要排序的列上创建索引。
6) 避免重复的索引。
7) 避免在已有索引的前缀上创建索引。例如:若是存在index(a,b)则去掉index(a)。
8) 控制单个索引的长度。使用key(name(8))在数据的前面几个字符创建索引。
9) 越是短的键值越好,最好使用integer。
10) 在查询中要使用到索引(使用explain查看),能够减小读磁盘的次数,加速读取数据。
11) 相近的键值比随机好。Auto_increment就比uuid好。
12) Optimize table能够压缩和排序index,注意不要频繁运行。
13) Analyze table能够更新数据。
2.2 Designing queries
查询语句的优化是一个Case by case的问题,不一样的sql有不一样的优化方案,在这里我只列出一些通用的技巧。并发
1) 在有index的状况下,尽可能保证查询使用了正确的index。可使用EXPLAIN select …查看结果,分析查询。
2) 查询时使用匹配的类型。例如select * from a where id=5, 若是这里id是字符类型,同时有index,这条查询则使用不到index,会作全表扫描,速度会很慢。正确的应该是 … where id=”5” ,加上引号代表类型是字符。
3) 使用--log-slow-queries –long-query-time=2查看查询比较慢的语句。而后使用explain分析查询,作出优化。
3. 服务器端优化
3.1 MySQL安装
MySQL有不少发行版本,最好使用MySQL AB发布的二进制版本。也能够下载源代码进行编译安装,可是编译器和类库的一些bug可能会使编译完成的MySQL存在潜在的问题。
若是安装MySQL的服务器使用的是Intel公司的处理器,可使用intel c++编译的版本,在Linux World2005的一篇PPT中提到,使用intel C++编译器编译的MySQL查询速度比正常版本快30%左右。Intel c++编译版本能够在MySQL官方网站下载。
3.2 服务器设置优化
MySQL默认的设置性能不好,因此要作一些参数的调整。这一节介绍一些通用的参数调整,不涉及具体的存储引擎(主要指MyISAM,InnoDB,相关优化在4中介绍)。
--character-set:若是是单一语言使用简单的character set例如latin1。尽可能少用Utf-8,utf-8占用空间较多。
--memlock:锁定MySQL只能运行在内存中,避免swapping,可是若是内存不够时有可能出现错误。
--max_allowed_packet:要足够大,以适应比较大的SQL查询,对性能没有太大影响,主要是避免出现packet错误。
--max_connections:server容许的最大链接。太大的话会出现out of memory。
--table_cache:MySQL在同一时间保持打开的table的数量。打开table开销比较大。通常设置为512。
--query_cache_size: 用于缓存查询的内存大小。
--datadir:mysql存放数据的根目录,和安装文件分开在不一样的磁盘能够提升一点性能。
4. 存储引擎优化
MySQL支持不一样的存储引擎,主要使用的有MyISAM和InnoDB。
4.1 MyISAM
MyISAM管理非事务表。它提供高速存储和检索,以及全文搜索能力。MyISAM在全部MySQL配置里被支持,它是默认的存储引擎,除非配置MySQL默认使用另一个引擎。
4.1.1 MyISAM特性
4.1.1.1 MyISAM Properties
1) 不支持事务,宕机会破坏表
2) 使用较小的内存和磁盘空间
3) 基于表的锁,并发更新数据会出现严重性能问题
4) MySQL只缓存Index,数据由OS缓存
4.1.1.2 Typical MyISAM usages
1) 日志系统
2) 只读或者绝大部分是读操做的应用
3) 全表扫描
4) 批量导入数据
5) 没有事务的低并发读/写
4.1.2 MyISAM优化要点
1) 声明列为NOT NULL,能够减小磁盘存储。
2) 使用optimize table作碎片整理,回收空闲空间。注意仅仅在很是大的数据变化后运行。
3) Deleting/updating/adding大量数据的时候禁止使用index。使用ALTER TABLE t DISABLE KEYS。
4) 设置myisam_max_[extra]_sort_file_size足够大,能够显著提升repair table的速度。
4.1.3 MyISAM Table Locks
1) 避免并发insert,update。
2) 可使用insert delayed,可是有可能丢失数据。
3) 优化查询语句。
4) 水平分区。
5) 垂直分区。
6) 若是都不起做用,使用InnoDB。
4.1.4 MyISAM Key Cache
1) 设置key_buffer_size variable。MyISAN最主要的cache设置,用于缓存MyISAM表格的index数据,该参数只对MyISAM有影响。一般在只使用MyISAM的Server中设置25-33%的内存大小。
2) 可使用几个不一样的Key Caches(对一些hot data)。
a) SET GLOBAL test.key_buffer_size=512*1024;2) Preload index到Cache中能够提升查询速度。由于preloading index是顺序的,因此很是快。
b) CACHE INDEX t1.i1, t2.i1, t3 IN test;
a) LOAD INDEX INTO CACHE t1, t2 IGNORE LEAVES;
4.2 InnoDB
InnoDB给MySQL提供了具备提交,回滚和崩溃恢复能力的事务安全(ACID兼容)存储引擎。InnoDB提供row level lock,而且也在SELECT语句提供一个Oracle风格一致的非锁定读。这些特点增长了多用户部署和性能。没有在InnoDB中扩大锁定的须要,由于在InnoDB中row level lock适合很是小的空间。InnoDB也支持FOREIGN KEY约束。在SQL查询中,你能够自由地将InnoDB类型的表与其它MySQL的表的类型混合起来,甚至在同一个查询中也能够混合。
InnoDB是为在处理巨大数据量时得到最大性能而设计的。它的CPU使用效率很是高。
InnoDB存储引擎已经彻底与MySQL服务器整合,InnoDB存储引擎为在内存中缓存数据和索引而维持它本身的缓冲池。 InnoDB存储它的表&索引在一个表空间中,表空间能够包含数个文件(或原始磁盘分区)。这与MyISAM表不一样,好比在MyISAM表中每一个表被存在分离的文件中。InnoDB 表能够是任何大小,即便在文件尺寸被限制为2GB的操做系统上。
许多须要高性能的大型数据库站点上使用了InnoDB引擎。著名的Internet新闻站点Slashdot.org运行在InnoDB上。 Mytrix, Inc.在InnoDB上存储超过1TB的数据,还有一些其它站点在InnoDB上处理平均每秒800次插入/更新的负荷。
4.2.1 InnoDB特性
4.2.1.1 InnoDB Properties
1) 支持事务,ACID,外键。
2) Row level locks。
3) 支持不一样的隔离级别。
4) 和MyISAM相比须要较多的内存和磁盘空间。
5) 没有键压缩。
6) 数据和索引都缓存在内存hash表中。
4.2.1.2 InnoDB Good For
1) 须要事务的应用。
2) 高并发的应用。
3) 自动恢复。
4) 较快速的基于主键的操做。
4.2.2 InnoDB优化要点
1) 尽可能使用short,integer的主键。
2) Load/Insert数据时按主键顺序。若是数据没有按主键排序,先排序而后再进行数据库操做。
3) 在Load数据是为设置SET UNIQUE_CHECKS=0,SET FOREIGN_KEY_CHECKS=0,能够避免外键和惟一性约束检查的开销。
4) 使用prefix keys。由于InnoDB没有key压缩功能。
4.2.3 InnoDB服务器端设定
innodb_buffer_pool_size:这是InnoDB最重要的设置,对InnoDB性能有决定性的影响。默认的设置只有8M,因此默认的数据库设置下面InnoDB性能不好。在只有InnoDB存储引擎的数据库服务器上面,能够设置60-80%的内存。更精确一点,在内存容量容许的状况下面设置比InnoDB tablespaces大10%的内存大小。
innodb_data_file_path:指定表数据和索引存储的空间,能够是一个或者多个文件。最后一个数据文件必须是自动扩充的,也只有最后一个文件容许自动扩充。这样,当空间用完后,自动扩充数据文件就会自动增加(以8MB为单位)以容纳额外的数据。例如: innodb_data_file_path=/disk1/ibdata1:900M;/disk2/ibdata2:50M:autoextend两个数据文件放在不一样的磁盘上。数据首先放在ibdata1中,当达到900M之后,数据就放在ibdata2中。一旦达到50MB,ibdata2将以8MB为单位自动增加。若是磁盘满了,须要在另外的磁盘上面增长一个数据文件。
innodb_autoextend_increment: 默认是8M, 若是一次insert数据量比较多的话, 能够适当增长.
innodb_data_home_dir:放置表空间数据的目录,默认在mysql的数据目录,设置到和MySQL安装文件不一样的分区能够提升性能。
innodb_log_file_size:该参数决定了recovery speed。太大的话recovery就会比较慢,过小了影响查询性能,通常取256M能够兼顾性能和recovery的速度
。
innodb_log_buffer_size:磁盘速度是很慢的,直接将log写道磁盘会影响InnoDB的性能,该参数设定了log buffer的大小,通常4M。若是有大的blob操做,能够适当增大。
innodb_flush_logs_at_trx_commit=2: 该参数设定了事务提交时内存中log信息的处理。
1) =1时,在每一个事务提交时,日志缓冲被写到日志文件,对日志文件作到磁盘操做的刷新。Truly ACID。速度慢。innodb_file_per_table:能够存储每一个InnoDB表和它的索引在它本身的文件中。
2) =2时,在每一个事务提交时,日志缓冲被写到文件,但不对日志文件作到磁盘操做的刷新。只有操做系统崩溃或掉电才会删除最后一秒的事务,否则不会丢失事务。
3) =0时, 日志缓冲每秒一次地被写到日志文件,而且对日志文件作到磁盘操做的刷新。任何mysqld进程的崩溃会删除崩溃前最后一秒的事务
transaction-isolation=READ-COMITTED: 若是应用程序能够运行在READ-COMMITED隔离级别,作此设定会有必定的性能提高。
innodb_flush_method: 设置InnoDB同步IO的方式:
1) Default – 使用fsync()。innodb_thread_concurrency: InnoDB kernel最大的线程数。
2) O_SYNC 以sync模式打开文件,一般比较慢。
3) O_DIRECT,在Linux上使用Direct IO。能够显著提升速度,特别是在RAID系统上。避免额外的数据复制和double buffering(mysql buffering 和OS buffering)。
1) 最少设置为(num_disks+num_cpus)*2。
2) 能够经过设置成1000来禁止这个限制
5. 缓存
缓存有不少种,为应用程序加上适当的缓存策略会显著提升应用程序的性能。因为应用缓存是一个比较大的话题,因此这一部分还须要进一步调研。
6. Reference 1) http://www.mysqlperformanceblog.com/2) Advanced MySQL Performance Optimization, Peter Zaitsev, Tobias Asplund, MySQL Users Conference 20053) Improving MySQL Server Performance with Intel C++ Compiler,Peter Zaitsev,Linux World 20054) MySQL Performance Optimization, Peter Zaitsev, Percona Ltd, OPEN SOURCE DATABASE CONFERENCE 20065) MySQL Server Settings Tuning, Peter Zaitsev, co-founder, Percona Ltd, 20076) MySQL Reference Manual