MySQL 性能管理及架构设计指南

 1、什么影响了数据库查询速度

1.1 影响数据库查询速度的四个因素

1.2 风险分析

QPSQueries Per Second意思是“每秒查询率”,是一台服务器每秒可以相应的查询次数,是对一个特定的查询服务器在规定时间内所处理流量多少的衡量标准。php

TPS:是TransactionsPerSecond的缩写,也就是事务数/秒。它是软件测试结果的测量单位。客户机在发送请求时开始计时,收到服务器响应后结束计时,以此来计算使用的时间和完成的事务个数。html

注:最好不要在主库上数据库备份,大型活动前取消这样的备份计划。前端

    1. 效率低下的 sql:会产生超高的QPS与TPS。mysql

    2. 大量的并发:数据链接数被占满(max_connection默认100,通常把链接数设置得大一些)。 
      并发量:同一时刻数据库服务器处理的请求数量
      sql

    3. 超高的CPU使用率:CPU资源耗尽出现宕机。thinkphp

    4. 磁盘IO:磁盘IO性能忽然降低、大量消耗磁盘性能的计划任务。解决:更快磁盘设备、调整计划任务、作好磁盘维护。数据库

1.3 网卡流量:

如何避免没法链接数据库的状况?缓存

  1. 减小从服务器的数量(从服务器会从主服务器复制日志)安全

  2. 进行分级缓存(避免前端大量缓存失效)性能优化

  3. 避免使用select * 进行查询

  4. 分离业务网络和服务器网

1.4 大表带来的问题(重要)

1.4.1 大表的特色
  1. 记录行数巨大,单表超千万

  2. 表数据文件巨大,超过10个G

1.4.2 大表的危害
  1. 慢查询:很难在短期内过滤出须要的数据 
    查询字区分度低 -> 要在大数据量的表中筛选出来其中一部分数据会产生大量的磁盘io -> 下降磁盘效率
  2. DDL影响: 
    创建索引须要很长时间:
  • MySQL -v<5.5创建索引会锁表

  • MySQL -v>=5.5创建索引会形成主从延迟(mysql创建索引,先在组上执行,再在库上执行)

 

修改表结构须要长时间的锁表:会形成长时间的主从延迟

1.4.3 如何处理数据库上的大表

分库分表把一张大表分红多个小表 
难点: 
a. 分表主键的选择 
b. 分表后跨分区数据的查询和统计

1.5 大事务带来的问题(重要)

1.5.1 什么是事务

1.5.2 事务的 ACID属性

一、原子性(atomicity):所有成功,所有回滚失败。银行存取款。 
二、一致性(consistent):银行转帐的总金额不变。 
三、隔离性(isolation): 

隔离性等级:

  • 未提交读(READ UNCOMMITED) 脏读,两个事务之间互相可见;

  • 已提交读(READ COMMITED)符合隔离性的基本概念,一个事务进行时,其它已提的事物对于该事务是可见的,便可以获取其它事务提交的数据。

  • 可重复读(REPEATABLE READ) InnoDB的默认隔离等级。事务进行时,其它全部事务对其不可见,即屡次执行读,获得的结果是同样的!

  • 可串行化(SERIALIZABLE) 在读取的每一行数据上都加锁,会形成大量的锁超时和锁征用,严格数据一致性且没有并发是可以使用。

查看系统的事务隔离级别show variables like '%iso%'; 
修改事物的隔离级别set session tx_isolation='read-committed'; 
四、持久性(DURABILITY):从数据库的角度的持久性,磁盘损坏就不行了

 

 redo log机制保证事务更新的一致性和持久性

1.5.3 大事务

运行时间长,操做数据比较多的事务; 
风险:锁定数据太多,回滚时间长,执行时间长

 

锁定太多数据,形成大量阻塞和锁超时;

回滚时所需时间比较长,且数据仍然会处于锁定;

若是执行时间长,将形成主从延迟,由于只有当主服务器所有执行完写入日志时,从服务器才会开始进行同步,形成延迟。

 

解决思路:

避免一次处理太多数据,能够分批次处理;

移出没必要要的SELECT操做,保证事务中只有必要的写操做。

 

 

2、什么影响了MySQL性能(很是重要)

2.1 影响性能的几个方面

    1. 服务器硬件。

    2. 服务器系统(系统参数优化)。

    3. 存储引擎。 
      MyISAM: 不支持事务,表级锁。 
      InnoDB: 支持事务,支持行级锁,事务ACID。

    4. 数据库参数配置。

    5. 数据库结构设计和SQL语句。(重点优化)

 

2.2 MySQL体系结构

分三层:客户端->服务层->存储引擎 

 

 

  1. MySQL是插件式的存储引擎,其中存储引擎分不少种。只要实现符合mysql存储引擎的接口,能够开发本身的存储引擎!

  2. 全部跨存储引擎的功能都是在服务层实现的。

  3. MySQL的存储引擎是针对表的,不是针对库的。也就是说在一个数据库中可使用不一样的存储引擎。可是不建议这样作。

 

2.3 InnoDB存储引擎

MySQL5.5及以后版本默认的存储引擎:InnoDB。

2.3.1 InnoDB使用表空间进行数据存储

show variables like 'innodb_file_per_table'; 
若是innodb_file_per_table 为 ON 将创建独立的表空间,文件为tablename.ibd

若是innodb_file_per_table 为 OFF将数据存储到系统的共享表空间,文件为ibdataX(X为从1开始的整数); 
.frm :是服务器层面产生的文件,相似服务器层的数据字典,记录表结构。

2.3.2 (MySQL5.5默认)系统表空间与(MySQL5.6及之后默认)独立表空间
  1. 系统表空间没法简单的收缩文件大小,形成空间浪费,并会产生大量的磁盘碎片。

  2. 独立表空间能够经过optimeze table 收缩系统文件,不须要重启服务器也不会影响对表的正常访问。

  3. 若是对多个表进行刷新时,其实是顺序进行的,会产生IO瓶颈。

  4. 独立表空间能够同时向多个文件刷新数据。

强烈创建对Innodb使用独立表空间,优化什么的会更方即可控。

2.3.3 系统表空间的表转移到独立表空间中的方法

一、使用mysqldump 导出全部数据库数据(存储过程、触发器、计划任务一块儿都要导出 )能够在从服务器上操做。

二、中止MYsql 服务器,修改参数(my.cnf加入innodb_file_per_table),并删除Inoodb相关文件(能够重建Data目录)。

三、重启MYSQL,并重建Innodb系统表空间。

四、 从新导入数据。

或者 Alter table 一样能够的转移,可是没法回收系统表空间中占用的空间。

2.4 InnoDB存储引擎的特性

2.4.1 特性一:事务性存储引擎及两个特殊日志类型:Redo Log 和 Undo Log

一、Innodb 是一种事务性存储引擎。

二、彻底支持事务的ACID特性。

三、支持事务所须要的两个特殊日志类型:Redo Log 和Undo Log 
Redo Log:实现事务的持久性(已提交的事务)。 
Undo Log:未提交的事务,独立于表空间,须要随机访问,能够存储在高性能io设备上。 
Undo 日志记录某数据被修改前的值,能够用来在事务失败时进行rollback; 
Redo 日志记录某数据块被修改后的值,能够用来恢复未写入data file的已成功事务更新的数据。

2.4.2 特性二:支持行级锁

一、InnoDB支持行级锁。

二、行级锁能够最大程度地支持并发。

三、行级锁是由存储引擎层实现的。

2.5 什么是锁

2.5.1 锁

2.5.2 锁类型

2.5.3 锁的粒度

MySQL的事务支持不是绑定在MySQL服务器自己,而是与存储引擎相关

table_name加表级锁命令:lock table table_name write; 写锁会阻塞其它用户对该表的'读写’操做,直到写锁被释放:unlock tables;

 

一、锁的开销越大,粒度越小,并发度越高。

二、表级锁一般是在服务器层实现的。

三、行级锁是存储引擎层实现的。innodb的锁机制,服务器层是不知道的

2.5.4 阻塞和死锁

一、阻塞是因为资源不足引发的排队等待现象。

二、死锁是因为两个对象在拥有一份资源的状况下申请另外一份资源,而另外一份资源刚好又是这两对象正持有的,致使两对象没法完成操做,且所持资源没法释放。

2.6 如何选择正确的存储引擎

参考条件:

一、事务

二、备份(Innobd免费在线备份)

三、崩溃恢复

四、存储引擎的特有特性 
总结:Innodb大法好。 
注意:尽可能别使用混合存储引擎,好比回滚会出问题在线热备问题。

2.7 配置参数

2.7.1 内存配置相关参数

肯定可使用的内存上限。内存的使用上限不能超过物理内存,不然容易形成内存溢出;(对于32位操做系统,MySQL只能试用3G如下的内存。)

肯定MySQL的每一个链接单独使用的内存。

sort_buffer_size #定义了每一个线程排序缓存区的大小,MySQL在有查询、须要作排序操做时才会为每一个缓冲区分配内存(直接分配该参数的所有内存);
join_buffer_size #定义了每一个线程所使用的链接缓冲区的大小,若是一个查询关联了多张表,MySQL会为每张表分配一个链接缓冲,致使一个查询产生了多个链接缓冲;
read_buffer_size #定义了当对一张MyISAM进行全表扫描时所分配读缓冲池大小,MySQL有查询须要时会为其分配内存,其必须是4k的倍数;
read_rnd_buffer_size #索引缓冲区大小,MySQL有查询须要时会为其分配内存,只会分配须要的大小。

  

注意:以上四个参数是为一个线程分配的,若是有100个链接,那么须要×100。 
MySQL数据库实例: 
 ①MySQL是单进程多线程(而oracle是多进程),也就是说MySQL实例在系统上表现就是一个服务进程,即进程; 
 ②MySQL实例是线程和内存组成,实例才是真正用于操做数据库文件的;

通常状况下一个实例操做一个或多个数据库;集群状况下多个实例操做一个或多个数据库。

 

如何为缓存池分配内存: 
Innodb_buffer_pool_size,定义了Innodb所使用缓存池的大小,对其性能十分重要,必须足够大,可是过大时,使得Innodb 关闭时候须要更多时间把脏页从缓冲池中刷新到磁盘中; 
默认为128M,推荐设置为系统内存的50%~80%

总内存-(每一个线程所须要的内存*链接数)-系统保留内存

key_buffer_size,定义了MyISAM所使用的缓存池的大小,因为数据是依赖存储操做系统缓存的,因此要为操做系统预留更大的内存空间;

select sum(index_length) from information_schema.tables where engine='myisam';

  

注意:即便开发使用的表所有是Innodb表,也要为MyISAM预留内存,由于MySQL系统使用的表仍然是MyISAM表。

max_connections 控制容许的最大链接数, 通常2000更大。 
不要使用外键约束保证数据的完整性。

由于

1.数据库须要维护外键的内部管理;

2.外键等于把数据的一致性事务实现,所有交给数据库服务器完成;

3.有了外键,当作一些涉及外键字段的增,删,更新操做以后,须要触发相关操做去检查,而不得不消耗资源;

4.外键还会由于须要请求对其余表内部加锁而容易出现死锁状况;

2.8 性能优化顺序

从上到下: 

 

3、数据库结构优化(很是重要

3.1 数据库结构优化目的

一、减小数据冗余:(数据冗余是指在数据库中存在相同的数据,或者某些数据能够由其余数据计算获得),注意,尽可能减小不表明彻底避免数据冗余; 
二、尽可能避免数据维护中出现更新,插入和删除异常: 

 

 

 总结:要避免异常,须要对数据库结构进行范式化设计

三、节约数据存储空间。 
四、提升查询效率。

3.2 数据库结构设计步骤

一、需求分析:全面了解产品设计的存储需求、数据处理需求、数据安全性与完整性; 
二、逻辑设计(重要):设计数据的逻辑存储结构。数据实体之间的逻辑关系,解决数据冗余和数据维护异常。数据范式能够帮助咱们设计; 
三、物理设计:表结构设计,存储引擎与列的数据类型; 
四、维护优化:索引优化、存储结构优化。

3.3 数据库方式设计

 

3.4 物理设计

注:虽然更小的一般更好 更小的数据类型一般更快,由于它们占用 更小的磁盘、内存和CPU缓存,而且处理时须要的CPU时间也更少,可是尽可能使用能够正确存储数据的最小数据类型好比财务计算,金融交易的时候,这时候小的数据类型每每计算不精确好比(float,double)

3、高可用架构设计

 

4.1 读写分离

  1. 用中间件好比360的atlas 或则 基于阿里开源的Cobar产品而研发的mycat

  2. 开发在应用程序上作一些读写分离

 

5、数据库索引优化(很是重要

5.1 两种主要数据结构:B-tree和Hash

5.1.1 B-tree结构

B-tree索引的限制:

5.1.2 Hash结构

 

  • Hash索引的限制:Hash索引必须进行二次查找

  • Hash索引没法用于排序

  • Hash索引不支持部分索引查找也不支持范围查找

  • Hash索引中Hash码的计算可能存在Hash冲突,不适合重复值很高的列,如性别,身份证比较合适。

5.1.3 MySQL常见索引和各类索引区别
PRIMARY KEY(主键索引)  ALTER TABLE `table_name` ADD PRIMARY KEY ( `column` ) 
UNIQUE(惟一索引)     ALTER TABLE `table_name` ADD UNIQUE (`column`)
INDEX(普通索引)     ALTER TABLE `table_name` ADD INDEX index_name ( `column` ) 
FULLTEXT(全文索引)      ALTER TABLE `table_name` ADD FULLTEXT ( `column` )
组合索引   ALTER TABLE `table_name` ADD INDEX index_name ( `column1`, `column2`, `column3` ) 

  

普通索引:最基本的索引,没有任何限制 
惟一索引:与”普通索引”相似,不一样的就是:索引列的值必须惟一,但容许有空值。 
主键索引:它 是一种特殊的惟一索引,不容许有空值。 
全文索引:仅可用于 MyISAM 表,针对较大的数据,生成全文索引很耗时好空间。 
组合索引:为了更多的提升mysql效率可创建组合索引,遵循”最左前缀“原则。

5.2 使用索引好处和索引缺陷

5.2.1 为何要使用索引

一、索引大大减小了存储引擎须要扫描的数据量;

二、索引能够帮助咱们进行排序以免使用临时表;

三、索引能够把随机I/O变为顺序I/O。

5.2.2 索引不是越多越好

一、索引会增长写操做的成本;

二、太多的索引会增长查询优化器的选择时间。

索引就比如一本书的目录,它会让你更快的找到内容,显然目录(索引)并非越多越好,假如这本书1000页,而有500页是目录,它固然效率低,目录是要占纸张的,而索引是要占磁盘空间的。

  

5.3 索引优化策略

5.3.1 索引列上不能使用表达式和函数

5.3.2 前缀索引和索引列的选择性

Innodb索引列最大宽度为667个字节(utf-8 差很少255个字符),MyIsam索引类宽度最大为1000个字节,因而出现前缀索引,索引的选择性。

对于列的值较长,好比BLOB、TEXT、VARCHAR,就必须创建前缀索引,即将值的前一部分做为索引。这样既能够节约空间,又能够提升查询效率。但没法使用前缀索引作 ORDER BY 和 GROUP BY,也没法使用前缀索引作覆盖扫描。

 语法: ALTER TABLE table_name ADD KEY(column_name(prefix_length))

  

 

 

 找区别度的方法:

insert into students_1 values (1,'aaaabbcbdaaavvsdfasdfgasdfasdfasdfasdfasdfgasdfgadsf',1,1,now()),
(2,'aaacbbcbdaaavvsdfasdfgasadfasdfasdfsadfsdfgasdfgadsf',1,1,now()),
(3,'aaaebbcbdaaavvsdfasdfasdfaasdfasdfsdfgasdfgasdfgadsf',1,1,now()),
(4,'aaagbbcbdaaavvsdfasdfgasdasdfasdfasdfasdffgasdfgadsf',1,1,now());

SELECT COUNT(DISTINCT LEFT(sname, 1))/COUNT(*) AS sel3,
COUNT(DISTINCT LEFT(sname, 2))/COUNT(*) AS sel4,
COUNT(DISTINCT LEFT(sname, 3))/COUNT(*) AS sel5,
COUNT(DISTINCT LEFT(sname, 4))/COUNT(*) AS sel6,
COUNT(DISTINCT LEFT(sname, 5))/COUNT(*) AS sel7
FROM students_1;

  

如何选择索引列的顺序:

一、常常会被使用到的列优先(选择性差的列不适合,如性别,查询优化器可能会认为全表扫描性能更好); 
二、选择性高的列优先;

三、宽度小的列优先(一页中存储的索引越多,下降I/O,查找越快);

5.3.3 组合/联合索引策略

若是索引了多列,要遵照最左前缀法则。指的是查询从索引的最左前列开始而且不跳过索引中的列。

5.3.4 覆盖索引策略

跟组合索引有点相似,若是索引包含全部知足查询须要的数据的索引则成为覆盖索引(Covering Index),也就是平时所说的不须要回表操做。即索引的叶子节点上面包含了他们索引的数据(hash索引不能够)。 
判断标准:使用explain,能够经过输出的extra列来判断,对于一个索引覆盖查询,显示为using index,MySQL查询优化器在执行查询前会决定是否有索引覆盖查询。

优势: 
一、能够优化缓存,减小磁盘IO操做; 
二、能够减小随机IO,变随机IO操做变为顺序IO操做; 
三、能够避免对InnoDB主键索引的二次查询; 
四、能够避免MyISAM表进行系统调用;

没法使用覆盖索引的状况:

一、存储引擎不支持覆盖索引; 
二、查询中使用了太多的列(如SELECT * ); 
三、使用了双%号的like查询(底层API所限制);

5.3.5 SQL索引优化总结口诀(套路重点

全值匹配我最爱,最左前缀要遵照; 
带头大哥不能死,中间兄弟不能断; 
索引列上不计算,范围以后全失效; 
LIKE百分写最右,覆盖索引不写 * 
不等空值还有or,索引失效要少用; 
字符单引不可丢,SQL高级也不难 ;

 

5.4 使用索引来优化查询

5.4.1 利用索引排序

一、group by 实质是先排序后分组,遵守索引的最佳左前缀。;

二、索引中全部列的方向(升序、降序)和Order By子句彻底一致;

三、当没法使用索引列,增大max_length_for_sort_data参数的设置+增大sort_buffer_size参数的设置;

四、若是最左列使用了范围,则排序会失效;

五、where 高于having,能写在where限定的条件就不要去having去限定了

 

5.5 索引的维护和优化

5.5.1 删除重复索引

 

 

注:主键约束至关于(惟一约束 + 非空约束)

一张表中最多有一个主键约束,若是设置多个主键,就会出现以下提示:Multiple primary key defined!!!

5.5.2 删除冗余索引

检查工具:pt-duplicate-key-checker

能够看看: 
http://www.javashuo.com/article/p-vugvdulg-k.html

 

6、SQL查询优化(重要

6.1 获取有性能问题SQL的三种方式

一、经过用户反馈获取存在性能问题的SQL;

二、经过慢查日志获取存在性能问题的SQL;

三、实时获取存在性能问题的SQL;

6.1.2 慢查日志分析工具

相关配置参数:

slow_query_log # 启动中止记录慢查日志,慢查询日志默认是没有开启的能够在配置文件中开启(on)
slow_query_log_file # 指定慢查日志的存储路径及文件,日志存储和数据从存储应该分开存储

long_query_time # 指定记录慢查询日志SQL执行时间的阀值默认值为10秒一般,对于一个繁忙的系统来讲,改成0.001秒(1毫秒)比较合适
log_queries_not_using_indexes #是否记录未使用索引的SQL

  

经常使用工具:mysqldumpslowpt-query-digest

pt-query-digest --explain h=127.0.0.1,u=root,p=p@ssWord  slow-mysql.log

  

6.1.3 实时获取有性能问题的SQL(推荐)

SELECT id,user,host,DB,command,time,state,info
FROM information_schema.processlist
WHERE TIME>=60

  

6.2 SQL的解析预处理及生成执行计划(重要)

6.2.1 查询过程描述(重点!!!)

 

 经过上图能够清晰的了解到MySql查询执行的大体过程:

  1. 发送SQL语句。

  2. 查询缓存,若是命中缓存直接返回结果。

  3. SQL解析,预处理,再由优化器生成对应的查询执行计划。

  4. 执行查询,调用存储引擎API获取数据。

  5. 返回结果。

6.2.2 查询缓存对性能的影响(建议关闭缓存)

第一阶段: 
相关配置参数:

query_cache_type # 设置查询缓存是否可用
query_cache_size # 设置查询缓存的内存大小
query_cache_limit # 设置查询缓存可用的存储最大值(加上sql_no_cache能够提升效率)
query_cache_wlock_invalidate # 设置数据表被锁后是否返回缓存中的数据
query_cache_min_res_unit # 设置查询缓存分配的内存块的最小单

  

缓存查找是利用对大小写敏感的哈希查找来实现的,Hash查找只能进行全值查找(sql彻底一致),若是缓存命中,检查用户权限,若是权限容许,直接返回,查询不被解析,也不会生成查询计划。

在一个读写比较频繁的系统中,建议关闭缓存,由于缓存更新会加锁。将query_cache_type设置为off,query_cache_size设置为0。

 

第二阶段 
MySQL依照执行计划和存储引擎进行交互

这个阶段包括了多个子过程:

一条查询能够有多种查询方式,查询优化器会对每一种查询方式的(存储引擎)统计信息进行比较,找到成本最低的查询方式,这也就是索引不能太多的缘由。

6.3 会形成MySQL生成错误的执行计划的缘由

一、统计信息不许确 
二、成本估算与实际的执行计划成本不一样

 

三、给出的最优执行计划与估计的不一样 

四、MySQL不考虑并发查询 
五、会基于固定规则生成执行计划 
六、MySQL不考虑不受其控制的成本,如存储过程,用户自定义函数

6.4 MySQL优化器可优化的SQL类型

查询优化器:对查询进行优化并查询mysql认为的成本最低的执行计划。 为了生成最优的执行计划,查询优化器会对一些查询进行改写

能够优化的sql类型 
一、从新定义表的关联顺序;

二、将外链接转换为内链接;

三、使用等价变换规则;

四、优化count(),min(),max();

 

五、将一个表达式转换为常数; 
六、子查询优化;

七、提早终止查询,如发现一个不成立条件(如where id = -1),当即返回一个空结果;

八、对in()条件进行优化;

6.5 查询处理各个阶段所须要的时间

6.5.1 使用profile(目前已经不推荐使用了)
set profiling = 1; #启动profile,这是一个session级的配制执行查询

show profiles; # 查询每个查询所消耗的总时间的信息

show profiles for query N; # 查询的每一个阶段所消耗的时间

  

6.5.2 performance_schema是5.5引入的一个性能分析引擎

(5.5版本时期开销比较大) 
启动监控和历史记录表:

use performance_schema;
update setup_instruments set enabled='YES',TIME = 'YES' WHERE NAME LIKE 'stage%';

update set_consumbers set enabled='YES',TIME = 'YES' WHERE NAME LIKE 'event%';

  

 

 

6.6 特定SQL的查询优化

6.6.1 大表的数据修改

 

 

6.6.2 大表的结构修改

一、添加一个新表(修改后的结构),老表数据导入新表,老表创建触发器,修改数据同步到新表, 老表加一个排它锁(重命名), 新表重命名, 删除老表。

alter table sbtest4 modify c varchar(150) not null default ''

  

二、利用工具修改pt-online-schema-change(推荐)

 

6.6.3 优化not in 和 <> 查询

子查询改写为关联查询: 

7、分库分表

7.1 分库分表的几种方式

分担读负载 可经过 一主多从,升级硬件来解决。

7.1.1 把一个实例中的多个数据库拆分到不一样实例(集群)

 

 拆分简单,不容许跨库。但并不能减小写负载。

7. 1.2 把一个库中的表分离到不一样的数据库中

该方式只能在必定时间内减小写压力。 
以上两种方式只能暂时解决读写性能问题。

7. 1.3 数据库分片

对一个库中的相关表进行水平拆分到不一样实例的数据库中

1.如何选择分区键

  1. 分区键要能尽量避免跨分区查询的发生

  2. 分区键要尽量使各个分区中的数据平均

 2. 分片中如何生成全局惟一ID

相关文章
相关标签/搜索