概述
互联网服务中最容易遇到瓶颈的就是数据库,在数据库调优上我们需要关注以下几个方向
1. 架构调优 2 mysql 配置调优
3
设计优化
4 sql
索引优化
架构调优
在架构调优上,我们需要关注的是提高整个mysql 的吞吐量与可用性
mysql 配置调优
per thread buffers 线程缓存调优
read_buffer_size
该参数用于表的顺序扫描,表示每个线程分配的缓冲区大小。比如,在进行全表扫描时,MySQL会按照数据的存储顺序依次读取数据块,每次读取的数据块首先会暂存在
read_buffer_size
中,当
buffer
空间被写满或者
或者全部数据读取结束后,再将buffer中的数据返回给上层调用者,以提高效率。默认为
128KB
。这个参数不要设置过大,一般在
128~256KB
即可。
read_rnd_buffer_size
该参数用于表的随机读取,表示每个线程分配的缓冲区大小。比如,按照一个非索引字段做orderby排序操作时,就会利用这个缓冲区来暂存读取的数据。默认为
256KB
。这个参数不要设置过大,一般在
128~256KB
即可。
sort_buffer_size
在表进行orderby和
groupby
排序操作时,由于排序的字段没有索引,会出现
Usingfilesort
,为了提高性能,可用此参数增加每个线程分配的缓冲区大小。默认为
2MB
。这个参数不要设置过大,一般在
128~256KB
即可。
thread_stack
该参数表示每个线程的堆栈大小。默认为192KB。如果是
64
位操作系统,设置为
256KB
即可,这个参数不要设置过大。
join_buffer_size
表进行join连接操作时,如果关联的字段没有索引,会出现
Usingjoinbuffer
,为了提高性能,可用此参数增加每个线程分配的缓冲区大小。默认为
128KB
。这个参数不要设置过大,一般在
128~256KB
即可。一般出现
Usingjoinbuffer
的时候,要通过增加索引来解决。
binlog_cache_size
一般来说,如果数据库中没有什么大事务,写入也不是特别频繁,将其设置为1~2MB是一个合适的选择。如果有很大的事务,可以适当增加这个缓存值,以获得更好的性能。
max_connections
该参数用来设置最大连接数,默认为100。一般设置为
512~1000
即可。
该参数设置过小的最明显特征是出现”
Toomanyconnections
”错误
global buffers 全局缓存
innodb_buffer_pool_size
这个参数是InnoDB存储引擎的核心参数,默认为
128MB
,在
mysql
专用服务器上,这个参数建议设置为物理内存的
60%~70%
。主要作用是缓存
innodb
表的索引,数据,插入数据时的缓冲。并不是设置的越大越好。设置的过大,会导致
system
的
swap
空间被占用,导致操作系统变慢,从而减低
sql
查询的效率。
innodb_additional_mem_pool_size
该参数用来存储数据字典信息和其他内部数据结构。表越多,需要在这里分配的内存越多。如果InnoDB用光了这个池内的内存,
InnoDB
开始从操作系统分配内存,并且往
MySQL
错误日志中写警告信息,默认值是
8MB
,当发现错误日志中已经有相关的警告信息时,就应该适当地增加该参数的大小。一般设置为
16MB
即可。
innodb_max_dirty_pages_pct
这个百分比是,最大脏页的百分数,当系统中脏页所占百分比超过这个值,INNODB就会进行写操作以把页中的已更新数据写入到磁盘文件中。
innodb_log_buffer_size
事务日志所使用的缓冲区。InnoDB在写事务日志的时候,为了提高性能,先将信息写入
InnodbLogBuffer
中,当满足
innodb_flush_log_trx_commit
参数所设置的相应条件(或者日志缓冲区写满)时,再将日志写到文件(或者同步到磁盘)中。可以通过
innodb_log_buffer_size
参数设置其可以使用的最大内存空间。默认为
8MB
,一般设置为
16~64MB
即可。
key_buffer_size该参数用来缓存
MyISAM
存储引擎的索引参数。
MySQL5.5
默认为
InnoDB
存储引擎,所以这个参数不用设置过大
query_cache_size
缓存select语句和结果集大小的参数。查询缓存的空间不要设置的太大。
因为查询缓存是靠一个全局锁操作保护的,如果查询缓存配置的内存比较大且里面存放了大量的查询结果,当查询缓存失效的时候,会长时间的持有这个全局锁。因为查询缓存的命中检测操作以及缓存失效检测也都依赖这个全局锁,所以可能会导致系统僵死的情况
query_cache_limit
query_cache_limit指定单个查询能够使用的缓冲区大小,缺省为
1M
。
query_cache_type
指定是否使用查询缓冲,可以设置为0、
1
、
2
query_cache_min_res_unit
是在4.1版本以后引入的,它指定分配缓冲区空间的最小单位,缺省为
4K
tmp_table_sizemax_heap_table_size
它规定了内部内存临时表的最大值,每个线程都要分配。(实际起限制作用的是tmp_table_size和
max_heap_table_size
的最小值。
优化查询语句的时候,要避免使用临时表,如果实在避免不了的话,要保证这些临时表是存在内存中的。如果需要的话并且你有很多groupby语句,并且你有很多内存,增大
tmp_table_size(
和
max_heap_table_size)
thread_cache_size
Thread_Cache中存放的最大连接线程数
.
在短连接的应用中
Thread_Cache
的功效非常明显
,
因为在应用中数据库的连接和创建是非常频繁的
,
如果不使用
Thread_Cache
那么消耗的资源是非常可观的
!
在长连接中虽然带来的改善没有短连接的那么明显
,
但是好处是显而易见的
.
但并不是越大越好大了反而浪费资源这个的确定一般认为和物理内存有一定关系
table_cache表缓存
table_cache指示表高速缓存的大小。当
Mysql
访问一个表时,如果在
Mysql
表缓冲区中还有空间,那么这个表就被打开并放入表缓冲区,这样做的好处是可以更快速地访问表中的内容。一般来说,可以通过查看数据库运行峰值时间的状态值
Open_tables
和
Opened_tables
,用以判断是否需要增加
table_cache
的值,即如果
open_tables
接近
table_cache
的时候,并且
Opened_tables
这个值在逐步增加,那就要考虑增加这个值的大小了。
在mysql默认安装情况下,
table_cache
的值在
2G
内存以下的机器中的值默认时
256
到
512
,如果机器有
4G
内存
,
则默认这个值是
2048
,但这决意味着机器内存越大,这个值应该越大,因为
table_cache
加大后,使得
mysql
对
SQL
响应的速度更快了,不可避免的会产生更多的死锁(
deadlock
),这样反而使得数据库整个一套操作慢了下来,严重影响性能。所以平时维护中还是要根据库的实际情况去作出判断,找到最适合你维护的库的
table_cache
值
Innodb事务日志参数
innodb_log_file_size
日志组里每个日志文件的大小。早期的版本中,在32位的计算机上,日志文件的合并大小必须小于
4GB
。默认是
5MB
。不太好去确定
innodb_log_file_size
这个参数的大小,早期
MySQL
版本的配置文件里建议的
InnoDB
缓冲大小的
25%
是没有什么道理的,首先
InnoDB
缓冲不一定就设置对了,而且
InnoDB
事务日志一般和你的日志写入量、写入频率有关系,和你的缓冲池大小不存在必然的关系。我的经验值是
256~512MB
。
innodb_flush_log_at_trx_commit
当innodb_flush_log_at_trx_commit被设置为
0
时,日志缓冲将每秒一次被写到日志文件中,并且对日志文件进行磁盘操作的刷新,但是在事务提交时不进行任何操作。当这个值为
1
(默认值)时,在每个事务进行提交时,日志缓冲将被写到日志文件,且把对日志文件的变更刷新到磁盘中。当设置为
2
时,在每个事务进行提交时,日志缓冲将被写到文件,但不会对日志文件进行到磁盘操作的刷新,对日志文件的刷新每秒发生一次。我们的生产环境一般推荐设置为
innodb_flush_log_at_trx_commit=2
,因为它可以兼顾效率和一定的安全性,理想情况下,最多可能丢失
1
秒的事务。如果设置为
1
,则对于性能的影响会很大,因为每次提交事务,都会伴随着磁盘
I/O
的操作,需要把数据刷新到磁盘,
I/O
可能会成为瓶颈,对于高安全性的数据,在能够满足
I/O
性能的前提下,可以考虑将其设置为
1
。
sync_binlog
这个参数是设置,每当写了sync_binlog次二进制日志后,把日志实际刷新到磁盘中,默认值是
0
,不与硬盘同步。生产环境的推荐设置是
8~20
,这样可以兼顾效率和安全,如果设置为
1
,你可能会碰到
I/O
瓶颈,你需要选用更好的
SSD
设备,或者使用带电池的
RAID
卡来缓解
I/O
瓶颈,优化文件系统也是一个选项,
ext4
和
xfs
就比
ext3
的表现要好得多。
innodb_thread_concurrency
InnoDB试着在
InnoDB
内部保持操作系统线程的数量少于或等于这个参数给出的限制。官方建议是将其设置为处理器数目加磁盘数之和,对于高并发事务,也许你应该把这个值设置得更大一些。对于一些资源等待异常的情况,后来的事务会被已经在等待队列中的事务卡住,你可以通过临时增大这个值,让更多的事务并发执行。
innodb_log_files_in_group
该变量控制日志文件数。默认值为2。日志是以顺序的方式写入
innodb_autoextend_increment
这个参数的作用是控制innodb共享表空间文件自动扩展的大小,在
mysql5.6.5
版本之前默认值是
8Mb
,从
5.6.6
版本之后默认为
64Mb
,最小值为
1Mb
最大值为
1000Mb
有大批量插入数据时可适当调大
innodb_fush_method
Windows和非
Windows
的操作系统对这个选项的值是互斥的:
async_unbuffered
、
unbuffered
和
normal
只能在
Windows
下使用,并且
Windows
下不能使用其他的值。
其他参数
skip_name_resolve
必须设置此项,因MySQL的
DNS
解析可能会导致严重的性能问题。注意设置了此项之后,
MySQL
权限表将使用
IP
来统一标识主机,而不能使用主机名来标识了。
设计优化
范式与反范式
第一范式是指数据库表的每一列(属性)都是不可分割的基本数据项,这就要求数据库的每一列都只能存放单一值,即实体中的某个属性不能有多个值或不能有重复的属性。
第二范式一个数据表符合第二范式的前提是该数据表符合第一范式。它的规则是要求数据表里的所有数据都要和该数据表的主键有完全相依的关系;如果有哪些数据只和主键的一部分有关的话,就得把它们独立出来变成另一个数据表。如果一个数据表的主键只有单一一个字段的话,那么它就一定符合第二范式。
第三范式
要求一个数据库表中不包含已在其它表中已包含的非主关键字信息
反范式
反范式是试图通过增加冗余数据或通过分组数据来优化数据库读取性能的过程。在某些情况下,反范式是解决数据库性能和可伸缩性的极佳策略。
引擎选择
·如果没有特殊的情况,建议选择
InnoDB
引擎。
字段类型选择
保小不保大
选择字段的一般原则是保小不保大,能用占用字节少的字段就不用大字段。比如,主键,强烈建议用int整型,不用
guid
,来省空间。空间就是效率!按
4
个字节和按
32
个字节定位一条记录,谁快谁慢太明显了。涉及几个表做
join
时,效果就更明显了。更小的字段类型占用的内存就更少,占用的磁盘空间和磁盘
I/O
也会更少,而且还会占用更少的带宽。因此,在日常选择字段时必须要遵守这一规则。
数值类型
在MySQL中支持的
5
个主要整数类型是
TINYINT
、
SMALLINT
、
MEDIUMINT
、
INT
和
BIGINT
手机号使用bigint(20),bigint(20)宽度为
20
,只占用
8
字节,
chart(11)
占用
33
字节
IP地址也可采用
int
整型
MySQL里提供了一个很好用的函数:
INET_ATON()
,它负责把
IP
地址转为数字,而另一个函数
INET_NTOA()
负责把数字转换为
IP
用户在线状态,0表示离线、
1
表示在线、
2
表示离开、
3
表示忙碌、
4
表示隐身等,其实类似这样的情况,用
int
都是没有必要的,浪费空间,采用
tinyint
完全可以满足需要,不推荐使用枚举类型,存在扩展性问题
字符类型
char和
varchar
是日常使用最多的字符类型。
char(N)
用于保存固定长度的字符串,长度最大为
255
,比指定长度大的值将被截短,而比指定长度小的值将会用空格进行填补。
varchar(N)
用于保存可变长度的字符串,长度最大为
65535
,只存储字符串实际需要的长度(它会增加一个额外字节来存储字符串本身的长度),
varchar
使用额外的
1~2
字节来存储值的长度,如果列的最大长度小于或等于
255
,则使用
1
字节,否则就是用
2
字节。
char
和
varchar
跟字符编码也有密切联系,
latin1
占用
1
个字节,
gbk
占用
2
个字节,
utf8
占用
3
个字节。经常变化的值,如家庭住址,由于每个人地址都不同,有的地址很长有的很短,那么用
varchar
相对比较合适,因为它只存储字符串实际的长度。而对于固定长度的值,比如
uuid
函数,是数字和字母组成的
36
位长度的字符类型,长度是固定不变的,或者是
md5
加密后的
32
位长度的字符类型,那么可以设置为
char(36)
和
char(32)
,这样相比于
varchar
,还节省空间,因为
varchar
还要用
1
字节存储值的长度。,不可使用大于其实际长度的
varchar
长度,
MySQL
需要先在内存中分配固定的空间来保存值,这无形中就浪费了内存,而且对表的排序或使用临时表尤其不好,所以只分配真正需要的那部分空间即可。
时间类型
在MySQL中支持的
5
个时间类型是
DATE
、
TIME
、
DATETIME
、
TIMESTAMP
和
YEAR
,类型,
datetime
和
timestamp
都可以精确到秒,但
datetime
占用
8
字节,而
timestamp
只占用
4
字节,在日常建表时应优先选择
timestamp
类型。
timestamp
还具有自动更新时间功能
浮点型
·存储精确浮点数时必须使用
DECIMAL
替代
FLOAT
和
DOUBLE
。
默认值
MySQL字段建议设置为非
NULL
,默认值,方便程序处理,减少
insert
时数据的插入量,避免
count
计数时忽略
null
的情况,
NULL
值的存储需要额外的空间,且会导致比较运算更为复杂,这会使优化器更难以优化
SQL
。
Sql优化
定位慢查询
慢查询带来的直接性能损耗就是数据库系统中最为昂贵的I/O资源。对于
MySQL
数据库来说,来说,
I/O
出现瓶颈,会导致连接数增大、锁表,更有可能导致业务访问失败,尤其是在高并发场合下。开启慢查询记录功能带来的好处是可以通过分析慢
SQL
来优化
SQL
语句,从而解决因慢
SQL
引起的各种问题
explain 分析
项目中已经实现的explain 分析拦截器,只需要在开发时开启即可
Explain 介绍
id
SELECT识别符。这是
SELECT
查询序列号。这个不重要
,
查询序号即为
sql
语句执行的顺序
select_type
select类型,它有以下几种值
simple 它表示简单的
select,
没有
union
和子查询
primary 最外面的
select,
在有子查询的语句中,最外面的
select
查询就是
primary
union union语句的第二个或者说是后面那一个
.
现执行一条语句
dependent union UNION中的第二个或后面的
SELECT
语句,取决于外面的查询
union result UNION的结果
table
输出的行所用的表,这个参数显而易见,容易理解
type
连接类型。有多个参数,先从最佳类型到最差类型介绍
system
表仅有一行,这是const类型的特列,平时不会出现,这个也可以忽略不计
const
表最多有一个匹配行,const用于比较
primary key
或者
unique
索引。因为只匹配一行数据,所以很快,一定是用到
primary key
或者
unique
,并且只检索出两条数据的 情况下才会是
const
eq_ref
对于eq_ref的解释,
mysql
手册是这样说的
:"
对于每个来自于前面的表的行组合,从该表中读取一行。这可能是最好的联接类型,除了
const
类型。它用在一个索引的所有部分被联接使用并且索引是
UNIQUE
或
PRIMARY KEY
。
eq_ref
可以用于使用
=
比较带索引的列
ref
对于每个来自于前面的表的行组合,所有有匹配索引值的行将从这张表中读取。如果联接只使用键的最左边的前缀,或如果键不是UNIQUE或
PRIMARY KEY
ref_or_null
该联接类型如同ref,但是添加了
MySQL
可以专门搜索包含
NULL
值的行。在解决子查询中经常使用该联接类型的优化
index_merge
该联接类型表示使用了索引合并优化方法。在这种情况下,key列包含了使用的索引的清单,
key_len
包含了使用的索引的最长的关键元素
range
给定范围内的检索,使用一个索引来检查行
index
该联接类型与ALL相同,除了只有索引树被扫描。这通常比
ALL
快,因为索引文件通常比数据文件小
ALL 对于每个来自于先前的表的行组合,进行完整的表扫描。如果表是第一个没标记
const
的表,这通常不好,并且通常在它情况下很差
possible_keys
提示使用哪个索引会在该表中找到行,不太重要
keys
MYSQL使用的索引,简单且重要
key_len
MYSQL使用的索引长度
ref
ref列显示使用哪个列或常数与
key
一起从表中选择行。
rows
显示MYSQL执行查询的行数,简单且重要,数值越大越不好,说明没有用好索引
Extra
该列包含MySQL解决查询的详细信息
Distinct MySQL发现第
1
个匹配行后,停止为当前的行组合搜索更多的行。一直没见过这个值
Not exists
range checked for each record 没有找到合适的索引
using filesort
这意味着MySQL会对结果使用一个外部索引排序,而不是按索引次序从表里读取行。
MySQL
有两种文件排序算法,两种方式都可以在内存或磁盘上完成。
EXPLAIN
不会告诉你
MySQL
将使用哪一种文件排序,也不会告诉你排序会在内存里还是磁盘上完成。
using index
只使用索引树中的信息而不需要进一步搜索读取实际的行来检索表中的信息。这个比较容易理解,就是说明是否使用了索引
using temporary
为了解决查询,MySQL需要创建一个临时表来容纳结果。典型情况如查询包含可以按不同情况列出列的
GROUP BY
和
ORDER BY
子句时。
出现using temporary就说明语句需要优
using where
WHERE子句用于限制哪一个行匹配下一个表或发送到客户。除非你专门从表中索取或检查所有行,如果
Extra
值不为
Using where
并且表联接类型为
ALL
或
index
,查询可能会有一些错误化了
query cost
在MySQL 5.7 中,提供了
json
形式的
explain
,只要在
explain
后加上
FORMAT=JSON
,就可以输出,应该查看的一个组件是“
query cost
”。
query cost
是指
MySQL
根据查询执行的总开销来考虑这个特定查询的代价,并且基于许多不同的因素。
简单查询的查询开销通常小于1,
000
。开销在
1
,
000
到
100
,
000
之间的查询被认为是中等开销的查询,而且如果每秒只运行数百个这样的查询
(
而不是数万个
)
,通常会比较快。
开销超过100,
000
的查询可以当作是昂贵的。通常,当您是系统上的单个用户时,这些查询仍会快速运行,但您应该仔细考虑在交互式应用程序中使用此类查询的频率
where 条件
Where 条件的字段在符号前方使用函数,计算,正则表达式不走索引
,
字段类型不匹配不走索引,使用
or
不走索引,当如
like
%% 这样的条件存在时,可以考虑使用延迟关联来提高查询效率
关联查询
尽可能少使用关联查询,如遇到关联查询的情况应尽可能拆分查询,或增加冗余字段的方式来解决,如果要使用,请确保
on
上的字段有索引
子查询
子查询尽可能使用关联查询或拆分查询代替
limit 分页
优化大表的limit 查询时,可加上相应区间缩小查找范围,也可通过内联方式来提高查询速度
or
使用union all 来代替
or
查询
In
Mysql 会使用二分查找去定位
in
中的数据,
in
不易过长,最好不要超过
200
,对于高并发业务,小于几十为佳
not in
使用 join 来代替
not in
having
避免使用having,用
where
代替
Order by group by
优化order by group by 关键是要避免 文件排序和临时表,主要策略是对
order by
和
group by
的字段建立索引,另外要避免对多个字段排序和分组的情况,
group by
默认进行了排序,如果不需要排序,请加上
order by null
Union
如果使用union 务必要使用
union all
,
Count
在使用count 统计数量时,如果只需要得到非精确数量,可以使用
explain
来进行统计数量
INSERT
·
INSERT
语句 如果 使用 批量 提交( 如
INSERT INTO table VALUES(),(),()
……), 那么
VALUES
的 个数 不应 过多。
索引建立
1.对
where
条件中的字段建立单列或联合索引,联合索引遵守最左匹配原则
2.取出数据量超过表中数据的
20%
,
mysql
优化器不会使用索引
3.唯一性太差的字段不建立索引
4.一个表中索引不超过
5
个
5.建议索引中的字段不超过
5
个
6.唯一主键和索引不要重复,优先使用主键查询
7.ORDERBY、
GROUPBY
、
DISTINCT
的字段需要放在复合索引的后面,也就是说,复合索引的前面部分用于等值查询,后面的部分用于排序。
8.UPDATE、
DELETE
语句需要根据
WHERE
条件添加索引。
9·对长度过长的
VARCHAR
字段(比如网页地址)建立索引时,需要增加散列字段,对
VARCHAR
使用散列算法时,散列后的字段最好是整型,然后对该字段建立索引。
10.把范围条件放到复合索引的最后,
WHERE
条件中的范围条件(
BETWEEN
、
<
、
<=
、
>
、
>=
)会导致后面的条件使用不了索引。
11.当查询
where
没有用到索引时,可以利用覆盖索引提高查询效率,即建立索引的字段正好是覆盖查询语句
[select
子句
]
与查询条件
[Where
子句
]
中所涉及的字段,也即,索引包含了查询正在查找的所有数据