mysql优化常识

MySQL一直以一个非常容易使用的数据库产品来展示于大众者面前。这是其能够迅速发展并在竞争激烈的数据库市场中赢得一席之地的重要原因,但同 时也给其进一步发展带来了一定的阻力,容易使用的形象让大部分人误以为也很容易驾驭,间接阻碍了技术人员的发展,造成目前 MySQL 领域技术人员数量的巨大空缺,大量的公司找不到和是的 MySQL 技术人员。

  对于排序的优化,最有效的办法就是借助索引的帮助,让索引的有序特性自动带给你有序的数据,这在 MySQL 中是非常常见的优化方式,屡试不爽。当然,对于其他数据库如 Oracle 等也同样奏效。

  简朝阳:MySQL性能调优最佳实践经验

  ▲找出瓶颈

  以前数据库的性能瓶颈大多时候都出现在 IO 层面,现在经常也遇到 CPU 成为瓶颈的时候,而且并不是在使用高性能IO设备如SSD之类的场景下。对于 CPU 成为瓶颈的数据库优化,目前遇到的场景大多是排序和重复读取过多造成。

  简朝阳:MySQL性能调优最佳实践经验

  ▲方法

  简朝阳:MySQL性能调优最佳实践经验

  ▲避免的误区

  优化目标

  减少 IO 次数

  IO永远是数据库最容易瓶颈的地方,这是由数据库的职责所决定的,大部分数据库操作中超过90%的时间都是 IO 操作所占用的,减少 IO 次数是 SQL 优化中需要第一优先考虑,当然,也是收效最明显的优化手段。

  降低 CPU 计算

  除了 IO 瓶颈之外,SQL优化中需要考虑的就是 CPU 运算量的优化了。order by, group by,distinct … 都是消耗 CPU 的大户(这些操作基本上都是 CPU 处理内存中的数据比较运算)。当我们的 IO 优化做到一定阶段之后,降低 CPU 计算也就成为了我们 SQL 优化的重要目标

  优化方法

  改变 SQL 执行计划

  明确了优化目标之后,我们需要确定达到我们目标的方法。对于 SQL 语句来说,达到上述2个目标的方法其实只有一个,那就是改变 SQL 的执行计划,让他尽量“少走弯路”,尽量通过各种“捷径”来找到我们需要的数据,以达到 “减少 IO 次数” 和 “降低 CPU 计算” 的目标

  常见误区

  count(1)和count(primary_key) 优于 count(*)

  很多人为了统计记录条数,就使用 count(1) 和 count(primary_key) 而不是 count(*) ,他们认为这样性能更好,其实这是一个误区。对于有些场景,这样做可能性能会更差,应为数据库对 count(*) 计数操作做了一些特别的优化。

  count(column) 和 count(*) 是一样的

  这个误区甚至在很多的资深工程师或者是 DBA 中都普遍存在,很多人都会认为这是理所当然的。实际上,count(column) 和 count(*) 是一个完全不一样的操作,所代表的意义也完全不一样。

  count(column) 是表示结果集中有多少个column字段不为空的记录

  count(*) 是表示整个结果集有多少条记录

  select a,b from … 比 select a,b,c from … 可以让数据库访问更少的数据量

  这个误区主要存在于大量的开发人员中,主要原因是对数据库的存储原理不是太了解。

  实际上,大多数关系型数据库都是按照行(row)的方式存储,而数据存取操作都是以一个固定大小的IO单元(被称作 block 或者 page)为单位,一般为4KB,8KB… 大多数时候,每个IO单元中存储了多行,每行都是存储了该行的所有字段(lob等特殊类型字段除外)。

  所以,我们是取一个字段还是多个字段,实际上数据库在表中需要访问的数据量其实是一样的。

  当然,也有例外情况,那就是我们的这个查询在索引中就可以完成,也就是说当只取 a,b两个字段的时候,不需要回表,而c这个字段不在使用的索引中,需要回表取得其数据。在这样的情况下,二者的IO量会有较大差异。

  order by 一定需要排序操作

  我们知道索引数据实际上是有序的,如果我们的需要的数据和某个索引的顺序一致,而且我们的查询又通过这个索引来执行,那么数据库一般会省略排序操作,而直接将数据返回,因为数据库知道数据已经满足我们的排序需求了。

  实际上,利用索引来优化有排序需求的 SQL,是一个非常重要的优化手段

  延伸阅读:MySQL ORDER BY 的实现分析 ,MySQL 中 GROUP BY 基本实现原理 以及 MySQL DISTINCT 的基本实现原理 这3篇文章中有更为深入的分析,尤其是第一篇

  执行计划中有 filesort 就会进行磁盘文件排序

  有这个误区其实并不能怪我们,而是因为 MySQL 开发者在用词方面的问题。filesort 是我们在使用 explain 命令查看一条 SQL 的执行计划的时候可能会看到在 “Extra” 一列显示的信息。

  实际上,只要一条 SQL 语句需要进行排序操作,都会显示“Using filesort”,这并不表示就会有文件排序操作。

  延伸阅读:理解 MySQL Explain 命令输出中的filesort,我在这里有更为详细的介绍

  基本原则

  尽量少 join

  MySQL 的优势在于简单,但这在某些方面其实也是其劣势。MySQL 优化器效率高,但是由于其统计信息的量有限,优化器工作过程出现偏差的可能性也就更多。对于复杂的多表 Join,一方面由于其优化器受限,再者在 Join 这方面所下的功夫还不够,所以性能表现离 Oracle 等关系型数据库前辈还是有一定距离。但如果是简单的单表查询,这一差距就会极小甚至在有些场景下要优于这些数据库前辈。

  尽量少排序

  排序操作会消耗较多的 CPU 资源,所以减少排序可以在缓存命中率高等 IO 能力足够的场景下会较大影响 SQL 的响应时间。

  对于MySQL来说,减少排序有多种办法,比如:

  上面误区中提到的通过利用索引来排序的方式进行优化

  减少参与排序的记录条数

  非必要不对数据进行排序

  …

  尽量避免 select *

  很多人看到这一点后觉得比较难理解,上面不是在误区中刚刚说 select 子句中字段的多少并不会影响到读取的数据吗?

  是的,大多数时候并不会影响到 IO 量,但是当我们还存在 order by 操作的时候,select 子句中的字段多少会在很大程度上影响到我们的排序效率,这一点可以通过我之前一篇介绍 MySQL ORDER BY 的实现分析 的文章中有较为详细的介绍。

  此外,上面误区中不是也说了,只是大多数时候是不会影响到 IO 量,当我们的查询结果仅仅只需要在索引中就能找到的时候,还是会极大减少 IO 量的。

  尽量用 join 代替子查询

  虽然 Join 性能并不佳,但是和 MySQL 的子查询比起来还是有非常大的性能优势。MySQL 的子查询执行计划一直存在较大的问题,虽然这个问题已经存在多年,但是到目前已经发布的所有稳定版本中都普遍存在,一直没有太大改善。虽然官方也在很早就承认这一问题,并且承诺尽快解决,但是至少到目前为止我们还没有看到哪一个版本较好的解决了这一问题。

  尽量少 or

  当 where 子句中存在多个条件以“或”并存的时候,MySQL 的优化器并没有很好的解决其执行计划优化问题,再加上 MySQL 特有的 SQL 与 Storage 分层架构方式,造成了其性能比较低下,很多时候使用 union all 或者是union(必要的时候)的方式来代替“or”会得到更好的效果。

  尽量用 union all 代替 union

  union 和 union all 的差异主要是前者需要将两个(或者多个)结果集合并后再进行唯一性过滤操作,这就会涉及到排序,增加大量的 CPU 运算,加大资源消耗及延迟。所以当我们可以确认不可能出现重复结果集或者不在乎重复结果集的时候,尽量使用 union all 而不是 union。

  尽量早过滤

  这一优化策略其实最常见于索引的优化设计中(将过滤性更好的字段放得更靠前)。

  在 SQL 编写中同样可以使用这一原则来优化一些 Join 的 SQL。比如我们在多个表进行分页数据查询的时候,我们最好是能够在一个表上先过滤好数据分好页,然后再用分好页的结果集与另外的表 Join,这样可以尽可能多的减少不必要的 IO 操作,大大节省 IO 操作所消耗的时间。

  避免类型转换

  这里所说的“类型转换”是指 where 子句中出现 column 字段的类型和传入的参数类型不一致的时候发生的类型转换:

  人为在column_name 上通过转换函数进行转换

  直接导致 MySQL(实际上其他数据库也会有同样的问题)无法使用索引,如果非要转换,应该在传入的参数上进行转换

  由数据库自己进行转换

  如果我们传入的数据类型和字段类型不一致,同时我们又没有做任何类型转换处理,MySQL 可能会自己对我们的数据进行类型转换操作,也可能不进行处理而交由存储引擎去处理,这样一来,就会出现索引无法使用的情况而造成执行计划问题。

  优先优化高并发的 SQL,而不是执行频率低某些“大”SQL

  对于破坏性来说,高并发的 SQL 总是会比低频率的来得大,因为高并发的 SQL 一旦出现问题,甚至不会给我们任何喘息的机会就会将系统压跨。而对于一些虽然需要消耗大量 IO 而且响应很慢的 SQL,由于频率低,即使遇到,最多就是让整个系统响应慢一点,但至少可能撑一会儿,让我们有缓冲的机会。

  从全局出发优化,而不是片面调整

  SQL 优化不能是单独针对某一个进行,而应充分考虑系统中所有的 SQL,尤其是在通过调整索引优化 SQL 的执行计划的时候,千万不能顾此失彼,因小失大。

  尽可能对每一条运行在数据库中的SQL进行 explain

  优化 SQL,需要做到心中有数,知道 SQL 的执行计划才能判断是否有优化余地,才能判断是否存在执行计划问题。在对数据库中运行的 SQL 进行了一段时间的优化之后,很明显的问题 SQL 可能已经很少了,大多都需要去发掘,这时候就需要进行大量的 explain 操作收集执行计划,并判断是否需要进行优化。


最基本的优化之一就是使表在磁盘上占据的空间尽可能小。这能带来性能非常大的提升,因为数据小,磁盘读入较快,并且在查询过程中表内容被处理所占用的内存更少。同时,在更小的列上建索引,索引也会占用更少的资源。

  可以使用下面的技术可以使表的性能更好并且使存储空间最小:

  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;

  b) CACHE INDEX t1.i1, t2.i1, t3 IN test;

  2) Preload index到Cache中可以提高查询速度。因为preloading index是顺序的,所以非常快。

  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。速度慢。

  2) =2时,在每个事务提交时,日志缓冲被写到文件,但不对日志文件做到磁盘操作的刷新。只有操作系统崩溃或掉电才会删除最后一秒的事务,不然不会丢失事务。

  3) =0时, 日志缓冲每秒一次地被写到日志文件,并且对日志文件做到磁盘操作的刷新。任何mysqld进程的崩溃会删除崩溃前最后一秒的事务

  innodb_file_per_table:可以存储每个InnoDB表和它的索引在它自己的文件中。

  transaction-isolation=READ-COMITTED: 如果应用程序可以运行在READ-COMMITED隔离级别,做此设定会有一定的性能提升。

  innodb_flush_method: 设置InnoDB同步IO的方式:

  1) Default – 使用fsync()。

  2) O_SYNC 以sync模式打开文件,通常比较慢。

  3) O_DIRECT,在Linux上使用Direct IO。可以显著提高速度,特别是在RAID系统上。避免额外的数据复制和double buffering(mysql buffering 和OS buffering)。

  innodb_thread_concurrency: InnoDB kernel最大的线程数。

  1) 最少设置为(num_disks+num_cpus)*2。

  2) 可以通过设置成1000来禁止这个限制

  5. 缓存

  缓存有很多种,为应用程序加上适当的缓存策略会显著提高应用程序的性能。由于应用缓存是一个比较大的话题,所以这一部分还需要进一步调研。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值