MySQL的写和读
MySQL中有两个重要的动作,就是write和read,本篇文章就来介绍一下MySQL数据库具体是怎么实现读和写的,读的操作是比些操作复杂的操作。
1.MySQL的写
MySQL的写操作主要是指在MySQL中的数据经过变更(update,delete,insert)之后发生的后继操作。我们知道当数据库发生update,delete,insert操作后数据首先发生的变化是在buffer pool中的,此时这些数据所在的page称为脏叶,然后MySQL数据库根据其自身的压力(其实就是MySQL内部的一些算法了),异步(asynchronous)的将这些发生改变的数据刷新到磁盘上去。关系型数据库为了保证其ACID属性,都使用了预写redo日志(WLA,write log ahead)的机制,当数据库发生意外宕机后,在数据库下次启动的时候可以通过redo log来恢复数据。从细节上我们接下来考虑这样一个情形,当数据库发生宕机时,可能InnoDB存储引擎正在写某个内存中的脏叶到磁盘上,例如一个16KB的页,只写了前4KB,之后宕机,这种情况被称为部分写失效(partial page write),也许我们会说不是关系型数据库使用的WLA吗,我们用redo 日志来恢复剩下来的数据啊?但是我们必须清楚redo log中记录的是对页的物理操作,如在offset 800,写‘aaaa'记录。如果这个也本身已经发生了损坏,在对其进行重做是没有意义的。这里稍微有点不好理解,就是说redo log中只记录了发生变化的数据的操作,没有发生变化的数据并没有记录在redo log中,这样当数据页发生了损坏时,没有发生变化的数据却不能通过redo log来恢复。这样就会造成数据的丢失,为了防止这样的情况出现,MySQL使用了两次写(doublewrite),就是说在对页apply redo log前,MySQL需要这个页的一个副本,当写入失效发生时,先通过这个页的副本来还原页,再进行重做。
doublewrite由两部分组成,一部分是在内存中的,大小为2M,另一部分是在物理磁盘上的共享表空间中连续的128个页,即两个区,大小同样为2M,在对缓冲池中的脏页进行刷新时,并不直接写磁盘,而是会通过memcpy函数将脏页拷贝到doublewrite buffer中,因为是内存之间的拷贝,所以速度是非常快的,然后马上调用fsync函数,同步磁盘,避免缓冲写带来的问题,在这个过程中因为doublewrite页是连续的,因此这个过程是顺序写的,开销并不大。在完成doublewrite页的写入后,再将doublewrite buffer中的脏页刷新到磁盘中,此时的写是离散的。有两个doublewrite相关的状态量我们可以观察一下doublewrite的运行:
mysql> show status like 'innodb_dblwr%';
+---------------------------------------+-------------+
| Variable_name | Value |
+---------------------------------------+-------------+
| Innodb_dblwr_pages_written | 94 |
| Innodb_dblwr_writes | 2 |
+---------------------------------------+-------------+
2 rows in set (0.00 sec)
innodb_dblwr_pages_written:通过doublewrite一共写的page数
innodb_dblwr_writes:通过doublewrite一共写入的次数
因为1M=64*16K,所以在理论上,当系统的写压力最大时,innodb_dblwr_pages_written/innodb_dblwr_writes=64:1,当系统的比值远远小于这个比值的时候,说明此时MySQL服务器的压力写的压力并不大。
另外,还有一个状态量需要注意,innodb_buffer_pool_page_flushed,它和innodb_dblwr_pages_written是保持一致的。
mysql> show status like 'innodb_buffer_pool_pages_flushed';
+-----------------------------------------------+---------+
| Variable_name | Value |
+-----------------------------------------------+---------+
| Innodb_buffer_pool_pages_flushed | 94 |
+-----------------------------------------------+---------+
2.mysql的读
+--------------------------------------+---------------+
| Variable_name | Value |
+--------------------------------------+--------------+
| Innodb_data_pending_reads | 0 |
| Innodb_data_read | 10948608 |
| Innodb_data_reads | 569 |
| Innodb_pages_read | 534 |
+--------------------------------------+---------------+
+-------------------------+----------+
| Variable_name | Value |
+-------------------------+----------+
| Qcache_free_blocks | 1 |
| Qcache_free_memory | 10455416 |
| Qcache_hits | 1 |
| Qcache_inserts | 6 |
| Qcache_lowmem_prunes | 0 |
| Qcache_not_cached | 7 |
| Qcache_queries_in_cache | 6 |
| Qcache_total_blocks | 19 |
+-------------------------+----------+