如何在 MySQL 中进行备份?
我希望有比每“x”小时运行一次 mysqldump 更好的东西。
有没有像 SQL Server 这样的东西,你可以每天进行一次完整备份,然后每小时进行一次增量备份,所以如果你的数据库死了,你可以恢复到最新的备份吗?
像数据库日志这样的东西,只要日志没有死,您就可以恢复到数据库死的确切点?
另外,这些事情如何影响锁定?如果我执行 mysqldump,我希望在线交易会被锁定一段时间。
您可能想查看增量备份。
mysqldump 是一种合理的方法,但请记住,对于某些引擎,这将在转储期间锁定您的表 - 这对大型生产数据集存在可用性问题。
一个明显的替代方法是来自 Maatkit ( http://www.maatkit.org/ ) 的 mk-parallel-dump,如果您是 mysql 管理员,您应该检查一下。这会使用 mysqldump 并行转储多个表或数据库,从而减少转储所需的总时间。
如果您在复制设置中运行(并且如果您将 MySQL 用于生产中的重要数据,那么您没有理由不这样做),从专用于该目的的复制从属设备中获取转储将防止任何锁定问题制造麻烦。
下一个明显的替代方案——至少在 Linux 上——是使用 LVM 快照。您可以锁定表、快照文件系统并再次解锁表;然后使用该快照的挂载启动一个额外的 MySQL,从那里转储。这种方法在这里描述: http ://www.mysqlperformanceblog.com/2006/08/21/using-lvm-for-mysql-backup-and-replication-setup/
我使用 mysqlhotcopy,这是一个用于本地 MySQL 数据库和表的快速在线热备份实用程序。我很满意。
Percona 的家伙做了一个开源的替代 innobackup ......
超备份
https://launchpad.net/percona-xtrabackup/
Read this article about XtraDB http://www.linux-mag.com/cache/7356/1.html
您可能想用MySQL 复制来补充您当前的离线备份方案。
然后,如果您有硬件故障,您可以更换机器。如果您快速发现故障,您的用户甚至不会注意到任何停机或数据丢失。
我使用一个简单的脚本将 mysql 数据库转储到 tar.gz 文件中,使用 gpg 对其进行加密并将其发送到邮件帐户(Google Mail,但这实际上无关紧要)
该脚本是一个 Python 脚本,它基本上运行以下命令,并通过电子邮件发送输出文件。
mysqldump -u theuser -p mypassword thedatabase | gzip -9 - | gpg -e -r 12345 -r 23456 > 2008_01_02.tar.gz.gpg
这是整个备份。它还有网络备份部分,它只是 tar/gzips/加密文件。这是一个相当小的站点,因此网络备份远小于 20MB,因此可以毫无问题地发送到 GMail 帐户(MySQL 转储很小,压缩后大约 300KB)。这是非常基本的,并且不会很好地扩展。我每周使用 cron 运行一次。
我不太确定我们应该如何将冗长的脚本放在答案中,所以我将它作为代码块推..
#!/usr/bin/env python
#encoding:utf-8
#
# Creates a GPG encrypted web and database backups, and emails it
import os, sys, time, commands
################################################
### Config
DATE = time.strftime("%Y-%m-%d_%H-%M")
# MySQL login
SQL_USER = "mysqluser"
SQL_PASS = "mysqlpassword"
SQL_DB = "databasename"
# Email addresses
BACKUP_EMAIL=["email1@example.com", "email2@example.com"] # Array of email(s)
FROM_EMAIL = "root@myserver.com" # Only one email
# Temp backup locations
DB_BACKUP="/home/backupuser/db_backup/mysite_db-%(date)s.sql.gz.gpg" % {'date':DATE}
WEB_BACKUP="/home/backupuser/web_backup/mysite_web-%(date)s.tar.gz.gpg" % {'date':DATE}
# Email subjects
DB_EMAIL_SUBJECT="%(date)s/db/mysite" % {'date':DATE}
WEB_EMAIL_SUBJECT="%(date)s/web/mysite" % {'date':DATE}
GPG_RECP = ["MrAdmin","MrOtherAdmin"]
### end Config
################################################
################################################
### Process config
GPG_RECP = " ".join(["-r %s" % (x) for x in GPG_RECP]) # Format GPG_RECP as arg
sql_backup_command = "mysqldump -u %(SQL_USER)s -p%(SQL_PASS)s %(SQL_DB)s | gzip -9 - | gpg -e %(GPG_RECP)s > %(DB_BACKUP)s" % {
'GPG_RECP':GPG_RECP,
'DB_BACKUP':DB_BACKUP,
'SQL_USER':SQL_USER,
'SQL_PASS':SQL_PASS,
'SQL_DB':SQL_DB
}
web_backup_command = "cd /var/www/; tar -c mysite.org/ | gzip -9 | gpg -e %(GPG_RECP)s > %(WEB_BACKUP)s" % {
'GPG_RECP':GPG_RECP,
'WEB_BACKUP':WEB_BACKUP,
}
# end Process config
################################################
################################################
### Main application
def main():
"""Main backup function"""
print "Backing commencing at %s" % (DATE)
# Run commands
print "Creating db backup..."
sql_status,sql_cmd_out = commands.getstatusoutput(sql_backup_command)
if sql_status == 0:
db_file_size = round(float( os.stat(DB_BACKUP)[6] ) /1024/1024, 2) # Get file-size in MB
print "..successful (%.2fMB)" % (db_file_size)
try:
send_mail(
send_from = FROM_EMAIL,
send_to = BACKUP_EMAIL,
subject = DB_EMAIL_SUBJECT,
text = "Database backup",
files = [DB_BACKUP],
server = "localhost"
)
print "Sending db backup successful"
except Exception,errormsg:
print "Sending db backup FAILED. Error was:",errormsg
#end try
# Remove backup file
print "Removing db backup..."
try:
os.remove(DB_BACKUP)
print "...successful"
except Exception, errormsg:
print "...FAILED. Error was: %s" % (errormsg)
#end try
else:
print "Creating db backup FAILED. Output was:", sql_cmd_out
#end if sql_status
print "Creating web backup..."
web_status,web_cmd_out = commands.getstatusoutput(web_backup_command)
if web_status == 0:
web_file_size = round(float( os.stat(WEB_BACKUP)[6] ) /1024/1024, 2) # File size in MB
print "..successful (%.2fMB)" % (web_file_size)
try:
send_mail(
send_from = FROM_EMAIL,
send_to = BACKUP_EMAIL,
subject = WEB_EMAIL_SUBJECT,
text = "Website backup",
files = [WEB_BACKUP],
server = "localhost"
)
print "Sending web backup successful"
except Exception,errormsg:
print "Sending web backup FAIELD. Error was: %s" % (errormsg)
#end try
# Remove backup file
print "Removing web backup..."
try:
os.remove(WEB_BACKUP)
print "...successful"
except Exception, errormsg:
print "...FAILED. Error was: %s" % (errormsg)
#end try
else:
print "Creating web backup FAILED. Output was:", web_cmd_out
#end if web_status
#end main
################################################
################################################
# Send email function
# needed email libs..
import smtplib
from email.MIMEMultipart import MIMEMultipart
from email.MIMEBase import MIMEBase
from email.MIMEText import MIMEText
from email.Utils import COMMASPACE, formatdate
from email import Encoders
def send_mail(send_from, send_to, subject, text, files=[], server="localhost"):
assert type(send_to)==list
assert type(files)==list
msg = MIMEMultipart()
msg['From'] = send_from
msg['To'] = COMMASPACE.join(send_to)
msg['Date'] = formatdate(localtime=True)
msg['Subject'] = subject
msg.attach( MIMEText(text) )
for f in files:
part = MIMEBase('application', "octet-stream")
try:
part.set_payload( open(f,"rb").read() )
except Exception, errormsg:
raise IOError("File not found: %s"%(errormsg))
Encoders.encode_base64(part)
part.add_header('Content-Disposition', 'attachment; filename="%s"' % os.path.basename(f))
msg.attach(part)
#end for f
smtp = smtplib.SMTP(server)
smtp.sendmail(send_from, send_to, msg.as_string())
smtp.close()
#end send_mail
################################################
if __name__ == '__main__':
main()
您可以通过带有“--single-transaction --skip-lock-tables”选项的 mysqldump 对 InnoDB 数据库/表进行完全转储,而无需锁定(停机时间)。适用于制作每周快照 + 每日/每小时二进制日志增量(#Using the Binary Log to Enable Incremental Backups)。
@杰克,
谢谢(你的)信息。现在看来只有商业版才有备份功能。
MySQL中没有内置任何东西来进行体面的备份吗?
MySQL 官方页面甚至推荐诸如“好吧,你可以复制文件,只要它们没有被更新”......
直接备份 mysql 数据库文件夹的问题是备份不一定是一致的,除非您在备份期间执行写锁定。
我运行一个遍历所有数据库的脚本,对每个数据库执行 mysqldump 和 gzip 到备份文件夹,然后将该文件夹备份到磁带。
然而,这意味着不存在增量备份之类的东西,因为每晚转储是完整转储。但我认为这可能是一件好事,因为从完整备份恢复比从增量恢复要快得多 - 如果您要备份到磁带,这可能意味着在您之前收集许多磁带可以进行完全还原。
在任何情况下,无论您使用哪种备份计划,请务必进行试用恢复以确保其正常工作,并了解可能需要多长时间,以及您需要执行的确切步骤。
运行 mysql 服务器的增量或连续备份的正确方法是使用二进制日志。
首先,锁定所有表或关闭服务器。使用 mysql dump 进行备份,或者只是复制数据目录。您只需执行一次,或者在您想要完整备份的任何时候执行此操作。
在您恢复服务器之前,请确保启用了二进制日志记录。
要进行增量备份,请登录服务器并发出 FLUSH LOGS 命令。然后备份最近关闭的二进制日志文件。
如果你有所有的 innodb 表,那么使用 inno 热备份(不是免费的)或带有 --single-transaction 选项的 mysqldump 会更简单(你最好有很多内存来处理事务)。
二进制日志可能是进行增量备份的正确方法,但如果您不信任二进制文件格式用于永久存储,这里是一种 ASCII 方式来进行增量备份。
mysqldump 不是一种不好的格式,主要问题是它将表格输出为一条大线。以下琐碎的 sed 将沿记录边界拆分其输出:
mysqldump --opt -p | sed -e "s/,(/,\n(/g" > database.dump
生成的文件对 diff 非常友好,而且我已经相当成功地将它们保存在标准 SVN 存储库中。如果您发现最后一个版本出现问题并且您需要上周的版本,这还允许您保留备份历史记录。
This is a pretty solid solution for Linux shell. I have been using it for years:
http://sourceforge.net/projects/automysqlbackup/
@丹尼尔,
如果您仍然感兴趣,Paul Galbraith分享了一个新的(对我来说是新的)解决方案,该工具允许在线备份名为ibbackup从 oracle 引用 Paul 的 innodb 表,
与 innobackup结合使用时,在创建夜间备份方面效果很好,备份期间没有停机时间
更多细节可以在Paul 的博客上找到
听起来你在谈论事务回滚。
那么就你所需要的而言,如果你有包含所有历史查询的日志,那不是已经备份了吗?为什么需要增量备份,它基本上是数据库日志中所有信息的冗余副本?
如果是这样,为什么不使用 mysqldump 并每隔一段时间进行一次备份呢?