備份與恢復
存儲引擎和一致性
3.復制
從備庫中備份最大的好處是可以不干擾主庫,避免在主庫上增加額外的負載。這是一個建立備庫的好理由,即使不需要用它做負載均衡或高可用。如果錢是個問題,也可以把備份用的備庫用于其他用戶,例如報表服務——只要不對其做寫操作,以確保備份不會修改數據。備庫不必只用于備份的目的;只需要在下次備份時能及時跟上主庫,即使有時因作為其他用途導致復制延時也沒有關系。當從備庫備份時,應該保存所有關于復制進程的信息,例如備庫相對于主庫的位置。這對于很多情況都非常有用:克隆新的備庫,重新應用二進制日志到主庫上以獲得指定時間點的恢復,將備庫提升為主庫等。如果停止備庫,需要確保沒有打開的臨時表,因為它們可能導致不餓能重啟備庫。故意將一個備庫延時一段時間對于某些災難場景非常有用。例如延時復制一小時,當一個不期望的語句在主庫上運行后,將有一個小時的時間觀察到并在中繼日志中方之前停掉復制。然后可以將備庫提升為主庫,重放少量的日志事件,跳過錯誤的語句。這比后面要討論的指定事件點的恢復技術可能要快得多。Percona Toolkit中pt-slave-delay工具可以幫助實現這個方案。
備庫可能與主庫數據不完全一樣。許多人認為備庫是主庫完全一樣的副本,但以經驗,主庫與備庫數據不匹配是很常見,并且MySQL沒有方法檢測這個問題。檢測這個問題的唯一方法是使用Percona Toolkit中的pt-table-checksum之類的工具。擁有一個復制的備庫可鞥在諸如主庫的硬盤燒壞時提供幫助,但卻不能提供保證。復制不是備份。
管理和備份二進制日志
服務器的二進制日志時備份的最重要因素之一。它們對于基于時間點的恢復是必需的,并且通常比數據要小,所以更容易進行頻繁的備份。如果有某個時間點的數據備份和所有從那時以后的二進制日志,就可以重放自上次全備以來的二進制日志并"前滾"所有的變更。MySQL復制也使用二進制日志。因此備份和恢復的策略經常和復制配置相互影響。二進制日志很"特別"。如果丟失了數據,你一定不希望同時丟失了二進制日志。為了讓這種情況發送的幾率減少到最小,可以在不同的卷上保存數據和二進制日志,即使在LVM下生成二進制日志的快照,也是可以的。為了額外的安全起見,可以將它們保存在SAN上,或用DRBD復制到另外一個設備上。經常備份二進制日志是個好主意。如果不能承受丟失超過30分鐘數據的價值,至少需要每30分鐘就備份一次。也可以用一個配置–log_slave_update的只讀備庫,這樣可以獲得額外的安全性。備庫上日志位置與主庫不匹配,但找到恢復時正確的位置并不難。最后,MySQL5.6版本的mysqlbinlog有一個非常方便的特性,可以連接到服務器上來實時對二進制日志做鏡像,比起運行一個mysqld實例要簡單和輕便,它與老版本時向后兼容的。
二進制日志格式
二進制日志包含一系列的事件。每個事件有一個固定長度的頭,其中有各種信息,例如當前時間戳和默認的數據庫。可以使用mysqlbinlog工具來查看二進制日志的內容,打印一些頭信息。下面是一個輸出的例子。
$ mysqlbinlog mysql-bin.000002
第一行包含日志文件內的偏移字節值
第二行寶行以下幾項:
- 1.事件的日期和事件,MySQL會使用它們來產生SET TIMESTAMP語句。
- 2.原服務器的服務器ID,對于防止復制之間無限循環和其他問題是非常有必要的。
- 3.end_log_pos,下一個事件的偏移字節值。該值對一個多語句事務中的大部分事件是不正確的。在此類事務過程中,MySQL的主庫會復制事件到一個緩沖區,但這樣做的時候它并不知道下個日志事件的位置
- 4.事件類型。本例中的類型是Query,但還有許多不同的類型
- 5.原服務器上執行事件的線程ID,對于審計和執行CONNECTION_ID()函數很重要。
- 6.exec_time,這是語句的時間戳和寫入二進制日志的時間之差。不要依賴這個值,因為它可能在復制落后的備庫上會有很大的偏差
- 7.在原服務器上事件產生的錯誤代碼。如果事件在一個備庫上重放時導致不同的錯誤,那么復制將因安全預警而失敗。
后續的行包含重放變更時所需的數據。用戶自定義的變更和任何其他特定設置,例如當語句執行時有效的時間戳,也將會出現在這里。如果使用的是MySQL5.1中基于行的日志,事件將不再是SQL.而是可讀性較差的由語句對表所做變更的"鏡像"
安全地清除老的二進制日志
需要決定日志的過期策略以防止磁盤被二進制日志寫滿。日志增長多大取決于負載和日志格式(基于行的日志回導致更大的日志記錄)。我們建議,如果可能,只要日志有用就盡可能保留。保留日志對于設置復制、分析服務器負載、審計和從上次全備按時間點進行恢復,都很有幫助。當決定想要保留日志多久時,應該考慮這些需求。
一個常見的設置是使用expire_log_days變量來告訴MySQL定期清理日志。這個變量直到MySQL4.1才引入;在此之前的版本,必須手動清理二進制日志。因此,你可能看到一些用類似下面的cron項來刪除老的二進制日志的建議。
0 0 * * * /usr/bin/ find /var/log/mysql -mtime +N -name "mysql-bin.[0-9]*" | xargs rm
盡管這是在MySQL 4.1之前清除日志的唯一辦法,但在新版本中不要這么做!用rm刪除日志會導致mysql-bin.index狀態文件與磁盤上的文件不一致,有些語句,例如SHOW MASTER LOGS可能會受到影響而悄然失敗。手動修改mysql-bin.index文件也不會修復這個問題。應該用類似下面的cron命令
0 0 * * * /usr/bin/mysql -e "PURGE MASTER LOGS BEFORE CURRENT_DATE - INTERVAL N DAY"
expire_logs_days設置在服務器啟動或MySQL切換二進制日志時生效,因此,如果二進制日志從沒有增長和切換,服務器不會清除老條目。此設置時通過查看日志的修改事件而不是內容來決定哪個文件需要被清除。
備份數據
大多時候,生成備份有好的也有差的方法——有時候顯而易見的方法并不是好方法。一個有用的技巧時應該最大化利用網絡、磁盤和CPU的能力以盡可能快地完成備份。這是一個需要不斷取平衡的事情,必須通過實驗以找到"最佳平衡點"
生成邏輯備份
對于邏輯備份,首先要意識到的是它們并不是以同樣方式創建的。實際上有兩種類型的邏輯備份:SQL導出和符號分割文件。
- 1.SQL導出
SQL導出是很多人所熟悉的,因為它們是mysqldump默認的方式。例如,用默認選項導出一個小表將產生如下輸出:
可以是如下命令:
mysqldump -u root -p sakila actor > myactor.sql
導出文件包含表結構和數據,均以有效的SQL命令形式寫出。文件以設置MySQL各種選項的注釋開始。這些要么是為了使恢復工作更高效,要么使因為兼容性和正確性。接下來可以看到表結構,然后是數據,最后,腳本重置在導出開始時變更的選項。導出的輸出對于還原操作來說是可執行的。這很方便。但mysqldump默認選項讀與生成一個巨大的備份卻不是太適合。mysqldump不是生成SQL邏輯備份的唯一工具。例如,也可以用mydumper或phpMyAdmin工具來創建。想指出的是,不是某一個特定的工具有多大的問題,而是做SQL邏輯備份本身就有一些缺點。下面是主要問題點:
- 1.Schema和數據存儲在一起
如果想從單個文件恢復這樣做會非常方便,但如果只想恢復一個表或指向恢復數據就很困難了。可以通過導出兩次的方法來環節這個問題——一次只導出數據,另外一次只導出Schema——但還會有下一個麻煩 - 2.巨大的SQL語句
服務器分析和執行SQL語句的工作量非常大,所以加載數據時會非常慢 - 3.單個巨大的文件
大部分文本編輯器不能編輯巨大的或者包含非常長的行的文件。盡管有時候可以用命令行的流編輯器——例如sed或grep——來抽出需要的數據,但保持文件小型化仍然是更合適的 - 4.邏輯備份的成本很高
比起邏輯備份這種從存儲引擎中讀取數據然后通過客戶端/服務器協議發送結果集的方式,還有其他更高效的方式
這些限制意味著SQL導出在表變大時可能變得不可用。不過,還有另外一個選擇;導出數據到符號分割的文件中