MySql优化过程及原理

一、整体思路

MYSQL优化首先应该定位问题,可能导致MYSQL低性能的原因有:业务逻辑过多的查询、表结构不合理、sql语句优化以及硬件优化,从优化效果来看,这四个优化点的优化效果依次降低:理清业务逻辑能够帮助我们避免不必要的查询,合理设计表结构也能帮助我们少查询数据库。

二、定位慢查询

1、定位慢查询

命令查看慢查询次数:show status like 'slow_queries';

使用该命令只能查看慢查询次数,但是我们没有办法知道是哪些查询产生了慢查询,如果想要知道是哪些查询导致的慢查询。

必须开启慢查询日志:set global slow_query_log=ON;

修改慢查询时间:set global long_query_time=5;

指定慢查询存储的方式:set global log_output=file;

指定慢查询日志的文件:set global slow_query_log_file='slow_query.log';

记录没有索引的查询:set global log_queries_not_using_indexes=ON;

此时我们在mysql中运行命令show variables like 'slow_query%';可以看到slow_query_log是ON状态,log_file也是我们指定的文件。

命令查看设定的慢查询时间是否生效:show variables like 'long_query_time';

运行超过5s的查询,然后查看mysql安装目录下的data目录,该目录会产生一个慢查询日志文件:slow_query.log。在该日志文件中,我们可以知道慢查询产生的时间,最终产生了几行结果,测试了几行结果,以及运行语句是什么。

2、EXPLAIN执行计划

慢查询日志可以帮助我们把所有查询时间过长的sql语句记录下来,在优化这些语句之前,我们应该使用explain命令查看mysql的执行计划,得知SQL语句的具体执行情况,索引使用等,寻找其中的可优化点。explain命令的使用十分简单,只需要"explain + sql语句"即可。

为了进一步理解mysql为什么选择这样一个执行计划,可以把执行计划优化器的追踪参数打开,再次执行扩展分析计划,然后从INFORMATION_SCHEMA.OPTIMIZER_TRACE表中获取详细信息。
set optimizer_trace="enabled=on";
explain EXTENDED select * from articles where id in (?, ?, ?, ...) and deleted_at is null;
select * from INFORMATION_SCHEMA.OPTIMIZER_TRACEG;

查看数据表的状态:show table status like 'articles'G;

查看索引的状态:show index from articles where key_name = 'index_articles_on_deleted_at'G;

3、Profiling的使用(确定消耗的CPU计算太多,还是需要的的IO 操作太多)

mysql除了提供explain命令用于查看命令执行计划外,还提供了profiling工具用于查看语句查询过程中的资源消耗情况。

profiling默认是关闭的,首先我们要使用以下命令开启Profiling功能:set profiling=1;

获取系统中保存的所有 Query 的 profile 概要信息:show profiles;

查看具体的某一次查询的profile信息:show profile cpu,block io for query n;

耗时以及CPU和Block IO的消耗:show profile cpu,block io,memory,swaps,context switches,source for query n; 

查看后关闭Profiling:set profiling=0;

以下为profile详细信息意义:

starting:开始
checking permissions:检查权限
Opening tables:打开表
init : 初始化
System lock :系统锁
optimizing : 优化
statistics : 统计
preparing :准备,在查询优化过程中出现这个状态。

Creating tmp table :创建临时表在内存或者磁盘上。如果这个表创建在内存上,之后被转换到磁盘上,这个状态在运行Copying to tmp table on disk 的时候保持。
executing :执行语句

Copying to tmp table:建立一个临时表来满足查询要求,产生巨大的恐怖的i/o压力。发生在索引及现有结构无法涵盖查询条件时。
Sending data :从物理磁盘获取数据。期间会执行大量的磁盘访问(读操作),这个状态在一个指定查询的生命周期中经常是耗时最长的。如果大量相似的SQL语句出现在show proesslist列表中,并且都处于sending data状态,优化查询索引,记住用影响结果集的思路去思考。
Sorting result :排序结果。结果集过大,排序条件没有索引化,需要在内存里排序,甚至需要创建临时结构排序。

Creating sort index:使用内部临时表处理一个SELECT 操作。

converting HEAP to MyISAM:将一个内部临时表转换为磁盘上的MyISAM表。
end :结束
query end :查询结束

logging slow query:将语句写入慢查询日志。
closing tables : 关闭表 /去除TMP 表(removing tmp table)
freeing items : 释放。理论上不会出现很多。偶尔出现无碍。如果大量出现,内存,硬盘可能已经出现问题。比如硬盘满或损坏。另外 i/o压力过大时,也可能出现Free items执行时间较长的情况。
cleaning up :清理。正准备释放内存和重置某些状态变量。

三、索引优化

1、MySQL不会使用索引的情况:非独立的列

“独立的列”是指索引列不能是表达式的一部分,也不能是函数的参数。比如:

    select * from where id + 1 = 5

我们很容易看出其等价于 id = 4,但是MySQL无法自动解析这个表达式,使用函数是同样的道理。

2、前缀索引

如果列很长,通常可以索引开始的部分字符,这样可以有效节约索引空间,从而提高索引效率。

3、多列索引和索引顺序


在多数情况下,在多个列上建立独立的索引并不能提高查询性能。理由非常简单,MySQL不知道选择哪个索引的查询效率更好,所以在老版本,比如MySQL5.0之前就会随便选择一个列的索引,而新的版本会采用合并索引的策略。举个简单的例子,在一张电影演员表中,在actor_id和film_id两个列上都建立了独立的索引,然后有如下查询:select film_id,actor_id from film_actor where actor_id = 1 or film_id = 1
老版本的MySQL会随机选择一个索引,但新版本做如下的优化:
select film_id,actor_id from film_actor where actor_id = 1  union all  select film_id,actor_id from film_actor where film_id = 1 and actor_id <> 1

当出现多个索引做相交操作时(多个AND条件),通常来说一个包含所有相关列的索引要优于多个独立索引。
当出现多个索引做联合操作时(多个OR条件),对结果集的合并、排序等操作需要耗费大量的CPU和内存资源,特别是当其中的某些索引的选择性不高,需要返回合并大量数据时,查询成本更高。所以这种情况下还不如走全表扫描。

因此explain时如果发现有索引合并(Extra字段出现Using union),应该好好检查一下查询和表结构是不是已经是最优的,如果查询和表都没有问题,那只能说明索引建的非常糟糕,应当慎重考虑索引是否合适,有可能一个包含所有相关列的多列索引更适合。

有多列索引时,索引的顺序对于查询是至关重要的,很明显应该把选择性更高的字段放到索引的前面,这样通过第一个字段就可以过滤掉大多数不符合条件的数据。

索引选择性是指不重复的索引值和数据表的总记录数的比值,选择性越高查询效率越高,因为选择性越高的索引可以让MySQL在查询时过滤掉更多的行。唯一索引的选择性是1,这是最好的索引选择性,性能也是最好的。

理解索引选择性的概念后,就不难确定哪个字段的选择性较高了,查一下就知道了,比如:

select * from payment where staff_id = 2 and customer_id = 584

是应该创建(staff_id,customer_id)的索引还是应该颠倒一下顺序?执行下面的查询,哪个字段的选择性更接近1就把哪个字段索引前面就好。select count(distinct staff_id)/count(*) as staff_id_selectivity,  

count(distinct customer_id)/count(*) as customer_id_selectivity,  

count(*) from payment

多数情况下使用这个原则没有任何问题,但仍然注意你的数据中是否存在一些特殊情况。

举个简单的例子,比如要查询某个用户组下有过交易的用户信息:select user_id from trade where user_group_id = 1 and trade_amount > 0

MySQL为这个查询选择了索引(user_group_id,trade_amount),如果不考虑特殊情况,这看起来没有任何问题,但实际情况是这张表的大多数数据都是从老系统中迁移过来的,由于新老系统的数据不兼容,所以就给老系统迁移过来的数据赋予了一个默认的用户组。这种情况下,通过索引扫描的行数跟全表扫描基本没什么区别,索引也就起不到任何作用。

推广开来说,经验法则和推论在多数情况下是有用的,可以指导我们开发和设计,但实际情况往往会更复杂,实际业务场景下的某些特殊情况可能会摧毁你的整个设计。

4、避免多个范围条件

实际开发中,我们会经常使用多个范围条件,比如想查询某个时间段内登录过的用户:

select user.* from user where login_time > '2017-04-01' and age between 18 and 30;

这个查询有一个问题:它有两个范围条件,login_time列和age列,MySQL可以使用login_time列的索引或者age列的索引,但无法同时使用它们。

5、覆盖索引

SELECT的数据列只用从索引中就能够取到, 不必读取数据行, MySQL可以利用索引返回SELECT列表中的字段, 而不必根据索引再次读取数据文件, 换句话说, 查询列要被所建的索引覆盖。
索引是高效找到行的一个方法, 但是一般数据库也能使用索引找到一个列的数据, 因此它不必读取整个行。毕竟索引的叶子节点存储了它们索引的数据; 当能通过读取索引就可以得到想要的数据, 那就不需要读取行了。一个索引包含了(或覆盖了)满足查询结果的数据就叫做覆盖索引。扫描索引会带来许多好处:

    索引条目远小于数据行大小,如果只读取索引,极大减少数据访问量
    索引是有按照列值顺序存储的,对于I/O密集型的范围查询要比随机从磁盘读取每一行数据的IO要少的多

6、使用索引扫描来排序

MySQL有两种方式可以生产有序的结果集,其一是对结果集进行排序的操作,其二是按照索引顺序扫描得出的结果自然是有序的。如果explain的结果中type列的值为index表示使用了索引扫描来做排序。

扫描索引本身很快,因为只需要从一条索引记录移动到相邻的下一条记录。但如果索引本身不能覆盖所有需要查询的列,那么就不得不每扫描一条索引记录就回表查询一次对应的行。这个读取操作基本上是随机I/O,因此按照索引顺序读取数据的速度通常要比顺序地全表扫描要慢。

在设计索引时,如果一个索引既能够满足排序,又满足查询,是最好的。

只有当索引的列顺序和ORDER BY子句的顺序完全一致,并且所有列的排序方向也一样时,才能够使用索引来对结果做排序。如果查询需要关联多张表,则只有ORDER BY子句引用的字段全部为第一张表时,才能使用索引做排序。ORDER BY子句和查询的限制是一样的,都要满足最左前缀的要求(有一种情况例外,就是最左的列被指定为常数,下面是一个简单的示例),其他情况下都需要执行排序操作,而无法利用索引排序。

// 最左列为常数,索引:(date,staff_id,customer_id)  select  staff_id,customer_id from demo where date = '2015-06-01' order by staff_id,customer_id  

7、冗余和重复索引

冗余索引是指在相同的列上按照相同的顺序创建的相同类型的索引,应当尽量避免这种索引,发现后立即删除。比如有一个索引(A,B),再创建索引(A)就是冗余索引。冗余索引经常发生在为表添加新索引时,比如有人新建了索引(A,B),但这个索引不是扩展已有的索引(A)。

大多数情况下都应该尽量扩展已有的索引而不是创建新索引。但有极少情况下出现性能方面的考虑需要冗余索引,比如扩展已有索引而导致其变得过大,从而影响到其他使用该索引的查询。

8、删除长期未使用的索引

定期删除一些长时间未使用过的索引是一个非常好的习惯。

关于索引这个话题打算就此打住,最后要说一句,索引并不总是最好的工具,只有当索引帮助提高查询速度带来的好处大于其带来的额外工作时,索引才是有效的。对于非常小的表,简单的全表扫描更高效。对于中到大型的表,索引就非常有效。对于超大型的表,建立和维护索引的代价随之增长,这时候其他技术也许更有效,比如分区表。最后的最后,explain后再提测是一种美德。

四、特定类型查询优化

1、优化COUNT()查询

COUNT()可能是被大家误解最多的函数了,它有两种不同的作用,其一是统计某个列值的数量,其二是统计行数。统计列值时,要求列值是非空的,它不会统计NULL。如果确认括号中的表达式不可能为空时,实际上就是在统计行数。最简单的就是当使用COUNT(*)时,并不是我们所想象的那样扩展成所有的列,实际上,它会忽略所有的列而直接统计行数。

我们最常见的误解也就在这儿,在括号内指定了一列却希望统计结果是行数,而且还常常误以为前者的性能会更好。但实际并非这样,如果要统计行数,直接使用COUNT(*),意义清晰,且性能更好。

有时候某些业务场景并不需要完全精确的COUNT值,可以用近似值来代替,EXPLAIN出来的行数就是一个不错的近似值,而且执行EXPLAIN并不需要真正地去执行查询,所以成本非常低。通常来说,执行COUNT()都需要扫描大量的行才能获取到精确的数据,因此很难优化,MySQL层面还能做得也就只有覆盖索引了。如果不还能解决问题,只有从架构层面解决了,比如添加汇总表,或者使用redis这样的外部缓存系统。

2、优化关联查询

在大数据场景下,表与表之间通过一个冗余字段来关联,要比直接使用JOIN有更好的性能。如果确实需要使用关联查询的情况下,需要特别注意的是:

    确保ON和USING字句中的列上有索引。在创建索引的时候就要考虑到关联的顺序。当表A和表B用列c关联的时候,如果优化器关联的顺序是A、B,那么就不需要在A表的对应列上创建索引。没有用到的索引会带来额外的负担,一般来说,除非有其他理由,只需要在关联顺序中的第二张表的相应列上创建索引(具体原因下文分析)。
    确保任何的GROUP BY和ORDER BY中的表达式只涉及到一个表中的列,这样MySQL才有可能使用索引来优化。

要理解优化关联查询的第一个技巧,就需要理解MySQL是如何执行关联查询的。当前MySQL关联执行的策略非常简单,它对任何的关联都执行嵌套循环关联操作,即先在一个表中循环取出单条数据,然后在嵌套循环到下一个表中寻找匹配的行,依次下去,直到找到所有表中匹配的行为为止。然后根据各个表匹配的行,返回查询中需要的各个列。

太抽象了?以上面的示例来说明,比如有这样的一个查询:SELECT A.xx,B.yy  FROM A INNER JOIN B USING(c)  WHERE A.xx IN (5,6)  

假设MySQL按照查询中的关联顺序A、B来进行关联操作,那么可以用下面的伪代码表示MySQL如何完成这个查询:

outer_iterator = SELECT A.xx,A.c FROM A WHERE A.xx IN (5,6);  

outer_row = outer_iterator.next;  

while(outer_row) {  

   inner_iterator = SELECT B.yy FROM B WHERE B.c = outer_row.c;  

  inner_row = inner_iterator.next;  

  while(inner_row) {

    output[inner_row.yy,outer_row.xx];  

    inner_row = inner_iterator.next;  

   }

  outer_row = outer_iterator.next;  

}  

可以看到,最外层的查询是根据A.xx列来查询的,A.c上如果有索引的话,整个关联查询也不会使用。再看内层的查询,很明显B.c上如果有索引的话,能够加速查询,因此只需要在关联顺序中的第二张表的相应列上创建索引即可。

3、优化LIMIT分页

当需要分页操作时,通常会使用LIMIT加上偏移量的办法实现,同时加上合适的ORDER BY字句。如果有对应的索引,通常效率会不错,否则,MySQL需要做大量的文件排序操作。

一个常见的问题是当偏移量非常大的时候,比如:LIMIT 10000 20这样的查询,MySQL需要查询10020条记录然后只返回20条记录,前面的10000条都将被抛弃,这样的代价非常高。

优化这种查询一个最简单的办法就是尽可能的使用覆盖索引扫描,而不是查询所有的列。然后根据需要做一次关联查询再返回所有的列。对于偏移量很大时,这样做的效率会提升非常大。考虑下面的查询:SELECT film_id,description FROM film ORDER BY title LIMIT 50,5;

如果这张表非常大,那么这个查询最好改成下面的样子:SELECT film.film_id,film.description  FROM film INNER JOIN (  SELECT film_id FROM film ORDER BY title LIMIT 50,5  AS tmp USING(film_id);  

这里的延迟关联将大大提升查询效率,让MySQL扫描尽可能少的页面,获取需要访问的记录后在根据关联列回原表查询所需要的列。

有时候如果可以使用书签记录上次取数据的位置,那么下次就可以直接从该书签记录的位置开始扫描,这样就可以避免使用OFFSET,

比如下面的查询:SELECT id FROM t LIMIT 10000, 10;

改为:SELECT id FROM t WHERE id > 10000 LIMIT 10;

其他优化的办法还包括使用预先计算的汇总表,或者关联到一个冗余表,冗余表中只包含主键列和需要做排序的列。

4、优化UNION

MySQL处理UNION的策略是先创建临时表,然后再把各个查询结果插入到临时表中,最后再来做查询。因此很多优化策略在UNION查询中都没有办法很好的时候。经常需要手动将WHERE、LIMIT、ORDER BY等字句“下推”到各个子查询中,以便优化器可以充分利用这些条件先优化。

除非确实需要服务器去重,否则就一定要使用UNION ALL,如果没有ALL关键字,MySQL会给临时表加上DISTINCT选项,这会导致整个临时表的数据做唯一性检查,这样做的代价非常高。当然即使使用ALL关键字,MySQL总是将结果放入临时表,然后再读出,再返回给客户端。虽然很多时候没有这个必要,比如有时候可以直接把每个子查询的结果返回给客户端。

5、优化GROUP BY和DISTINCT

在很多情况下,MySql对这两种方式的优化方式基本都是一样的。实际上,优化过程要求他们可以互相转化。通常来说,索引是优化它们的一种重要的手段。

当不能使用索引的时候,MySql有两种优化GROUP BY的策略:使用临时表或文件排序进行分组。任何一种方式对于特定的查询都有可能是高效的。可以使用SQL_SMALL_RESULT强制MySql选择临时表,或者使用SQL_BIG_RESULT强制它使用文件排序。

五、Scheme设计与数据类型优化

选择数据类型只要遵循小而简单的原则就好,越小的数据类型通常会更快,占用更少的磁盘、内存,处理时需要的CPU周期也更少。越简单的数据类型在计算时需要更少的CPU周期,比如,整型就比字符操作代价低,因而会使用整型来存储ip地址,使用DATETIME来存储时间,而不是使用字符串。

这里总结几个可能容易理解错误的技巧:

1、通常来说把可为NULL的列改为NOT NULL不会对性能提升有多少帮助,只是如果计划在列上创建索引,就应该将该列设置为NOT NULL。

2、对整数类型指定宽度,比如INT(11),没有任何卵用。INT使用32位(4个字节)存储空间,那么它的表示范围已经确定,所以INT(1)和INT(20)对于存储和计算是相同的。

3、UNSIGNED表示不允许负值,大致可以使正数的上限提高一倍。比如TINYINT存储范围是-128 ~ 127,而UNSIGNED TINYINT存储的范围却是0 - 255。

4、通常来讲,没有太大的必要使用DECIMAL数据类型。即使是在需要存储财务数据时,仍然可以使用BIGINT。比如需要精确到万分之一,那么可以将数据乘以一百万然后使用BIGINT存储。这样可以避免浮点数计算不准确和DECIMAL精确计算代价高的问题。

5、TIMESTAMP使用4个字节存储空间,DATETIME使用8个字节存储空间。因而,TIMESTAMP只能表示1970 - 2038年,比DATETIME表示的范围小得多,而且TIMESTAMP的值因时区不同而不同。

6、大多数情况下没有使用枚举类型的必要,其中一个缺点是枚举的字符串列表是固定的,添加和删除字符串(枚举选项)必须使用ALTER TABLE(如果只是在列表末尾追加元素,不需要重建表)。

7、schema的列不要太多。原因是存储引擎的API工作时需要在服务器层和存储引擎层之间通过行缓冲格式拷贝数据,然后在服务器层将缓冲内容解码成各个列,这个转换过程的代价是非常高的。如果列太多而实际使用的列又很少的话,有可能会导致CPU占用过高。

8、大表ALTER TABLE非常耗时,MySQL执行大部分修改表结果操作的方法是用新的结构创建一个张空表,从旧表中查出所有的数据插入新表,然后再删除旧表。尤其当内存不足而表又很大,而且还有很大索引的情况下,耗时更久。当然有一些技巧可以解决这个问题,有兴趣可自行查阅。

六、分区表优化

1、分表的条件

合理的使用索引可以极大提升MySQL的查询性能,但如果单表数据量达到一定的程度,索引就无法起作用,因为在数据量超大的情况下,除非覆盖索引,因回表查询会产生大量的随机I/O,数据库的响应时间可能会达到不可接受的程度。而且索引维护(磁盘空间、I/O操作)的代价也会非常大。

因此,当单表数据量达到一定程度时(在MySQL4.x时代,MyISAM存储引擎业内公认的性能拐点是500W行,MySQL5.x时代的性能拐点则为1KW ~ 2KW行级别,具体需根据实际情况测试),为了提升性能,最为常用的方法就是分表。分表的策略可以是垂直拆分(比如:不同订单状态的订单拆分到不同的表),也可以是水平拆分(比如:按月将订单拆分到不同表)。
2、产生的问题

在业务层分表大大增加了编码的复杂程度,而且处理数据库的相关代码会大量散落在应用各处,维护困难。那是否可以将分表的逻辑抽象出来,统一处理,这样业务层就不用关心底层是否分表,只需要专注在业务即可。答案当然是肯定的,目前有非常多的数据库中间件都可以屏蔽分表后的细节,让业务层像查询单表一样查询分表后的数据。如果再将抽象的逻辑下移到数据库的服务层,就是分区表。
 
3、分区表的概念
 
分区可以简单的理解,是MySQL底层帮我们实现分表,分区表是一个独立的逻辑表,底层由多个物理子表组成。存储引擎管理分区的各个底层表和管理普通表一样(所有底层表必须使用相同的存储引擎),分区表的索引也是在各个底层表上各自加上一个完全相同的索引。从存储引擎的角度来看,底层表和普通表没有任何不同,存储引擎也无须知道。在执行查询时,优化器会根据分区的定义过滤那些没有我们需要数据的分区,这样查询就无需扫描所有分区,只需要查找包含需要数据的分区就可以了。

4、例子

更好的理解分区表,我们从一个示例入手:一张订单表,数据量大概有10TB,如何设计才能使性能达到最优?

首先可以肯定的是,因为数据量巨大,肯定不能走全表扫描。使用索引的话,你会发现数据并不是按照想要的方式聚集,而且会产生大量的碎片,最终会导致一个查询产生成千上万的随机I/O,应用随之僵死。所以需要选择一些更粗粒度并且消耗更少的方式来检索数据。比如先根据索引找到一大块数据,然后再在这块数据上顺序扫描。

5、分区的原理

以代价非常小的方式定位到需要的数据在哪一片“区域”,在这片“区域”中,你可以顺序扫描,可以建索引,还可以将数据都缓存在内存中。因为分区无须额外的数据结构记录每个分区有哪些数据,所以其代价非常低。只需要一个简单的表达式就可以表达每个分区存放的是什么数据。

6、分区表上的各种操作逻辑
  • SELECT:当查询一个分区表时,分区层先打开并锁住所有的底层表,优化器先判断是否可以过滤部分分区,然后在调用对应的存储引擎接口访问各个分区的数据。
  • INSERT:当插入一条记录时,分区层先打开并锁住所有的底层表,然后确定哪个分区接收这条记录,再将记录写入对应的底层表,DELETE操作与其类似。
  • UPDATE:当更新一条数据时,分区层先打开并锁住所有的底层表,然后确定数据对应的分区,然后取出数据并更新,再判断更新后的数据应该存放到哪个分区,最后对底层表进行写入操作,并对原数据所在的底层表进行删除操作。
七、系统配置优化

1、max_connections

MySQL的最大连接数,增加该值增加mysqld 要求的文件描述符的数量。如果服务器的并发连接请求量比较大,建议调高此值,以增加并行连接数量,当然这建立在机器能支撑的情况下,因为如果连接数越多,介于MySQL会为每个连接提供连接缓冲区,就会开销越多的内存,所以要适当调整该值,不能盲目提高设值。

数值过小会经常出现ERROR 1040: Too many connections错误,可以过’conn%’通配符查看当前状态的连接数量,以定夺该值的大小。

show variables like ‘max_connections’ 最大连接数

show  status like ‘max_used_connections’响应的连接数

max_used_connections / max_connections * 100% (理想值≈ 85%),如果max_used_connections跟max_connections相同,就是max_connections设置过低或者超过服务器负载上限了,低于10%则设置过大。

2、back_log

MySQL能暂存的连接数量。当主要MySQL线程在一个很短时间内得到非常多的连接请求,这就起作用。
如果MySQL的连接数据达到max_connections时,新来的请求将会被存在堆栈中,以等待某一连接释放资源,该堆栈的数量即back_log,如果等待连接的数量超过back_log,将不被授予连接资源。

back_log值指出在MySQL暂时停止回答新请求之前的短时间内有多少个请求可以被存在堆栈中。只有如果期望在一个短时间内有很多连接,你需要增加它,换句话说,这值对到来的TCP/IP连接的侦听队列的大小。

当观察你主机进程列表(mysql> show full processlist),发现大量264084 | unauthenticated user | xxx.xxx.xxx.xxx | NULL | Connect | NULL | login | NULL 的待连接进程时,就要加大back_log 的值了。

默认数值是50,可调优为128,对于Linux系统设置范围为小于512的整数。

3、interactive_timeout

一个交互连接在被服务器在关闭前等待行动的秒数。一个交互的客户被定义为对mysql_real_connect()使用CLIENT_INTERACTIVE 选项的客户。默认数值是28800,可调优为7200。

4、key_buffer_size

key_buffer_size指定索引缓冲区的大小,它决定索引处理的速度,尤其是索引读的速度。通过检查状态值Key_read_requests和Key_reads,可以知道key_buffer_size设置是否合理。

比例key_reads / key_read_requests应该尽可能的低,至少是1:100,1:1000更好(上述状态值可以使用SHOW STATUS LIKE ‘key_read%’获得)。

key_buffer_size只对MyISAM表起作用。即使你不使用MyISAM表,但是内部的临时磁盘表是MyISAM表,也要使用该值。可以使用检查状态值created_tmp_disk_tables得知详情。

show variables like ‘key_buffer_size‘;

 show global status like ‘key_read%‘;

计算索引未命中缓存的概率:key_cache_miss_rate =Key_reads / Key_read_requests * 100%,设置在1/1000左右较好。

默认配置数值是8388600(8M),主机有4GB内存,可以调优值为268435456(256MB)。

5、query_cache_size

使用查询缓冲,MySQL将查询结果存放在缓冲区中,今后对于同样的SELECT语句(区分大小写),将直接从缓冲区中读取结果。

通过检查状态值Qcache_*,可以知道query_cache_size设置是否合理(上述状态值可以使用SHOW STATUS LIKE ‘Qcache%’获得)。

如果Qcache_lowmem_prunes的值非常大,则表明经常出现缓冲不够的情况,如果Qcache_hits的值也非常大,则表明查询缓冲使用非常频繁,此时需要增加缓冲大小;

如果Qcache_hits的值不大,则表明你的查询重复率很低,这种情况下使用查询缓冲反而会影响效率,那么可以考虑不用查询缓冲。

此外,在SELECT语句中加入SQL_NO_CACHE可以明确表示不使用查询缓冲。

6、record_buffer_size

每个进行一个顺序扫描的线程为其扫描的每张表分配这个大小的一个缓冲区。如果你做很多顺序扫描,你可能想要增加该值。

默认数值是131072(128K),可改为16773120 (16M)

7、read_rnd_buffer_size

随机读缓冲区大小。当按任意顺序读取行时(例如,按照排序顺序),将分配一个随机读缓存区。进行排序查询时,MySQL会首先扫描一遍该缓冲,以避免磁盘搜索,提高查询速度。

如果需要排序大量数据,可适当调高该值。但MySQL会为每个客户连接发放该缓冲空间,所以应尽量适当设置该值,以避免内存开销过大。一般可设置为16M。

8、sort_buffer_size

每个需要进行排序的线程分配该大小的一个缓冲区。增加这值加速ORDER BY或GROUP BY操作。默认数值是2097144(2M),可改为16777208 (16M)。

9、join_buffer_size

联合查询操作所能使用的缓冲区大小

record_buffer_size,read_rnd_buffer_size,sort_buffer_size,join_buffer_size为每个线程独占,也就是说,如果有100个线程连接,则占用为16M*100

10、table_cache

表高速缓存的大小。每当MySQL访问一个表时,如果在表缓冲区中还有空间,该表就被打开并放入其中,这样可以更快地访问表内容。

通过检查峰值时间的状态值Open_tables和Opened_tables,可以决定是否需要增加table_cache的值。

如果你发现open_tables等于table_cache,并且opened_tables在不断增长,那么你就需要增加table_cache的值了(上述状态值可以使用SHOW STATUS LIKE ‘Open%tables’获得)。

注意,不能盲目地把table_cache设置成很大的值。如果设置得太高,可能会造成文件描述符不足,从而造成性能不稳定或者连接失败。

1G内存机器,推荐值是128-256。内存在4GB左右的服务器该参数可设置为256M或384M。

11、max_heap_table_size

用户可以创建的内存表(memory table)的大小。这个值用来计算内存表的最大行数值。这个变量支持动态改变,即set @max_heap_table_size=#

这个变量和tmp_table_size一起限制了内部内存表的大小。如果某个内部heap(堆积)表大小超过tmp_table_size,MySQL可以根据需要自动将内存中的heap表改为基于硬盘的MyISAM表。

12、tmp_table_size

通过设置tmp_table_size选项来增加一张临时表的大小,例如做高级GROUP BY操作生成的临时表。如果调高该值,MySQL同时将增加heap表的大小,可达到提高联接查询速度的效果。
建议尽量优化查询,要确保查询过程中生成的临时表在内存中,避免临时表过大导致生成基于硬盘的MyISAM表。

13、thread_cache_size

可以复用的保存在缓存中的线程的数量。如果有,新的线程从缓存中取得,当断开连接的时候如果有空间,客户的线置在缓存中。如果有很多新的线程,为了提高性能可以这个变量值。

通过比较 Connections和Threads_created状态的变量,可以看到这个变量的作用。默认值为110,可调优为80。

14、thread_concurrency

推荐设置为服务器 CPU核数的2倍,例如双核的CPU, 那么thread_concurrency的应该为4;2个双核的cpu, thread_concurrency的值应为8。默认为8

15、wait_timeout

指定一个请求的最大连接时间,对于4GB左右内存的服务器可以设置为5-10。

原文地址:https://www.cnblogs.com/wd326150354/p/11127179.html