MySQL 瓶颈及应对措施和实践

大纲 

序言

一  MySQL 瓶颈及应对措施

二  mysql  具体优化参数

三  mysql 优化实践

四 总结

参考文献

 

序言 

1  首先给出性能瓶颈定位Show命令

我们可以通过show命令查看MySQL状态及变量,找到系统的瓶颈:

Mysql> show status ——显示状态信息(扩展show status like ‘XXX’)

Mysql> show variables ——显示系统变量(扩展show variables like ‘XXX’)

Mysql> show innodb status ——显示InnoDB存储引擎的状态

Mysql> show processlist ——查看当前SQL执行,包括执行状态、是否锁表等

Shell> mysqladmin variables -u username -p password——显示系统变量

Shell> mysqladmin extended-status -u username -p password——显示状态信息

查看状态变量及帮助:

Shell> mysqld –verbose –help 

2  开启 慢查询日志 定位慢 sql 有那些

3  其实最好的办法就是感觉响应慢老板接受不了,必须优化。
 

一 MySQL 瓶颈及应对措施

MySQL 是存在瓶颈的。 当 MySQL 单表数据量达到千万级别以上时,无论如何对 MySQL 进行优化,查询如何简单,MySQL 的性能都会显著降低。 采取措施:

1)增加 MySQL 配置中的 buffer 和 Cache 的数值,增加服务器 CPU 数量和内存的大小,这样能很大程度上应对 MySQL 的性能瓶颈。 
    性能优化中,效果最显著、成本最低的当属硬件和服务器优化,所以这是应该首先考虑的。 
2)使用第三方引擎或衍生版本。
    如 Percona 在功能和性能上较 MySQL 有着显著的提升;由 MySQL 创始人开发的免费 MariaDB 在 InnoDB 引擎上的性能也比 MySQL优秀;
    据官网借号,另一款改进的产品 TokuDB,性能是 MySQL 的 10 倍以上。以上这些衍生版或改进版的 MySQL 主要都是针对 MySQL 的 InnoDB
    引擎。 InnoDB 每次提交事物时,为了保证数据已经持久化到磁盘(Durable),需要调用一次 fsync 告知文件系统,将可能在缓存中的数据
    刷新到次哦按。而 fsync 操作本身是非常『昂贵』的(消耗较多的 I/O 资源,响应较慢),如果每次事物提交都单独做 fsync 操作,那么这里
    将是系统 TPS 的一个瓶颈,所以就有了 Group Commit 的概念。 MySQL 5.0 后,处于分布式架构的考虑,为了保证 InnoDB 内部 Commit 
    和 MySQL 日志的顺序一致,InnoDB 被迫放弃支持 Group Commit。之后,就一直没有好的解决方案了。直到出现 MariaDB,才比较完美的解决
    了这个问题。
3)迁移到其它数据库。
    如开源的 Post供热SQL 和商业的 Oracle。 与 Oracle 和 PostgreSQL 相比, MySQL 属于线程模式,并且采用了插件引擎的架构。这种实现
    的确有自己的优势:轻巧快速、系统资源消耗小、支持更多并发连接,但进程模式能更充分的应用 CPU 资源,在应付复杂业务查询上更有优势。比如,
    在常规优化的前提下,Oracle 的但别性能瓶颈经验值在 2 亿数据量的级别,远远优于 MySQL。 不仅如此,在关联查询和内置函数等功能上,
    Oracle 都是完胜 MySQL 数据库的。 再比如,PostgreSQL 数据库相比 MySQL,拥有更强大的查询优化器,不会频繁重建索引,支持物化视图等
    优势。当然,迁移到其他数据库还要看应用的类型,比如是偏向 OLTP( On-Line Transaction Processing,联机事物处理)的应用还是偏向
    OLAP(On-Line Analytical Processing,联机分析处理)应用。 
4)对数据库进行分区、分表,减少单表体积。
5)使用NoSQL 等辅助解决方案,如 Memcached、Redis。
6)使用中间件做数据拆分和分布式部署,这方面的典型案例有阿里巴巴对外开源的数据库中间件 Cobar。
7)使用数据库连接池技术。 
    在第 3 点,我们讲过,MySQL 是线程模式,可以支持更多的并发连接数。MySQL 能支持远比 Oracle 和 PostgreSQL 更多的连接。所以 Oracle
    和 PostgreSQL 应用中通常用数据库连接池技术来复用连接。那么 MySQL 为什么还需要用这些数据库应用中常见的数据库连接池技术呢? 原因在于
    MySQL 的所机制还不够完善,同时程序中的一些问题都有可能导致 MySQL 数据库连接阻塞,在并发大的情况下,就会浪费很多连接资源和反复连接
    的消耗。使用数据库连接池,让连接进行排队和复用,一定程度上可以缓解高并发下的连接压力。
MySQL 瓶颈是真实存在的,但是不少大型互联网公司仍然在使用 MySQL,并且能使用的很好,这一方面是因为这些公司的技术实力足以对 MySQL 进行二次开发和改进,另一方面则得益于其成熟的架构。所以,一个工具能不能用好,人的因素占很大的比重。
 

二  mysql  具体优化实践

先普及以下 GB和Gb区别

0.1342 GB (gigabytes)
1.074 Gb (gigabits)
内存一般不会说Gb,只有GB。
这里,其实就是著名的大B和小b的区别。
一般,通讯上说的,是小b,即bit,意思是位。
8个位是一个字节,叫Byte,就是大B。
我们一般说的宽带,4M,就是4Mb,要除8,才是电脑上一般用的B,下载速度也就是400KB左右。
内存的容量不说Gb,带宽才会涉及到Gb。
基本上,b就是个传输速率才会涉及到的概念。

总结 容量大小度量GB,通讯传输速率大小Gb;

1  transaction_isolation


解读:事务隔离级别,Oracle, SQL Server等商业知名数据库默认级别为READ-COMMITTED,而MySQL默认为REPEATABLE-READ,它利用自身独有的Gap Lock解决了"幻读"。但也因为Gap Lock的缘故,相比于READ-COMMITTED级别的Record Lock,REPEATABLE-READ的事务并发插入性能受到很大的限制。
设置:隔离级别的选择取决于实际的业务需求(安全与性能的权衡),如果不是金融、电信等事务级别要求很高的业务,完全可以设置成transaction_isolation=READ-COMMITTED。

2 innodb_buffer_pool_size


解读:InnoDB缓冲池大小,它决定了MySQL可以在内存中缓存多少数据和索引,而不是每次都从磁盘上读取。我们都知道Redis的读写很快,其最重要的原因是它的所有数据都缓存在内存中。试想一下如果innodb_buffer_pool_size足够大,MySQL所有表数据和索引都能被缓存,那将是一种什么体验?
设置:如果是专用的MySQL服务器,一般设置为操作系统内存的75%左右,但至少保留2G内存用于操作系统维护和MySQL异常事件处理。

3 innodb_buffer_pool_instances


解读:InnoDB缓冲池实例个数,InnoDB缓冲池是通过一整个链表的方式来管理页面(段、簇、页)的,由于互斥锁的存在(保护链表中的页面),高并发事务下,页面的读取和写入就需要锁的竞争和等待。通过设置innodb_buffer_pool_instances,将一整个链表划分为多个,每个缓冲池实例管理自己的页面和互斥,从而提高效率。
设置:如果缓冲池比较大(8G以上),可以按照innodb_buffer_pool_size / innodb_buffer_pool_instances = 1G进行设置,但如果缓冲池特别大(32G以上),可以按照每个实例2~3G进行划分,实例数不是越多越好,多实例代表多线程,线程的开销(CPU、MEM)也得考虑。

4 innodb_log_file_size


解读:InnoDB日志文件大小(Redo Log),它将事务对InnoDB表的修改记录保存在ib_logfile0、ib_logfile1中。innodb_log_file_size越大,缓冲池中的脏数据需要检查点(checkpoint)进行刷盘的频率就越少,从而减少磁盘IO来降低高并发负载造成的峰值。但日志文件也不是越大约好,由于内存中脏数据刷盘的频率减少,一旦数据库发生异常崩溃,数据库重启时从innodb_log_file中读取数据进行恢复的时间越长。

设置:一般选取业务高峰期一个小时的日志量作为标准,计算过程如下:

# 命令
mysql -uroot -h172.26.96.146 -p -e 'show engine innodb status\G'|grep 'Log sequence number' \
&& sleep 60 \
&& mysql -uroot  -h172.26.96.146 -p -e 'show engine innodb status\G'|grep 'Log sequence number'
# 输出
Log sequence number 149949388055
Log sequence number 149959622102
 
# 计算
( 149959622102 - 149949388055 ) / 1024 / 1024 = 10M
10 / 60 * 3600 = 600M
600 / 2 = 300M
 
# 解释
Log sequence number代表InnoDB运行至今写入日志的总字节数,两次打印之间线程休眠60秒
得到一分钟之内事务日志记录的总量10M,再转换成一个小时的总量600M
因为`ib_logfile0、ib_logfile1`两个文件循环写入,一个文件为300M
最终,innodb_log_file_size=300M

5 innodb_flush_log_at_trx_commit


解读:InnoDB事务日志刷盘时机

当0时,事务提交到日志缓冲区,后台Write线程每隔一秒将缓冲区的日志写入系统缓冲区,实际写入物理日志文件的时机取决于操作系统。

当1时,事务提交到日志缓冲区,Master线程同步将缓冲区的日志直接写入物理日志文件,这完全符合InnoDB ACID事务标准,数据不会丢失。

当2时,事务提交到系统缓冲区,Master线程每隔一秒将系统缓冲区的日志写入物理日志文件。
设置:安全1 > 2 > 0,速度0 > 2 > 1,根据实际业务需求(安全与速度权衡)选择合理的刷盘时机。


6 innodb_file_per_table


解读:InnoDB独立表空间,innodb_file_per_table = ON表示每张表在独立的物理文件中(.ibd)存储数据和索引,innodb_file_per_table = OFF表示所有表都共享表空间即一个物理文件(ibdata1)。如果通过drop/truncate table操作,独立表空间的物理存储会立即被回收(删除/初始化),而共享表空间不会被回收且只会一直增大。
设置:innodb_file_per_table = ON,但需要注意的是,独立表空间只存储数据和索引,如回滚日志缓冲(Undo Log)、插入索引缓冲(Insert Buffer)、二次写缓冲(Doublewrite Buffer)等还是放在共享表空间。

query_cache_size
解读:查询缓存大小,它是为了在追踪表的数据未发生变化时,本次查询命中之前的查询语句,从而跳过解析、优化、执行阶段,直接返回缓冲池中的数据。但实际在OLTP系统中,极少能命中查询缓存(前提是数据库中的数据变化频率很小),因为一旦数据有变则缓存失效。且因为查询缓存会跟踪所有表的变化,它也会成为整个数据库的瓶颈(资源竞争点)。
设置:query_cache_size = 0,同时配合设置query_cache_type = 0,MySQL5.7.20以上、MySQL8.0会直接弃用所有查询缓存配置项。

max_connections
解读:最大连接数,当max_connections设置太小时(默认151),MySQL可能会报错Too many connections。当max_connections设置太大时(1000以上),操作系统可能忙于线程间的切换而失去响应。
设置:每个连接都会消耗一定内存,计算过程如下:

 

三  接着说mysql 5.7 性能优化实战

调整Innodb_Buffer_Pool_size大小必须重启mysql进程才可以生效,如今在MySQL5.7里,可以直接动态设置,方便了很多。

这个功能应用的场景:

  • A 全部数据查询数据量大,需要调优。
  • B 机器增加内存,DBA粗心大意忘记调大Innodb_Buffer_Pool_size了。
  • C 工作交接,新来的DBA发现前任DBA设置的Innodb_Buffer_Pool_size不合理。

需要注意的地方,在调整Buffer_Pool期间,用户的请求将会阻塞,直到调整完毕,所以请勿在白天调整,在凌晨3-4点低峰期调整。

调整时,内部把数据页移动到一个新的位置,单位是块。如果想增加移动的速度,需要调整innodb_buffer_pool_chunk_size参数的大小,默认是128M。

例(把BP 128M增大为384M):

mysql> SELECT @@innodb_buffer_pool_size;

+---------------------------+

| @@innodb_buffer_pool_size |

+---------------------------+

|                134217728 |

+---------------------------+

1 row in set (0.00 sec)

mysql> SELECT @@innodb_buffer_pool_chunk_size;

+---------------------------------+

| @@innodb_buffer_pool_chunk_size |

+---------------------------------+

|                      134217728 |

+---------------------------------+

1 row in set (0.00 sec)

mysql> SET GLOBAL innodb_buffer_pool_size=402653184;

Query OK, 0 rows affected (0.01 sec)

mysql> SELECT @@innodb_buffer_pool_size;

+---------------------------+

| @@innodb_buffer_pool_size |

+---------------------------+

|                402653184 |

+---------------------------+

1 row in set (0.00 sec)

 

innodb_buffer_pool_chunk_size的大小,计算公式是innodb_buffer_pool_size / innodb_buffer_pool_instances

比如现在初始化innodb_buffer_pool_size为2G,innodb_buffer_pool_instances实例为4,innodb_buffer_pool_chunk_size设置为1G,那么会自动把innodb_buffer_pool_chunk_size 1G调整为512M,例:
./mysqld --innodb_buffer_pool_size=2147483648 --innodb_buffer_pool_instances=4 
--innodb_buffer_pool_chunk_size=1073741824;

mysql> SELECT @@innodb_buffer_pool_size;

+---------------------------+

| @@innodb_buffer_pool_size |

+---------------------------+

|                2147483648 |

+---------------------------+

1 row in set (0.00 sec)

 

mysql> SELECT @@innodb_buffer_pool_instances;

+--------------------------------+

| @@innodb_buffer_pool_instances |

+--------------------------------+

|                              4 |

+--------------------------------+

1 row in set (0.00 sec)

 

# Chunk size was set to 1GB (1073741824 bytes) on startup but was

# truncated to innodb_buffer_pool_size / innodb_buffer_pool_instances

mysql> SELECT @@innodb_buffer_pool_chunk_size;

+---------------------------------+

| @@innodb_buffer_pool_chunk_size |

+---------------------------------+

|                      536870912 |

+---------------------------------+

1 row in set (0.00 sec)


监控Buffer Pool调整进程

mysql> SHOW STATUS WHERE Variable_name='InnoDB_buffer_pool_resize_status';

+----------------------------------+----------------------------------+

| Variable_name                    | Value                            |

+----------------------------------+----------------------------------+

| Innodb_buffer_pool_resize_status | Resizing also other hash tables. |

+----------------------------------+----------------------------------+

1 row in set (0.00 sec)

如下命令查看 告警xing'i
mysql> show warnings;
+---------+------+------------------------------------------------------------------+
| Level   | Code | Message                                                          |
+---------+------+------------------------------------------------------------------+
| Warning | 1292 | Truncated incorrect innodb_buffer_pool_size value: '1073741824' |
+---------+------+------------------------------------------------------------------+
1 row in set (0.00 sec)

出现警告了,再看实际大小

mysql> show variables like '%innodb_buffer_pool_size%';
+-------------------------+-------------+
| Variable_name           | Value       |
+-------------------------+-------------+
| innodb_buffer_pool_size | 1073741824|
+-------------------------+-------------+
1 row in set (0.00 sec)

 

如果有警告信息,这是因为,调整后的buffer pool大小必须满足如下条件:
innodb_buffer_pool_chunk_size* innodb_buffer_pool_instances的倍数,即128M的倍数。

 

四 总结

1  实战总结innodb_buffer_pool_size =2G,innodb_buffer_pool_instances=1 效果很好。

2  性能优化面广,涉及的方面多,需要根据实际情况而定。

3  仅仅建立索引对性能优化效果有限。

 

参考文献

https://www.cnblogs.com/liugx/p/9935749.html

https://blog.csdn.net/nanyanglu/article/details/79109838

https://www.cnblogs.com/sandea/p/11695521.html

Ubuntu 14.04下安装MySQL http://www.linuxidc.com/Linux/2014-05/102366.htm

《MySQL权威指南(原书第2版)》清晰中文扫描版 PDF http://www.linuxidc.com/Linux/2014-03/98821.htm

Ubuntu 14.04 LTS 安装 LNMP Nginx\PHP5 (PHP-FPM)\MySQL http://www.linuxidc.com/Linux/2014-05/102351.htm

Ubuntu 14.04下搭建MySQL主从服务器 http://www.linuxidc.com/Linux/2014-05/101599.htm

Ubuntu 12.04 LTS 构建高可用分布式 MySQL 集群 http://www.linuxidc.com/Linux/2013-11/93019.htm

Ubuntu 12.04下源代码安装MySQL5.6以及Python-MySQLdb http://www.linuxidc.com/Linux/2013-08/89270.htm

MySQL-5.5.38通用二进制安装 http://www.linuxidc.com/Linux/2014-07/104509.htm

你可能感兴趣的:(性能优化)