MySQL数据库优化法则总结

Posted shujutongbu

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了MySQL数据库优化法则总结相关的知识,希望对你有一定的参考价值。

    网上关于SQL优化的教程很多,但是比较杂乱。近日有空整理了一下,写出来跟大家分享一下,其中有错误和不足的地方,还请大家纠正以及补充

     这篇文章我花费了大量的时间查找资料、修改希望大家阅读之后,感觉好的话推荐给更多的人,让更多的人看到、纠正以及补充。



     要正确的优化SQL,我们需要快速定位能性的瓶颈点,也就是说快速找到我们SQL主要的开销在哪里?而大多数情况性能最慢的设备会是瓶颈点,如下载时网络速度可能会是瓶颈点,本地复制文件时硬盘可能会是瓶颈点,为什么这些一般的工作我们能快速确认瓶颈点呢,因为我们对这些慢速设备的性能数据有一些基本的认识,如网络带宽是2Mbps,硬盘是每分钟7200转等等。因此,为了快速找到SQL的性能瓶颈点,我们也需要了解我们计算机系统的硬件基本性能指标,下图展示的当前主流计算机性能指标数据

技术图片


     从图上可以看到基本上每种设备都有两个指标:

延时(响应时间):表示硬件的突发处理能力;

带宽(吞吐量):代表硬件持续处理能力。


      从上图可以看出,计算机系统硬件性能从高到代依次为:

CPU——Cache(L1-L2-L3)——内存——SSD硬盘——网络——硬盘


     根据数据库知识,我们可以列出每种硬件主要的工作内容:

CPU及内存:缓存数据访问、比较、排序、事务检测、SQL解析、函数或逻辑运算;

网络:结果数据传输、SQL请求、远程数据库访问(dblink);

硬盘:数据访问、数据写入、日志记录、大数据量排序、大表连接。

 

     根据当前计算机硬件的基本性能指标及其在数据库中主要操作内容,可以整理出如下图所示的性能基本优化法则:

技术图片  

 

这个优化法则归纳为5个层次:

1、  减少数据访问(减少磁盘访问)

2、  返回更少数据(减少网络传输或磁盘访问)

3、  减少交互次数(减少网络传输)

4、  减少服务器CPU开销(减少CPU及内存开销)

5、  利用更多资源(增加资源)

 

        由于每一层优化法则都是解决其对应硬件的性能问题,所以带来的性能提升比例也不一样。传统数据库系统设计是也是尽可能对低速设备提供优化方法,因此针对低速设备问题的可优化手段也更多,优化成本也更低。我们任何一个SQL的性能优化都应该按这个规则由上到下来诊断问题并提出解决方案,而不应该首先想到的是增加资源解决问题。

        以下是每个优化法则层级对应优化效果及成本经验参考:

优化法则

性能提升效果

优化成本

减少数据访问

1~1000

返回更少数据

1~100

减少交互次数

1~20

减少服务器CPU开销

1~5

利用更多资源

@~10


    接下来,我们针对5种优化法则列举常用的优化手段

a: 表的设计合理化(符合3NF)

b: 优化SQL语句(索引)

c: 分表技术(水平分割、垂直分割)、分区技术

d: 读写[写: update/delete/add]分离

e: 存储过程 [模块化编程,可以提高速度]

f: 对mysql配置优化 [配置最大并发数, 调整缓存大小 ]

g: mysql服务器硬件升级

h: 定时的去清除不需要的数据,定时进行碎片整理


1、表的设计合理化(符合3NF)

1NF(第一范式)

    1NF的限定条件如下:(只要数据库是关系型数据库,就自动的满足1NF)

         1. 每个列必须有一个唯一的名称

         2. 行和列的次序无关紧要

         3. 每一列都必须有单个数据类型

         4. 不允许包含相同值的两行

         5. 每一列都必须包含一个单值 (一个列不能保存多个数据值)

         6. 列不能包含重复的组

第一范式会存在更新、删除和插入异常。


2NF(第二范式)

    2NF的限定条件如下:(通常我们设计一个主键来实现)

         1. 它符合第一范式

         2. 所有的非键值字段均依赖于所有的键值字段

第二范式也会存在更新、删除和插入异常。


3NF(第三范式)

    3NF的限定条件如下:    

         1. 符合2NF

         2. 不包含传递相关性,(即,一个非键值字段的值依赖于另一个非键值字段的值),不含冗余数据


反3NF :没有冗余的数据库未必是最好的数据库,有时为了提高运行效率,就必须降低范式标准,适当保留冗余数据。

具体做法:

在概念数据模型设计时遵守第三范式,降低范式标准的工作放到物理数据模型设计时考虑。降低范式就是增加字段,允许冗余。



2、优化SQL语句

(1)迅速的定位执行速度慢的语句 

     a 开启慢查询

     b 设置慢查询时间

     c 启用慢查询日志

     d 通过mysqldumoslow工具对慢日志进行分类汇总


(2)分析SQL语句

     a 通过explain分析查询 

     b 通profiling可以得到更详细的信息


(3)SQL语句优化

     a 创建索引(主键索引/唯一索引/全文索引/普通索引)

     b 避免Select * (不查询多余的列与行)

     c Where中少用NOT、!=、<>、!<、!>、NOT EXISTS、NOT IN、NOT LIKE、OR,它们会忽略索引,引起全表扫描

     d 用Where子句替代having子句,having只会在检索出所有记录之后才对结果集进行过滤

     e 使用视图(经常被查询的列数据,并且这些数据不被经常的修改,删除)


    数据库索引的原理非常简单,但在复杂的表中真正能正确使用索引的人很少,即使是专业的DBA也不一定能完全做到最优。

    索引会大大增加表记录的DML(INSERT,UPDATE,DELETE)开销,正确的索引可以让性能提升100,1000倍以上,不合理的索引也可能会让性能下降100倍,因此在一个表中创建什么样的索引需要平衡各种业务需求。

    如果我们把一个表的内容认为是一本字典,那索引就相当于字典的目录,如下图所示:

技术图片

技术图片



    图中是一个字典按部首+笔划数的目录,相当于给字典建了一个按部首+笔划的组合索引。

一个表中可以建多个索引,就如一本字典可以建多个目录一样(按拼音、笔划、部首等等)。

一个索引也可以由多个字段组成,称为组合索引,如上图就是一个按部首+笔划的组合目录。


我们一般在什么字段上建索引?

    这是一个非常复杂的话题,需要对业务及数据充分分析后再能得出结果。主键及外键通常都要有索引,其它需要建索引的字段应满足以下条件:

a 字段出现在查询条件中,并且查询条件可以使用索引;

b 语句执行频率高,一天会有几千次以上;

c 通过字段条件可筛选的记录集很小,那数据筛选比例是多少才适合?

    这个没有固定值,需要根据表数据量来评估,以下是经验公式,可用于快速评估:

小表(记录数小于10000行的表):筛选比例<10%;

大表:(筛选返回记录数)<(表总记录数*单条记录长度)/10000/16

单条记录长度≈字段平均内容长度之和+字段数*2


如何知道SQL是否使用了正确的索引?

       简单SQL可以根据索引使用语法规则判断,复杂的SQL不好办,判断SQL的响应时间是一种策略,但是这会受到数据量、主机负载及缓存等因素的影响,有时数据全在缓存里,可能全表访问的时间比索引访问时间还少。要准确知道索引是否正确使用,需要到数据库中查看SQL真实的执行计划,这个话题比较复杂,详见SQL执行计划专题介绍。

 

索引对DML(INSERT,UPDATE,DELETE)附加的开销有多少?

      这个没有固定的比例,与每个表记录的大小及索引字段大小密切相关,以下是一个普通表测试数据,仅供参考:

索引对于Insert性能降低56%

索引对于Update性能降低47%

索引对于Delete性能降低29%

       因此对于写IO压力比较大的系统,表的索引需要仔细评估必要性,另外索引也会占用一定的存储空间。


       切记,性能优化是无止境的,当性能可以满足需求时即可,不要过度优化。在实际数据库中我们不可能把每个SQL请求的字段都建在索引里,所以这种只通过索引访问数据的方法一般只用于核心应用,也就是那种对核心表访问量最高且查询字段数据量很少的查询



3、分表技术(水平分割、垂直分割)、分区技术

为什么要分表和分区? 

    如果遇到大表的情况下,SQL语句优化已经无法继续优化了,我们可以考虑分表和分区,目的就是减少数据库的负担,提高数据库的效率,通常点来讲就是提高表 的增删改查效率。


什么是分表?

      分表是将一个大表按照一定的规则分解成多张具有独立存储空间的实体表,我们可以称为子表,每个表都对应三个文件,MYD数据文件,.MYI索引文件,.frm表结构文件。这些子表可以分布在同一块磁盘上,也可以在不同的机器上。app读写的时候根据事先定义好的规则得到对应的子表名,然后去操作它。


什么是分区?

        分区和分表相似,都是按照规则分解表。不同在于分表将大表分解为若干个独立的实体表,而分区是将数据分段划分在多个位置存放,可以是同一块磁盘也可以在不同的机器。分区后,表面上还是一张表,但数据散列到多个位置了。app读写的时候操作的还是大表名字,db自动去组织分区的数据。


mysql分表和分区有什么联系呢?

(1)都能提高mysql的性能,在高并发状态下都有一个良好的表现。

(2)分表和分区不矛盾,可以相互配合的,对于那些大访问量,并且表数据比较多的表,我们可以采取分表和分区结合的方式,访问量不大,但是表数据很多的表,我们可以采取分区的方式等。

(3)分表技术是比较麻烦的,需要手动去创建子表,app服务端读写时候需要计算子表名。采用merge好一些,但也要创建子表和配置子表间的union关系。

(4)表分区相对于分表,操作方便,不需要创建子表。



4、读写[写: update/delete/add]分离

        大型网站为了缓解大量的并发访问,除了在网站实现分布式负载均衡,远远不够。如果还是传统的数据结构,或者只是单单靠一台服务器扛,如此多的数据库连接操作,数据库必然会崩溃,数据丢失的话,后果更是不堪设想。这时候,我们会考虑如何减少数据库的联接,一方面采用优秀的代码框架,进行代码的优化,采用优秀的数据缓存技术如:memcached,如果资金丰厚的话,必然会想到架设服务器群,来分担主数据库的压力

        因此,一般来说都是通过主从复制(Master-Slave)的方式来同步数据,再通过读写分离(MySQL-Proxy,是MySQL官方提供的MySQL中间件服务)来提升数据库的并发负载能力 这样的方案来进行部署与实施的


实现方式

第一种:php程序上自己做逻辑判断,写php代码的时候,自己在程序上做逻辑判读写匹配。select,insert、update、delete做正则匹配,根据结果选择写服务器(主服务器)。如果是select操作则选择读服务器(从服务器器) mysql_connect('读写的区分')

第二种:MySQL中间件,基本的原理是让主数据库处理写操作(insert、update、delete),而从数据库处理查询操作(select)。而数据库的一致性则通过主从复制来实现。所以说主从复制是读写分离的基础。

      下面是一些常用的MySQL中间件的背景介绍

技术图片



5、存储过程 

(1)为什么需要存储过程

     a 数据不安全,网络传送SQL代码,容易被未授权者截获

     b 每次提交SQL代码都要经过语法编译后在执行,影响应用程序的运行性能

     c 网络流量大,对于反复执行的SQL代码,在网络上多次传送,影响网络传输量

(2)什么是存储过程

       存储过程是SQL语句和控制语句的预编译集合,保存在数据库中,可有应用程序调用执行,而且允许用户声明变量、逻辑控制语句及其他强大的编程功能。包含逻辑控制语句和数据操作语句,可以接收参数、输出参数、返回单个或多个结果值及返回值


(3)使用存储过程的优点

     a 模块化程序设计,只需创建一次,以后即可调用该存储过程任意次

     b 执行速度快,效率高

     c 减少网络流量

     d 具有良好的安全性

 

6、对mysql配置优化   

    下面是一些配置的优化,具体参数的解释就不写了,请自行查找资料

技术图片



7、mysql服务器硬件升级

(1)磁盘 

MySQL每秒钟都在进行大量、复杂的查询操作,对磁盘的读写量可想而知。所以,通常认为磁盘I/O是制约MySQL性能的最大因素之一

解决方案: 使用RAID-10 、磁盘阵列设备SAN 


(2)CPU  对于MySQL应用,推荐使用S.M.P.架构的多路对称CPU

(3)内存  越大越好

(4)网卡  至少两个网卡,均为1GBE。通常我会将这两个nics绑定在一起以提供冗余



8、定时的去清除不需要的数据,定时进行碎片整理

什么是磁盘碎片?

        简单的说,删除数据必然会在数据文件中造成不连续的空白空间,而当插入数据时,这些空白空间则会被利用起来.于是造成了数据的存储位置不连续,以及物理存储顺序与理论上的排序顺序不同,这种是数据碎片.实际上数据碎片分为两种,一种是单行数据碎片,另一种是多行数据碎片.前者的意思就是一行数据,被分成N个片段,存储在N个位置.后者的就是多行数据并未按照逻辑上的顺序排列.

        当有大量的删除和插入操作时,必然会产生很多未使用的空白空间,这些空间就是多出来的额外空间.索引也是文件数据,所以也会产生索引碎片,理由同上,大概就是顺序紊乱的问题.Engine 不同,OPTIMIZE 的操作也不一样的,MyISAM 因为索引和数据是分开的,所以 OPTIMIZE 可以整理数据文件,并重排索引。这样不但会浪费空间,并且查询速度也更慢。


解决方案:

(1)查看表碎片的方法

select ROW_FORMAT,TABLE_ROWS,DATA_LENGTH,INDEX_LENGTH,MAX_DATA_LENGTH,DATA_FREE,ENGINE from TABLES where TABLE_SCHEMA='test_db' and TABLE_NAME='table_name' limit 1;


(2)Innodb存储引擎清理碎片方法

ALTER TABLE tablename ENGINE=InnoDB


(3)Myisam存储引擎清理碎片方法

OPTIMIZE TABLE table_name


切记,一定要在夜里执行,表越大,越耗资源时间,不要频繁修复,可以几个月甚至一年修复一次,如果表频繁被更改,可以做个计划任务,按周/月来整理。







以上是关于MySQL数据库优化法则总结的主要内容,如果未能解决你的问题,请参考以下文章

MySQL索引优化系列:索引全用及最左法则

mysql 索引优化法则

#yyds干货盘点#MySQL索引优化系列:索引全用及最左法则

MYSQL索引优化法则

mysql 优化

MySQL数据库优化