腾讯云 MYSQL DB 数据备份/同步

This is the programmer's leisure time series!

前言:

前段时间有这样一个需求:客户希望我们把数据同步到他们服务器,包含全量数据和每日增量数据

一般来说,使用主从复制就可以解决这个问题了,但公司使用的是云数据库,没办法通过配置 my.cnf 来使用主从复制功能。云数据库虽然也提供了只读实例灾备实例来对数据进行备份与同步,但是这存在诸多限制(1.需要云数据库满足【容量100G,内存4000MB】规格。2.从库也需要是他们家的。3.按时收费😂),而且这也不符合当前的业务场景,于是只能想其它办法来解决这个问题了。


正文(上):

需求分析:

  • 首先全量数据备份到用户数据库(这个比较简单,mysqldump就可以搞定
  • 然后增量数据实时更新与同步(这个有些棘手

现存问题:

  • 无法使用主从复制
  • 客户服务器无公网IP
  • 数据总量较大(100GB+)
  • 每日更新/新增数据量较多(3000条+)
  • 数据类别繁多(200类+)

解决方案(才疏学浅只想到这几个😂):

  1. 主从复制
  2. 修改数据产生源,更新/新增数据存放多份(自有数据库、用户数据库)
  3. 编写数据同步脚本,通过 cron 定时同步
  4. 利用 mysqlbinlog 工具,通过 cron 定时恢复数据
  5. 对接云服务商 API,通过 cron 定时下载 mysqlbinlog 文件,利用该文件恢复数据
  6. 通过爬虫获取 mysqlbinlog 下载链接,利用该文件定时恢复数据

我当时接到这个需求后,首先想到的就是使用第一种方案主从复制来解决这个问题,通过深入了解后,发现这个方法在当前环境行不通,于是第一种方案我直接给pass掉了。

第二种方案,主要开发工作就集中在修改现有系统与数据库打交道的逻辑,使其能在数据新增与修改的时候,推送数据到不同服务器上。仔细想一下这个方法也不行,为什么呢?首先,要修改好几个系统才能完成这个功能,而且这种方式不仅会对系统性能造成影响,同时还不利于系统维护,然后如果以后还有更多的客户有类似需求,那这个工作将会是繁重且痛苦的。一番思考后,这个方案也给pass掉了。

第三种方案,需要编写数据同步脚本,不过由于设计到的库表比较多,估计需要一些时间才能完成脚本的编写。当然这个不是主要问题,主要问题是对于每日新增/更新的数据做到优雅的同步,毕竟数据量也在这放着,用这种方式,在最坏情况下将会全表全库扫描,性能上的消耗就可想而知了。在我想到下一个方法后,这个方案也pass掉了。

第四种方案,本以为可行性非常高。流程是这样的:我会先把全量数据还原到客户机上,然后定时对主库进行 mysqlbinlog 备份与还原。mysqlbinlog 可以基于 positiondatetime 这些方式精确备份与还原。这一切看起来都挺美好的,当我在实际操作的时候,首先就遇到了一些坑,填坑用了一些时间。然后我使用 mysqlbinlog 备份时,无论如何操作得到的文件一直非二进制文件,不是二进制文件就没办法使用 mysqlbinlog 进行精确还原,如下:

mysqlbinlog --no-defaults --start-datetime="2018-01-15 14:00:00" --stop-datetime="2018-01-15 14:30:00" --database=yqj /home/sdu/Downloads/cdb177112_bin_mysqlbin.000004 | mysql -usdu -p123456 -P3306 -h 192.168.0.166

起初我以为备份的方式有问题,如下:

 mysqlbinlog --no-defaults -uyour_server_uname -pyour_server_pwd -Pyour_server_port -hyour_server_ip --database=yqj --start-datetime='2018-01-12 20:10:00' --stop-datetime='2018-01-12 20:18:00' --read-from-remote-server mysql-bin.000004 > mysql-bin.000004

尝试了 n 多种方法,google前几十页都看完了,官方文档也看了几遍,依旧没能解决备份下来的文件非二进制文件的问题。我慢慢发现几乎没人使用我这种方法来对数据库进行 binlog 备份与还原,通常 binlog backup 下来的文件只是起到阅读/分析的作用。就算有人使用 binlog 恢复数据,也只会 copy mysql 生成的 binlog 源文件。在对 mysqlbinlog 备份二进制log 绝望后,我想到了一些歪招😏,通过命令对备份下来的文件二进制化,不过经过一番尝试后,也没能达到预期效果。到现在为止这个方法我也只能依依不舍的 pass 掉了。

第五种方案,在第四种方案被 pass 掉后,我开始了这个方法的实践。之所以没有先用这种方法,是因为通过API得到的 mysqlbinlog 文件虽然测试是可用的,但是这个文件包含了所有库与表的 binlog,不可避免的就是文件较大,如果能精确拿到想要的 binlog ,这个方法我肯定不会用的。还有一个问题就是,对接云商API我粗略估计没有个把小时是搞不定的,中间涉及到了很多加密与算法,这些加密与算法是为了保证API对接的安全性。现在用这个方案也是没有办法的办法了。你可能会说还有第六种方案,这个和对接API所存在的问题类似,同样会存在 binlog 较大问题,而且爬虫模拟登陆和认证也会设计到一些算法与解密工作。第六种方案还有一个不好的地方就是,我打个比方吧,通过云商 API 拿到数据,如同走前门,通过爬虫拿到数据,如同走后门😏。。 之后我开始写对接云商 API 的程序了,这也是一个入坑的节奏,不知道是文档有问题,还是程序出现了错乱,同样的方法部分 API 的数据可以拿到,部分就拿不到😂。这个问题花了我一天时间,到最后都开始怀疑人生了😵。程序如下:
Sdu's GitHub Tools - api_test.py

这个程序的 bug 始终没能 fix,次日,在部门主管的指点下,找到了新的道路。在 github 上竟然有针对该云商开源的sdk ,传入一些参数,调用该 sdk 的方法就可以了,我上面写那么多程序,其实可以用这个轮子代替╮(╯▽╰)╭。然后我通过这个 sdk ,用了很少的时间就把这个实现出来了。代码如下:

API 调用相关代码

Sdu's GitHub Tools - api.py

全量还原相关代码

Sdu's GitHub Tools - init_db.py

增量同步相关代码

Sdu's GitHub Tools - inc_restore_db.py

上述代码在本地测试没问题后,我开始远程到客户机上进行操作了。


正文(中):

本部分主要介绍了 Centos5.5 x86_64 和 Ubuntu16.04 x86_32 安装/配置 MySQL 的入坑之旅!

全量/增量数据同步*** 脚本在本地测试没问题后,我开始远程到客户机上进行操作。远程到服务器后,我发现我敲击的命令都不对,敲什么错什么,系统一直无情的对我说:

-bash: xxx :command not found

我第一时间意识到这个可能不是我经常使用的系统类型。

lsb_release -a 

后,果然这不是 debian 系列,这是一个 centos。

我开始搜索 centos 如何安装 MySQL,很快就找到了安装步骤。估计这个服务器很久没用过了吧,当我开始安装时,也是无止境的报错。一看就知道是 source 的问题,我又开始为这系统找 source. 更换了ali 的源以后,果然可以开始安装 MySQL 了,很快就把 MySQL 安装完了。

之后的第一件事就是配置 my.cnf

[client]
default-character-set=utf8

[mysqld]
character-set-server = utf8
collation-server = utf8_general_ci

bind-address = 0.0.0.0

为其添加utf8 编码方式,同时修改 bind-address。
第二件事就是添加用户和用户访问权限,使新用户有局域内访问的权限。

grant all privileges on *.* to sdu@"%" identified by '123456';
grant all privileges on yqj.* to your_name@"%" identified by '123456';

这一切搞定后,我开始运行脚本还原全量数据了,我以为已经踩完了所有的坑,可以喝杯咖啡☕️放松下。

半小时后,开始新一轮的报错🤣

The table “xxx” is full… 

查阅了一些文档,说这种情况可能有两个原因:一个是磁盘空间满了,另一个是 tmp_table_size 太小。我潜意识下觉得这个服务器的磁盘不可能会满的,毕竟上次客户给我用来部署服务的服务器用的可是 E7的CPU,单个CPU 都要一万八左右呢,磁盘空间这块不可能不够用的。然后我看了一下 tmp_table_size 这个值,才16mb,我觉得问题肯定在这个地方。

于是开始折腾 MySQL 的配置,只是改了个值,结果 mysql 死活不起来,心累呀。还好我在修改配置文件前有备份的好习惯,我把配置还原了,但是这个 MySQL 就是不给面子,一直无法启动。我一怒之下把这个MySQL 给彻底删了,准备再安装一遍,重新安装后,果然就没问题了😂 安装软件后,我习惯性地对系统来了一个update,果然有一些东西需要更新的,一个 Y 后,就开始了update。一分钟不到,就又开始报错了,刚开始看到这个error时,我有些怀疑自己的眼睛了,我又仔细看了一遍:xxx need 785mb disk store…,这时候我才意识到是系统磁盘空间的问题。通过

df -hl 

果然不出所料,系统还剩下20mb左右的空间。看来一开始解决问题的方向就错了,“The table xxx is full” 是因为磁盘没空间了,MySQL 一直无法启动 估计也是因为磁盘没空间了😞 好吧,我对这个服务器盲目自信了。

然后就开始向客户申请磁盘空间,半天后给了我们150g的空间,让我自己挂载。当时内心是无语的,这服务太不周到了吧😂 紧接着开始折腾 centos 磁盘分区、挂载磁盘的问题,敲了一通 fdisk 的命令,发现电脑卡死了😂 之后就再也连不上这个服务器了,不知道 fdisk 把服务器怎么了,还是服务器本身就有问题(远程的时候经常会出现连不上去的情况),只知道这个服务器好像出了点问题😅 此路不通,我也不想一直麻烦用户来帮着搞服务器,就决定把数据放到我以前给他们部署服务的机器上。给客户说了一下,他们也爽朗的同意了。

以前那个部署服务的机器出奇的卡,作为一个程序员简直要忍无可忍,我敲击一个字母需要等待3到5秒。一个命令没有一分钟写不出来,内心是崩溃的,行动是拒绝的。不过没有办法,任务还是要完成滴🤣

工欲善其事 必先利其器,我决定先解决卡顿的问题。用户的机器本来是一个 Windows Server 2008,上次部署服务的时候给 Windows Server 2008 安装了一个虚拟机。目前的情况是,Windows Server 2008 是一个虚拟机,Windows Server 2008 里面还有一个虚拟机,服务与数据都放在这个里面的虚拟机。虚拟机里面的虚拟机,这关系有够深🌵。通过任务管理器,我发现CPU一直都处于低负载状态。于是决定给虚拟机的虚拟机再多分配一些 CPU,于是我就这样做了,结果现状并没有太大改善。然后留意到网络这块的带宽好像有点小,我下载了个东西测试了一下,果然是这样的。于是我决定删除虚拟机的图形界面,只保留终端界面,因为终端界面对网络的要求要低一些。remove了一些软件包后,只有一个黑底白字的界面了,这下果然好了很多,命令敲起来顺手多了,接下来就开始做还原与同步的工作了。

同样地先配置了 MySQL 的编码方式,加入对应用户,然后就开始还原了,漫长的等待后,给我抛出这个错误:

ERROR 2013 (HY000) at line 4435: Lost connection to MySQL server during query

通过一番搜索,大多数人在 my.cnf 中加入:

max_allowed_packet = 20G
net_read_timeout = 5000

然后就平安无事了,我也如此照做🙂

又是一个漫长的等待,MySQL 继续无情的抛出:

ERROR 2013 (HY000) at line 4435: Lost connection to MySQL server during query

我心好痛💔

配置 my.cnf 无效后,我开始尝试使用第二种方式:更换 MySQL 版本。网上有很多人说 MySQL5.7 不太稳定,降级到 MySQL5.6 可以避免这个问题。

连续踩太多坑了,都开始心烦意乱了😠 到降级这里又折腾了一段时间,开始死活降不下去。到后面发现 source.list.d/ 里面配置的是 MySQL5.7 的源,修改完源后,默认安装的还是 MySQL5.7。。。多番尝试后,冷雨玫瑰的博文给了我启迪,最后终于把 MySQL5.7 降级到 MySQL5.6 了,不容易呀😭

我觉得这个踩坑之旅该结束了🙂 我开始对 MySQL5.6 进行常规配置,待我打开 /etc/mysql/my.cnf、/etc/mysql/mysql.conf.d/mysql.cnf 后,我惊讶了,它们都是空的,这是要难为我吗😂。 我已经不想再折腾了😭,从数据脚本折腾到服务器折腾,一路走来,遭遇了千万次重击,内心早已疲惫不堪😔。。。

唉,不想再多说什么了,撸起袖子就是一顿狂敲。。。 结果无论是在 /etc/mysql/my.cnf 中添加配置项,还是在 /etc/mysql/mysql.conf.d/mysql.cnf 中添加配置项,都丝毫影响不到 MySQL ╮(╯▽╰)╭ 我开始在想是不是我找错文件了😫,于是我在系统中全局搜索 MySQL 的配置文件:

sudo find / -name "mysql*"

结果只有刚才那两个文件是关于 MySQL 的,我还能说什么呢。。。

我跳过了配置,直接开始运行全量还原脚本。在做这件事之前,我写了个脚本,然后对着 Terminal 烧了根香🙃

//                            _ooOoo_  
//                           o8888888o  
//                           88" . "88  
//                           (| -_- |)  
//                            O\ = /O  
//                        ____/`---'\____  
//                      .   ' \\| |// `.  
//                       / \\||| : |||// \  
//                     / _||||| -:- |||||- \  
//                       | | \\\ - /// | |  
//                     | \_| ''\---/'' | |  
//                      \ .-\__ `-` ___/-. /  
//                   ___`. .' /--.--\ `. . __  
//                ."" '< `.___\_<|>_/___.' >'"".  
//               | | : `- \`.;`\ _ /`;.`/ - ` : | |  
//                 \ \ `-. \_ __\ /__ _/ .-` / /  
//         ======`-.____`-.___\_____/___.-`____.-'======  
//                            `=---='  
//  
//         .............................................  
//                  佛祖保佑             永无BUG 
//          佛曰:  
//                  写字楼里写字间,写字间里程序员;  
//                  程序人员写程序,又拿程序换酒钱。  
//                  酒醒只在网上坐,酒醉还来网下眠;  
//                  酒醉酒醒日复日,网上网下年复年。  
//                  但愿老死电脑间,不愿鞠躬老板前;  
//                  奔驰宝马贵者趣,公交自行程序员。  
//                  别人笑我忒疯癫,我笑自己命太贱;  
//                  不见满街漂亮妹,哪个归得程序员?

嗯~ o( ̄▽ ̄)o,脚本执行半天了,还没有报错,我内心在窃喜,看来佛祖还是有用的😀

等到次日,脚本还在运行,佛祖的力量也太强大了吧,这是完全停不下来的节奏呀😂 作为一个程序员要淡定,“不以物喜,不以己悲”。。。之后,我开始检查 MySQL 的状况。

mysql -usdu -p123456

ERROR 2002 (HY000): Can't connect to local MySQL server through socket '/var/run/mysqld/mysqld.sock' (2 "No such file or directory")

netstat -tnpl

MySQL 不在这。。。

cd /var/lib/mysql/
sudo du -h

总共才十几GB。

看来 MySQL 又挂了,这次还是默默无闻的挂,着实令人伤心(T_T)。这次我对 MySQL5.6 已经不抱有任何信心了,我决定还是换回 MySQL5.7 ,准备再尝试一次,如果还不行,那我就要在 Windows Server 2008 上安装/配置 MySQL 了。和以前一样,安装完 MySQL 以后,我就做了些常规配置,就开始执行脚本命令了。因为这个要等待比较长的时间,我在这段时间就去踩 Windows Server 安装/配置 MySQL 的坑了。


正文(下):

本部分主要介绍了 Windows Server 2008 安装/配置 MySQL 的入坑之旅!

在对 虚拟机中的虚拟机配置 绝望后,我开始尝试在Windows Server 2008上安装/配置 MySQL。

Windows 操作系统安装软件倒是挺方便,软件包下载以后,点几个下一步就🆗了。安装完成后,我依照个人经验,对 my.ini 进行了常规配置:

[client]
default-character-set=UTF8

[mysql]
default-character-set=UTF8

[mysqld]
character-set-server = utf8
collation-server = utf8_general_ci

bind-address = 0.0.0.0

然后加了一些不同权限的用户:

grant all privileges on *.* to sdu@"%" identified by '123456';
grant all privileges on yqj.* to jiangsu@"%" identified by '123456';

重启 MySQL 服务后,我先查看了当前 MySQL 的编码:

mysql>  SHOW VARIABLES LIKE 'character%';
+--------------------------+-------------------------------------------------------+
| Variable_name            | Value                                                  
+--------------------------+-------------------------------------------------------+
| character_set_client     | utf8                                                   
| character_set_connection | utf8                                                   
| character_set_database   | utf8                                                   
| character_set_filesystem | binary                                                 
| character_set_results    | utf8                                                   
| character_set_server     | utf8                                                   
| character_set_system     | utf8                                                   
| character_sets_dir       | C:\Program Files\MySQL\MySQL Server 5.5\share\charsets\
+--------------------------+-------------------------------------------------------+
8 rows in set (0.01 sec)

Windows Server 下的 MySQL 表现很不错,字符编码都没啥问题了。然后我测试新用户局域内访问是否正常:

mysql -usdu -p -h10.132.1.75 

也可以完美访问,这让我有些惊喜😃

紧接着我发现,刚才在虚拟机中的虚拟机里运行的还原脚本又把数据库给搞挂了,我已经不准备再折腾 Ubuntu16.04 X86 下的 MySQL5.7 ,现在把全部精力都放到 Windows Server 2008 下面的 MySQL 了。由于刚才的一些小惊喜,我觉得 Windows Server 下面的 MySQL,金石可镂也。

我开始直接对 Windows Server 下面的 MySQL 执行还原操作,运行了命令,在下一秒就抛出了一个 Error:

ERROR 1064 (42000) at line 279: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'STATS_PERSISTENT=0' at line 11

Windows 有些浮躁,刚夸了一句,就飘了起来,真是的😳

通过 Google 我找到了这样的说法:

The STATS_PERSISTENT is a table option that was introduced in MySQL 5.6. The option won't be recognized in an earlier version of MySQL. So you're probably trying to restore the dump to an instance of MySQL 5.5 or older.

One workaround is to use "mysqldump --skip-create-options" when you produce your dump file, which will omit all MySQL-specific table options.

提供的解决方案是添加--skip-create-options参数。

由于公司用的是5.6版本,虽然通过刚才这种方式可以解决,但是我担心以后还会有其它坑要踩,于是我决定为 Windows Server 2008 升级 MySQL 版本。

一杯咖啡的功夫,mysql-installer-community-5.6.39.0.msi 下载完成了。当我安装的时候,弹出一个错误窗口: This application requires .NET Framework 4.5.2...,看来我还需要安装这个家伙,然后我就下载并安装了 .NET Framework 4.5.2。当我再次安装 MySQL5.6 时,又弹出一个错误窗口:Requirement: Microsoft Visual C++ 2010 ...,这让我无语😶。继续下载安装 Microsoft Visual C++ 2010 ,令我惊奇的是这个软件才5.6MB,几分钟就安装完成了。再次安装 MySQL5.6,这次真的安装完成了😂

对 MySQL 简单配置后,开始一些常规测试,没啥问题。我突然想到要还原的数据库文件比较大,我就查询了当前 MySQL 数据文件存放位置:

mysql> show global variables like "%datadir%";
+---------------+---------------------------------------------+
| Variable_name | Value                                       |
+---------------+---------------------------------------------+
| datadir       | C:\ProgramData\MySQL\MySQL Server 5.6\Data\ |
+---------------+---------------------------------------------+
1 row in set (0.02 sec)

果然是放到 C 盘了,不过 C 盘空间比较小,我需要修改这个文件存放路径,不然等下肯定会失败的。
尝试去修改 my.ini 发现没这个文件,只有一个 my-default.ini,查阅官方文档,得到的结论是:

The my-default.cnf template replaces the older sample option files formerly supplied with MySQL distributions (my-small.cnf, my-medium.cnf, my-large.cnf, and my-huge.cnf).

On Windows, MySQL Installer interacts with the user and creates a file named my.ini in the base installation directory as the default option file. If you install on Windows from a Zip archive, you can copy the my-default.ini template file in the base installation directory to my.ini and use the latter as the default option file.

我按照官方所说,把 my-default.ini -> my.ini, 并修改 datadir 配置,然后根本就没啥效果。起初我以为我的姿势有问题,经过多番尝试依旧未果😪 我开始通过 Google 看大家怎么说,结果还真的让我找到了。我证实了官方说法的错误,MySQL5.6 虽然也有 my-default.ini, 但是 MySQL 真正加载的文件是: C:\ProgramData\MySQL\my.ini。这个文件也不在 MySQL 的安装目录,而在其它地方。C 盘下的 ProgramData 本身属于隐藏文件夹,一般情况下是看不到的。我通过显示隐藏文件,最终找到了这个 my.ini。我对 datadir 进行了修改,然后 MySQL 无法启动,我把配置还原,MySQL 就可以启动了。首先我刚才的推断是正确的,然后我开始对比自己写 datadir 和 原本的 datadir,浪费了半小时,尝试了多种路径,还是不行。我猜测很有可能是我文件夹权限问题,对于Windows的路径权限,我也不想去折腾,我怕这又是一个坑。

到目前为止,我亟待解决的问题是:C 盘空间太小。我决定换种方式解决这个问题,那就是把其他磁盘进行压缩,然后对 C 盘扩展容量,这个要折腾的地方应该不多。当我准备做的时候,发现自己没有操作磁盘的权限😰😂 暂时没有啥思路,就先忙其他的事去了。过了一会,突然有了个灵感,我安装的时候指定安装目录不就可以了😀

果然,我重新安装时选择了空间比较大的磁盘,然后等待安装完成,╰(°▽°)╯

mysql> show global variables like "%datadir%";
+---------------+---------------------------------------------+
| Variable_name | Value                                       |
+---------------+---------------------------------------------+
| datadir       | F:\ProgramData\MySQL\MySQL Server 5.6\Data\ |
+---------------+---------------------------------------------+
1 row in set (0.01 sec)

哈哈😄,果然可以了,接下来我就可以还原数据了。

先烧香!先烧香!先烧香!重要事情,必须说三遍!!!

 /** ━━━━━━神兽出没━━━━━━
 *    ┏┓   ┏┓
 *   ┏┛┻━━━┛┻┓
 *   ┃       ┃
 *   ┃   ━   ┃
 *   ┃ ┳┛ ┗┳ ┃
 *   ┃       ┃
 *   ┃   ┻   ┃
 *   ┃       ┃
 *   ┗━┓   ┏━┛Code is far away from bug with the animal protecting
 *     ┃   ┃    神兽保佑,代码无bug
 *     ┃   ┃
 *     ┃   ┗━━━┓
 *     ┃       ┣┓
 *     ┃       ┏┛
 *     ┗┓┓┏━┳┓┏┛
 *      ┃┫┫ ┃┫┫
 *      ┗┻┛ ┗┻┛
 */
 
 /** ━━━━━━感觉萌萌哒━━━━━━
 *
 *         ┏┓   ┏┓
 *        ┏┛┻━━━┛┻┓
 *        ┃       ┃  
 *        ┃   ━   ┃
 *        ┃ >   < ┃
 *        ┃       ┃
 *        ┃... ⌒ ... ┃
 *        ┃       ┃
 *        ┗━┓   ┏━┛
 *          ┃   ┃ Code is far away from bug with the animal protecting          
 *          ┃   ┃   神兽保佑,代码无bug
 *          ┃   ┃           
 *          ┃   ┃        
 *          ┃   ┃
 *          ┃   ┃           
 *          ┃   ┗━━━┓
 *          ┃       ┣┓
 *          ┃       ┏┛
 *          ┗┓┓┏━┳┓┏┛
 *           ┃┫┫ ┃┫┫
 *           ┗┻┛ ┗┻┛
 */

 /**        ┏┓   ┏┓+ +
 *       ┏┛┻━━━┛┻┓ + +
 *       ┃       ┃  
 *       ┃   ━   ┃ ++ + + +
 *       ████━████ ┃+
 *       ┃       ┃ +
 *       ┃   ┻   ┃
 *       ┃       ┃ + +
 *       ┗━┓   ┏━┛
 *         ┃   ┃           
 *         ┃   ┃ + + + +
 *         ┃   ┃    Code is far away from bug with the animal protecting       
 *         ┃   ┃ +     神兽保佑,代码无bug  
 *         ┃   ┃
 *         ┃   ┃  +         
 *         ┃    ┗━━━┓ + +
 *         ┃        ┣┓
 *         ┃        ┏┛
 *         ┗┓┓┏━┳┓┏┛ + + + +
 *          ┃┫┫ ┃┫┫
 *          ┗┻┛ ┗┻┛+ + + +
 */
 

期待(☆▽☆)
。。。
这次的期待,依旧是落空了,数据库还是挂了😂 目前是真的没办法了,我准备停下脚步,Thinking、Thinkinng、Thinking...

这次虽然也失败了,但是我注意到一点,当我在执行还原操作时,mysqld 的进程会占满 16GB RAM,磁盘也会满载,唯有 CPU 只占用一半。从中应该能看出,mysqldump 在进行备份还原操作时,会先把读取数据到 RAM ,然后再写入磁盘。通过我的细心观察,还发现还原操作一般是执行到某一张非常大的表,然后失败的。通过以上的一些信息,我的猜想如下:mysqldump 还原和备份是以表为最小单位,而不是以行或列为最小单位。还原或备份操作执行时,会把整张表读取到内存中,然后再写入磁盘。当一张表过于庞大,那一般电脑的性能是没办法完成这个操作的。虽然不知道我的猜想是否会不太严谨,但现在对于这个还原数据问题,我倒真没有什么可行的解决办法了。


正文(后续):

当我写这部分博文的时候,这个问题已经被我解决了,中间踩了很多很多坑。在前几篇博文中,我也大概的描述了遇到的一些坑,在这篇博文我会把解决方案给大家说一下。踩坑的过程真是一把辛酸、一把泪😂

当时在折腾 Windows Server 2008 失败后,同事推荐我用 Percona XtraBackup 进行数据库的备份与还原。官方是这样描述这个工具的:

Percona XtraBackup is the world’s only open-source, free MySQL hot backup software that performs non-blocking backups for InnoDB and XtraDB databases. With Percona XtraBackup, you can achieve the following benefits:

  • Backups that complete quickly and reliably
  • Uninterrupted transaction processing during backups
  • Savings on disk space and network bandwidth
  • Automatic backup verification
  • Higher uptime due to faster restore time
    ...

这个工具感觉还不错,通过阅读更多,我发现这个应该是我想要的。因为这工具是基于 datadir 的备份,而不是针对 sql语句 的备份。如果基于 datadir 进行备份,好比是直接把 MySQL 数据库的物理文件备份下来,而不用调用 MySQL 进程也不用调用 MySQL 相关操作。

我按照文档的说明把工具安装到本地,其他准备工作也都已做好,接下来就可以开始备份与还原了。
先把云数据库备份到 /tmp/mysql_backup/ 下:

xtrabackup --defaults-file=/etc/mysql/mysql.conf.d/mysqld.cnf --user=your_user --password=your_pwd --host=your_server_host --port=your_server_port --databases=your_db --backup --target-dir=/tmp/mysql_backup/

好吧,这一步就无法通过,并向我抛出一堆异常:

180126 02:45:47  version_check Connecting to MySQL server with DSN 'dbi:mysql:;mysql_read_default_group=xtrabackup;host=your_server_host;port=your_server_port' as 'shendu'  (using password: YES).
180126 02:45:47  version_check Connected to MySQL server
180126 02:45:47  version_check Executing a version check against the server...
180126 02:45:47  version_check Done.
180126 02:45:47 Connecting to MySQL server host: your_server_host, user: shendu, password: set, port: your_server_port, socket: not set
Using server version 5.6.28-20170830-log
Warning: MySQL variable 'datadir' points to nonexistent directory '/data1/mysql_root/data/20136/'
Warning: option 'datadir' has different values:
  '/var/lib/mysql' in defaults file
  '/data1/mysql_root/data/20136/' in SHOW VARIABLES
xtrabackup version 2.4.9 based on MySQL server 5.7.13 Linux (i686) (revision id: a467167cdd4)
xtrabackup: uses posix_fadvise().
xtrabackup: cd to /var/lib/mysql
xtrabackup: open files limit requested 0, set to 1024
xtrabackup: using the following InnoDB configuration:
xtrabackup:   innodb_data_home_dir = .
xtrabackup:   innodb_data_file_path = ibdata1:12M:autoextend
xtrabackup:   innodb_log_group_home_dir = /data/mysql_root/log/20136
xtrabackup:   innodb_log_files_in_group = 2
xtrabackup:   innodb_log_file_size = 536870912
InnoDB: Number of pools: 1
InnoDB: Operating system error number 2 in a file operation.
InnoDB: The error means the system cannot find the path specified.
InnoDB: File /data/mysql_root/log/20136/ib_logfile0: 'open' returned OS error 71. Cannot continue operation
InnoDB: Cannot continue operation.

通过阅读官方文档和查看多方技术博客,我大概明白了为什么。这个工具会获取 datadir 的路径,但是云服务器的这个路径是没办法获取的,就如我最初所说:公司使用的是云数据库,没办法通过配置 my.cnf 来使用主从复制功能,到这里也是类似的问题,由于是云数据库,我这边根本没办法拿到 datadir 的路径。这个工具到这里也夭折了,使用这工具中间还是遇到了不少的坑,我上面只列出了一个重要且致命的问题,是因为这篇博客不是来讲 Percona XtraBackup 踩坑之路的。

虽然 Percona XtraBackup 也不可行,但是在研究它的过程中,我有了新的认知。① Percona XtraBackup 工具的出现,是因为 mysqldump 对 10GB 以上的数据库备份与还原不太友好;② mysqldump 的备份原理无法用于较大型数据库。③ 对于生产环境上的数据库应该使用 non-blocking backups这种备份还原方式。④要想解决我现在的问题,应该采用物理文件备份的方式。

我开始研究如何备份数据库物理文件,在查阅很多相关的资料后,我终于找到了相关的文献:腾讯云备份方式: 物理备份/逻辑备份如何利用备份文件恢复实例使用物理备份文件恢复数据库

我首先把云数据库备份方式从逻辑备份修改为物理备份,等备份完成后我开始按这个文献 如何利用备份文件恢复实例 来操作。结果我又失败了,我再次阅读该文献,发现这个文献是针对 MariaDB 数据库的,这个不适用于我。紧接着我按照这个文献 使用物理备份文件恢复数据库 上说的来做:

shendu@ubuntu:~/backup$ mysqld_safe --defaults-file=/home/shendu/backup/data/backup-my.cnf --user=mysql --datadir=/home/shendu/backup/data &
[1] 10293
/usr/bin/mysqld_safe: 647: /usr/bin/mysqld_safe: cannot create /home/shendu/backup/data/ubuntu.err: Permission denied
Logging to '/home/shendu/backup/data/ubuntu.err'.
2018-01-26T06:24:58.357432Z mysqld_safe Starting mysqld daemon with databases from /home/shendu/backup/data
/usr/bin/mysqld_safe: 144: /usr/bin/mysqld_safe: cannot create /home/shendu/backup/data/ubuntu.err: Permission denied
/usr/bin/mysqld_safe: 1: eval: cannot create /home/shendu/backup/data/ubuntu.err: Permission denied
/usr/bin/mysqld_safe: 906: /usr/bin/mysqld_safe: cannot create /home/shendu/backup/data/ubuntu.err: Permission denied
2018-01-26T06:24:58.424121Z mysqld_safe mysqld from pid file /home/shendu/backup/data/ubuntu.pid ended
/usr/bin/mysqld_safe: 144: /usr/bin/mysqld_safe: cannot create /home/shendu/backup/data/ubuntu.err: Permission denied
^C
[1]+  Done                    mysqld_safe --defaults-file=/home/shendu/backup/data/backup-my.cnf --user=mysql --datadir=/home/shendu/backup/data

首先出现了权限问题,虽然我已经按照文献说明修改了备份文件的权限。我决定直接使用 root 权限来操作:

shendu@ubuntu:~/backup$ sudo !!
sudo mysqld_safe --defaults-file=/home/shendu/backup/data/backup-my.cnf --user=mysql --datadir=/home/shendu/backup/data &
[1] 10387
Logging to '/home/shendu/backup/data/ubuntu.err'.
2018-01-26T06:25:11.652037Z mysqld_safe Starting mysqld daemon with databases from /home/shendu/backup/data
2018-01-26T06:25:29.665875Z mysqld_safe mysqld from pid file /home/shendu/backup/data/ubuntu.pid ended
[1]+  Done  

这次果然没有任何报错,这应该是通过了 mysqld_safe 验证。然后我继续按照文档的说明来操作:

shendu@ubuntu:~/backup/data$ mysql -uroot 
ERROR 1045 (28000): Access denied for user 'root'@'localhost' (using password: NO)

这个没成功,我又继续试了其它 MySQL 用户:

shendu@ubuntu:~/backup/data$ mysql -usdu -p
Enter password: 
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 18
Server version: 5.7.20-0ubuntu0.16.04.1 (Ubuntu)

Copyright (c) 2000, 2017, Oracle and/or its affiliates. All rights reserved.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| mysql              |
| performance_schema |
| sys                |
+--------------------+
4 rows in set (0.06 sec)

mysql> 

倒是可以进去,不过和我期待的不一致,我原以为进去后就有那些数据库了。
刚才的文献到这里就已经结束了,但我的问题还未解决。首先我检查了刚才的操作是否有误,确认无误后,我开始思考对策。一道灵光闪现,我回忆到解压后的文件夹的内容,和我以前在 /var/lib/mysql/ 下面看到东西有很多类似的。我再次看了一下解压后的文件:

shendu@ubuntu:~$ ls -rl ~/backup/data
total 1212504
drwxr-x--- 2 mysql mysql   55512288 Jan 25 01:13 your_db_name
-rw-r----- 1 mysql mysql       157 Jan 25 01:13 xtrabackup_slave_info
-rw-r----- 1 mysql mysql   8388608 Jan 25 01:13 xtrabackup_logfile
-rw-r----- 1 mysql mysql       785 Jan 25 01:13 xtrabackup_info
-rw-r----- 1 mysql mysql       121 Jan 25 01:13 xtrabackup_checkpoints
-rw-r----- 1 mysql mysql       328 Jan 25 01:13 xtrabackup_cdb_result
-rw-r--r-- 1 mysql mysql        26 Jan 25 01:13 xtrabackup_binlog_pos_innodb
-rw-r----- 1 mysql mysql       117 Jan 25 01:13 xtrabackup_binlog_info
-rw-r----- 1 mysql mysql  79691776 Jan 25 22:25 undo001
-rw-r----- 1 mysql mysql     10205 Jan 25 22:25 ubuntu.err
drwxr-x--- 2 mysql mysql      4096 Jan 25 01:13 test
drwxr-x--- 2 mysql mysql      4096 Jan 25 01:13 your_db_name
drwxr-x--- 2 mysql mysql      4096 Jan 25 01:13 performance_schema
drwxr-x--- 2 mysql mysql      4096 Jan 25 01:13 mysql
-rw-r----- 1 mysql mysql 536870912 Jan 25 22:25 ib_logfile1
-rw-r----- 1 mysql mysql 536870912 Jan 25 22:25 ib_logfile0
-rw-r----- 1 mysql mysql  79691776 Jan 25 22:25 ibdata1
-rw-r----- 1 mysql mysql       861 Jan 25 22:25 ib_buffer_pool
drwxr-x--- 2 mysql mysql      4096 Jan 25 01:13 your_db_name
drwxr-x--- 2 mysql mysql      4096 Jan 25 01:13 your_db_name
-rw-r----- 1 mysql mysql       425 Jan 25 22:13 backup-my.cnf
-rw-r----- 1 mysql mysql        56 Jan 25 22:25 auto.cnf

shendu@ubuntu:~$ ls -rl /var/lib/mysql/
total 176184
-rw-rw---- 1 mysql mysql    24576 Jan 26 11:56 tc.log
drwx------ 2 mysql mysql     4096 Aug 27 14:06 performance_schema
drwx------ 2 mysql mysql     4096 Aug 27 14:06 mysql
-rw-rw---- 1 mysql mysql        0 Jun 28  2017 multi-master.info
-rw-rw---- 1 mysql mysql 50331648 Jan 26 11:56 ib_logfile1
-rw-rw---- 1 mysql mysql 50331648 Jan 26 11:56 ib_logfile0
-rw-rw---- 1 mysql mysql 79691776 Jan 26 11:56 ibdata1
-rw-r--r-- 1 root  root         0 Aug 27 14:06 debian-10.1.flag
-rw-rw---- 1 mysql mysql       52 Jan 26 11:39 aria_log_control
-rw-rw---- 1 mysql mysql    16384 Jan 26 11:39 aria_log.00000001

于是我觉得把备份下来的db文件,复制到当前数据库就可以了。
全部复制以后,我打开数据库,激动人心的时候到了:

mysql> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| crawler            |
| your_db_name       |
| your_db_name       |
| mysql              |
| performance_schema |
| sys                |
| test               |
| your_db_name       |
| your_db_name       |
+--------------------+
10 rows in set (0.00 sec)

果然都有了(我把公司业务数据库的名字换成了your_db_name),这是一件振奋人心的事情!👏👏👏

我接下来要看看数据是否一致:

mysql> use your_db_name;
Database changed
mysql> show tables;
+------------------------------+
| Tables_in_your_db_name       |
+------------------------------+
| your_table_name              |
| your_table_name              |
| your_table_name              |
| .....                        |
+------------------------------+
xxx rows in set (0.00 sec)

mysql> select count(*) from your_table_name;
ERROR 1146 (42S02): Table 'yqj.yqj_user' doesn't exist

ERROR 1146 (42S02): Table 'yqj.yqj_user' doesn't exist”,居然报错了,这不科学呀😂

我通过 Google 搜索该问题,得到了一些解答,如:

如果是myisam类型的表可以将.frm .MYD .MYI一起复制,如果是InnoDB的话就丢失.ibd就不行了,可以通过redo log恢复

mysql是采用缓冲方式来讲数据写入到ibdata1文件中的,这正是flush()函数存在的理由。因此当别人的mysql在运行时,你对data文件夹进行拷贝,即对ibdata1进行拷贝肯定会导致该文件中的数据出错的.

Just in case anyone still cares: I had the same issue after copying a database directory directly using command cp -r /path/to/my/database /var/lib/mysql/new_database If you do this with a database that uses InnoDB tables, you will get this crazy 'table does not exist' error mentioned above. The issue is that you need the ib* files in the root of the MySQL datadir (e.g. ibdata1, ib_logfile0 and ib_logfile1). When I copied those it worked for me.

看来我现在遇到的问题是 .ibd、ibdata1 等MySQL核心文件有问题,导致数据库的一些信息加载不出来。我现在需要解决文件受损问题,希望不太复杂😂。

通过一番搜索,我发现对 .ibd、ibdata1 这些文件修复,都是 高阶DBA 干的活。想搞定这些无疑是要研究一番 MySQL 底层了😂 在我看了几篇 TB大牛 出版的数据库内核月报 后,我发现这个比我想象中要复杂,目前我还不适合在这里杀怪 😭

我决定重新下载数据库备份,之前很可能是下载的过程中出现了一些问题,导致部分数据受损。漫长的等待后,文件终于下载完了,我开始执行刚才的那些还原操作。一起就绪后,我发现还是有刚才的那个问题,在我进一步确认后,我发现只有一个张表有这个问题,其它都没有问题。我去 /var/lib/mysql/ 查看了该表所占空间,发现这是一张最大,也是最核心的表,占据数据库总空间的 80% ,一张表竟然有几十GB😂


写到这里,我想说下和这个问题相关的另一个问题😶。我刚才曾说到,一个表占据总空间的 80%,达到了几十GB,这个足以说明一个问题:那就是该数据库的设计存在诸多不合理之处! 在我来公司一年多里,随着我对业务了解的深入、技术实力的提升,我早就想把这些数据库重构了😂 万事俱备只欠东风,几个月前,公司开始了部分产品的优化升级工作,借着这个机会,我终于可以大刀阔斧地优化这些结构混乱、数据冗余的数据库了😎

之前部分库是这个样子:

1.1-11
1.2-7

我对这两个库进行第一次合并与重构后,它们变成这样了:

1.3-6

紧接着做了第二次优化:

1.4-5

是不是要清晰很多,这个就是最开始那两张图的合并升级版👏👏👏 可能大家看到这些乱麻麻的图没啥概念,那我就用数据说话😜:

2.1
2.2
1.5-5

由于目前产品升级优化还未全部完成,所以现在只能先折腾这个冗余的数据库,等产品升级优化完成后,就可以切到新数据库,这些问题就可以避免了。


好了,题外话就说到这吧。接下来我准备转换思路来解决这个问题~

1.6-4

经过这番漫长的折腾,我找到了问题所在,那就是某个表太过于庞大和冗余了。导致我通过 热备、冷备、mysqldump、xtrabackup 等等方式均失败而归,那我干脆跳过这个张表,只对其它表进行备份与还原。等其它库/表成功后,我再针对该表编写一个脚本,把所有数据同步下来不就可以了吗!最后等待所有库/表同步完成,我启动定时任务增量更新数据,应该就没什么问题了。

首先,忽略部分表,还原其它所有表数据:

shendu@ubuntu:~$ mysqldump --host=your_server_host --port=your_server_port -uyour_server_user -pyour_server_password -C --databases your_dbs --ignore-table=your_ignore_table  | mysql -usdu -p123456 your_db 
 
mysql: [Warning] Using a password on the command line interface can be insecure.
mysqldump: [Warning] Using a password on the command line interface can be insecure.
Warning: A partial dump from a server that has GTIDs will by default include the GTIDs of all transactions, even those that changed suppressed parts of the database. If you don't want to restore GTIDs, pass --set-gtid-purged=OFF. To make a complete dump, pass --all-databases --triggers --routines --events. 

然后,要先把那些被忽略的表创建出来(推荐使用 mysqldump 只备份表结构,然后直接还原),之后再启动脚本同步:

shendu@ubuntu:~/Project/tools/db_to_db$ ls
api.py  inc_restore_db.py  init_db.py  __init__.py  mysql.py  __pycache__  README.md  requirement.txt  restore_article.py
shendu@ubuntu:~/Project/tools/db_to_db$ source ../VENV/bin/activate
(VENV) shendu@ubuntu:~/Project/tools/db_to_db$ python restore_article.py 
Current article id :  18974859
Current article id :  18974858
Current article id :  18974857
Current article id :  18974856
Current article id :  18974855
Current article id :  18974854
Current article id :  18974853
...

还原被忽略表相关代码:

(VENV) shendu@ubuntu:~/Project/tools/yqj_to_yqj$ cat restore_article.py 
import time

from mysql import query, query_one, save

db_config = {
    'host': 'your_server_host',
    'port': your_server_port,
    'user': 'your_server_user',
    'passwd': 'your_server_password',
    'db': 'your_server_db',
}

db_config2 = {
    'host': '127.0.0.1',
    'port': 3306,
    'user': 'sdu',
    'passwd': '123456',
    'db': 'your_local_db',
}

def remote_to_local():
    article_count = query_one(sql='SELECT COUNT(*) FROM `article`',
                              db_config=db_config,
                              )[0]

    index = 0
    while index < article_count:
        article_list = query(sql='SELECT * FROM `article` ORDER BY `id` DESC LIMIT %s, 100',
                             db_config=db_config,
                             list1=(index,),
                             )

        for article in article_list:
            a_id = article[0]
            author = article[1]
            title = article[2]
            url = article[3]
            content = article[4]
            source = article[5]
            pubtime = article[6]
            uuid = article[7]
            area_id = article[8]
            publisher_id = article[9]
            feeling_factor = article[10]
            website_type = article[11]

            print('Current article id : ', a_id)

            save(sql='''INSERT INTO `article` VALUES('{0}', '{1}', '{2}', '{3}', '{4}', '{5}', '{6}', '{7}', '{8}', '{9}', '{10}', '{11}')'''.format(a_id, author, title, url, content, source, pubtime, uuid, area_id, publisher_id, feeling_factor, website_type, ),
                 db_config=db_config2,
                 )

        time.sleep(1)
        index += 100


def main():
    remote_to_local()

if __name__ == '__main__':
    main()

MySQL工具类相关代码:

(VENV) shendu@ubuntu:~/Project/tools/yqj_to_yqj$ cat mysql.py 
import pymysql

'''config like this:
{
    'host':'127.0.0.1',
    'port':3306,
    'user':'root',
    'passwd':'123456',
    'db':'mysql',
}
'''

class MySQL:

    def __init__(self, db_config):
        self.host = db_config.get('host')
        self.port = db_config.get('port')
        self.user = db_config.get('user')
        self.passwd = db_config.get('passwd')
        self.db = db_config.get('db')

    def open(self):
        return pymysql.connect(
            host=self.host,
            port=self.port,
            user=self.user,
            passwd=self.passwd,
            db=self.db,
            charset='utf8')


def query(sql, db_config, list1=()):
    db = MySQL(db_config).open()
    cursor = db.cursor()
    cursor.execute(sql, list1)
    result = cursor.fetchall()

    db.commit()
    db.close()
    return result


def query_one(sql, db_config, list1=()):
    db = MySQL(db_config).open()
    cursor = db.cursor()
    cursor.execute(sql, list1)
    result = cursor.fetchone()

    db.commit()
    db.close()
    return result


def save(sql, db_config, list1=()):
    db = MySQL(db_config).open()
    cursor = db.cursor()
    try:
        result = cursor.execute(sql, list1)
        db.commit()
        return result
    except Exception as e:
        print(e)
        db.rollback()
        return None
    db.close()

最后,当所有数据同步完成后,只用把增量同步脚本定时启动就可以了。Linux 定时任务配置与启动非常简单,我这里就不过多赘述了!

增量同步脚本相关代码:

(VENV) shendu@ubuntu:~/Project/tools/yqj_to_yqj$ cat inc_restore_db.py 
import subprocess
import datetime

from operator import itemgetter

from api import call_api


def get_binlog():
    '''
    action: 对应接口的接口名,请参考wiki文档上对应接口的接口名
    '''
    action = 'GetCdbExportLogUrl'

    # 接口参数
    action_params = {
        'cdbInstanceId': 'cdb-ko3zdkzs',
        'type': 'binlog',
    }

    result = eval(bytes.decode(call_api(action, action_params)))
    result_by_date = sorted(result['data'], key=itemgetter('date'), reverse=True)[0]

    out_url = result_by_date['out_url'].replace('\\', '')
    backup_time = '{0} {1}'.format(result_by_date['date'].split(' ')[0], '00:00:00')

    return out_url, backup_time


def download_backup(out_url):
    command = 'rm /tmp/cdb177112_bin_mysqlbin'
    subprocess.call(command, shell=True)

    command = 'wget -O /tmp/cdb177112_bin_mysqlbin "{0}"'.format(out_url)
    subprocess.call(command, shell=True)


def increment_restore(backup_time):
    stop_time = datetime.datetime.strptime(backup_time, "%Y-%m-%d %H:%M:%S")
    start_time = stop_time + datetime.timedelta(days=-1)

    command = 'mysqlbinlog --no-defaults --start-datetime="{0}" --stop-datetime="{1}" -d yqj /tmp/cdb177112_bin_mysqlbin | mysql -usdu -p123456'.format(start_time, stop_time)
    subprocess.call(command, shell=True)


def main():
    result = get_binlog()

    out_url = result[0]
    backup_time = result[1]

    download_backup(out_url)
    increment_restore(backup_time)

if __name__ == '__main__':
    main()

API 调用相关代码:

from QcloudApi.qcloudapi import QcloudApi

'''
module: 设置需要加载的模块
已有的模块列表:
cvm      对应   cvm.api.qcloud.com
cdb      对应   cdb.api.qcloud.com
lb       对应   lb.api.qcloud.com
trade    对应   trade.api.qcloud.com
sec      对应   csec.api.qcloud.com
image    对应   image.api.qcloud.com
monitor  对应   monitor.api.qcloud.com
cdn      对应   cdn.api.qcloud.com
'''
module = 'cdb'

'''
config: 云API的公共参数
'''
config = {
    'Region': 'ap-guangzhou',
    'secretId': 'your_secretId',
    'secretKey': 'your_secretKey',
    'method': 'GET',
    'SignatureMethod': 'HmacSHA1'
}


def call_api(action, action_params):

    try:
        service = QcloudApi(module, config)

        '''
        # 请求前可以通过下面几个方法重新设置请求的secretId/secretKey/region/method/SignatureMethod参数
        # 重新设置请求的secretId
        secretId = '你的secretId'
        service.setSecretId(secretId)
        # 重新设置请求的secretKey
        secretKey = '你的secretKey'
        service.setSecretKey(secretKey)
        # 重新设置请求的region
        region = 'ap-shanghai'
        service.setRegion(region)
        # 重新设置请求的method
        method = 'POST'
        service.setRequestMethod(method)
        # 重新设置请求的SignatureMethod
        SignatureMethod = 'HmacSHA256'
        service.setSignatureMethod(SignatureMethod)
        # 生成请求的URL,不发起请求
        print(service.generateUrl(action, action_params))
        '''

        # 调用接口,发起请求
        return service.call(action, action_params)

    except Exception as e:
        import traceback
        print('traceback.format_exc():\n%s' % traceback.format_exc())
        return None


if __name__ == '__main__':
    '''
    action: 对应接口的接口名,请参考wiki文档上对应接口的接口名
    '''
    action = 'GetCdbExportLogUrl'

    # 接口参数
    action_params = {
        'cdbInstanceId': 'cdb-ko3zdkzs',
        'type': 'coldbackup',
    }

    call_api(action, action_params)

到这里终于结束了😀 大约花了我两周的时间在折腾这个问题,中间涉及到很多自己不了解的知识。一路走来,遇到了很多 Bug,不过这番折腾收获颇丰😋 我把这些踩过的坑,用自己喜欢的方式记录下来并分享给了大家,希望能对后人有所帮助😂 最后对那些乐于奉献与分享的博主表示深深的感谢🙏,是他们的分享帮我解决了很多问题,也规避了很多问题!

共享改变世界!


参考文献 (核心)

END!😜

Show Comments

Get the latest posts delivered right to your inbox.