MySQL内存参数及调整
本文主要介绍MySQL的内存参数的作用及其对性能的影响和调整,以及MySQL的内存分配
欢迎转载,请注明作者、出处。
作者:张正
blog:http://space.itpub.net/26355921
QQ:176036317
如有疑问,欢迎联系。
MySQL内存参数配置推荐:https://tools.percona.com/wizard
1.慢查询日志:
slow_launch_time=2 查询大于某个时间的值(单位:s)
slow_query_log=on/off 开启关闭慢查询日志
slow_query_log_file=/opt/data/host-slow.log 慢查询日志位置
2.连接数:
max_connections MySQL最大连接数
back_log 当连接数满了后,设置一个值,允许多少个连接进入等待堆栈
max_connect_errors 账号连接到服务器允许的错误次数
connect_timeout 一个连接报文的最大时间(单位:s)
skip-name-resolve 加入my.cnf即可,MySQL在收到连接请求的时候,会根据请求包
中获得的ip来反向追查请求者的主机名。然后再根据返回
的主机名又一次去获取ip。如果两次获得的ip相同,那么连接就成功建立了。
加了次参数,即可省去这个步骤
NOTES:
查询当前连接数:show global status like 'connections';
3.key_buffer_size 索引缓存大小,是对MyISAM表性能影响最大的一个参数
32bit平台上,此值不要超过2GB,64bit平台不用做此限制,但也不要超过4GB
根据3点计算:
a.系统索引总大小
b.系统物理内存
c.系统当前keycache命中率
粗略计算公式:
Key_Size =key_number*(key_length+4)/0.67
Max_key_buffer_size <>
Threads_Usage = max_connections * (sort_buffer_size + join_buffer_size +
read_buffer_size+read_rnd_buffer_size+thread_stack)
key_cache_block_size ,是key_buffer缓存块的单位长度,以字节为单位,默认值为1024。
key_cache_division_limit 控制着缓存块重用算法。默认值为100,此值为key_buffer_size中暖链所占的大小百分比(其中有暖链和热链),100意味着全是暖链。(类似于Oracle Data Buffer Cache中的default、keep、recycle)
key_cache_age_threshold 如果key_buffer里的热链里的某个缓存块在这个变量所设定的时间里没有被访问过,MySQL服务器就会把它调整到暖链里去。这个参数值越大,缓存块在热链里停留的时间就越长。
这个参数默认值为 300,最小值为100。
Myisam索引默认是缓存在原始key_buffer中的,我们可以手动创建新的key_buffer,如在my.cnf中加入参数new_cache.key_buffer_size=20M。指定将table1和table2的索引缓存到new_cache的key_buffer中:
cache index table1,table2 in new_cache;
(之前默认的key_buffer为default,现在手动创建的为new_cache)
手动将table1和table2的索引载入到key_buffer中:
load index into cache table1,table2;
系统中记录的与Key Cache相关的性能状态参数变量: global status
◆Key_blocks_not_flushed,已经更改但还未刷新到磁盘的DirtyCacheBlock;
◆Key_blocks_unused,目前未被使用的CacheBlock数目;
◆Key_blocks_used,已经使用了的CacheBlock数目;
◆Key_read_requests,CacheBlock被请求读取的总次数;
◆Key_reads,在CacheBlock中找不到需要读取的Key信息后到“.MYI”文件中(磁盘)读取的次数;
◆Key_write_requests,CacheBlock被请求修改的总次数;
◆Key_writes,在CacheBlock中找不到需要修改的Key信息后到“.MYI”文件中读入再修改的次数;
索引命中缓存率:
key_buffer_read_hits=(1-Key_reads/Key_read_requests)*100%
key_buffer_write_hits=(1-Key_writes/Key_write_requests)*100%
该命中率就代表了MyISAM类型表的索引的cache
4.临时表 tmp_table_size (用于排序)
show global status like ‘created_tmp%’;
| Variable_name | Value |
| Created_tmp_disk_tables | 21197 | #在磁盘上创建临时表的次数
| Created_tmp_files | 58 | #在磁盘上创建临时文件的次数
| Created_tmp_tables | 1771587 | #使用临时表的总次数
TmpTable的状况主要是用于监控MySQL使用临时表的量是否过多,
是否有临时表过大而不得不从内存中换出到磁盘文件上。
a.如果:
Created_tmp_disk_tables/Created_tmp_tables>10%,则需调大tmp_table_size
比较理想的配置是:
Created_tmp_disk_tables/Created_tmp_tables<=25%
b.如果:
Created_tmp_tables非常大 ,则可能是系统中排序操作过多,或者是表连接方式不是很优化。
相关参数:
tmp_table_size 内存中,临时表区域总大小
max_heap_table_size 内存中,单个临时表的最大值,超过的部分会放到硬盘上。
5.table cache相关优化 :
参数table_open_cache,将表的文件描述符打开,cache在内存中
global status:
open_tables 当前系统中打开的文件描述符的数量
opened_tables 系统打开过的文件描述符的数量
如果:
Opened_tables数量过大,说明配置中table_open_cache值可能太小
比较合适的值为:
Open_tables / Opened_tables * 100% >= 85%
Open_tables / table_open_cache * 100% <= 95%
6.进程的使用情况
在MySQL中,为了尽可能提高客户端请求创建连接这个过程的性能,实现了一个ThreadCache池,
将空闲的连接线程存放在其中,而不是完成请求后就销毁。这样,当有新的连接请求的时候,
MySQL首先会检查ThreadCache池中是否存在空闲连接线程,如果存在则取出来直接使用,
如果没有空闲连接线程,才创建新的连接线程。
参数:thread_cache_size
thread cache 池中存放的最大连接数
调整参考:
在短连接的数据库应用中,数据库连接的创建和销毁是非常频繁的,
如果每次都需要让MySQL新建和销毁相应的连接线程,那么这个资源消耗实际上是非常大的,因此
thread_cache_size的值应该设置的相对大一些,不应该小于应用系统对数据库的实际并发请求数。
参数:thread_stack - 每个连接线程被创建的时候,MySQL给他分配的内存大小,
类似PGA中存放数据的内存部分(不包括排序的空间)
show status like 'connections';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| Connections | 80 | #接受到的来自客户端的总连接数,包括以前和现在的连接。
+---------------+-------+
show status like 'thread%';
+-------------------+-------+
| Variable_name | Value |
+-------------------+-------+
| Threads_cached | 0 | #当前系统中,缓存的连接数
| Threads_connected | 1 | #当前系统中正连接的线程数
| Threads_created | 77 | #创建过的总线程数
| Threads_running | 1 |
+-------------------+-------+
a.如果:
Threads_created 值过大,说明MySQL一直在创建线程,这是比较消耗资源的,应该适当增大
thread_cache_size的值
b.如果:
Threads_cached的值比参数thread_cache_size小太多,则可以适当减小thread_cache_size的值
ThreadCache命中率:
Threads_Cache_Hit=(Connections-Threads_created)/Connections*100%
一般来说,当系统稳定运行一段时间之后,我们的ThreadCache命中率应该保持在90%
左右甚至更高的比率才算正常。
7.查询缓存(Query Cache) -- optional
将客户端的SQL语句(仅限select语句)通过hash计算,放在hash链表中,同时将该SQL的结果集
放在内存中cache。该hash链表中,存放了结果集的内存地址以及所涉及到的所有Table等信息。
如果与该结果集相关的任何一个表的相关信息发生变化后(包扩:数据、索引、表结构等),
就会导致结果集失效,释放与该结果集相关的所有资源,以便后面其他SQL能够使用。
当客户端有select SQL进入,先计算hash值,如果有相同的,就会直接将结果集返回。
Query Cache的负面影响:
a.使用了Query Cache后,每条select SQL都要进行hash计算,然后查找结果集。对于大量SQL
访问,会消耗过多额外的CPU。
b.如果表变更比较频繁,则会造成结果集失效率非常高。
c.结果集中保存的是整个结果,可能存在一条记录被多次cache的情况,这样会造成内存资源的
过度消耗。
Query Cache的正确使用:
a.根据表的变更情况来选择是否使用Query Cache,可使用SQL Hint:SQL_NO_CACHE和SQL_CACHE
b.对于 变更比较少 或 数据基本处于静态 的表,使用SQL_CACHE
c.对于结果集比较大的,使用Query Cache可能造成内存不足,或挤占内存。
可使用1.SQL_NO_CACHE 2.query_cache_limit控制Query Cache的最大结果集(系统默认1M)
mysql> show variables like '%query_cache%';
+------------------------------+---------+
| Variable_name | Value |
+------------------------------+---------+
| have_query_cache | YES | #是否支持Query Cache
| query_cache_limit | 1048576 | #单个结果集的最大值,默认1M
| query_cache_min_res_unit | 4096 | #每个结果集存放的最小内存,默认4K
| query_cache_size | 0 | #Query Cache总内存大小,必须是1024的整数倍
| query_cache_type | ON | #ON,OFF,DEMAND(包含SQL_CACHE的查询中才开启)
| query_cache_wlock_invalidate | OFF |
+------------------------------+---------+
#query_cache_wlock_invalidate:
针对于MyISAM存储引擎,设置当有WRITELOCK在某个Table上面的时候,
读请求是要等待WRITE LOCK释放资源之后再查询还是允许直接从QueryCache中读取结果,
默认为FALSE(可以直接从QueryCache中取得结果)
mysql> show status like 'qcache%';
+-------------------------+-------+
| Variable_name | Value |
+-------------------------+-------+
| Qcache_free_blocks | 0 |
| Qcache_free_memory | 0 |
| Qcache_hits | 0 |
| Qcache_inserts | 0 |
| Qcache_lowmem_prunes | 0 |
| Qcache_not_cached | 0 |
| Qcache_queries_in_cache | 0 |
| Qcache_total_blocks | 0 |
+-------------------------+-------+
# Qcache_free_blocks
QueryCache中目前还有多少剩余的blocks
a.如果Qcache_free_blocks值较大,说明Query Cache中内存碎片比较多
b.如果Qcache_free_blocks约等于Qcache_total_blocks/2,说明内存碎片非常严重
移除碎片:
flush query cache;
这个命令会把所有的存储块向上移动,并把自由块移到底部。
查询缓存碎片率:
查询缓存碎片率 = Qcache_free_blocks / Qcache_total_blocks * 100%
c.如果:
查询缓存碎片率超过20%, 可以用flush query cache整理碎片,或者减小
query_cache_min_res_unit(如果该系统的查询都是小数据量的话)
# Qcache_free_memory
QueryCache中目前剩余的内存大小
查询缓存利用率:
查询缓存利用率 = (query_cache_size – Qcache_free_memory) / query_cache_size * 100%
a.如果:
查询缓存利用率在25%以下,说明query_cache_size设置过大,可适当减小。
b.如果:
查询缓存利用率>80%,且Qcache_lowmem_prunes>50,说明query_cache_size可能有点小,或者
有太多的碎片
# Qcache_hits
Query Cache的命中次数,可以看到QueryCache的基本效果;
# Qcache_inserts
Query Cache未命中然后插入的次数
Query Cache的命中率:
=Qcache_hits/(Qcache_hits+Qcache_inserts)
# Qcache_lowmem_prunes
因为内存不足而被清除出Query Cache的SQL数量。
如果:
Qcache_lowmem_prunes的值正在增加,并且有大量的Qcache_free_blocks,
这意味着碎片导致查询正在被从缓存中永久删除。
# Qcache_not_cached
因为query_cache_type的设置或者不能被cache的select SQL数量
# Qcache_queries_in_cache
Query Cache中cache的select SQL数量
# Qcache_total_blocks
当前Query Cache中block的总数量
Query Cache限制:
a) 5.1.17之前的版本不能Cache帮定变量的Query,但是从5.1.17版本开始,QueryCache已经开
始支持帮定变量的Query了;
b) 所有子查询中的外部查询SQL不能被Cache;
c) 在Procedure,Function以及Trigger中的Query不能被Cache;
d) 包含其他很多每次执行可能得到不一样结果的函数的Query不能被Cache。
8.排序使用情况:
参数 :sort_buffer_size - 单个thread能用来排序的内存空间大小,系统默认2M
mysql> show variables like 'sort%';
+------------------+---------+
| Variable_name | Value |
+------------------+---------+
| sort_buffer_size | 2097144 |
+------------------+---------+
mysql> show global status like 'sort%';
+-------------------+-------+
| Variable_name | Value |
+-------------------+-------+
| Sort_merge_passes | 0 |#在内存中无法完成排序,而在磁盘上创建临时文件的次数(两倍)
| Sort_range | 0 |#在范围内执行的排序的数量
| Sort_rows | 0 |#已经排序的行数
| Sort_scan | 0 |#通过扫描表完成的排序的数量
+-------------------+-------+
9.文件打开数 open_files_limit
mysql> show variables like 'open%';
+------------------+-------+
| Variable_name | Value |
+------------------+-------+
| open_files_limit | 1024 | #mysql总共能够打开的文件的数量
+------------------+-------+
mysql> show global status like 'open%file%';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| Open_files | 79 | # 系统当前打开的文件数
| Opened_files | 278 | # 系统打开过的文件总数
+---------------+-------+
比较合适的设置:Open_files / open_files_limit * 100% <= 75%
10.表锁情况
mysql> show global status like 'table%';
+-----------------------+-------+
| Variable_name | Value |
+-----------------------+-------+
| Table_locks_immediate | 96 | # 表示立即释放的表锁数
| Table_locks_waited | 0 | # 表示需要等待的表锁数
+-----------------------+-------+
如果 Table_locks_immediate / Table_locks_waited > 5000,最好采用InnoDB引擎。
因为InnoDB是行锁而MyISAM是表锁,对于高并发写入的应用InnoDB效果会好些。
11.表扫描情况
mysql> show global status like 'handler_read%';
+-----------------------+-------+
| Variable_name | Value |
+-----------------------+-------+
| Handler_read_first | 60 |
| Handler_read_key | 2442 |
| Handler_read_next | 286 |
| Handler_read_prev | 0 |
| Handler_read_rnd | 28 |
| Handler_read_rnd_next | 3191 |
+-----------------------+-------+
mysql> show global status like 'com_select';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| Com_select | 23 |
+---------------+-------+
计算表扫描率:
表扫描率 = Handler_read_rnd_next / Com_select
如果:
表扫描率超过4000,说明进行了太多表扫描,很有可能索引没有建好,
增加read_buffer_size值会有一些好处,但最好不要超过8MB。
# Handler_read_first
此选项表明SQL是在做一个全索引扫描(注意是全部,而不是部分),所以说如果存在WHERE语句,
这个值是不会变的。如果这个值的数值很大,既是好事 也是坏事。
说它好是因为毕竟查询是在索引里完成的,而不是数据文件里,说它坏是因为大数据量时,
即便是索引文件,做一次完整的扫描也是很费时的。
# Handler_read_key
此选项数值如果很高,说明系统高效的使用了索引,一切运转良好
# Handler_read_next
此选项表明在进行索引扫描时,按照索引从数据文件里取数据的次数
# Handler_read_prev
此选项表明在进行索引扫描时,按照索引倒序从数据文件里取数据的次数,
一般就是ORDER BY ... DESC。
# Handler_read_rnd
简单的说,就是查询直接操作了数据文件,很多时候表现为没有使用索引或者文件排序
可能是有大量的全表扫描或连接时没恰当使用keys。
The number of requests to read a row based on a fixed position. This value is
high if you are doing a lot of queries that require sorting of the result. You
probably have a lot of queries that require MySQL to scan entire tables or you
have joins that do not use keys properly.
# Handler_read_rnd_next
此选项表明在进行数据文件扫描时,从数据文件里取数据的次数。(物理IO次数)
12.dalayed_queue_size
在被插入到实际的数据表里之前,来自insert delayed语句的数据航将在每个队列里等待
MySQL来处理他们。delayed_queue_size就是这个队列所能容纳的数据航的最大个数。当
这个队列满是,后续的insert delayed语句将被阻塞,直到这个队列里有容纳他们的空间
为止。
如果有很多客户在发出insert delayed语句以避免受阻塞,但你发现这些语句有阻塞的迹象,
加大这个变量的值将使更多的insert delayed语句更快地得到处理。
13.max_allowed_packet(最大值1G,默认值1M)
MySQL服务器在于客户端程序之间进行通信时使用的缓冲区的最大长度。
如果你的客户端经常批量传输一些非常长的语句,就需要在服务器端和客户端同时加大这个变量的值。
一般推荐,最少32M。
14.MySQL内存分配
mysql服务器为每个连接上的客户端线程,分配的内存空间:
read_buffer_size + read_rnd_buffer_size + sort_buffer_size +
thread_stack + join_buffer_size
从内存的使用方式MySQL 数据库的内存使用主要分为以下两类
· 线程独享内存
· 全局共享内存
先分析 MySQL 中主要的 “线程独享内存” 的。
在 MySQL 中,线程独享内存主要用于各客户端连接线程存储各种操作的独享数据,如线程栈信息,分组排序操作,数据读写缓冲,结果集暂存等等,而且大多数可以通过相关参数来控制内存的使用量。
线程栈信息使用内存(thread_stack):
主要用来存放每一个线程自身的标识信息,如线程id,线程运行时基本信息等等,我们可以通过 thread_stack 参数来设置为每一个线程栈分配多大的内存。
排序使用内存(sort_buffer_size):
MySQL 用此内存区域进行排序操作(filesort),完成客户端的排序请求。当我们设置的排序区缓存大小无法满足排序实际所需内存的时候,MySQL 会将数据写入磁盘文件来完成排序。由于磁盘和内存的读写性能完全不在一个数量级,所以sort_buffer_size参数对排序操作的性能影响绝对不可小视。排序操作的实现原理请参考:MySQL Order By 的实现分析(http://www.kuqin.com/database/20081206/29716.html)。
Join操作使用内存(join_buffer_size):
应用程序经常会出现一些两表(或多表)Join的操作需求,MySQL在完成某些 Join 需求的时候(all/index join),为了减少参与Join的“被驱动表”的读取次数以提高性能,需要使用到 Join Buffer 来协助完成 Join操作(具体 Join 实现算法请参考:
MySQL 中的 Join 基本实现原理(http://www.kuqin.com/database/20081206/29717.html))。当 Join Buffer 太小,MySQL 不会将该 Buffer 存入磁盘文件,而是先将Join Buffer中的结果集与需要 Join 的表进行 Join 操作,然后清空 Join Buffer 中的数据,继续将剩余的结果集写入此 Buffer 中,如此往复。这势必会造成被驱动表需要被多次读取,成倍增加 IO 访问,降低效率。
顺序读取数据缓冲区使用内存(read_buffer_size):
这部分内存主要用于当需要顺序读取数据的时候,如无法使用索引的情况下的全表扫描,全索引扫描等。在这种时候,MySQL 按照数据的存储顺序依次读取数据块,每次读取的数据快首先会暂存在read_buffer_size中,当 buffer 空间被写满或者全部数据读取结束后,再将buffer中的数据返回给上层调用者,以提高效率。
随机读取数据缓冲区使用内存(read_rnd_buffer_size):
和顺序读取相对应,当 MySQL 进行非顺序读取(随机读取)数据块的时候,会利用这个缓冲区暂存读取的数据。如根据索引信息读取表数据,根据排序后的结果集与表进行Join等等。总的来说,就是当数据块的读取需要满足一定的顺序的情况下,MySQL 就需要产生随机读取,进而使用到 read_rnd_buffer_size 参数所设置的内存缓冲区。
连接信息及返回客户端前结果集暂存使用内存(net_buffer_size):
这部分用来存放客户端连接线程的连接信息和返回客户端的结果集。当 MySQL 开始产生可以返回的结果集,会在通过网络返回给客户端请求线程之前,会先暂存在通过 net_buffer_size 所设置的缓冲区中,等满足一定大小的时候才开始向客户端发送,以提高网络传输效率。不过,net_buffer_size 参数所设置的仅仅只是该缓存区的初始化大小,MySQL 会根据实际需要自行申请更多的内存以满足需求,但最大不会超过 max_allowed_packet 参数大小。
批量插入暂存使用内存(bulk_insert_buffer_size):
当我们使用如 insert … values(…),(…),(…)… 的方式进行批量插入的时候,MySQL 会先将提交的数据放如一个缓存空间中,当该缓存空间被写满或者提交完所有数据之后,MySQL 才会一次性将该缓存空间中的数据写入数据库并清空缓存。此外,当我们进行 LOAD DATA INFILE 操作来将文本文件中的数据 Load 进数据库的时候,同样会使用到此缓冲区。
MySQL对硬件的"收益递减点“为256G内存,32CPU。
(percona-server 5.1版本)
本文转自ITPUB博客84223932的博客,原文链接:MySQL内存参数及调整,如需转载请自行联系原博主。