本文主旨:从现有的软件和硬件配置中获得最佳的数据库性能。

  1. 在数据库级别进行优化
  2. 在硬件级别进行优化

1. 在数据库级别进行优化

优化思路:

  1. 表格的结构,字段的数据类型。例如,执行频繁更新的应用程序应多表少列,而分析大量数据的应用程序少表多列。
  2. 是否有合适的索引。
  3. 存储引擎,是否利用了存储引擎的优势和功能。事务性存储引擎(如InnoDB)或非事务性存储引擎(如MyISAM)的选择对于性能和可伸缩性非常重要。  InnoDB是新的默认存储引擎,高级的InnoDB性能特性优于更简单的MyISAM表,特别是对于繁忙的数据库。
  4. 行格式,取决于表使用的存储引擎。压缩表使用较少的磁盘空间,需要较少的磁盘I / O来读取和写入数据。压缩适用于带有 InnoDB表的所有工作负载以及只读 MyISAM表。
  5. 锁定策略,例如,通过在可能的情况下允许共享访问,以便数据库操作可以同时运行,并在适当的时候请求独占访问,以使关键操作获得最高优先级。同样,存储引擎的选择很重要。InnoDB存储引擎处理大部分锁定问题,不需要人为参与,允许在数据库更好的并发。  、
  6. 缓存的大小,需要足够大以容纳经常访问的数据,但又不能太大以至于它们会使物理内存过载并导致分页。要配置的主要内存区域是InnoDB缓冲池,MyISAM键高速缓存和MySQL查询高速缓存。

2 在硬件级别进行优化

  1. 磁盘搜索。磁盘查找数据需要花费时间。对于现代磁盘,此操作的平均时间通常小于10毫秒,因此理论上我们可以执行约100秒钟的搜索。这段时间随着新磁盘的使用而缓慢改善,并且很难为单个表进行优化。优化寻道时间的方法是将数据分发到多个磁盘上。
  2. 磁盘读写。当磁盘位于正确的位置时,我们需要读取或写入数据。一个磁盘至少可提供10–20MB / s的吞吐量。与查找相比,优化起来更容易,您可以从多个磁盘并行读取。
  3. CPU周期。当数据位于主存储器中时,我们必须对其进行处理以获得结果。与内存量相比,拥有较大的表是最常见的限制因素。但是对于小型表,速度通常不是问题。
  4. 内存带宽。当CPU需要的数据超出CPU缓存的容量时,主内存带宽将成为瓶颈。对于大多数系统来说,这是一个不常见的瓶颈,但是要意识到这一点。

3 查找需要优化的 SQL 语句

3.1 查看运行的进程

show processlist;

id列:一个标识,你要kill 一个语句的时候很有用。

command列:显示当前连接的执行的命令,一般就是休眠(sleep),查询(query),连接(connect)。

time列:此这个状态持续的时间,单位是秒。

state只是语句执行中的某一个状态,是判断性能好坏的关键.

state

create tmp table  -- 创建临时表, 索引及现有结构无法涵盖查询条件,才会建立一个临时表来满足查询要求
copying to tmp table on disk  把内存临时表复制到磁盘
locked  被其他查询锁住
loggin slow query 慢查询记录
sorting result  排序

3.2 开启慢查询日志

MySQL通过慢查询日志定位那些执行效率较低的SQL 语句,通常意义上来讲,只要返回时间大于 >1 sec上的查询都可以称为慢查询。慢查询会导致CPU,内存消耗过高。

-- long_query_time 当SQL语句执行时间超过此数值时,就会被记录到日志中
-- 默认如图,建议设置为1或者更短。
show variables like 'long%'; -- 查询
set long_query_time=1; -- 设值

windows mysql性能优化配置 mysql性能优化策略_数据

-- slow_query_log 这个参数设置为ON,可以捕获执行时间超过一定数值的SQL语句。
-- slow_query_log_file 记录日志的文件名。
-- slow_query_log变量被设置为ON,MySQL会立即开始记录。

show variables like 'slow%'; -- 查询
set global slow_query_log='on'; -- 设值

windows mysql性能优化配置 mysql性能优化策略_数据_02

用--log-slow-queries[=file_name]选项启动时,mysqld 会写一个包含所有执行时间超过 long_query_time 秒的SQL语句的日志文件 。

修改完配置信息后,通过    show variables like 'long%';    show variables like 'slow%';  两条命令看配置是否生效。

3.2.1 Windows下开启MySQL慢查询

MySQL在Windows系统中的配置文件一般是是my.ini找到[mysqld]下面加

查找my.ini位置,可通过windows服务所对应mysql启动项,查看其对应属性->可执行文件路径,获取my.ini路径。

更改后重启mysql服务生效

-- 设置日志位置及文件名
log_slow_queries = F:/MySQL/log/mysqlslowquery.log
long_query_time = 1

-- MySQL5.7
-- 默认情况下,将禁用慢速查询日志。要显式指定初始慢速查询日志状态
-- 如果没有参数或参数为1,--slow_query_log将启用日志。
-- 参数为0时,此选项将禁用日志。
slow_query_log[={0 | 1}]

-- 指定日志文件名
slow_query_log_file=file_name

3.2.2 Linux下启用MySQL慢查询

MySQL在Linux系统中的配置文件一般是是my.cnf找到[mysqld]下面加上

-- 设置日志文件位置及文件名
log slow queries = /data/mysqldata/slowquery.log

long_query_time = 1

3.2.3 mysqldumpslow 工具对日志进行分析

我们可以使用 mysqldumpslow --help 查看命令相关用法。(windows安装MySQL默认不安装一些插件,需要先配置Perl 环境,好难一年之内都没有大佬写这个文. . . . . .)

参数如下:

-s:排序方式,后边接着如下参数
        c:访问次数
        l:锁定时间
        r:返回记录
        t:查询时间
        al:平均锁定时间
        ar:平均返回记录书
        at:平均查询时间
    -t:返回前面多少条的数据
    -g:搭配一个正则表达式,大小写不敏感

案例:

-- 得到返回记录最多的10个sql
mysqldumpslow -s r -t 10 slow.log

-- 得到平均访问次数最多的10条sql
mysqldumpslow -s ar -t 10 slow.log

-- 得到平均访问次数最多,并且里面含有user字符的20条sql
mysqldumpslow -s ar -t 20 -g "user" slow.log

4  分析SQL语句

4.1 explain语句 

查看 SQL 执行计划情况(关联表,表查询顺序、索引使用情况等)。

为了避免本文太长,指路 2.5 explain 查看索引是否生效  

4.2 profiling 分析

使用 profiling 命令可以了解 SQL 语句消耗资源的详细信息(每个执行步骤的开销)。

-- 查看 profile 开启情况
-- 0 表示关闭状态,1 表示开启
select @@profiling;

-- 启用 profile
set profiling = 1; 

-- 注意 :在连接关闭后,profiling 状态自动设置为关闭状态。

windows mysql性能优化配置 mysql性能优化策略_windows mysql性能优化配置_03

-- 查看执行的 SQL 列表
show profiles;

windows mysql性能优化配置 mysql性能优化策略_mysql_04

-- 查询指定 ID 的执行详细信息
-- 每行都是状态变化的过程以及它们持续的时间。
-- Status 这一列和 show processlist 的 State 是一致的。
-- 注意点与上文描述的一样 3.1 查看运行的进程。
show profile for query Query_ID;

windows mysql性能优化配置 mysql性能优化策略_MySQL_05

-- 获取 CPU、 Block IO 等信息
show profile block io,cpu for query Query_ID;
show profile cpu,block io,memory,swaps,context switches,source for query Query_ID;
show profile all for query Query_ID;

5 优化手段

5.1 查询语句优化

1) 避免 SELECT *,需要什么数据,就查询对应的字段。

2) 小表驱动大表,即小的数据集驱动大的数据集。如:以 A,B 两表为例,两表通过 id 字段进行关联。

当 B 表的数据集小于 A 表时,用 in 优化 exist;使用 in ,两表执行顺序是先查 B 表,再查 A 表

select * from A where id in (select id from B)

当 A 表的数据集小于 B 表时,用 exist 优化 in;使用 exists,两表执行顺序是先查 A 表,再查 B 表

select * from A where exists (select 1 from B where B.id = A.id)

3) 一些情况下,可以使用连接代替子查询,因为使用 join,MySQL 不会在内存中创建临时表。左连接性能取决于左表, 右连接取决于右表。

4) 对于频繁操作的数据,可适当添加冗余字段,减少表关联。反第三范式做法。

5) 在使用group by 分组查询时,默认分组后,还会排序,可能会降低速度,在group by 后面增加 order by null 就可以防止排序。

5.2 正确使用索引

指路 : 2 索引  

5.3 数据库表设计

5.3.1 遵循三大范式 

  • 第一范式要求列保证原子性
  • 第二范式用于限制多对多关系. 使用中间表配合联合主键.
  • 第三范式用于限制一对多(多对一)关系. 使用外键.

5.3.2 选择适合的数据类型

1) 使用可以存下数据最小的数据类型

2) 使用简单的数据类型。int 要比 varchar 类型在 MySQL 处理简单

3) 尽量使用 tinyint、smallint、mediumint 作为整数类型而非 int

4) 尽可能使用 not null 定义字段,因为 null 占用4字节空间

5) 尽量少用 text 类型,非用不可时最好考虑分表

6) 尽量使用 timestamp 而非 datetime

7) 单表不要有太多字段,建议在 20 以内

5.3.3 分表技术

大表的优化!!!在完成以上优化后,考虑表的拆分。 让每张表的数据量变小,从而提高查询效率。

为什么要分表?

(1) 如果一个表的每条记录的内容很大,那么就需要更多的IO操作,如果字段值比较大,而使用频率相对比较低,可以将大字段移到另一张表中,当查询不查大字段的时候,这样就减少了I/O操作
(2)如果表的数据量非常非常大,那么查询就变的比较慢;也就是表的数据量影响这查询的性能。
(3)表中的数据本来就有独立性,例如分别记录各个地区的数据或者不同时期的数据,特别是有些数据常用,而另外一些数据不常用。

1)垂直拆分 

将表中多个列分开放到不同的表中。例如用户表中一些字段经常被访问,将这些字段放在一张表中,另外一些不常用的字段放在另一张表中,分离冷热数据。插入数据时,使用事务确保两张表的数据一致性。

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

垂直拆分的缺点:主键会出现冗余,需要管理冗余列,并会引起Join操作,可以通过在应用层进行Join来解决。此外,垂直分区会让事务变得更加复杂。

2) 水平拆分

按照行进行拆分。例如用户表中,使用用户ID,对用户ID取10的余数,将用户数据均匀的分配到0~9的10个用户表中。查找时也按照这个规则查询数据。水平分表尽可能使每张表的数据量相当,比较均匀。

  1. 水平拆分会给应用增加复杂度,它通常在查询是需要多个表名,查询所有数据需要union操作。在许多数据库应用中,这种复杂性会超过它带来的优点。
  2. 因为只要索引关键字不大,则在索引用于查询时,表中增加2-3倍数据量,查询时也就增加读一个索引层的磁盘次数,所以水平拆分要考虑数据量的增长速度,根据实际情况决定是否需要对表进行水平拆分。

水平分割最重要的是找到分割的标准,不同的表应根据业务找出不同的标准

  1. 用户表可以根据用户的手机号段进行分割如user183、user150、user153、user189等,每个号段就是一张表
  2. 用户表也可以根据用户的 id 进行分割,加入分3张表user0,user1,user2,如果用户的id%3=0就查询user0表,
    如果用户的id%3=1就查询user1表
  3. 对于订单表可以按照订单的时间进行分表

5.3.4 读写分离

一般情况下对数据库而言都是“读多写少”。数据库的压力多数是因为大量的读取数据的操作造成的。我们可以采用数据库集群的方案,使用一个库作为主库,负责写入数据;其他库为从库,负责读取数据。这样可以缓解对数据库的访问压力。

读写分离就是只在主服务器上写,只在从服务器上读。基本原理是让主数据库处理事务性查询,而从服务器处理select查询。数据库复制被用来把事务性查询导致的变更同步到从数据库中。

实现MySQL读写分离的前提是我们已经将MySQL主从复制配置完毕,读写分离实现方式:
(1)配置多数据源。
(2)使用 MySQL 的 proxy 中间件代理工具。