MySQL体系结构——内存
内存
1.缓冲池
InnoDB存储引擎是基于磁盘存储的,并将其中的记录按照页的方式进行管理,因此可将其视为基于磁盘的数据库系统(Disk-base Database)。在数据库中CPU速度与磁盘速度是有很大差距的,基于磁盘的数据库系统通常使用缓冲池技术来提高数据库的整体性能。
缓冲池简单来说就是一块内存区域,通过内存的速度来弥补磁盘速度较慢对数据库性能的影响。在数据库中进行读取页的操作,首先将从磁盘读到的页存放到缓冲池中,这个过程称为将页‘FIX’在缓冲池中。下一次再读相同的页的时候,首先判断页是否在缓冲池中,若在缓冲池中,称该页在缓冲池中命中,直接读取该页。否则读取磁盘上的页。
对于数据库中页的修改操作,首先也是先修改缓冲池中的页,然后再以一定的频率刷新到磁盘上,这里的刷盘并不是每次改动都会直接刷新到磁盘上而是以Checkpoint的方式进行刷盘,这样也是为了提升数据库的整体性能。
缓冲池配置参数:innodb_buffer_pool_size来设置
mysql> show variables like 'innodb_buffer_pool_size'\G
*************************** 1. row ***************************
Variable_name: innodb_buffer_pool_size
Value: 3221225472
1 row in set (0.05 sec)
InnoDB内存数据对象:
具体而言,缓存区缓存的数据页类型有:索引页,数据页,undo页,插入缓冲(insert buffer),自适应哈希索引(adaptive hash index),InnoDB存储锁信息(lock info),数据字典信息(data dictionary)。数据页和索引页占据了缓冲池很大部分。
InnoDB1.0.x版本开始,允许有多个缓冲池实例,每个页根据哈希值平均分配到不同缓冲池的实例中,这样可以减少数据库内部资源竞争,增加数据库的并发处理能力。
缓冲池个数配置参数:innodb_buffer_pool_instances来设置
mysql> show variables like 'innodb_buffer_pool_instances'\G
*************************** 1. row ***************************
Variable_name: innodb_buffer_pool_instances
Value: 4
1 row in set (0.01 sec)
查看每个buffer pool使用状态,使用show engine innodb status 查看。
mysql> show engine innodb status \G (这个命令并不是当前的状态而是过去的)
...
----------------------
INDIVIDUAL BUFFER POOL INFO
----------------------
---BUFFER POOL 0
Buffer pool size 49152
Free buffers 48868
Database pages 283
Old database pages 0
Modified db pages 0
Pending reads 0
Pending writes: LRU 0, flush list 0, single page 0
Pages made young 0, not young 0
0.00 youngs/s, 0.00 non-youngs/s
Pages read 280, created 3, written 6
0.00 reads/s, 0.00 creates/s, 0.00 writes/s
No buffer pool page gets since the last printout
Pages read ahead 0.00/s, evicted without access 0.00/s, Random read ahead 0.00/s
LRU len: 283, unzip_LRU len: 0
I/O sum[0]:cur[0], unzip sum[0]:cur[0]
---BUFFER POOL 1
Buffer pool size 49152
Free buffers 48876
Database pages 275
Old database pages 0
Modified db pages 0
Pending reads 0
Pending writes: LRU 0, flush list 0, single page 0
Pages made young 0, not young 0
0.00 youngs/s, 0.00 non-youngs/s
Pages read 209, created 66, written 66
0.00 reads/s, 0.00 creates/s, 0.00 writes/s
No buffer pool page gets since the last printout
Pages read ahead 0.00/s, evicted without access 0.00/s, Random read ahead 0.00/s
LRU len: 275, unzip_LRU len: 0
I/O sum[0]:cur[0], unzip sum[0]:cur[0]
---BUFFER POOL 2
Buffer pool size 49152
Free buffers 48799
Database pages 352
Old database pages 0
Modified db pages 0
Pending reads 0
Pending writes: LRU 0, flush list 0, single page 0
Pages made young 0, not young 0
0.00 youngs/s, 0.00 non-youngs/s
Pages read 285, created 67, written 71
0.00 reads/s, 0.00 creates/s, 0.00 writes/s
No buffer pool page gets since the last printout
Pages read ahead 0.00/s, evicted without access 0.00/s, Random read ahead 0.00/s
LRU len: 352, unzip_LRU len: 0
I/O sum[0]:cur[0], unzip sum[0]:cur[0]
---BUFFER POOL 3
Buffer pool size 49152
Free buffers 48789
Database pages 362
Old database pages 0
Modified db pages 0
Pending reads 0
Pending writes: LRU 0, flush list 0, single page 0
Pages made young 0, not young 0
0.00 youngs/s, 0.00 non-youngs/s
Pages read 356, created 6, written 12
0.00 reads/s, 0.00 creates/s, 0.00 writes/s
No buffer pool page gets since the last printout
Pages read ahead 0.00/s, evicted without access 0.00/s, Random read ahead 0.00/s
LRU len: 362, unzip_LRU len: 0
I/O sum[0]:cur[0], unzip sum[0]:cur[0]
--------------
ROW OPERATIONS
--------------
0 queries inside InnoDB, 0 queries in queue
0 read views open inside InnoDB
Process ID=9546, Main thread ID=140496452282112 , state=sleeping
Number of rows inserted 0, updated 313, deleted 0, read 4294
0.00 inserts/s, 0.00 updates/s, 0.00 deletes/s, 0.00 reads/s
----------------------------
END OF INNODB MONITOR OUTPUT
============================
1 row in set (0.00 sec)
在MySQL5.6版本开始,可以使用information_schema下的innodb_buffer_pool_stats来观察缓冲池的状态。
mysql> select pool_id,pool_size,free_buffers,database_pages from innodb_buffer_pool_stats;
+---------+-----------+--------------+----------------+
| pool_id | pool_size | free_buffers | database_pages |
+---------+-----------+--------------+----------------+
| 0 | 49152 | 48868 | 283 |
| 1 | 49152 | 48868 | 282 |
| 2 | 49152 | 48794 | 356 |
| 3 | 49152 | 48783 | 367 |
+---------+-----------+--------------+----------------+
4 rows in set (0.00 sec)
2.LRU List,Free List和Flush List--管理InnoDB内存区域
通常来说,数据库的缓冲池是通过LRU算法来进行管理的。最频繁使用的页在LRU链表的前端,而最少使用的放在链表尾端,当缓冲池不能存放新的缓存页时,会首先释放LRU尾端的页。
在InnoDB存储引擎中,缓存池中页的大小默认为16kb,同样也是用LRU算法对缓存池进行管理。稍有不同的是InnoDB存储引擎对传统的LRU算法进行了优化,在这个链表中加入了midpoint位置,当读取到新的页时,并不会将这个页放到LRU链表的首部而是放到midpoint位置。这个算法在InnoDB存储引擎中被称为midpoint insertion strategy。在默认配置下,这个位置在这个链表的5/8处。这个点的位置可以通过参数innodb_old_block_pct来控制
mysql> show variables like 'innodb_old_blocks_pct'\G
ERROR 2006 (HY000): MySQL server has gone away
No connection. Trying to reconnect...
Connection id: 9
Current database: information_schema
*************************** 1. row ***************************
Variable_name: innodb_old_blocks_pct
Value: 37
1 row in set (0.06 sec)
可以看出新读取的页会被插入到LRU链表尾端的37%处,在InnoDB存储引擎中把midpoint之后的链表称为old链表,之前的称为new链表。new链表中存储的页通常是比较活跃的页。 为了保证热端都是常用的页,而不是每次读入新页都把它放到热端(有些新页可能只是这次查询用到,如果置于热端会导致真正经常被访问的页被释放,导致性能下降),这个问题解决的方案就是,MySQL引入了新的参数 innodb_blocks_old_time,表示新叶读到mid位置需要等待多久才会被加入到LRU热端。
LRU链表是来管理已经读取的页,当数据库刚刚启动的时候,LRU链表是空的,没有任何页。
当LRU链表中的页被修改后,称该页为脏页,即缓冲池中的页和磁盘上的页数据不一致。这时数据库会通过checkpoint机制将脏页刷新回磁盘。而Flush列表中的页即为脏页链表。脏页既存在于LRU上也存在于Flush上。LRU链表用来管理缓冲池中页的可用性,Flush用来管理将页刷回磁盘上二者互不影响。
3.重做日志缓冲
重做日志缓冲区(redo log buffer)。InnoDB存储引擎首先将重做日志信息放入到这个缓冲区中,然后按照一定频率将其刷新到重做日志文件,重做日志缓冲区不需要很大,因为一般情况下每一秒都会将重做日志刷新到重做日志文件中,因此用户只需要保证每秒产生的事务量在这个缓冲大小之内即可。
重做日志缓冲大小:innodb_log_buffer_size设置。(默认大小8MB)
mysql> show variables like 'innodb_log_buffer_size'\G
ERROR 2006 (HY000): MySQL server has gone away
No connection. Trying to reconnect...
Connection id: 10
Current database: information_schema
*************************** 1. row ***************************
Variable_name: innodb_log_buffer_size
Value: 33554432
1 row in set (0.06 sec)
触发将重做日志缓冲刷新到重做日志文件的方式:
- Master Thread 每一秒都会将重做日志缓冲刷新到重做日志文件中。
- 每个事务提交时都会将重做日志缓冲刷新到重做日志文件。
- 当重做日志缓冲池剩余空间小于1/2 时,重做日志缓冲刷新到重做日志文件。
4.其他内存池
在InnoDB存储引擎中,使用内存堆(heap)对内存进行管理。在对一些数据结构本身的内存进行分配时,需要从额外的内存池中进行申请,当该区域内存不足时,会从缓冲池中进行申请。