> For the complete documentation index, see [llms.txt](https://willh.gitbook.io/build-web-application-with-golang-zhtw/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://willh.gitbook.io/build-web-application-with-golang-zhtw/12.0/12.4.md).

# 備份和還原

這小節我們要討論應用程式管理的另一個方面：生產伺服器上資料的備份和還原。我們經常會遇到生產伺服器的網路斷了、硬碟壞了、作業系統崩潰、或者資料庫不可用了等各種異常情況，所以維護人員需要對生產伺服器上的應用和資料做好異地災備，冷備熱備的準備。在接下來的介紹中，講解了如何備份應用、如何備份/還原 Mysql 資料庫和 redis 資料庫。

## 應用備份

在大多數叢集環境下，Web 應用程式基本不需要備份，因為這個其實就是一個程式碼副本，我們在本地開發環境中，或者版本控制系統中已經保持這些程式碼。但是很多時候，一些開發的站點需要使用者來上傳檔案，那麼我們需要對這些使用者上傳的檔案進行備份。目前其實有一種合適的做法就是把和網站相關的需要儲存的檔案儲存到雲儲存，這樣即使系統崩潰，只要我們的檔案還在雲端儲存上，至少資料不會丟失。

如果我們沒有采用雲儲存的情況下，如何做到網站的備份呢？這裡我們介紹一個檔案同步工具 rsync：rsync 能夠實現網站的備份，不同系統的檔案的同步，如果是 windows 的話，需要 windows 版本 cwrsync。

### rsync 安裝

rysnc 的官方網站：<http://rsync.samba.org/> 可以從上面取得最新版本的原始碼。當然，因為 rsync 是一款非常有用的軟體，所以很多 Linux 的發行版本都將它收錄在內了。

軟體套件安裝

```
# sudo apt-get  install  rsync  注：在 debian、ubuntu 等線上安裝方法；
# yum install rsync    注：Fedora、Redhat、CentOS 等線上安裝方法；
# rpm -ivh rsync       注：Fedora、Redhat、CentOS 等 rpm 套件安裝方法；
```

其它 Linux 發行版，請用相應的軟體套件管理方法來安裝。原始碼套件安裝

```
tar xvf  rsync-xxx.tar.gz
cd rsync-xxx
./configure --prefix=/usr  ;make ;make install   注：在用原始碼套件編譯安裝之前，您得安裝 gcc 等編譯工具才行；
```

### rsync 配置

rsync 主要有以下三個配置檔案 rsyncd.conf(主配置檔案)、rsyncd.secrets(密碼檔案)、rsyncd.motd(rysnc 伺服器資訊)。

關於這幾個檔案的配置大家可以參考官方網站或者其他介紹 rsync 的網站，下面介紹伺服器端和客戶端如何開啟

* 伺服器端開啟：

  ```
    #/usr/bin/rsync --daemon  --config=/etc/rsyncd.conf
  ```

  \--daemon 參數方式，是讓 rsync 以伺服器模式執行。把 rsync 加入開機啟動

  ```
    echo 'rsync --daemon' >> /etc/rc.d/rc.local
  ```

  設定 rsync 密碼

  ```
    echo '你的使用者名稱 : 你的密碼' > /etc/rsyncd.secrets
    chmod 600 /etc/rsyncd.secrets
  ```
* 客戶端同步：

  客戶端可以透過如下命令同步伺服器上的檔案：

  ```
    rsync -avzP  --delete  --password-file=rsyncd.secrets   使用者名稱@192.168.145.5::www /var/rsync/backup
  ```

  這條命令，簡要的說明一下幾個要點：

  1. -avzP 是啥，讀者可以使用--help 檢視
  2. \--delete 是為了比如 A 上刪除了一個檔案，同步的時候，B 會自動刪除相對應的檔案
  3. \--password-file 客戶端中/etc/rsyncd.secrets 設定的密碼，要和伺服器端的 /etc/rsyncd.secrets 中的密碼一樣，這樣 cron 執行的時候，就不需要密碼了
  4. 這條命令中的"使用者名稱"為伺服器端的 /etc/rsyncd.secrets 中的使用者名稱
  5. 這條命令中的 192.168.145.5 為伺服器端的 IP 地址
  6. ::www，注意是 2 個 : 號，www 為伺服器端的配置檔案 /etc/rsyncd.conf 中的\[www]，意思是根據伺服器端上的/etc/rsyncd.conf 來同步其中的 \[www] 段內容，一個 : 號的時候，用於不根據配置檔案，直接同步指定目錄。

     為了讓同步即時性，可以設定 crontab，保持 rsync 每分鐘同步，當然使用者也可以根據檔案的重要程度設定不同的同步頻率。

## MySQL 備份

應用資料庫目前還是 MySQL 為主流，目前 MySQL 的備份有兩種方式：熱備份和冷備份，熱備份目前主要是採用 master/slave 方式（master/slave 方式的同步目前主要用於資料庫讀寫分離，也可以用於熱備份資料），關於如何配置這方面的資料，大家可以找到很多。冷備份的話就是資料有一定的延遲，但是可以保證該時間段之前的資料完整，例如有些時候可能我們的誤操作引起了資料的丟失，那麼 master/slave 模式是無法找回丟失資料的，但是透過冷備份可以部分還原資料。

冷備份一般使用 shell 指令碼來實現定時備份資料庫，然後透過上面介紹 rsync 同步非本地機房的一臺伺服器。

下面這個是定時備份 mysql 的備份指令碼，我們使用了 mysqldump 程式，這個命令可以把資料庫匯出到一個檔案中。

```
#!/bin/bash

# 以下配置資訊請自己修改
mysql_user="USER" #MySQL 備份使用者
mysql_password="PASSWORD" #MySQL 備份使用者的密碼
mysql_host="localhost"
mysql_port="3306"
mysql_charset="utf8" #MySQL 編碼
backup_db_arr=("db1" "db2") #要備份的資料庫名稱，多個用空格分開隔開 如("db1" "db2" "db3")
backup_location=/var/www/mysql  #備份資料存放位置，末尾請不要帶"/"，此項可以保持預設，程式會自動建立資料夾
expire_backup_delete="ON" #是否開啟過期備份刪除 ON 為開啟 OFF 為關閉
expire_days=3 #過期時間天數 預設為三天，此項只有在 expire_backup_delete 開啟時有效

# 本行開始以下不需要修改
backup_time=`date +%Y%m%d%H%M`  #定義備份詳細時間
backup_Ymd=`date +%Y-%m-%d` #定義備份目錄中的年月日時間
backup_3ago=`date -d '3 days ago' +%Y-%m-%d` #3 天之前的日期
backup_dir=$backup_location/$backup_Ymd  #備份資料夾全路徑
welcome_msg="Welcome to use MySQL backup tools!" #歡迎語

# 判斷 MYSQL 是否啟動,mysql 沒有啟動則備份退出
mysql_ps=`ps -ef |grep mysql |wc -l`
mysql_listen=`netstat -an |grep LISTEN |grep $mysql_port|wc -l`
if [ [$mysql_ps == 0] -o [$mysql_listen == 0] ]; then
        echo "ERROR:MySQL is not running! backup stop!"
        exit
else
        echo $welcome_msg
fi

# 連線到 mysql 資料庫，無法連線則備份退出
mysql -h$mysql_host -P$mysql_port -u$mysql_user -p$mysql_password <<end
use mysql;
select host,user from user where user='root' and host='localhost';
exit
end

flag=`echo $?`
if [ $flag != "0" ]; then
        echo "ERROR:Can't connect mysql server! backup stop!"
        exit
else
        echo "MySQL connect ok! Please wait......"
        # 判斷有沒有定義備份的資料庫，如果定義則開始備份，否則退出備份
        if [ "$backup_db_arr" != "" ];then
                #dbnames=$(cut -d ',' -f1-5 $backup_database)
                #echo "arr is (${backup_db_arr[@]})"
                for dbname in ${backup_db_arr[@]}
                do
                        echo "database $dbname backup start..."
                        `mkdir -p $backup_dir`
                        `mysqldump -h$mysql_host -P$mysql_port -u$mysql_user -p$mysql_password $dbname --default-character-set=$mysql_charset | gzip > $backup_dir/$dbname-$backup_time.sql.gz`
                        flag=`echo $?`
                        if [ $flag == "0" ];then
                                echo "database $dbname success backup to $backup_dir/$dbname-$backup_time.sql.gz"
                        else
                                echo "database $dbname backup fail!"
                        fi

                done
        else
                echo "ERROR:No database to backup! backup stop"
                exit
        fi
        # 如果開啟了刪除過期備份，則進行刪除操作
        if [ "$expire_backup_delete" == "ON" -a  "$backup_location" != "" ];then
                 #`find $backup_location/ -type d -o -type f -ctime +$expire_days -exec rm -rf {} \;`
                 `find $backup_location/ -type d -mtime +$expire_days | xargs rm -rf`
                 echo "Expired backup data delete complete!"
        fi
        echo "All database backup success! Thank you!"
        exit
fi
```

修改 shell 指令碼的屬性：

```
chmod 600 /root/mysql_backup.sh
chmod +x /root/mysql_backup.sh
```

設定好屬性之後，把命令加入 crontab，我們設定了每天 00:00 定時自動備份，然後把備份的指令碼目錄/var/www/mysql 設定為 rsync 同步目錄。

```
00 00 * * * /root/mysql_backup.sh
```

## MySQL 還原

前面介紹 MySQL 備份分為熱備份和冷備份，熱備份主要的目的是為了能夠即時的還原，例如應用伺服器出現了硬碟故障，那麼我們可以透過修改配置檔案把資料庫的讀取和寫入改成 slave，這樣就可以儘量少時間的中斷服務。

但是有時候我們需要透過冷備份的 SQL 來進行資料還原，既然有了資料庫的備份，就可以透過命令匯入：

```
mysql -u username -p databse < backup.sql
```

可以看到，匯出和匯入資料庫資料都是相當簡單，不過如果還需要管理許可權，或者其他的一些字符集的設定的話，可能會稍微複雜一些，但是這些都是可以透過一些命令來完成的。

## redis 備份

redis 是目前我們使用最多的 NoSQL，它的備份也分為兩種：熱備份和冷備份，redis 也支援 master/slave 模式，所以我們的熱備份可以透過這種方式實現，相應的配置大家可以參考官方的文件配置，相當的簡單。我們這裡介紹冷備份的方式：redis 其實會定時的把記憶體裡面的快取資料儲存到資料庫檔案裡面，我們備份只要備份相應的檔案就可以，就是利用前面介紹的 rsync 備份到非本地機房就可以實現。

## redis 還原

redis 的還原分為熱備份還原和冷備份還原，熱備份還原的目的和方法同 MySQL 的還原一樣，只要修改應用的相應的資料庫連線即可。

但是有時候我們需要根據冷備份來還原資料，redis 的冷備份還原其實就是隻要把儲存的資料庫檔案 copy 到 redis 的工作目錄，然後啟動 redis 就可以了，redis 在啟動的時候會自動載入資料庫檔案到記憶體中，啟動的速度根據資料庫的檔案大小來決定。

## 小結

本小節介紹了我們的應用部分的備份和還原，即如何做好災備，包括檔案的備份、資料庫的備份。同時也介紹了使用 rsync 同步不同系統的檔案，MySQL 資料庫和 redis 資料庫的備份和還原，希望透過本小節的介紹，能夠給作為開發的你對於線上產品的災備方案提供一個參考方案。

## links

* [目錄](https://github.com/doggy8088/build-web-application-with-golang-zhtw/tree/4cbbaa31bdfc3678915eb91f23db2f1bad554a20/preface.md)
* 上一章: [應用部署](/build-web-application-with-golang-zhtw/12.0/12.3.md)
* 下一節: [小結](/build-web-application-with-golang-zhtw/12.0/12.5.md)
