mysql的读和写

来源:互联网 发布:js 点击添加class 编辑:程序博客网 时间:2024/05/22 06:41

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的读

MySQL的读操作时一个相对而言比较复杂的动作。当MySQL发生物理读后,buffer pool中会缓存读取的page,将其放在LRU list上,如果在一定的生命周期中,又发生了同样的读操作,则没有必要再发生物理读,而只需要逻辑读就可以了,所谓的物理读是指数据是从磁盘上加载到缓存中,然后再从缓存中获取所需的数据,而逻辑读是指所要读取的数据直接从缓存中获取,这样可以极大的减小IO,提高数据库的性能。可以运行下面的语句来观察MySQL的物理读:
mysql> show status like 'innodb%a%read%';
+--------------------------------------+---------------+
| Variable_name                      | Value         |
+--------------------------------------+--------------+
| Innodb_data_pending_reads | 0              |
| Innodb_data_read                 | 10948608 |
| Innodb_data_reads               | 569           |
| Innodb_pages_read              | 534           |
+--------------------------------------+---------------+
innodb_data_pending_reads:挂起读的次数
innodb_data_read:自数据库启动以来一共读取的数据量(bit)
innodb_data_reads:发生读操作的次数
innodb_pages_read:在InnoDB表上发生的读操作的page总数
但是在MySQL数据库中还有一个所谓的查询缓存,具体的使用query_cache_type和query_cache_size这两个参数来确定,而关于观察查询缓存我们可以运行下面的语句:
mysql> show status like 'Qcache%';
+-------------------------+----------+
| 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       |
+-------------------------+----------+
当在查询缓存中的表上发生了update,delete,insert等操作时,查询缓存中的缓存数据便会失效。
那么疑问来了,既然有buffer pool为什么还需要查询缓存呢?它们之间的关系是什么呢?

1 0
原创粉丝点击