sábado, 13 de julho de 2019

MySQL Como você restaura o tablespace

MySQL Como você restaura o tablespace?

Esta não é uma informação nova, mas eu não a abordei tanto, então dirijo-a agora para aqueles que precisam dela.

Se você perder seus arquivos ibd ... você perderá seus dados. Portanto, se você tiver uma cópia de uma disponível ... ou mesmo se estiver sincronizando a partir de outro banco de dados, ainda poderá importá-la. O que / como você perde o tablespace?

Aqui está um exemplo simples para recuperar o espaço de tabelas.



mysql> Create database demo;

mysql> use demo;

mysql> CREATE TABLE `demotable` (
-> `id` int(11) NOT NULL AUTO_INCREMENT,
-> `dts` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
-> PRIMARY KEY (`id`)
-> ) ENGINE=InnoDB;


Agora nós armazenamos alguns dados ...


mysql> INSERT INTO demotable (id) VALUES (NULL);
Query OK, 1 row affected (0.10 sec)

mysql> INSERT INTO demotable (id) VALUES (NULL);
Query OK, 1 row affected (0.08 sec)

mysql> SELECT * FROM demotable;
+----+---------------------+
| id | dts |
+----+---------------------+
| 1 | 2019-07-12 23:31:34 |
| 2 | 2019-07-12 23:31:35 |
+----+---------------------+
2 rows in set (0.00 sec)


OK agora vamos quebrar ..


# systemctl stop mysqld
# cd /var/lib/mysql/demo/
# ls -ltr
total 80
-rw-r-----. 1 mysql mysql 114688 Jul 12 23:31 demotable.ibd
# mv demotable.ibd /tmp/

# systemctl start mysqld
# mysql demo

mysql> show tables;
+----------------+
| Tables_in_demo |
+----------------+
| demotable |
+----------------+
1 row in set (0.00 sec)

mysql> desc demotable;
+-------+-----------+------+-----+-------------------+-----------------------------------------------+
| Field | Type | Null | Key | Default | Extra |
+-------+-----------+------+-----+-------------------+-----------------------------------------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| dts | timestamp | NO | | CURRENT_TIMESTAMP | DEFAULT_GENERATED on update CURRENT_TIMESTAMP |
+-------+-----------+------+-----+-------------------+-----------------------------------------------+
2 rows in set (0.01 sec)

mysql> INSERT INTO demotable (id) VALUES (NULL);
ERROR 1812 (HY000): Tablespace is missing for table `demo`.`demotable`.


Espaço de tabelas quebrado e perdido ... Agora podemos recuperá-lo ..


demo]# cp /tmp/demotable.ibd .

mysql> ALTER TABLE demotable DISCARD TABLESPACE;

demo]# cp /tmp/demotable.ibd .
demo]# ls -ltr
total 112
-rw-r-----. 1 root root 114688 Jul 12 23:50 demotable.ibd
demo]# chown mysql:mysql demotable.ibd
demo]# mysql demo
mysql> ALTER TABLE demotable IMPORT TABLESPACE;
ERROR 1034 (HY000): Incorrect key file for table 'demotable'; try to repair it

mysql> REPAIR TABLE demotable;
+----------------+--------+----------+---------------------------------------------------------+
| Table | Op | Msg_type | Msg_text |
+----------------+--------+----------+---------------------------------------------------------+
| demo.demotable | repair | note | The storage engine for the table doesn't support repair |
+----------------+--------+----------+---------------------------------------------------------+


Note agora que nós também temos outro erro .. Isto geralmente está ligado ao espaço disponível para o tmpdir, e o reparo não funciona para o .ibd de qualquer maneira.


mysql> select @@tmpdir;
+----------+
| @@tmpdir |
+----------+
| /tmp |
+----------+

# vi /etc/my.cnf
tmpdir=/var/lib/mysql-files/

# systemctl restart mysqld
# mysql demo


OK usou o diretório mysql-files apenas por exemplo.
Agora podemos tentar novamente.


mysql> ALTER TABLE demotable IMPORT TABLESPACE;
Query OK, 0 rows affected, 1 warning (0.61 sec)

mysql> INSERT INTO demotable (id) VALUES (NULL);
Query OK, 1 row affected (0.11 sec)

mysql> SELECT * FROM demotable;
+----+---------------------+
| id | dts |
+----+---------------------+
| 1 | 2019-07-12 23:31:34 |
| 2 | 2019-07-12 23:31:35 |
| 3 | 2019-07-12 23:56:08 |
+----+---------------------+


OK funcionou.
Agora, tudo isso é legal e simples se você tiver apenas uma tabela. Mas e quanto aos 100s ...

Automatize, é claro, e use seu information_schema para ajudar.

Faça mais algumas cópias para teste.

mysql> create table demotable1 like demotable;
Query OK, 0 rows affected (0.51 sec)

mysql> create table demotable2 like demotable;
Query OK, 0 rows affected (1.04 sec)

mysql> create table demotable3 like demotable;
Query OK, 0 rows affected (0.74 sec)

mysql> create table demotable4 like demotable;
Query OK, 0 rows affected (2.21 sec)


Quebre todos eles ..

demo]# mv *.ibd /tmp/


Agora, usando sua tabela information_schema.tables, você pode construir todos os comandos necessários.

# vi build_discard.sql
# cat build_discard.sql
SELECT CONCAT(" ALTER TABLE ",TABLE_SCHEMA,".",TABLE_NAME," DISCARD TABLESPACE; ") as CMD FROM information_schema.TABLES WHERE TABLE_SCHEMA='demo';

# vi build_import.sql
# cat build_import.sql
SELECT CONCAT(" ALTER TABLE ",TABLE_SCHEMA,".",TABLE_NAME," IMPORT TABLESPACE; ") as CMD FROM information_schema.TABLES WHERE TABLE_SCHEMA='demo';



# mysql -N < build_import.sql > import_tablespace.sql
# mysql -N < build_discard.sql | mysql demo

demo]# cp /tmp/*.ibd .
demo]# chown mysql:mysql *.ibd
# systemctl restart mysqld
# mysql demo < import_tablespace.sql
# mysql demo

mysql> INSERT INTO demotable (id) VALUES (NULL);
Query OK, 1 row affected (0.08 sec)

mysql> INSERT INTO demotable1 (id) VALUES (NULL);
Query OK, 1 row affected (0.05 sec)

mysql> INSERT INTO demotable2 (id) VALUES (NULL);
Query OK, 1 row affected (0.09 sec)

mysql> INSERT INTO demotable3 (id) VALUES (NULL);
^[[AQuery OK, 1 row affected (0.37 sec)

mysql> INSERT INTO demotable4 (id) VALUES (NULL);
Query OK, 1 row affected (0.12 sec)



E funcionou. 

sexta-feira, 12 de julho de 2019

MySQL Binlogs :: Como recuperar

Então percebi que não havia feito um post sobre isso depois dessa situação que surgiu recentemente.

Aqui está o cenário: Um backup foi feito à meia-noite, eles usaram o MySQL Dumps por banco de dados. Então, às dez da manhã do dia seguinte, o banco de dados caiu. Uma série de eventos aconteceu antes de eu ser chamado, mas eles conseguiram uma versão do banco de dados com tabelas MyISAM e os arquivos IBD faltando no tablespace.

Portanto, a opção 1, a restauração do backup, nos levaria à meia-noite e perderíamos horas de dados. Opção 2, nós reimportamos os milhares de arquivos ibd e mantemos tudo. Em seguida, tivemos a opção 3, restaurar a partir do backup e, em seguida, aplicar os logs binários para alterações recentes.

Para torná-lo mais interessante, eles não tinham todos os arquivos ibd que me contaram, e eu vi alguns desaparecidos. Portanto, não tenho certeza de como isso foi possível, mas a opção 2 se tornou uma opção inválida. Eles, claro, queriam a menor perda de dados possível, então fomos com a opção 3.

Para fazer isso com segurança, iniciei outra instância do MySQL na porta 3307. Isso me permitiu um local seguro para trabalhar enquanto o tráfego tinha acesso de leitura aos dados do MyISAM na instância da porta 3306.

Depois que todos os arquivos de backup foram descompactados e importados para a instância 3307, consegui me concentrar nos arquivos do log binário.

A princípio, esse conceito parece muito mais arriscado do que realmente é. Na verdade, é bem direto e simples.

Então, primeiro você tem que encontrar os dados depois. Uma revisão dos arquivos binlogs lhe dá uma vantagem sobre quais arquivos são relevantes. No meu caso, de alguma forma eles conseguiram redefinir o log binário para que o arquivo 117 tivesse 2 intervalos de datas dentro dele.

Primeiro para a revisão do binlog, o comando a seguir exibe os dados em formato legível.
mysqlbinlog --defaults-file=/root/.my.cnf --base64-output=DECODE-ROWS --verbose mysql-bin.000117 > review_mysql-bin.000117.sql

* Nota ... Tenha cuidado ao executar o comando acima. Observe que eu tenho que despejar o arquivo diretamente no mesmo local que o log binário. Então, VALIDAR que seu nome de arquivo é válido. Este mysql-bin.000117.sql é diferente deste mysql-bin.000117 .sql. Você perderá seu log binário com a 2ª opção e um espaço antes de .sql.

Agora, salve os dados para que possam ser aplicados. Desde que eu tinha vários binlogs eu criei um arquivo e queria verificar novamente os intervalos de tempo de qualquer maneira.


mysqlbinlog --defaults-file=/root/.my.cnf --start-datetime="2019-07-09 00:00:00" --stop-datetime="2019-07-10 00:00:00" mysql-bin.000117 > binlog_restore.sql
mysqlbinlog --defaults-file=/root/.my.cnf mysql-bin.000118 >> binlog_restore.sql
mysqlbinlog --defaults-file=/root/.my.cnf mysql-bin.000119 >> binlog_restore.sql
mysqlbinlog --defaults-file=/root/.my.cnf --start-datetime="2019-07-10 00:00:00" --stop-datetime="2019-07-10 10:00:00" mysql-bin.000117 >> binlog_restore.sql
mysqlbinlog --defaults-file=/root/.my.cnf --stop-datetime="2019-07-10 10:00:00" mysql-bin.000120 >> binlog_restore.sql
mysqlbinlog --defaults-file=/root/.my.cnf --stop-datetime="2019-07-10 10:00:00" mysql-bin.000121 >> binlog_restore.sql

mysql --socket=/var/lib/mysql_restore/mysql.sock -e "source /var/lib/mysql/binlog_restore.sql"

Agora eu apliquei todos os dados desses binlogs para os intervalos de tempo fornecidos. O cliente checou novamente todos os dados e ficou muito feliz em tê-los de volta.

Várias opções diferentes existiam para esta situação, isso aconteceu para treinar melhor com o cliente.

Uma vez que o validado estava tudo bem na versão restaurada, era simples parar ambos os bancos de dados, mover os diretórios de dados (queria manter os padrões de datadir intactos), chover os diretórios apenas para serem seguros e iniciar o MySQL. Agora a instância restaurada estava na porta 3306.