Como uma instrução SQL de atualização é executada?

prefácio

No artigo anterior "Como é executada uma instrução SQL de consulta? Introduz alguns componentes comuns, geralmente por meio de módulos funcionais, como conectores, analisadores, otimizadores e executores, e finalmente chega ao mecanismo de armazenamento . Este artigo descreve como uma instrução de atualização é executada e também descreve outros componentes, então vamos começar.

Conjunto de buffers

Sabemos que ao executar o sql para operar em uma linha, cada operação de disco é realmente muito lenta. Para resolver esse problema, configuramos um pool de buffers. Este buffer pool é simplesmente uma área de memória, que é definida pelo InnoDB, não pelo servidor MySQL . Uma das razões de sua existência é evitar acessar o disco todas as vezes, e colocar os dados acessados ​​com mais frequência no cache para melhorar a velocidade de acesso dos dados.

Ao ler os dados, quando a página de dados onde os dados a serem pesquisados ​​estiverem localizados na memória, o resultado será retornado. Caso contrário, a página de dados correspondente será carregada na memória e, em seguida, o resultado será retornado; para a operação de escrita, se a página de dados onde se encontra a linha a ser modificada estiver na memória, o resultado correspondente será retornado após modificação. Caso contrário, a página de dados correspondente à linha será lida do disco para a memória e depois modificada.

Quando as páginas de dados na memória são inconsistentes com os dados no disco, nós as chamamos de páginas sujas . Há um thread de segundo plano especial no Innodb que grava os dados do Buffer Pool no disco e grava várias modificações no disco de uma só vez de vez em quando. Essa ação é chamada de sujar.

imagem.png

A existência do buffer pool pode reduzir a sobrecarga causada pela E/S de disco. Para a E/S de disco inevitável, como carregar páginas de dados, podemos otimizar ainda mais, ou seja, reduzir o número de E/S de disco, o que é necessário aqui Fale sobre o conceito de pré-leitura . Seja o sistema operacional ou o mecanismo de armazenamento, existe um conceito de leitura antecipada, ou seja, quando um dado no disco é lido, é muito provável que o local próximo a ele também seja lido imediatamente Isso é chamado de princípio da localidade. Assim, nós simplesmente lemos um pouco mais a cada vez, em vez de quanto ler. A menor unidade desta leitura é chamada de página.No sistema operacional, o tamanho da página é geralmente de 4kb, enquanto no InnoDB, o tamanho da página é de 16kb.

O buffer pool padrão é 128 M. Como o tamanho é limitado, haverá momentos em que os dados estarão cheios. Neste momento, o algoritmo LRU será usado para eliminar páginas não utilizadas.

imagem.png

refazer log

O redo log é chamado de redo log , porque a sujeira não é em tempo real, e a modificação dos dados do disco também ficará para trás da memória. Neste momento, se o processo ou a máquina travar, os dados da memória serão perdidos. Para garantir a consistência e durabilidade do próprio banco de dados, o InnoDB mantém o redo log. Este arquivo de log registra todas as operações de modificação na página. Quando a recuperação de falhas for reiniciada, o redo log será reproduzido para restaurar a página ao estado anterior à falha. Esse recurso é chamado de segurança contra falhas .

redo log也是记录在磁盘的,同样是写磁盘,为什么不直接把数据更新到db file里去,这样不是多此一举吗?这么做的原因其实是刷盘是随机I/O,而记录日志是顺序I/O,顺序I/O的效率更高,本质上是数据集中存储分散存储的区别。因此先把数据写入日志文件,在保证了内存数据的安全性的情况下,可以延迟刷盘时机,从而提高的系统的吞吐量。

redo log默认是2个文件,每个48M。 imagem.png

下图展示的是4个redo log,从头开始写,写到末尾就又回到开头循环写。write pos 是当前记录的位置,一边写一边后移,写到第 3 号文件末尾后就回到 0 号文件开头。checkpoint 是当前要擦除的位置,也是往后推移并且循环的,擦除记录前要把记录更新到数据文件。write pos 和 checkpoint 之间的是空着的部分,可以用来记录新的操作,绿色部分。 imagem.png

总结:

  • redo log是Innodb存储引擎实现的,并不是所有的存储引擎都有的。支持崩溃恢复是Innodb的一个特性。
  • redo log不是记录数据页更新之后的状态,而是记录在某个数据页上做了什么修改,属于物理日志
  • redo log的大小是固定的,前面的内容会被覆盖,一但写满,就会触发buffer pool到磁盘的同步,以便腾出空间给后面的修改。

undo log

除了redo log之外,还有一个跟修改有关的日志,叫做undo log。redo log和undo log与事务密切相关,统称为事务日志。undo log叫做撤销日志或者回滚日志,记录了事务发生之前的数据状态,分为insert undo log和update undo log。如果修改数据时出现异常,可以用undo log来实现回滚操作,保持原子性。

可以理解为undo log记录的是反向操作。比如insert会记录delete,update会记录update原来的值,跟redo log记录在哪个物理页面做了什么操作不同,所以叫做逻辑格式的日志。

undo的一些设置:

imagem.png

binlog

Server 层也有自己的日志,称为 binlog归档日志。最开始 MySQL 里并没有 InnoDB 引擎。MySQL 自带的引擎是 MyISAM,但是 MyISAM 没有 crash-safe 的能力,binlog 日志只能用于归档。而 InnoDB 是另一个公司以插件形式引入 MySQL 的,既然只依靠 binlog 是没有 crash-safe 能力的,所以 InnoDB 使用另外一套日志系统——也就是 redo log 来实现 crash-safe 能力。

redo log 是物理日志,记录的是在某个数据页上做了什么修改;binlog 是逻辑日志,记录的是这个语句的原始逻辑,比如 给 ID=2 这一行的 c 字段加 1。binlog以事件的形式记录了所有的DDL和 DML语句。

redo log 是循环写的,空间固定会用完;binlog 是可以追加写入的。“追加写”是指 binlog 文件写到一定大小后会切换到下一个,并不会覆盖以前的日志。

binlog可以做数据恢复,在开启了binlog功能的情况下,我们可以把binlog导出成SQL语句,把所有的操作重放一遍。binlog另一个功能就是做主从复制,它的原理就是从服务器读取主服务器的binlog,然后执行一遍。

一条更新SQL是如何执行的

介绍了上面这么多的功能,现在我们再来看一条更新语句是如何执行的?

update user set name='aaa'  where id=1;

整个执行过程如下:

  1. 执行器先通过存储引擎找到 id=1 这一行数据。ID 是主键,引擎直接用树搜索找到这一行。如果 ID=1 这一行所在的数据页本来就在内存中,就直接返回给执行器;否则,需要先从磁盘读入内存,然后再返回。
  2. 执行器拿到引擎给的行数据,把name改成aaa,再调用存储引擎接口写入这行新数据。
  3. 存储引擎将这行新数据更新到内存中,同时将这个更新操作记录到 redo log 里面,此时 redo log 处于 prepare 状态。然后告知执行器执行完成了,随时可以提交事务。
  4. 执行器收到这个通知后记录 binlog,并把 binlog 写入磁盘。
  5. 执行器调用引擎的提交事务接口,引擎把刚刚写入的 redo log 改成提交commit状态,更新完成。

整个流程图如下: imagem.png

写binlog和redo log采用的是两阶段提交。

InnoDB架构

dev.mysql.com/doc/refman/…

imagem.png 我们看这张架构图一共分两个部分,一个是内存区域,一个是磁盘区域。内存区域有Buffer Pool、Change Buffer、Adaptive Hash、Log Buffer四个组件。

Buffer Pool上面介绍过了。

Change Buffer

当需要更新一个数据页时,如果数据页在内存中就直接更新,而如果这个数据页还没有在内存中的话,在不影响数据一致性的前提下,InooDB 会将这些更新操作缓存在 change buffer 中,这样就不需要从磁盘中读入这个数据页了。在下次查询需要访问这个数据页的时候,将数据页读入内存,然后执行 change buffer 中与这个页有关的操作。通过这种方式就能保证这个数据逻辑的正确性。

前提是没有使用唯一索引,对于唯一索引来说,所有的更新操作都要先判断这个操作是否违反唯一约束性,而这必须要将数据页读入内存才能判断,如果都已经读入到内存了,那就没有必要时用change buffer 了。

5.5之前叫Insert Buffer插入缓冲,现在也能支持delete和update。将 change buffer 中的操作应用到原数据页,得到最新结果的过程称为 merge。除了访问这个数据页会触发 merge 外,系统有后台线程会定期 merge。在数据库正常关闭(shutdown)的过程中,也会执行 merge 操作。

如果数据库大部分索引都是非唯一索引,并且业务是写多读少,不会在写数据后立刻读取,就可以使用Change Buffer(写缓冲)。可以通过调大这个值,来扩大Change的大小,以支持写多读少的业务场景。Change Buffer 默认占 Buffer Pool 的比例是25%。

imagem.png

Adaptive Hash Index

索引是放在磁盘的,这里把Hash索引放在内存。

Redo Log Buffer

我们知道redo log是存储在磁盘的,但是也不是每次都直接写入磁盘,在Buffer Pool里面有一块内存区域Log Buffer专门用来保存即将要写入日志文件的数据,默认是16M,它一样是为了减少磁盘IO。

imagem.png

那么Log Buffer什么时候写入磁盘?log buffer写入磁盘的时机是由一个参数控制的,默认是1。

imagem.png

innodb_flush_log_at_trx_commit值为0

imagem.png innodb_flush_log_at_trx_commit值为1

imagem.png innodb_flush_log_at_trx_commit值为2

imagem.png

下面我们来看下磁盘区域,里面主要是各种各样的表,叫做Table space。表空间可以看做是InnoDB存储引擎逻辑结构的最高层,所有的数据都存放在表空间中。InnoDB的表空间分为5大类。

System tablespace

默认情况下InnoDB存储引擎有一个共享表空间,即/var/lib/mysql/ibdata1文件,也叫系统表空间。系统表空间里包含数据字典,双写缓冲区,Change Buffer和Undo Logs,如果没有指定file-per-table,也包含用户创建的表和索引数据

数据字典由内部系统表组成,存储表和索引的元数据(定义信息)。双写缓冲区是做什么用的?我们知道InnoDB的页大小默认为16K,操作系统的页是4k,从InnoDB写到磁盘的时候要分4次写。

imagem.png 如果存储引擎在写入页的数据到磁盘时发生了宕机,可能出现页只写了一部分的情况,刚写入了4K或8K数据,那么就不能保证该操作的原子性,称为部分页面写问题(Partial Write Page) 。此时就引入了双写缓存区的机制,当发生极端情况时,可以从系统表空间的Double Write Buffer【磁盘上】进行恢复,相当于是一个副本,通过它来实现数据页的可靠性。

imagem.png

Chang Buffer就是内存中的Chang Buffer同步到磁盘的。在默认的情况下,所有的表空间共享一个系统表空间,这个文件会越来越大,而且它的空间不会收缩。

file-per-table tablespaces

我们可以让每张表独占一个表空间,这个开关通过innodb_file_per_table设置,默认开启。

imagem.png

开启后,则每张表会开辟一个表空间,这个文件就是数据目录下的idb文件,例如/var/lib/mysql/jack/user_innodb.ibd,存放表的索引和数据

但是其他类的数据,如回滚信息undo log,插入缓冲索引页,系统事务信息,双写缓冲区double write buffer等还是存放在原来的共享表空间内。

general tablespaces

通用表空间也是一种共享的表空间,跟ibdata1类似。可以创建一个通用的表空间,用来存储不同数据库的表,数据路径和文件可以自定义。

CREATE TABLESPACE jacktablespace ADD DATAFILE '/meu/espaço de tabela/diretório/ts1.ibd' FILE_BLOCK_SIZE = 16k ENGINE =innodb;

Você pode especificar um tablespace ao criar uma tabela

CREATE TABLE t1 (c1 INT PRIMARY KEY) TABLESPACE jacktablespace;

Dados em diferentes tablespaces podem ser movidos, use alter para modificar o tablespace

ALTER TABLE t2 TABLESPACE jacktablespace;

Para excluir um tablespace, você precisa excluir todos os elementos nele primeiro.

drop table t1
drop table t2
drop tablespace jacktablespace

espaços de tabela temporários

Armazena dados em tabelas temporárias, incluindo tabelas temporárias criadas pelo usuário e tabelas temporárias internas em disco. Corresponde ao arquivo ibtmp1 no diretório de dados. Quando o servidor de dados é encerrado normalmente, o espaço de tabela é excluído e gerado novamente na próxima vez.

desfazer espaços de tabela

Os dados do log de undo são armazenados no arquivo ibdata1 do tablespace do sistema por padrão.Como o tablespace compartilhado não diminuirá automaticamente, você também pode criar um tablespace de undo separado.

Bem, através de dois artigos sobre como o SQL de consulta é executado e como o SQL de atualização é executado, os componentes do MySQL são apresentados. Acredito que todos tenham uma certa compreensão dos princípios subjacentes do MySQL. Obrigado por assistir e bem-vindo. like~

Supongo que te gusta

Origin juejin.im/post/7103092370849153061
Recomendado
Clasificación