MySQL 備份和恢復機制

1、 備份恢復策略

進行備份或恢復操做時須要考慮一些因素:mysql

  • 肯定要備份的表的存儲引擎是事務型仍是非事務型,兩種不一樣的存儲引擎備份方式在處理數據一致性方面是不太同樣的。linux

  • 肯定使用全備份仍是增量備份。全備份的優勢是備份保持最新備份,恢復的時候能夠花費更少的時間;缺點是若是數據量大,將會花費不少的時間,並對系統形成較長時間的壓力。增量備份相反,只須要備份天天的增量日誌,備份時間少,對負載壓力也小;缺點就是恢復的時候須要全備份加上次備份到故障前的全部日誌,恢復時間長一些。sql

  • 能夠考慮採用複製的方法來作異地備份,但不能代替備份,它對數據庫的誤操做也無能爲力。shell

  • 要按期作備份,備份的週期要充分考慮系統能夠承受的恢復時間。備份要在系統負載較小的時候進行數據庫

  • 確保 MySQL 打開 log-bin 選項,有了 binlog,MySQL 才能夠在必要的時候作完整恢復,或基於時間點的恢復,或基於位置的恢復。windows

  • 常常作備份恢復測試,確保備份時有效的,是能夠恢復的。session

2、 邏輯備份和恢復

在 MySQL 中,邏輯備份的最大優勢是對於各類存儲引擎均可以用一樣的方法來備份;而物理備份則不一樣,不一樣的存儲引擎有着不一樣的備份方法,所以,對於不一樣存儲引擎混合的數據庫,邏輯備份會簡單一點。工具

1. 備份

MySQL 中的邏輯備份是將數據庫中的數據備份爲一個文本文件,備份的文件能夠被查看和編輯。在 MySQL 中,可使用 mysqldump 工具來完成邏輯備份:測試

// 備份指定的數據庫或者數據庫中的某些表  
shell> mysqldump [options] db_name [tables]  

// 備份指定的一個或多個數據庫  
shell> mysqldump [options] --database DB1 [DB2,DB3...]  

// 備份全部數據庫  
shell> mysqldump [options] --all-database

若是沒有指定數據庫中的任何表,默認導出全部數據庫中的全部表。命令行

示例:
1. 備份全部數據庫:
shell>mysqldump -uroot -p --all-database > all.sql
2. 備份數據庫 test
shell>mysqldump -uroot -p test > test.sql
3. 備份數據庫 test 下的表 emp
shell> mysqldump -uroot -p test emp > emp.sql
4. 備份數據庫 test 下的表 emp 和 dept
shell> mysqldump -uroot -p test emp dept > emp_dept.sql
5. 備份數據庫test 下的全部表爲逗號分割的文本,備份到 /tmp:
shell> mysqlddump -uroot -p -T /tmp test emp --fields-terminated-by ','
shell> more emp.txt  

1,z1
2,z2
3,z3
4,z4

注意: 爲了保證數據備份的一致性,myisam 存儲引擎在備份時須要加上 -l 參數,表示將全部表加上讀鎖,在備份期間,全部表將只能讀而不能進行數據更新。可是對於事務存儲引擎來講,能夠採用更好的選項 --single-transaction,此選項使得 innodb 存儲引擎獲得一個快照(snapshot),使得備份的數據可以保證一致性。

2. 徹底恢復

mysqldump 的恢復也很簡單,將備份做爲輸入執行便可:

mysql -uroot -p db_name < backfile

注意,將備份恢復後數據並不完整,還須要將備份後執行的日誌進行重作:

mysqlbinlog binlog-file | mysql -uroot -p
完整的 mysqldump 備份與恢復示例:
1. 凌晨 2:00,備份數據庫:
root@bogon:/usr/local/mysql/bin$ ./mysqldump -uroot -p -l -F t2 > t2.dmp
Enter password:

其中 -l 參數表示給全部表加讀鎖,-F 表示生成一個新的日誌文件,此時,t2 中 emp 表的數據以下:

# 爲了便於測試,執行 reset master 刪除全部 binlog。
MySQL [(none)]> reset master;
Query OK, 0 rows affected (0.00 sec)

# 此時只有一個 binlog 日誌文件   mysql-bin.000001
MySQL [t2]> select * from test;
+------+------+
| id   | name |
+------+------+
|    1 | a    |
|    2 | b    |
+------+------+
2 rows in set (0.00 sec)
2. 備份完畢後,插入新的數據:
# 由於上一步執行是加入了 -F 選項, 因此接下來的操做會被記錄到新的二進制文件,即名爲 mysql-bin.000002 的文件
MySQL [t2]> insert into test values (3,'c');
Query OK, 1 row affected (0.00 sec)

MySQL [t2]> insert into test values (4,'d');
Query OK, 1 row affected (0.00 sec)
3. 數據庫忽然故障(實際上是小夥伴沒事兒刪庫練手玩兒),數據沒法訪問。須要恢復備份:

刪庫跑路:

# 這裏爲了便於測試,不把刪庫操做記入日誌,當前 session 設置 sql_log_bin 爲 off。
# 刪庫後,執行 flush logs,讓後續的 binlog 到新的文件中,即名爲 mysql-bin.000003中
MySQL [t2]> set sql_log_bin = 0;
Query OK, 0 rows affected (0.00 sec)

MySQL [t2]> show variables like "%sql_log_bin%";
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| sql_log_bin   | OFF   |
+---------------+-------+
1 row in set (0.00 sec)

MySQL [t2]> drop database t2;
Query OK, 1 row affected (0.01 sec)

MySQL [(none)]> flush logs;
Query OK, 0 rows affected (0.22 sec)


MySQL [t2]> drop database t2;
Query OK, 3 rows affected (0.23 sec)

MySQL [(none)]> exit;
Bye

數據恢復:

root@bogon:/usr/local/mysql/bin# ./mysql -e "create database t2"   
root@bogon:/usr/local/mysql/bin# ./mysql t2 < t2.dmp 

*******************************************************************
MySQL [t2]> select * from test;
+------+------+
| id   | name |
+------+------+
|    1 | a    |
|    2 | b    |
+------+------+
2 rows in set (0.00 sec)
4. 使用 mysqlbinlog 恢復自 mysqldump 備份以來的 binglog

根據前面操做的內容,可知從備份的時間點到刪庫的時間點之間的操做被記錄到了 mysql-bin.000002 文件中

root@bogon:/usr/local/mysql/bin# ./mysqlbinlog --no-defaults /data/mysql/mysql-bin.000002 | ./mysql t2

*******************************************************
MySQL [t2]> select * from test;
+------+------+
| id   | name |
+------+------+
|    1 | a    |
|    2 | b    |
|    3 | c    |
|    4 | d    |
+------+------+
4 rows in set (0.00 sec)

至此,數據恢復成功。

3. 基於時間點恢復

因爲誤操做,好比誤刪除了一張表,這時使用徹底恢復時沒有用的,由於日誌裏面還存在誤操做的語句,咱們須要的是恢復到誤操做以前的狀態,而後跳過誤操做語句,再恢復後面執行的語句,完成恢復。這種恢復叫不徹底恢復,在 MySQL 中,不徹底恢復分爲 基於時間點的恢復和基於位置的恢復。
基於時間點恢復的操做步驟:

(1) 若是是上午 10 點發生了誤操做,能夠用如下語句用備份和 binlog 將數據恢復到故障前:
shell>mysqlbinlog --stop-date="2017-09-30 9:59:59" /data/mysql/mysql-bin.123456 | mysql -uroot -ppassword
(2) 跳過故障時的時間點,繼續執行後面的 binlog,完成恢復。
shell>mysqlbinlog --start-date="2017-09-30 10:01:00" /data/mysql/mysql-bin.123456 | mysql -uroot -ppassword

4. 基於位置恢復

和基於時間點的恢復相似,可是更精確,由於同一個時間點可能有不少條 sql 語句同時執行。恢復的操做步驟以下:

(1) 在 shell 下執行命令:
shell>mysqlbinlog --start-date="2017-09-30 9:59:59" --stop-date="2017-09-30 10:01:00" /data/mysql/mysql-bin.123456 > /tmp/mysql_restore.sql

該命令將在 /tmp 目錄建立小的文本文件,編輯此文件,知道出錯語句先後的位置號,例如先後位置號分別爲 368312 和 368315。

(2) 恢復了之前的備份文件後,應從命令行輸入下面的內容:
shell>mysqlbinlog --stop-position="368312" /data/mysql/mysql-bin.123456 | mysql -uroot -ppassword  
shell>mysqlbinlog --start-position="368315" /data/mysql/mysql-bin.123456 | mysql -uroot -ppassword

上面的第一行將恢復到中止位置爲止的全部事務。下一行將恢復從給定的起始位置直到二進制日誌結束的全部事務。由於 mysqlbinlog 的輸出包括每一個 sql 語句記錄以前的 set timestamp 語句,所以恢復的數據和相關的 mysql 日誌將反應事務執行的原時間。

3、物理備份和恢復

物理備份又分爲冷備份和熱備份兩種,和邏輯備份相比,它的最大優勢是備份和恢復的速度更快,由於物理備份的原理都是基於文件的 cp。

1. 冷備份

冷備份其實就是停掉數據庫服務,cp 數據文件的方法。(基本不考慮這種方法)

2. 熱備份

在 MySQL 中,對於不一樣的存儲引擎熱備份的方法也有所不一樣。

(1) myisam 存儲引擎

myisam 存儲引擎的熱備份有不少方法,本質其實就是將要備份的表加讀鎖,而後再 cp 數據文件到備份目錄。經常使用的有如下兩種方法:

  • 使用 mysqlhotcopy 工具

// mysqlhotcopy 是 MySQL 的一個自帶的熱備份工具  
shell> mysqlhotcopy db_name [/path/to/new_directory]
  • 手工鎖表 copy

// 在 mysqlhotcopy 使用不正常的狀況下,能夠用手工來作熱備份

mysql>flush tables for read;

cp 數據文件到備份目錄便可,
(2) innodb 存儲引擎(另寫)

使用第三方工具 ibbackup、xtrabackup、innobacupex

4、 表的導入導出

在數據庫的平常維護中,表的導入導出時很頻繁的一類操做。

1. 導出

在某些狀況下,爲了一些特定的目的,常常須要將表裏的數據導出爲某些符號分割的純數據文本,而不是 sql 語句:

  • 用來做爲 Excel 顯示;

  • 單純爲了節省備份空間;

  • 爲了快速的加載數據,load data 的加載速度比普通 sql 加載要快 20 倍以上。

使用 select ...into outfile ... 命令來導出數據,具體語法以下:
mysql> select * from tablename into outfile 'target_file' [option];

其中 option 參數能夠是如下選項:

fields terminated by 'string'                   // 字段分隔符,默認爲製表符'\t'
fields [optionally] enclosed by 'char'          // 字段引用符,若是加 optionally 選項則只用在 char、varchar 和 text 等字符型字段上,默認不使用引用符  
fields escaped by ‘char’                        // 轉移字符、默認爲 '\'  
lines starting by 'string'                      // 每行前都加此字符串,默認''  
lines terminated by 'string'                    // 行結束符,默認爲'\n'  

# char 表示此符號只能是單個字符,string表示能夠是字符串。

例如,將 test 表中數據導出爲數據文本,其中,字段分隔符爲「,」,字段引用符爲「"」,記錄結束符爲回車符:

MySQL [t2]> select * from test into outfile '/data/mysql/outfile.txt' fields terminated by "," enclosed by '"';
Query OK, 4 rows affected (0.02 sec)
zj@bogon:/data/mysql$ more outfile.txt 
"1","a","helloworld"
"2","b","helloworld"
"3","c","helloworld"
"4","d","helloworld"

發現第一列是數值型,若是不但願字段兩邊用引號引發,則語句改成:

MySQL [t2]> select * from test into outfile '/data/mysql/outfile2.txt' fields terminated by "," optionally  enclosed by '"';
Query OK, 4 rows affected (0.03 sec)

zj@bogon:/data/mysql$ more outfile2.txt 
1,"a","helloworld"
2,"b","helloworld"
3,"c","helloworld"
4,"d","helloworld"

測試轉義字符,MySQL 導出數據中須要轉義的字符主要包括如下 3 類:

  • 轉義字符自己

  • 字段分隔符

  • 記錄分隔符

MySQL [t2]> update test set content = '\\"##!aa' where  id=1;
Query OK, 1 row affected (0.05 sec)
Rows matched: 1  Changed: 1  Warnings: 0

MySQL [t2]> select * from test into outfile '/data/mysql/outfile3.txt' fields terminated by "," optionally enclosed by '"';
Query OK, 4 rows affected (0.03 sec)

*******************************************
zj@bogon:/data/mysql$ more outfile3.txt 
1,"a","\\\"##!aa"
2,"b","helloworld"
3,"c","helloworld"
4,"d","helloworld"
  • 當導出命令中包含字段引用符時,數據中含有轉義字符自己和字段引用符的字符須要被轉義;

  • 當導出命令中不包含字段引用符時,數據中含有轉義字符自己和字段分割符的字符須要被轉義。

注意: select ... into outfile ... 產生的輸出文件若是在目標目錄下有重名文件,將不會被建立成功,源文件不會被自動覆蓋。

使用 mysqldump 導出數據爲文本的具體語法以下:

mysqldump -u username -T target_dir dbname tablename [option]

其中,option 參數能夠是如下選項:

  • --fields-terminated-by=name (字段分隔符);

  • --fields-enclosed-by=name (字段引用符);

  • --fields-optionally-enclosed-by=name (字段引用符,只用在 char、varchar 和 test 等字符型字段上);

  • --fields-escaped-by=name (轉義字符);

  • --lines-terminated-by=name (記錄結束符);

例子:

root@bogon:/usr/local/mysql/bin# ./mysqldump -uroot -p -T /data/mysql/dump t2 test --fields-terminated-by ',' --fields-optionally-enclosed-by '"'

**************** test.txt **********************
zj@bogon:/data/mysql/dump$ more test.txt 
1,"a","\\\"##!aa"
2,"b","helloworld"
3,"c","helloworld"
4,"d","helloworld"

***************** test.sql *********************
zj@bogon:/data/mysql/dump$ more test.sql 
-- MySQL dump 10.13  Distrib 5.7.18, for Linux (x86_64)
--
-- Host: localhost    Database: t2
-- ------------------------------------------------------
-- Server version    5.7.18-log

/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
/*!40101 SET NAMES utf8mb4 */;
/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
/*!40103 SET TIME_ZONE='+00:00' */;
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='' */;
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;

--
-- Table structure for table `test`
--

DROP TABLE IF EXISTS `test`;
/*!40101 SET @saved_cs_client     = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `test` (
  `id` int(11) DEFAULT NULL,
  `name` varchar(10) DEFAULT NULL,
  `content` varchar(100) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
/*!40101 SET character_set_client = @saved_cs_client */;

/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;

/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;

-- Dump completed on 2017-09-25 11:14:06

能夠發現,除多了一個表的建立腳本文件,mysqldump 和 select ... into outfile ... 的選項和語法很是類似。其實 mysqldump 實際調用的就是後者提供的接口,並在其上面添加了一些新的功能而已。

2. 導入 (導入用 select ... into outfile 或者 mysqldump 導出的純數據文本)

和導出相似,導入也有兩種不一樣的方法,分別是 load data infile... 和 mysqlimport,它們的本質是同樣的,區別只是在於一個在 MySQL 內部執行,另外一個在 MySQL 外部執行。

使用 「load data infile...」 命令,具體語法以下
mysql> load data [local]infile 'filename' into table tablename [option]

option 能夠是如下選項:

  • fields terminated by 'string' (字段分割符,默認爲製表符't');

  • fields [optionally] enclosed by 'char' (字段引用符,若是加 optionally 選項則只用在 char varchar text 等字符型字段上。默認不使用引用符);

  • fields escaped by 'char' (轉義字符,默認爲'')

  • lines starting by 'string' (每行前都加此字符串,默認爲'')

  • lines terminated by 'string' (行結束符,默認爲'n')

  • ignore number lines (忽略輸入文件中的前幾行數據)

  • (col_name_or_user_var,...) (按照列出的字段順序和字段數量加載數據);

  • set col_name = expr,...將列作必定的數值轉換後再加載。

fields 、lines 和前面 select...into outfile...的含義徹底相同,不一樣的是多了幾個不一樣的選項,下面的例子將文件'test.txt'中的數據加載到表 test 中:

// 清空表 test  
MySQL [t2]> truncate table test;
Query OK, 0 rows affected (0.07 sec)

MySQL [t2]> load data infile '/data/mysql/outfile.txt' into table test fields terminated by ',' enclosed by '"';
Query OK, 4 rows affected (0.10 sec)
Records: 4  Deleted: 0  Skipped: 0  Warnings: 0


MySQL [t2]> select * from test;
+------+------+------------+
| id   | name | content    |
+------+------+------------+
|    1 | a    | helloworld |
|    2 | b    | helloworld |
|    3 | c    | helloworld |
|    4 | d    | helloworld |
+------+------+------------+
4 rows in set (0.00 sec)

若是不但願加載文件中的前兩行,能夠進行以下操做:

MySQL [t2]> truncate table test;
Query OK, 0 rows affected (0.02 sec)

MySQL [t2]> load data infile '/data/mysql/outfile.txt' into table test fields terminated by ',' enclosed by '"' ignore 2 lines;
Query OK, 2 rows affected (0.00 sec)
Records: 2  Deleted: 0  Skipped: 0  Warnings: 0

MySQL [t2]> select * from test;
+------+------+------------+
| id   | name | content    |
+------+------+------------+
|    3 | c    | helloworld |
|    4 | d    | helloworld |
+------+------+------------+
2 rows in set (0.02 sec)

使用 mysqldump 實現

語法:

shell> mysqlimport -uroot -p [--local] dbname order_tab.txt [option]

其中,option 參數能夠是如下選項:

  • --fields-terminated-by=name (字段分隔符)

  • --fields-enclosed-by=name (字段引用符)

  • --fields-optionally-enclosed-by=name (字段引用符,只用在 char、varchar、text等字符型字段上)

  • --fields-escaped-by=name (轉義字符)

  • --lines-terminated-by=name (記錄結束符)

  • --ignore-lines=number (忽略前幾行)

注意: 若是導入和導出時跨平臺操做的(windows 和 linux),那麼要注意設置參數 line-terminated-by,windows 上設置爲 line-terminated-by='rn', linux 上設置爲 line-terminated-by='n'。

相關文章
相關標籤/搜索