mysql的调优

调优的最终目的:节省系统资源、提高响应速度下面从6个维度对mysql进行优化。

第一步:对服务器的参数进行调优

通过show profile命令分析,如果sql语句在执行过程中等待时间长,就需要对mysql服务器的参数进行调优。

1.1优化服务器的硬件

服务器的硬件性能直接决定着MySQL数据库的性能:

  • 配置较大的内存:减少对磁盘的IO次数
  • 配置高速磁盘系统:提高磁盘IO的性能
  • 合理分布磁盘I/O
  • 配置多处理器:供多个线程使用

1.2优化mysql的参数

linux系统中在/etc/my.cnf中进行配置:

  • innodb_buffer_pool_size:表示缓冲池的大小,缓存索引的数据和表中的数据,该值越大查询速度越快,该值的大小取决于内存的大小。
  • key_buffer_size:表示索引缓冲区的大小,该缓冲区所有的线程共享,该值越大代表可以更好的处理索引,该值的大小取决于内存的大小。
  • table_cache:表示同时打开的表的个数。
  • query_cache_size:表示查询缓冲区的大小,sql语句查询时先到查询缓冲区中进行查询,如果查询缓冲区中没有才会到磁盘中进行获取。该参数需要和query_cache_type配合使用。
  • query_cache_type:表示缓冲区的类型

值是0时,所有的查询都不使用查询缓存区。但是query_cache_type=0并不会导致MySQL释放query_cache_size所配置的缓存区内存。

值是1时,所有的查询都将使用查询缓存区,除非在查询语句中指定SQL_NO_CACHE ,如SELECT SQL_NO_CACHE * FROM tbl_name。

当query_cache_type=2时,只有在查询语句中使用 SQL_CACHE 关键字,查询才会使用查询缓存区。

  • sort_buffer_size:表示需要进行排序的线程分配的缓冲区的大小。增加这个参数的值可以提高ORDER BY或GROUP BY操作的速度。该参数对应的分配内存也是每个连接独享。
  • join_buffer_size = 8M:表示联合查询操作所能使用的缓冲区大小。该参数对应的分配内存也是每个连接独享。
  • read_buffer_size:表示每个线程连续扫描时为扫描的每个表分配的缓冲区的大小(字节)。
  • innodb_flush_log_at_trx_commit:表示何时将缓冲区的数据写入日志文件,redo log的刷盘策略。
  • innodb_log_buffer_size:这是InnoDB存储引擎的事务日志所使用的缓冲区。当满足innodb_flush_log_trx_commit参数所设置的相应条件后,才会将缓冲区中的数据写入日志文件。
  • max_connections:表示允许连接到MySQL数据库的最大数量,默认值是151。这个连接数不是越大越好,因为这些连接会浪费内存的资源。过多的连接可能会导致MySQL服务器僵死。
  • back_log:用于控制MySQL监听TCP端口时设置的积压请求栈大小。如果MySql的连接数达到max_connections时,新来的请求将会被存在堆栈中,以等待某一连接释放资源,该堆栈的数量即back_log,如果等待连接的数量超过back_log,将不被授予连接资源,将会报错。
  • thread_cache_size:线程池缓存线程数量的大小
  • wait_timeout:指定一个请求的最大连接时间
  • interactive_timeout:表示服务器在关闭连接前等待行动的秒数。

系统配置参数的参考:

系统配置参数的参考
[mysqld]
port = 3306
serverid = 1
socket = /tmp/mysql.sock skip-locking #避免MySQL的外部锁定,减少出错几率增强稳定性。
skip-name-resolve #禁止MySQL对外部连接进行DNS解析,使用这一选项可以消除MySQL进行DNS解析的时间。但需要注意,如果开启该选项,则所有远程主机连接授权都要使用IP地址方式,否则MySQL将无法正常处理连接请求! back_log = 384
key_buffer_size = 256M
max_allowed_packet = 4M
thread_stack = 256K
table_cache = 128K
sort_buffer_size = 6M
read_buffer_size = 4M
read_rnd_buffer_size=16M
join_buffer_size = 8M
myisam_sort_buffer_size =64M
table_cache = 512
thread_cache_size = 64
query_cache_size = 64M
tmp_table_size = 256M
max_connections = 768
max_connect_errors = 10000000
wait_timeout = 10
thread_concurrency = 8 #该参数取值为服务器逻辑CPU数量*2,在本例中,服务器有2颗物理CPU,而每颗物理CPU又支持H.T超线程,所以实际取值为4*2=8 skipnetworking #开启该选项可以彻底关闭MySQL的TCP/IP连接方式,如果WEB服务器是以远程连接的方式访问MySQL数据库服务器则不要开启该选项!否则将无法正常连接!
table_cache=1024
innodb_additional_mem_pool_size=4M #默认为2M innodb_flush_log_at_trx_commit=1
innodb_log_buffer_size=2M #默认为1M
innodb_thread_concurrency=8 #你的服务器CPU有几个就设置为几。建议用默认一般为8
tmp_table_size=64M #默认为16M,调到64-256最挂
thread_cache_size=120
query_cache_size=32M

第二步:优化表设计

数据表的设计也影响了SQL查询语句的效率

2.1表结构的设计遵循的范式

2.1.1表中键的概念



举例:

2.1.2表结构要尽量遵循三范式的原则,可以让数据结构更加清晰规范,减少冗余字段,减少在更新、插入、删除数据时发生的异常。

  • 第一范式(1NF):数据库表的每一列都是不可分割的基本数据项,一个列中不能包含其他列。

  • 第二范式(2NF):非主属性必须完全依赖于主键(尤其是复合主键),不能是部分依赖。

  • 第三范式(3NF):非主属性之间不能有依赖关系,确保非主属性与主键直接相关,而不是间接相关。

  • BCNF(巴斯范式):主属性不能对候选键有部分依赖或者传递依赖的关系。

  • 第四范式(4NF):一个表中不能存在多组一对多的关系,只能存在一组。

利用范式:范式等级越高,可以消除数据库的数据冗余,但是关联查询时降低查询的效率。

2.2可以利用反范式

反范式:在考虑到业务优先的情况下,可以通过在数据表中增加冗余字段来提高数据库的查询性能,这样就减少了关联查询,join表的次数,用空间换取时间。这些冗余这段不需要经常进行修改。

2.3优化数据类型

当数据量也越来越多的时候,你就不能只从系统稳定性的角度来思考问题了,还要考虑到系统整体的稳定性和效率。此时,优先选择符合存储需要的最小的数据类型。

  • 对整数类型数据进行优化

    遇到整数类型的字段可以用INT型。对于非负型的数据(如自增ID、整型IP)来说,要优先使用无符号整型UNSIGNED来存储。
  • 既可以使用文本类型也可以使用整数类型的字段,要选择使用整数类型。

    跟文本类型数据相比,大整数往往占用更少的存储空间。
  • 避免使用TEXT、BLOB数据类型

    在排序操作时,对于TEXT、BLOB数据类型的数据不能是用内存临时表,必须使用磁盘临时表。还需要进行二次查询进行回表。
  • 避免使用ENUM类型

    修改ENUM值需要使用ALTER语句,该类型的ORDER BY操作效率低,需要额外操作,使用TINYINT带代替。
  • 使用TIMESTAMP存储时间

    TIMESTAMP占4个字节,DATETIME使用8个字节,同时TIMESTAMP具有自动赋值以及自动更新的特性。
  • 用DECIMAL代替FLOAT和DOUBLE存储精确浮点数

    DECIMAL在财务类型的数据下要使用。

2.4ER模型

要素和数据表

2.5数据表的设计

数据表的设计原则:三少一多

  1. 数据表的个数越少越好
  2. 数据表中的字段个数越少越好
  3. 数据表中联合主键的字段个数越少越好
  4. 使用主键和外键越多越好

第三步:优化逻辑查询

逻辑查询优化就是改变sql语句的内容让sql执行效率更高,采用的方式时对sql语句进行等价交换,对查询进行重写。

3.1关联查询优化

3.1.1双表查询

1、当采用left join左外连接时,需要在右表的字段上建立索引。(驱动表是左表,被驱动表是右表,在被驱动表的join字段建立索引)

  • 建立索引:

    ALTER TABLE type ADD INDEX X (card); #【驱动表】,无法避免全表扫描

    ALTER TABLE book ADD INDEX Y (card); #【被驱动表】,可以避免全表扫描
  • explain分析sql语句:

    EXPLAIN SELECT SQL_NO_CACHE * FROM type LEFT JOIN book ON type.card = book.card;

2、当采用right join右外连接时,需要在左表的字段上建立索引。(驱动表是右表,被驱动表是左表,被驱动表的join字段上建立索引)

3、当采用inner join内连接时,会出现using join buffer。

  • 当只在一张表上建立索引,建立索引的表是被驱动表。

  • 当两张表都建立索引,被驱动表由mysql优化器自己决定,遵循小结果集驱动大结果集规则。

3.1.2join语句原理

可以使用straight_join让mysql使用固定的连接方式执行查询(左表为驱动表,右表为被驱动表)

1、join字段上有索引的情况

Index Nested-Loop Join(索引嵌套循环join)

表t1字段上没有索引,表t2字段上有索引,执行过程如下:

  1. 从表t1中读入一行数据R;
  2. 从数据行R中,取出a字段到表t2里利用索引去查找;
  3. 取出表t2中满足条件的行,跟R组成一行,作为结果集的一部分;
  4. 重复执行步骤1到3,直到表t1的末尾循环结束。

2、join字段上没有索引

Simple Nested-Loop Join(简单嵌套循环join)

如果join的列没有索引,先从驱动表中取一条数据,然后访问被驱动表,该表中的记录都会被加载到内存中,当匹配结束后清除内存中的数据,然后再从驱动表中加载一条记录,然后把被驱动表的记录在加载到内存匹配,这样周而复始,大大增加了l0的次数。为了减少被驱动表的IO次数,就出现了Block Nested-Loop Join的方式。

Block Nested-Loop Join(块嵌套循环join)

引入了join buffer缓冲区,将驱动表join相关的部分数据列(大小受join buffer的限制)缓存到join buffer缓冲区中,然后全表扫描被驱动表,被驱动表的每一条记录一次性和joinbuffer中的所有驱动表记录进行匹配(内存中操作),将简单嵌套循环中的多次加载被驱动表减少。

3、三种方式的开销比较
  • 整体效率:INLJ>BNLJ>SNLJ

  • 为被驱动表的join字段增加索引(减少内层表的join比较次数)
  • 需要JOIN 的字段,数据类型保持绝对一致,否则索引会失效。
  • 增加join buffer size的大小(减少内层表的扫描次数)
  • 减少驱动表的不必要字段查询(字段越少,join buffer中缓存的数据越多,减少内层表的扫描次数)
  • LEFT JOIN时,选择小表作为驱动表,大表作为被驱动表,减少外层循环的次数。
4、mysql8.0的对关联查询的优化

mysql8.0将BNLJ移除,引入了hash join

3.2子查询优化

子查询是MySQL的一项重要的功能,可以帮助我们通过一个SQL语句实现比较复杂的查询。但是,子查询的执行效率不高。

  • 执行子查询时,MySQL需要为内层查询语句的查询结果建立一个临时表,然后外层查询语句从临时表中查询记录。查询完毕后,再撤销这些临时表。这样会消耗过多的CPU和IO资源,产生大量的慢查询。
  • 子查询的结果集存储的临时表,不论是内存临时表还是磁盘临时表都不会存在索引,所以查询性能会受到一定的影响。
  • 对于返回结果集比较大的子查询,其对查询性能的影响也就越大。

可以将子查询进行优化,将子查询变为join多表连接查询,连接查询不需要建立临时表,速度比子查询快。

3.3排序优化

order by优化

1、在使用order by和group by中使用索引避免出现using filesort、using temporary。

  • where查询时使用范围时,order by进行排序时必须和索引定义顺序一致。
  • 单独使用order by语句时要遵循索引最左前列,否则会出现using filesort(order by后的字段与索引字段进行比较,如果顺序错、方向反都会导致索引失效,出现using filesort)
  • 单独使用order by时,如果数据量大,不使用limit进行限制也会出现using filesort
  • where字句与orderby字句条件列组合使用时,需要满足索引最左前列,否则会出现using filesort

2、当无法使用索引时,需要对filesort方式进行调优。

  • 双路排序:两次扫描磁盘,第一次读取磁盘对行指针和orderby列进行排序,第二次从磁盘读取已经排序好的字段在buffer中进行排序(两次扫描,IO耗时,因此出现了单路排序)

  • 单路排序:一次扫描磁盘,从磁盘中读取数据,按照orderby列在buffer对它们进行排序,然后扫描排序后的列表进行输出,效率更快,避免了二次读取数据并且把随机IO变成了顺序IO。但是消耗内存空间,可能一次不能读取全部的数据,导致多次IO。

  • 针对单独排序产生的问题(一次不能读取全部数据导致多次IO)进行参数调优:增大sort_buffer_size(该值的大小尽可能和max_length_for_sort_data的大小一致)

    如果总数据量小于max_length_for_sort_data就会用单路排序

    如果max_length_for_sort_data大于sort_buffer_size可能导致多次IO

    如果max_length_for_sort_data小于sort_buffer_size就会造成缓冲区的空间浪费

group by优化

  • group by实质是先排序后再进行分组,遵照索引建的最佳左前缀(和orderby原理一致)
  • 当无法使用索引列进行分组时,应该增大max_length_for_sort_data和sort_buffer_size的大小
  • where执行优先于having,尽量把限定条件写到where中
  • 减少使用order by,和业务沟通能不排序就不排序,或将排序放到程序端去做。order by、group by、distinct这些语句较为耗费CPU,数据库的CPU资源是极其宝贵的。

3.4使用覆盖索引

覆盖索引:select查询的结果包含于索引列中就叫做覆盖索引。

索引列+主键 包含 SELECT 到 FROM之间查询的列 。

覆盖索引的好处

  • 避免innodb表进行索引的回表查询:二级索引的键值中可以获得想要查询的数据,就可以避免进行回表,减少了IO次数。
  • 把随机IO变成顺序IO加快查询效率:因为回表需要对磁盘进行随机IO,对索引是顺序IO查询。

覆盖索引的弊端

  • 索引字段的维护需要代价。

3.5使用索引下推(ICP)

当where查询条件中出现了索引列,但是该索引列是失效的状态,就需要进行索引下推,使该失效的索引列起到过滤作用。ICP是一种在存储引擎层使用索引过滤数据的一种优化方式。

没有使用ICP

storage:存储引擎会将匹配索引的索引列进行回表并取出整行记录返回server层

server:由server层评估WHERE后面的条件是否保留行。

使用ICP

storage:存储引擎首先将匹配索引的索引列的索引记录区间确定,然后使用索引下推让失效的索引列也进行过滤。满足条件的索引记录才去回表取出整行记录返回server层。

server:由server层评估WHERE后面的条件是否保留行。

举例:

1、创建索引:



2、执行语句:(lastname是索引列,但是是失效的状态,如果开启索引下推,就可以使用该列进行过滤)



ICP的优点

使用ICP减少了存储引擎访问基表的次数和mysql访问存储引擎的次数。

使用ICP的条件

  • 只能用于二级索引(secondary index)
  • explain显示的执行计划中type值(join 类型)为 range 、 ref 、 eq_ref 或者 ref_or_null 。
  • 如果where条件的字段不在索引列中,该列是不可以使用索引下推
  • ICP可以用于MyISAM和InnnoDB存储引擎
  • MySQL 5.6版本的不支持分区表的ICP功能,5.7版本的开始支持。
  • 当SQL使用覆盖索引时,不支持ICP优化方法,因为覆盖索引不需要进行回表。

3.6EXISTS 和 IN 的区分

优化原则:用小的数据集驱动大的数据集

  • in:用小表查询出来的数据来驱动大表查询

  • exists:用小表查询出来数据,放到大表中做条件验证,根据验证结果(true或false)来决定小表的数据是否保留

3.7COUNT(*)与COUNT(具体字段)效率

  • COUNT(具体字段):来统计数据行数,要尽量采用二级索引。

  • COUNT(*)和COUNT(1):它们不需要查找具体的行,只是统计行数,系统会自动采用占用空间更小的二级索引来进行统计。如果有多个二级索引,会使用key_len小的二级索引进行扫描。当没有二级索引的时候,才会采用主键索引来进行统计。

3.8关于select(*)

  • 查询中应尽量避免使用*符号,应该使用具体的字段
  • 使用*符号无法使用覆盖索引

第四步:使用物理逻辑查询

物理逻辑的优化:主要是索引的创建和使用。(上一章有讲)

4.1优化插入记录的速度

MYISAM引擎的表:

  • 禁用索引:在对非空表插入记录之前,要禁用索引;如果未禁用索引在插入数据时,还需要对插入的记录建立索引,降低了插入记录的速度。

    禁用索引:alter table table_name disable keys;

    开启索引:alter table table_name enable keys;
  • 禁用唯一性检查:插入数据时,需要对记录进行唯一性校验,降低了插入数据的速度。

    禁用唯一性校验:set unique_checks=0;

    开启唯一性校验:set unique_checks=1;
  • 使用批量插入语句
  • 使用LOAD DATA INFILE批量导入数据:该操作比INSERT速度快

INNODB引擎的表

  • 禁用唯一性校验
  • 禁用外键检查
  • 禁止自动提交

第五步:使用redis或者memcached作为缓存

第六步:库级优化

6.1读写分离

一主一从:



双主双从:

6.2垂直分库

当数据量级达到千万级以上时,有时候我们需要把一个数据库切成多份,放到不同的数据库服务器上,减少对单一数据库服务器的访问压力。

6.3垂直分表和水平分表

垂直拆分的优点: 可以使得列数据变小,在查询时减少读取的Block数,减少I/O次数。此外,垂直分区可以简化表的结构,易于维护。

垂直拆分的缺点: 主键会出现冗余,需要管理冗余列,并会引起JOIN操作。此外,垂直拆分会让事务变得更加复杂。

6.4拆分数据表:冷热数据分离

将一张表中的数据分开,提交查询效率:

  • 热数据:表中字段的操作频率高的数据
  • 冷数据:表中字段操作频率低的数据

冷热分离数据的目的:

  • 减少磁盘IO:当数据分离时,一个页中可以存储更多的数据,这样在读取时可以减少磁盘的IO。
  • 更有效的利用缓存:避免无用的数据读入缓存

6.5增加中间表

把经常联合查询的数据插入中间表中,然后将原来的联合查询改为对中间表的查询,以此来提高查询的效率。

6.6使用非空约束

使用非空约束的好处:

  • 减少了对NULL值的字段判断是否为空的开销,提高存储效率。
  • 索引NULL列需要额外的空间来保存

最新文章

  1. 前端构建工具gulpjs的使用介绍及技巧
  2. hdu 1757 矩阵
  3. Swift 字符与字符串
  4. tail报错
  5. Oracle SQL Lesson (10) - 使用DDL语句创建和管理表
  6. Java经典编程题50道之三十六
  7. Mybaits简诉
  8. eclipse中导入jsp等工程使用过程中常遇问题
  9. 关于scanf,gets
  10. 机器学习基石:07 The VC Dimension
  11. 基于Retrofit2.0+RxJava+Dragger2实现不一样的Android网络构架搭建(转载)
  12. MySQL 字符集和校对
  13. Office 2016 自定义安装
  14. Docker多主机互联最佳实践
  15. c# Linq&Lambda
  16. WPF之TextBox和PasswordBox水印效果
  17. MVC+三层+ASP.NET简单登录验证
  18. Linux基础命令---arping
  19. Jquery源码探索
  20. appium简明教程(2)——appium的基本概念

热门文章

  1. 腾讯QQ是用什么语言写的?
  2. JAVA变量的数据类型
  3. ObjectInputStream java.io.StreamCorruptedException: invalid type code: AC问题解决
  4. js 保存并排序输入内容
  5. Nodejs path对象
  6. iOS团队代码规范
  7. 安装 mysql 8.0后;root用户在客户端连接不上
  8. linux_16
  9. java中LinkedList ArrayList 数组 HashSet 存储数据测试
  10. php spl_autoload_register 实现自动加载