search 2013 adfgs
作者:Sky.Jian | 可以任意转载, 但转载时务必以超链接形式标明文章原始出处 和 作者信息 及 版权声明
链接:http://isky000.com/database/mysql-memory-global-shared | del.icio.us | Twitter it

接着之前的一篇“ MySQL 内存使用-线程独享”,再写一篇 MySQL 全局共享内存的使用介绍。

全局共享内则主要是 MySQL Instance(mysqld进程)以及底层存储引擎用来暂存各种全局运算及可共享的暂存信息,如存储查询缓存的 Query Cache,缓存连接线程的 Thread Cache,缓存表文件句柄信息的 Table Cache,缓存二进制日志的 BinLog Buffer, 缓存 MyISAM 存储引擎索引键的 Key Buffer以及存储 InnoDB 数据和索引的 InnoDB Buffer Pool 等等。下面针对 MySQL 主要的共享内存进行一个简单的分析。

查询缓存(Query Cache):查询缓存是 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。当 query_cache_type 设置为0(或者 OFF)的时候不使用 Query Cache,当设置为1(或者 ON)的时候,当且仅当 Query 中使用了 SQL_NO_CACHE 的时候 MySQL 会忽略 Query Cache,当 query_cache_type 设置为2(或者DEMAND)的时候,当且仅当Query 中使用了 SQL_CACHE 提示之后,MySQL 才会针对该 Query 使用 Query Cache。可以通过 query_cache_size 来设置可以使用的最大内存空间。

连接线程缓存(Thread Cache):连接线程是 MySQL 为了提高创建连接线程的效率,将部分空闲的连接线程保持在一个缓存区以备新进连接请求的时候使用,这尤其对那些使用短连接的应用程序来说可以极大的提高创建连接的效率。当我们通过 thread_cache_size 设置了连接线程缓存池可以缓存的连接线程的大小之后,可以通过(Connections – Threads_created) / Connections * 100% 计算出连接线程缓存的命中率。注意,这里设置的是可以缓存的连接线程的数目,而不是内存空间的大小。

表缓存(Table Cache):表缓存区主要用来缓存表文件的文件句柄信息,在 MySQL5.1.3之前的版本通过 table_cache 参数设置,但从MySQL5.1.3开始改为 table_open_cache 来设置其大小。当我们的客户端程序提交 Query 给 MySQL 的时候,MySQL 需要对 Query 所涉及到的每一个表都取得一个表文件句柄信息,如果没有 Table Cache,那么 MySQL 就不得不频繁的进行打开关闭文件操作,无疑会对系统性能产生一定的影响,Table Cache 正是为了解决这一问题而产生的。在有了 Table Cache 之后,MySQL 每次需要获取某个表文件的句柄信息的时候,首先会到 Table Cache 中查找是否存在空闲状态的表文件句柄。如果有,则取出直接使用,没有的话就只能进行打开文件操作获得文件句柄信息。在使用完之后,MySQL 会将该文件句柄信息再放回 Table Cache 池中,以供其他线程使用。注意,这里设置的是可以缓存的表文件句柄信息的数目,而不是内存空间的大小。

表定义信息缓存(Table definition Cache):表定义信息缓存是从 MySQL5.1.3 版本才开始引入的一个新的缓存区,用来存放表定义信息。当我们的 MySQL 中使用了较多的表的时候,此缓存无疑会提高对表定义信息的访问效率。MySQL 提供了 table_definition_cache 参数给我们设置可以缓存的表的数量。在 MySQL5.1.25 之前的版本中,默认值为128,从 MySQL5.1.25 版本开始,则将默认值调整为 256 了,最大设置值为524288。注意,这里设置的是可以缓存的表定义信息的数目,而不是内存空间的大小。

二进制日志缓冲区(Binlog Buffer):二进制日志缓冲区主要用来缓存由于各种数据变更操做所产生的 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”。

MyISAM索引缓存(Key Buffer):MyISAM 索引缓存将 MyISAM 表的索引信息缓存在内存中,以提高其访问性能。这个缓存可以说是影响 MyISAM 存储引擎性能的最重要因素之一了,通过 key_buffere_size 设置可以使用的最大内存空间。

InnoDB 日志缓冲区(InnoDB Log Buffer)
:这是 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,解释如下:

  • 0:log buffer中的数据将以每秒一次的频率写入到log file中,且同时会进行文件系统到磁盘的同步操作,但是每个事务的commit并不会触发任何log buffer 到log file的刷新或者文件系统到磁盘的刷新操作;
  • 1:在每次事务提交的时候将log buffer 中的数据都会写入到log file,同时也会触发文件系统到磁盘的同步;
  • 2:事务提交会触发log buffer 到log file的刷新,但并不会触发磁盘文件系统到磁盘的同步。此外,每秒会有一次文件系统到磁盘同步操作。

此外,MySQL文档中还提到,这几种设置中的每秒同步一次的机制,可能并不会完全确保非常准确的每秒就一定会发生同步,还取决于进程调度的问题。实际上,InnoDB 能否真正满足此参数所设置值代表的意义正常 Recovery 还是受到了不同 OS 下文件系统以及磁盘本身的限制,可能有些时候在并没有真正完成磁盘同步的情况下也会告诉 mysqld 已经完成了磁盘同步。

InnoDB 数据和索引缓存(InnoDB Buffer Pool):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 的命中率。

InnoDB 字典信息缓存(InnoDB Additional Memory Pool):InnoDB 字典信息缓存主要用来存放 InnoDB 存储引擎的字典信息以及一些 internal 的共享数据结构信息。所以其大小也与系统中所使用的 InnoDB 存储引擎表的数量有较大关系。不过,如果我们通过 innodb_additional_mem_pool_size 参数所设置的内存大小不够,InnoDB 会自动申请更多的内存,并在 MySQL 的 Error Log 中记录警告信息。

这里所列举的各种共享内存,是我个人认为对 MySQL 性能有较大影响的集中主要的共享内存。实际上,除了这些共享内存之外,MySQL 还存在很多其他的共享内存信息,如当同时请求连接过多的时候用来存放连接请求信息的back_log队列等。

以上内容可能存在分析不妥之处,欢迎各位朋友拍砖,一起交流。

, , , , ,

已经有11个回复

  1. 怎样减肥 Says @ 09-04-30 9:16 am

    感谢分享 正在找这个资料呢

  2. 怀念 Says @ 09-05-20 10:53 am

    想问一个关于MySQL的事务MVCC问题!我自己做的测试,觉得很奇怪!
    会话1
    mysql> set autocommit=0;
    Query OK, 0 rows affected (0.00 sec)

    mysql> show create table i1;
    +——-+————————————————————————————-+
    | Table | Create Table |
    +——-+————————————————————————————-+
    | i1 | CREATE TABLE `i1` (
    `id` int(11) DEFAULT NULL
    ) ENGINE=InnoDB DEFAULT CHARSET=gbk |
    +——-+————————————————————————————-+
    1 row in set (0.00 sec)

    mysql> select * from i1;
    Empty set (0.00 sec)

    mysql> insert into i1 values(1);
    Query OK, 1 row affected (0.00 sec)

    mysql> select * from i1;
    +——+
    | id |
    +——+
    | 1 |
    +——+
    1 row in set (0.00 sec)

    会话2

    mysql> set autocommit=0;
    Query OK, 0 rows affected (0.00 sec)
    mysql> select * from i1;
    Empty set (0.00 sec)

    mysql> insert into i1 values (2);
    Query OK, 1 row affected (0.00 sec)

    mysql> select * from i1;
    +——+
    | id |
    +——+
    | 2 |
    +——+
    1 row in set (0.00 sec)

    此时在会话1中都提交:
    会话2:
    mysql> select * from i1;
    +——+
    | id |
    +——+
    | 2 |
    +——+ —并没有值1

    如果在会话2 再提交一次,则会话1和会话2 都可以看到值1、2

    此时在会话1中清空表数据
    mysql> delete from i1;
    Query OK, 2 rows affected (0.00 sec)
    mysql> commit;
    Query OK, 0 rows affected (0.02 sec)

    mysql> select * from i1;
    Empty set (0.00 sec)

    然后再会话2中查询表数据依然存在
    mysql> select * from i1;
    +——+
    | id |
    +——+
    | 1 |
    | 2 |
    +——+
    2 rows in set (0.00 sec)
    新开一个会话3查询表数据也是空的

    对于会话1和会话2来说,应该数据存在不一致性
    从这个试验看到INNODB的MVCC是有延后性的,问下你那里怎么看这个东西

  3. 朝阳 Says @ 09-05-20 11:17 pm

    @这个和你所使用的事务隔离级别是有很大关系的,Innodb对SQL92所定义的4种事务隔离级别标准是全部都实现了的,理论上不会出现异常情况。其默认的事务隔离级别为可重复读(Repeatable Read)。
    另外,你这里描述的不是太清楚,能否更详细的列出各个操作的时间点?

  4. laja Says @ 09-07-5 12:15 pm

    问老大一个缓冲的问题,在数据库读写并发很大的情况下,使用中间件缓冲并发写操作,而后定时|定量更新写入数据库时,是否能提升数据库性能。

    谢谢 :)

  5. 朝阳 Says @ 09-07-5 3:10 pm

    @laja
    这种做法对于并发较大的场景是一个很好的优化策略,通过暂存在内存中提高前端相应速度,通过合并小事务提高后端处理速度。
    当然前提可以忍受当数据还在缓存中间件且没有写入到数据库中持久化的时候中间件 crash 后造成部分数据丢失的问题。

  6. laja Says @ 09-07-5 10:24 pm

    嗯,谢谢。

    此前一直质疑这种理念和查询颗粒细小化的思想有冲突。

    另外在缓冲的前提下,存在瞬间大量写入,考虑到启用大幅降低写优先的策略可行吗?

  7. 朝阳 Says @ 09-07-6 9:26 am

    @laja
    其实使用缓存暂时保存数据而不急着写入到数据库对于数据库端来说是降低了写优先的策略,但是对于前端应用来说并没有任何影响,数据仍然可以通过缓存读取到。所以在将缓存的数据写入到数据库进行持久化的时候降低写优先策略是完全可以的,并不会造成什么负面问题。

  8. 刀尖红叶 Says @ 14-02-2 11:13 am

    binlog_cache_size应该是线程独享的吧?

    ‘If the binary log is active, this variable determines the size in bytes of the cache holding a record of binary log changes during a transaction. ’

    参考:https://mariadb.com/kb/en/replication-and-binary-log-server-system-variables/#binlog_cache_size

Trackbacks & Pingbacks

看完了要说点啥么?