如何优化大型数据库的mysqldump?


173

我有一个Symfony应用程序,其中有一个带有57个表的〜2GB InnoDB数据库。数据库的大部分大小都驻留在单个表中(约1.2GB)。我目前正在使用mysqldump每晚备份数据库。

由于使用comcast连接,通常,如果我手动运行转储,则在转储完成之前,我与服务器的连接会超时,导致不得不重新运行转储。[我目前运行一个每天晚上进行转储的cron,这仅用于我手动运行的转储。]

有没有一种方法可以加快转储以解决连接超时问题,还可以限制服务器占用此过程的时间?

顺便说一句,我目前正在努力减少整个数据库的大小,以解决此问题。


2
您要传递给mysqldump命令什么参数(如果有)?
Toby

添加--compact可能是您的一个选择。
Toby

真的没什么mysqldump [database] -u[user] -p'[password]' > db_backup.sql
Patrick

4
screen对于您的情况,一种简单的替代方法是使用nohup,即使连接断开,该命令也可以使命令继续在服务器上运行。例如nohup mysqldump [options] > backup.sql 2> backup.err &。如果不提供的输出文件nohup,则nohup.out默认情况下将创建该文件。
dabest1 2011年

1
请查看atscreen(如果已安装后者,但at在所有UNIX上都是标准的),或者使用ServerAliveIntervalSSH选项来处理防火墙,以便在空闲时间过长后关闭防火墙。
MattBianco 2013年

Answers:


134

这样的转储中的主要瓶颈是驱动器I / O。您正在读取大量数据并再次写入。您可以通过多种方式来加快速度:

  • 确保输出到与数据库文件存储所在的驱动器不同的驱动器–这将与旋转磁盘产生巨大的差异,因为驱动器磁头将不会在从读取的位置之间不断地滑动以及要写入的位置。
  • mysqldump的输出将是非常可压缩的,因此,如果您不能如上所述将输出与输入分开,请通过管道将输出通过gzip或类似的管道。这将减少编写工作量(从而减少总体IO负载和磁头移动量),但会浪费一些CPU时间(无论如何,这些时间您可能还有很多空闲时间)。
  • 另外,(以及压缩替代方法)将输出通过管道实用程序(例如pv)传递,该实用程序支持大型写缓冲区,以将写入驱动器的块更多地组合在一起,从而再次减少磁头移动延迟的影响-这将使如果使用该--quick选项来减少备份大表对RAM的影响,则完全不同。
  • 仅当IO负载较低时才运行备份过程。

但是,您可能正在解决错误的问题:解决连接断开可能更容易(尽管减少备份所施加的I / O负载将有助于减少对其他用户的影响,因此还是值得尝试的)。您可以通过屏幕(或类似的工具tmux)运行手动备份吗?这样,如果与服务器的连接断开,则可以重新连接并重新连接到screen会话,而不会中断任何进程。

如果直接通过连接发送数据(即,您正在本地计算机上针对远程数据库运行mysqldump,因此转储在本地显示),最好先在服务器上运行转储,根据需要进行压缩,然后再传输使用rsync支持部分传输的工具(例如)通过网络传输数据,以便在连接断开中断传输的情况下恢复传输(而不是重新启动)。

作为“减少整个数据库的大小来解决此问题”的一部分,我想您的大部分数据都不会更改。您也许可以将主表中的1.2Gb的很大一部分移到另一个表中,并将其从mysqldump调用复制的表中删除。如果数据永不更改,则无需每次都备份该数据。通过这种方式在表和数据库之间拆分数据通常称为数据分区,并且还可以使您将数据和I / O负载分散到多个驱动器上。高端数据库内置了对自动分区的支持,尽管在mysql中,您可能必须手动进行操作并更改数据访问层以解决该问题。

偏离该站点的主题(因此,您可能应该使用ServerFault或SuperUser来询问是否需要更多详细信息):如果您似乎由于不活动而失去了连接,请检查SSH服务器和SSH客户端中的选项以进行设置确保启用了保持活动数据包并发送了足够的频率。如果即使连接处于活动状态也看到掉线,您也可以尝试使用OpenVPN或类似方法包装连接-如果整个连接断开了几秒钟,它应该处理一小段掉线,甚至完全掉线,例如SSH客户端和服务器不通知。


我希望我可以减少到服务器的ssh连接丢失的数量。如果我希望在超过60秒内不使用终端,请运行top以确保连接不会断开。(而且我很确定这是comcast连接,因为我们仅在工作中使用标准的WRT路由器和防火墙,而我的家庭comcast连接也不会丢失)
Patrick

我已添加了一条有关SSH连接的简短说明。
David Spillett

2
在这个答案中的深度和洞察力。您应该为此获得+3。抱歉,我只能给您+1。
RolandoMySQLDBA 2011年

116

深入了解使用mysqldump进行备份

恕我直言,如果您知道如何进行备份,那么备份已成为一种艺术形式

你有选择

选项1:mysqldump整个mysql实例

这是最简单的一个,不必理会!!!

mysqldump -h... -u... -p... --hex-blob --routines --triggers --all-databases | gzip > MySQLData.sql.gz

所有内容都写在一个文件中:表结构,索引,触发器,存储过程,用户,加密密码。其他mysqldump选项也可以导出不同样式的INSERT命令,二进制日志中的日志文件和位置坐标,数据库创建选项,部分数据(--where选项)等。

选项2:mysqldump将单独的数据库转储到单独的数据文件中

首先创建数据库列表(执行此操作的两种技术)

技术1

mysql -h... -u... -p... -A --skip-column-names -e"SELECT schema_name FROM information_schema.schemata WHERE schema_name NOT IN ('information_schema','mysql')" > ListOfDatabases.txt

技术2

mysql -h... -u... -p... -A --skip-column-names -e"SELECT DISTINCT table_schema FROM information_schema.tables WHERE table_schema NOT IN ('information_schema','mysql')" > ListOfDatabases.txt

技术1是最快的方法。技术2是最可靠和最安全的。技术2更好,因为有时用户会在/ var / lib / mysql(datadir)中为通用目的创建与数据库无关的文件夹。information_schema会将文件夹注册为information_schema.schemata表中的数据库。技术2将绕过不包含mysql数据的文件夹。

编译数据库列表后,您可以继续遍历该列表并mysqldump它们,即使需要也可以并行进行。

for DB in `cat ListOfDatabases.txt`
do
    mysqldump -h... -u... -p... --hex-blob --routines --triggers ${DB} | gzip > ${DB}.sql.gz &
done
wait

如果一次要启动的数据库太多,则一次将其并行转储10个数据库:

COMMIT_COUNT=0
COMMIT_LIMIT=10
for DB in `cat ListOfDatabases.txt`
do
    mysqldump -h... -u... -p... --hex-blob --routines --triggers ${DB} | gzip > ${DB}.sql.gz &
    (( COMMIT_COUNT++ ))
    if [ ${COMMIT_COUNT} -eq ${COMMIT_LIMIT} ]
    then
        COMMIT_COUNT=0
        wait
    fi
done
if [ ${COMMIT_COUNT} -gt 0 ]
then
    wait
fi

选项3:mysqldump将单独的表转储到单独的数据文件中

首先创建表列表

mysql -h... -u... -p... -A --skip-column-names -e"SELECT CONCAT(table_schema,'.',table_name) FROM information_schema.tables WHERE table_schema NOT IN ('information_schema','mysql')" > ListOfTables.txt

然后将所有表以10为一组转储

COMMIT_COUNT=0
COMMIT_LIMIT=10
for DBTB in `cat ListOfTables.txt`
do
    DB=`echo ${DBTB} | sed 's/\./ /g' | awk '{print $1}'`
    TB=`echo ${DBTB} | sed 's/\./ /g' | awk '{print $2}'`
    mysqldump -h... -u... -p... --hex-blob --triggers ${DB} ${TB} | gzip > ${DB}_${TB}.sql.gz &
    (( COMMIT_COUNT++ ))
    if [ ${COMMIT_COUNT} -eq ${COMMIT_LIMIT} ]
    then
        COMMIT_COUNT=0
        wait
    fi
done
if [ ${COMMIT_COUNT} -gt 0 ]
then
    wait
fi

选项4:使用您的想象力

尝试使用上述选项的变体以及干净快照的技术

例子

  1. 按每个表的大小升序或降序对表列表进行排序。
  2. 使用单独的过程,在启动mysqldumps之前运行“带读锁的刷新表; SELECT SLEEP(86400)”。mysqldumps完成后,请终止该过程。如果数据库同时包含InnoDB和MyISAM,这将很有帮助
  3. 将mysqldumps保存在标有日期的文件夹中,并轮换出旧的备份文件夹。
  4. 将整个实例mysqldumps加载到独立服务器中。

警告

只有选项1才能带来一切。缺点是通过这种方式创建的mysqldumps只能重新加载到生成mysqldump的相同的majot发行版mysql中。换句话说,无法在5.1或5.5中加载来自MySQL 5.0数据库的mysqldump。原因 ?各个主要版本之间的mysql模式完全不同。

选项2和3不包括保存用户名和密码。

这是为用户转储SQL Grants的通用方法,该方法易读且更可移植

mysql -h... -u... -p... --skip-column-names -A -e"SELECT CONCAT('SHOW GRANTS FOR ''',user,'''@''',host,''';') FROM mysql.user WHERE user<>''" | mysql -h... -u... -p... --skip-column-names -A | sed 's/$/;/g' > MySQLGrants.sql

选项3不保存存储过程,因此您可以执行以下操作

mysqldump -h... -u... -p... --no-data --no-create-info --routines > MySQLStoredProcedures.sql &

应该注意的另一点是关于InnoDB。如果您有一个较大的InnoDB缓冲池,则在执行任何备份之前,应尽最大可能对其进行刷新。否则,MySQL将花费时间从缓冲池中清除带有剩余脏页的表。这是我的建议:

在执行备份之前大约1小时,请运行以下SQL命令

SET GLOBAL innodb_max_dirty_pages_pct = 0;

在MySQL 5.5中,默认innodb_max_dirty_pages_pct为75。在MySQL 5.1及更高版本中,默认innodb_max_dirty_pages_pct为90。通过将innodb_max_dirty_pages_pct设置为0,这将加快将脏页刷新到磁盘的速度。这将防止或至少减轻在对任何InnoDB表执行任何mysqldump之前清理InnoDB数据的任何不完整的两阶段提交的影响。

mysqldump上的最后一个字

大多数人都避开mysqldump,转而使用其他工具,而这些工具确实不错。

这些工具包括

  1. MAATKIT(并行转储 / 还原脚本,来自Percona [不推荐使用,但性能很好])
  2. XtraBackup(Percona的TopNotch快照备份)
  3. CDP R1Soft(采用时间点快照的MySQL模块选件
  4. MySQL企业备份(以前是InnoDB热备份[商业])

如果您本着真正的MySQL DBA的精神,则可以拥抱mysqldump并完全掌握它。可能您所有的备份都反映出您作为MySQL DBA的技能


2
+1可以很好地使用mysqldump以及::如果您具有真正的MySQL DBA的精神,则可以拥抱mysqldump并完全掌握它。愿您的所有备份都能反映出您作为MySQL DBA的技能。
Abdul Manaf

4
在InnoDB中,单独转储表会给您带来不一致的备份。
阿兰·柯林斯

5
@AlainCollins这就是为什么我在只读复制从属服务器上运行mysqldumps的原因。一旦Seconds_Behind_Master为0,您将运行STOP SLAVE。现在,您具有在上述任何一种样式中执行mysqldumps的一致时间点。在过去的5年中,我为在线贸易公司做到了这一点,而对我或我的公司所有者没有任何抱怨。截至目前,我为此客户端每10分钟执行一次并行mysqldumps。我还为其他客户端提供了更快的备份时间。
RolandoMySQLDBA 2012年

我有一个32GB的分贝,因此选项3正是我所想要的!谢谢!
雷蒙德2014年

我必须备份并重新导入1TB的数据,以缩小非常大的数据ibdata1。在使用硬件RAID支持的SSD时,选项3是我唯一的解决方案。
rabudde

18

看看MySQL复制主服务器到从服务器。它允许您将master数据库克隆到具有相同数据库的另一台数据库服务器。其中包括主身份和从身份。从站将自己制作主数据库服务器和/或其数据库的精确副本。主机和从机之间可能存在一对一,多对多的关系。

从属服务器连续读取主服务器上的二进制日志(bin日志存储在主数据库服务器上写入的查询),并输入到其从属数据库服务器。(这意味着您的主数据库完全不会受到影响)

好消息是,它不会对您的MySQL服务器造成太大影响,因为您不会注意到任何停机时间或查询响应缓慢。我们将其用于10Gb数据库,它的工作原理很不错,没有任何停机时间。

在同一台机器上的MySQL复制


虽然这对我有用,但我认为这可能有点过大。我目前不需要那种备份级别,但是如果应用程序的需求发生变化,我会记住这一点。
Patrick

4
+1用于备份副本,以从主数据库中删除备份的IO负载,并减少潜在的与锁定相关的问题,但有一个重要警告:请谨慎选择“在同一计算机上的副本”选项,以对从属服务器执行操作可能会与主机竞争IO带宽-确保从机的数据文件与主机的驱动器/阵列不同,以缓解此问题。
David Spillett

1
与David Splllet的评论同上。我为My Web Hosting Employer在从属服务器上设置和维护了数十个具有mysqldump备份的主/从属服务器。我也+1。
RolandoMySQLDBA 2011年

16

计划A:另请参阅Percona的Xtrabackup。这样就可以在线备份InnoDB,而无需进行任何重大锁定。

方案B:可以停止从站,并且可以通过多种方式(复制文件,mysqldump,xtrabackup等)中的任何一个进行一致的备份

计划C:LVM快照。进行一些神秘的设置后,无论数据库的大小如何,备份的停机时间都将少于一分钟。您停止mysqld,创建快照,重新启动mysqld,然后复制快照。最后一步可能需要很长时间,但是MySQL并没有失败。

计划D:从站快照-零停机时间。


2
所有四个计划的Hoorah。我只能给每个答案+0.25!+1(4 x 0.25)
RolandoMySQLDBA 2011年

15

首先要注意几个管理要点:您是否正在连接做ftp或被ssh'ed并且快要死了?如果是ssh,请确保使用屏幕,以便在comcast崩溃后可以恢复。如果是ftp,请确保在发送前先压缩它/ tar。

也尝试--opt参数或--quick

--opt此选项启用一组附加选项,以使转储和重新加载操作更有效。具体来说,这等效于一起使用--add-drop-table,-add-locks,-all,-quick,-extended-insert,-lock-tables和--disable-keys选项。注意,此选项使输出的可移植性较弱,并且不太可能被其他数据库系统理解。

--quick该选项告诉mysqldump在从服务器读取每一行时写入转储输出,这可能对于大型表很有用。默认情况下,mysqldump在写入输出之前将表中的所有行读入内存。对于大表,这需要大量内存,可能导致转储失败。


1
--opt不会增加最终将获得输出的文件的大小吗?
Toby

它将添加一些-我的意思是添加--quick,更多地是为了解决他的问题....现在编辑。谢谢!
David Hall,

屏幕+1,这完全避免了这个问题
Gaius

+1为mysqldump的--opt和--quick解释提供了一个非常简洁明了的答案。
RolandoMySQLDBA 2011年

1
--opt默认情况下处于启用状态。
约旦

5

在大型数据库转储期间,我也经常遇到超时问题。我终于解决了是否通过向db中的每个表发送单独的命令并将所有内容附加到一个文件来实现,如下所示:

TABLES=`mysql -u $USER -p$PWD -Bse 'show tables' $DB`
for TABLE in $TABLES
do
    mysqldump -u $USER -p$PWD $DB $TABLE >> dump.sql
done

4
这被认为是“不一致”的备份,因为在还原时,您可能在一个表中具有映射到另一个表但不存在的数据。
Morgan Tocker 2014年

3

我认为问题是关于如何从mysqldump创建的转储文件中更快地恢复,而不是其他备份解决方案。

一种方法是,通过在架构中创建表组,并为每个组创建一个单独的数据库用户,然后最终使用MySQL权限不允许除一个数据库用户外的所有表插入表。

这是一种经过验证的,快速的,几乎是并行的技术,但不能100%地确定从500G左右的大型转储中恢复需要多长时间。但以我的拙见,您需要一些平行的东西。查看以下链接以获取示例。

[从MySQL的SQL转储(mysqldump)进行快速,并行还原] [1]

http://geeksww.com/tutorials/database_management_systems/mysql/tips_and_tricks/fast_parallel_restore_from_sql_dumps_mysqldump_for_mysql.php

“从MySQL的SQL转储(mysqldump)快速并行还原”


2
这是对另一个问题的答案的精确副本。您可能想针对此特定问题对其进行更多自定义。
保罗·怀特

这个问题不是专门针对如何更快地恢复。
andrew lorien '16
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.