mysql的内存分配,是调优的重中之重,所以必须搞清楚内存是怎么分配的
mysql> show global variables like '%buffer%';
+-------------------------+------------+
| Variable_name | Value |
+-------------------------+------------+
| bulk_insert_buffer_size | 4194304 |
| innodb_buffer_pool_size | 2013265920 |
| innodb_change_buffering | inserts |
| innodb_log_buffer_size | 8388608 |
| join_buffer_size | 1048576 |
| key_buffer_size | 16777216 |
| myisam_sort_buffer_size | 262144 |
| net_buffer_length | 16384 |
| preload_buffer_size | 32768 |
| read_buffer_size | 1048576 |
| read_rnd_buffer_size | 1048576 |
| sort_buffer_size | 1048576 |
| sql_buffer_result | OFF |
+-------------------------+------------+
13 rows in set (0.01 sec)
mysql> show global variables like '%cache%';
+------------------------------+----------------------+
| Variable_name | Value |
+------------------------------+----------------------+
| binlog_cache_size | 1048576 |
| have_query_cache | YES |
| key_cache_age_threshold | 300 |
| key_cache_block_size | 1024 |
| key_cache_division_limit | 100 |
| max_binlog_cache_size | 18446744073709547520 |
| query_cache_limit | 1048576 |
| query_cache_min_res_unit | 4096 |
| query_cache_size | 0 |
| query_cache_type | ON |
| query_cache_wlock_invalidate | OFF |
| table_definition_cache | 256 |
| table_open_cache | 100 |
| thread_cache_size | 100 |
+------------------------------+----------------------+
14 rows in set (0.00 sec)
可以看到部分配置信息
内存的组成
1、线程共享内存 2、线程独享内存
used_Mem =
+ key_buffer_size
+ query_cache_size
+ innodb_buffer_pool_size
+ innodb_additional_mem_pool_size
+ innodb_log_buffer_size
+ max_connections *(
+ read_buffer_size
+ read_rnd_buffer_size
+ sort_buffer_size
+ join_buffer_size
+ binlog_cache_size
+ thread_stack
+ tmp_table_size
+ bulk_insert_buffer_size
)
线程独享内存
1、read_buffer_size: 顺序读取数据缓冲区使用内存
这部分内存主要用于当需要顺序读取数据的时候,如无发使用索引的情况下的全表扫描,全索引扫描等。在这种时候,MySQL 按照数据的存储顺序依次读取数据块,每次读取的数据快首先会暂存在read_buffer_size中,当 buffer 空间被写满或者全部数据读取结束后,再将buffer中的数据返回给上层调用者,以提高效率。
2、read_rnd_buffer_size:随机读取数据缓冲区使用内存
和顺序读取相对应,当 MySQL 进行非顺序读取(随机读取)数据块的时候,会利用这个缓冲区暂存读取的数据。如根据索引信息读取表数据,根据排序后的结果集与表进行Join等等。总的来说,就是当数据块的读取需要满足一定的顺序的情况下,MySQL 就需要产生随机读取,进而使用到 read_rnd_buffer_size 参数所设置的内存缓冲区。
3、sort_buffer_size:排序使用内存
MySQL 用此内存区域进行排序操作(filesort),完成客户端的排序请求。当我们设置的排序区缓存大小无法满足排序实际所需内存的时候,MySQL 会将数据写入磁盘文件来完成排序。由于磁盘和内存的读写性能完全不在一个数量级,所以sort_buffer_size参数对排序操作的性能影响绝对不可 小视
4、join_buffer_size:连接使用内存
应用程序经常会出现一些两表(或多表)Join的操作需求,MySQL在完成某些 Join 需求的时候(all/index join),为了减少参与Join的“被驱动表”的读取次数以提高性能,需要使用到 Join Buffer 来协助完成 Join操作。 当 Join Buffer 太小,MySQL 不会将该 Buffer 存入磁盘文件,而是先将Join Buffer中的结果集与需要 Join 的表进行 Join 操作,然后清空 Join Buffer 中的数据,继续将剩余的结果集写入此 Buffer 中,如此往复。这势必会造成被驱动表需要被多次读取,成倍增加 IO 访问,降低效率。
5、thread_stack:线程栈信息使用内存
主要用来存放每一个线程自身的标识信息,如线程id,线程运行时基本信息等等,我们可以通过 thread_stack 参数来设置为每一个线程栈分配多大的内存
6、tmp_table_size:临时表使用内存
当我们进行一些特殊操作如需要使用临时表才能完成的 Order By,Group By 等等,MySQL 可能需要使用到临时表。当我们的临时表较小(小于 tmp_table_size 参数所设置的大小)的时候,MySQL 会将临时表创建成内存临时表,只有当 tmp_table_size 所设置的大小无法装下整个临时表的时候,MySQL 才会将该表创建成 MyISAM 存储引擎的表存放在磁盘上。不过,当另一个系统参数 max_heap_table_size 的大小还小于 tmp_table_size 的时候,MySQL 将使用 max_heap_table_size 参数所设置大小作为最大的内存临时表大小,而忽略 tmp_table_size 所设置的值。而且 tmp_table_size 参数从 MySQL 5.1.2 才开始有,之前一直使用 max_heap_table_size。
7、bulk_insert_buffer_size:批量插入暂存使用内存
当我们使用如 insert … values(…),(…),(…)… 的方式进行批量插入的时候,MySQL 会先将提交的数据放如一个缓存空间中,当该缓存空间被写满或者提交完所有数据之后,MySQL 才会一次性将该缓存空间中的数据写入数据库并清空缓存。此外,当我们进行 LOAD DATA INFILE 操作来将文本文件中的数据 Load 进数据库的时候,同样会使用到此缓冲区
8、binlog_cache_size:二进制日志缓冲使用内存
我们知道InnoDB存储引擎是支持事务的,实现事务需要依赖于日志技术,为了性能,日志编码采用二进制格式。那么,我们如何记日志呢?有日志的时候,就 直接写磁盘?可是磁盘的效率是很低的,如果你用过Nginx,一般Nginx输出access log都是要缓冲输出的。因此,记录二进制日志的时候,我们是否也需要考虑Cache呢?答案是肯定的,但是Cache不是直接持久化,于是面临安全性的 问题——因为系统宕机时,Cache中可能有残余的数据没来得及写入磁盘。因此,Cache要权衡,要恰到好处:既减少磁盘I/O,满足性能要求;又保证 Cache无残留,及时持久化,满足安全要求
设置太大的话,会比较消耗内存资源;设置太小的话,如果用户提交一个“长事务(long_transaction)”,比如:批量导入数据。那么该事务必然会产生很多binlog,这样 cache可能不够用(默认binlog_cache_size是32K),不够用的时候mysql会把uncommitted的部分写入临时文件(临时 文件cache的效率必然没有内存cache高),等到committed的时候才会写入正式的持久化日志文件。
线程共享内存
1、query_cache_size:查询缓存
查询缓存是 MySQL 比较独特的一个缓存区域,用来缓存特定 Query 的结果集(Result Set)信息,共享给所有客户端。
通过对 Query 语句进行特定的 Hash 计算之后与结果集对应存放在 Query Cache 中,以提高完全相同的 Query 语句的相应速度。
当我们打开 MySQL 的 Query Cache 之后,MySQL 接收到每一个 SELECT 类型的 Query 之后都会首先通过固定的 Hash 算法得到该 Query 的 Hash 值,然后到 Query Cache 中查找是否有对应的 Query Cache。如果有,则直接将 Cache 的结果集返回给客户端。如果没有,再进行后续操作,得到对应的结果集之后将该结果集缓存到 Query Cache 中,再返回给客户端。
当任何一个表的数据发生任何变化之后,与该表相关的所有 Query Cache 全部会失效,所以 Query Cache 对变更比较频繁的表并不是非常适用,但对那些变更较少的表是非常合适的,可以极大程度的提高查询效率,如那些静态资源表,配置表等等。为了尽可能高效的利 用 Query Cache,MySQL 针对 Query Cache 设计了多个 query_cache_type 值和两个 Query Hint:SQL_CACHE 和 SQL_NO_CACHE。
a、当query_cache_type 设置为0(或者 OFF)的时候 不使用 Query Cache
b、当query_cache_type设置为1(或者 ON)的时候,当且仅当 Query 中使用了 SQL_NO_CACHE 的时候 MySQL 会忽略 Query Cache
c、query_cache_type 设置为2(或者DEMAND)的时候,当且仅当Query 中使用了 SQL_CACHE 提示之后,MySQL 才会针对该 Query 使用 Query Cache。
可以通过 query_cache_size 来设置可以使用的最大内存空间
2、binlog_cache_size:二进制日志缓冲区
二进制日志缓冲区主要用来缓存由于各种数据变更操做所产生的 Binary Log 信息。
为了提高系统的性能,MySQL 并不是每次都是将二进制日志直接写入 Log File,而是先将信息写入 Binlog Buffer 中,当满足某些特定的条件(如 sync_binlog参数设置)之后再一次写入 Log File 中。我们可以通过 binlog_cache_size 来设置其可以使用的内存大小,同时通过 max_binlog_cache_size 限制其最大大小(当单个事务过大的时候 MySQL 会申请更多的内存)。当所需内存大于 max_binlog_cache_size 参数设置的时候,MySQL 会报错:“Multi-statement transaction required more than ‘max_binlog_cache_size’ bytes of storage”。
3、key_buffer_size:MyISAM索引缓存
The key_buffer_size indicates the size of the key cache that MySQL uses to store indexes in memory. The cache stores index blocks in memory to avoid reading the disk repeatedly. The key_buffer_size is one of the most important variables to tune to improve MySQL database performance. The index blocks of MyISAM tables are stored in the key cache and are accessible to all processes which use MySQL globally.
The maximum size of the key_buffer_size variable is 4 GB on 32 bit machines, and larger for 64 bit machines. MySQL recommends that you keep the key_buffer_size less than or equal to 25% of the RAM on your machine. This also depends on the other processes that use memory on the machine and it is wise to check if you consistently have 25% of free memory using the Linux command free. More on this later.
4、innodb_log_buffer_size:InnoDB 日志缓冲区
这是 InnoDB 存储引擎的事务日志所使用的缓冲区。类似于 Binlog Buffer,InnoDB 在写事务日志的时候,为了提高性能,也是先将信息写入 Innofb Log Buffer 中,当满足 innodb_flush_log_trx_commit 参数所设置的相应条件(或者日志缓冲区写满)之后,才会将日志写到文件(或者同步到磁盘)中。可以通过 innodb_log_buffer_size 参数设置其可以使用的最大内存空间。
注:innodb_flush_log_trx_commit 参数对 InnoDB Log 的写入性能有非常关键的影响。该参数可以设置为0,1,2,解释如下:
mysql日志操作步骤 :log_buffer ---mysql写 (write)---> log_file ---OS刷新 (flush)---> disk
- 0:log buffer中的数据将以每秒一次的频率写入到log file中,且同时会进行文件系统到磁盘的同步操作,但是每个事务的commit并不会触发任何log buffer 到log file的刷新或者文件系统到磁盘的刷新操作;
- 1:在每次事务提交的时候将log buffer 中的数据都会写入到log file,同时也会触发文件系统到磁盘的同步;
- 2:事务提交会触发log buffer 到log file的刷新,但并不会触发磁盘文件系统到磁盘的同步。此外,每秒会有一次文件系统到磁盘同步操作。
具体的展示
- 0(延迟写): log_buffer --每隔1秒--> log_file —实时—> disk
- 1(实时写,实时刷): log_buffer —实时—>实时—> disk
- 2(实时写,延迟刷): log_buffer —实时—>--每隔1秒--> disk
推荐的做法是 innodb_flush_log_at_trx_commit=2 ,sync_binlog=N (N为500 或1000) 且使用带蓄电池后备电源的缓存cache,防止系统断电异常
注:sync_binlog =N (N>0) ,MySQL 在每写 N次 二进制日志binary log时,会使用fdatasync()函数将它的写二进制日志binary log同步到磁盘中去
此外,MySQL文档中还提到,这几种设置中的每秒同步一次的机制,可能并不会完全确保非常准确的每秒就一定会发生同步,还取决于进程调度 的问题。实际上,InnoDB 能否真正满足此参数所设置值代表的意义正常 Recovery 还是受到了不同 OS 下文件系统以及磁盘本身的限制,可能有些时候在并没有真正完成磁盘同步的情况下也会告诉 mysqld 已经完成了磁盘同步。
5、innodb_buffer_pool_size:InnoDB 数据和索引缓存
InnoDB Buffer Pool 对 InnoDB 存储引擎的作用类似于 Key Buffer Cache 对 MyISAM 存储引擎的影响,主要的不同在于 InnoDB Buffer Pool 不仅仅缓存索引数据,还会缓存表的数据,而且完全按照数据文件中的数据快结构信息来缓存,这一点和 Oracle SGA 中的 database buffer cache 非常类似。所以,InnoDB Buffer Pool 对 InnoDB 存储引擎的性能影响之大就可想而知了。可以通过 (Innodb_buffer_pool_read_requests – Innodb_buffer_pool_reads) / Innodb_buffer_pool_read_requests * 100% 计算得到 InnoDB Buffer Pool 的命中率。
6、innodb_additional_mem_pool_size:InnoDB 字典信息缓存
InnoDB 字典信息缓存主要用来存放 InnoDB 存储引擎的字典信息以及一些 internal 的共享数据结构信息。所以其大小也与系统中所使用的 InnoDB 存储引擎表的数量有较大关系。不过,如果我们通过 innodb_additional_mem_pool_size 参数所设置的内存大小不够,InnoDB 会自动申请更多的内存,并在 MySQL 的 Error Log 中记录警告信息。
这里所列举的各种共享内存,是我个人认为对 MySQL 性能有较大影响的集中主要的共享内存。实际上,除了这些共享内存之外,MySQL 还存在很多其他的共享内存信息,如当同时请求连接过多的时候用来存放连接请求信息的back_log队列等。