Require:利用MySQL binlog实现闪回操作
1,闪回原理
【binlog】MySQL binlog以event的形式,记录了MySQL server从启用binlog以来所有的变更信息,能够帮助重现这之间的所有变化。MySQL引入binlog主要有两个目的:一是为了主从复制;二是某些备份还原操作后需要重新应用binlog。
有三种可选的binlog格式,各有优缺点:
- statement:基于SQL语句的模式,binlog数据量小,但是某些语句和函数在复制过程可能导致数据不一致甚至出错;
- row:基于行的模式,记录的是行的完整变化。很安全,但是binlog会比其他两种模式大很多;
- mixed:混合模式,根据语句来选用是statement还是row模式;
利用binlog闪回,需要将binlog格式设置为row。row模式下,一条使用innodb的insert会产生如下格式的binlog:
# at 1129 #161225 23:15:38 server id 3773306082 end_log_pos 1197 Query thread_id=1903021 exec_time=0 error_code=0 SET TIMESTAMP=1482678938/*!*/; BEGIN /*!*/; # at 1197 #161225 23:15:38 server id 3773306082 end_log_pos 1245 Table_map: `test`.`user` mapped to number 290 # at 1245 #161225 23:15:38 server id 3773306082 end_log_pos 1352 Write_rows: table id 290 flags: STMT_END_FBINLOG ' muJfWBPiFOjgMAAAAN0EAAAAACIBAAAAAAEABHRlc3QABHVzZXIAAwMPEQMeAAAC muJfWB7iFOjgawAAAEgFAAAAACIBAAAAAAEAAgAD//gBAAAABuWwj+i1tVhK1hH4AgAAAAblsI/p krFYStYg+AMAAAAG5bCP5a2ZWE/onPgEAAAABuWwj+adjlhNeAD4BQAAAAJ0dFhRYJM= '/*!*/; # at 1352 #161225 23:15:38 server id 3773306082 end_log_pos 1379 Xid = 5327954 COMMIT/*!*/;
【闪回原理】既然binlog以event形式记录了所有的变更信息,因此闪回就是把需要回滚的event,从后往前回滚回去即可。对于单个event的回滚,以表test.user为例:
mysql> show create table test.user\G *************************** 1. row ***************************Table: user Create Table: CREATE TABLE `user` (`id` int(11) NOT NULL AUTO_INCREMENT,`name` varchar(10) DEFAULT NULL,PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8
- 对于delete操作,从binlog提取出delete信息,生成的回滚语句是insert。(注:为了方便解释,用binlog2sql将原始binlog转化成了可读SQL)
原始:DELETE FROM `test`.`user` WHERE `id`=1 AND `name`='小赵';回滚:INSERT INTO `test`.`user`(`id`, `name`) VALUES (1, '小赵');
- 对于insert操作,回滚SQL是delete。
原始:INSERT INTO `test`.`user`(`id`, `name`) VALUES (2, '小钱');回滚:DELETE FROM `test`.`user` WHERE `id`=2 AND `name`='小钱';
- 对于update操作,回滚sql应该交换SET和WHERE的值。
原始:UPDATE `test`.`user` SET `id`=3, `name`='小李' WHERE `id`=3 AND `name`='小孙';回滚:UPDATE `test`.`user` SET `id`=3, `name`='小孙' WHERE `id`=3 AND `name`='小李';
2,闪回工具
【第一类】以patch形式集成到官方工具mysqlbinlog中。以彭提交的patch为代表。
- 上手成本低。mysqlbinlog原有的选项都能直接利用,只是多加了一个闪回选项。闪回特性未来有可能被官方收录。
- 支持离线解析。
- 兼容性差、项目活跃度不高。由于binlog格式的变动,如果闪回工具作者不及时对补丁升级,则闪回工具将无法使用。目前已有多位人员分别针对mysql5.5,5.6,5.7开发了patch,部分项目代码公开,但总体上活跃度都不高。
- 难以添加新功能,实战效果欠佳。在实战中,经常会遇到现有patch不满足需求的情况,比如要加个表过滤,很简单的一个需求,代码改动也不会大,但对大部分DBA来说,改mysql源码还是很困难的事。
- 安装稍显麻烦。需要对mysql源码打补丁再编译生成。
【第二类】独立工具,通过伪装成slave拉取binlog来进行处理。以binlog2sql为代表。
- 兼容性好。伪装成slave拉binlog这项技术在业界应用的非常广泛,多个开发语言都有这样的活跃项目,MySQL版本的兼容性由这些项目搞定,闪回工具的兼容问题不再突出。
- 添加新功能的难度小。更容易被改造成DBA自己喜欢的形式。更适合实战。
- 安装和使用简单。
- 必须开启MySQL server。
【第三类】简单脚本。先用mysqlbinlog解析出文本格式的binlog,再根据回滚原理用正则进行匹配并替换。
- 脚本写起来方便,往往能快速搞定某个特定问题。
- 安装和使用简单。
- 支持离线解析。
- 通用性不好。
- 可靠性不好。
【关于DDL的闪回】如果误操作是DDL的话,是无法利用binlog做快速回滚的,因为即使在row模式下,binlog对于DDL操作也不会记录每行数据的变化。要实现DDL快速回滚,必须修改MySQL源码,使得在执行DDL前先备份老数据。目前有多个mysql定制版本实现了DDL闪回特性,阿里林晓斌团队提交了patch给MySQL官方,MariaDB预计在不久后加入包含DDL的flashback特性。DDL闪回的副作用是会增加额外存储。
3,使用方式
真实的闪回场景中,最关键的是能快速筛选出真正需要回滚的SQL。使用开源工具binlog2sql来进行实战演练。binlog2sql由美团点评DBA团队(上海)出品。
https://github.com/danfengcao/binlog2sqlMySQL server必须设置以下参数:
[mysqld] server_id = 1 log_bin = /var/log/mysql/mysql-bin.log max_binlog_size = 1G binlog_format = row binlog_row_image = fullsystemctl restart mysqlduser需要的最小权限集合:
select, super/replication client, replication slave建议授权 GRANT SELECT, REPLICATION SLAVE, REPLICATION CLIENT ON *.* TO
- select:需要读取server端information_schema.COLUMNS表,获取表结构的元信息,拼接成可视化的sql语句。
- super/replication client:两个权限都可以,需要执行'SHOW MASTER STATUS', 获取server端的binlog列表。
- replication slave:通过BINLOG_DUMP协议获取binlog内容的权限。
【解析出标准SQL】
shell> python binlog2sql.py -h127.0.0.1 -P3306 -uadmin -p'admin' -dtest -t test3 test4 --start-file='mysql-bin.000002'输出: INSERT INTO `test`.`test3`(`addtime`, `data`, `id`) VALUES ('2016-12-10 13:03:38', 'english', 4); #start 570 end 736 UPDATE `test`.`test3` SET `addtime`='2016-12-10 12:00:00', `data`='中文', `id`=3 WHERE `addtime`='2016-12-10 13:03:22' AND `data`='中文' AND `id`=3 LIMIT 1; #start 763 end 954 DELETE FROM `test`.`test3` WHERE `addtime`='2016-12-10 13:03:38' AND `data`='english' AND `id`=4 LIMIT 1; #start 981 end 1147
- mysql连接配置:
-h host; -P port; -u user; -p password
- 解析模式:
--stop-never 持续解析binlog。可选。默认False,同步至执行命令时最新的binlog位置。-K, --no-primary-key 对INSERT语句去除主键。可选。默认False-B, --flashback 生成回滚SQL,可解析大文件,不受内存限制。可选。默认False。与stop-never或no-primary-key不能同时添加。--back-interval -B模式下,每打印一千行回滚SQL,加一句SLEEP多少秒,如不想加SLEEP,请设为0。可选。默认1.0。
- 解析范围控制:
--start-file 起始解析文件,只需文件名,无需全路径 。必须。--start-position/--start-pos 起始解析位置。可选。默认为start-file的起始位置。--stop-file/--end-file 终止解析文件。可选。默认为start-file同一个文件。若解析模式为stop-never,此选项失效。--stop-position/--end-pos 终止解析位置。可选。默认为stop-file的最末位置;若解析模式为stop-never,此选项失效。--start-datetime 起始解析时间,格式'%Y-%m-%d %H:%M:%S'。可选。默认不过滤。--stop-datetime 终止解析时间,格式'%Y-%m-%d %H:%M:%S'。可选。默认不过滤。
- 对象过滤:
-d, --databases 只解析目标db的sql,多个库用空格隔开,如-d db1 db2。可选。默认为空。-t, --tables 只解析目标table的sql,多张表用空格隔开,如-t tbl1 tbl2。可选。默认为空。--only-dml 只解析dml,忽略ddl。可选。默认False。--sql-type 只解析指定类型,支持INSERT, UPDATE, DELETE。多个类型用空格隔开,如--sql-type INSERT DELETE。可选。默认为增删改都解析。用了此参数但没填任何类型,则三者都不解析。【解析出回滚SQL】
shell> python binlog2sql.py --flashback -h127.0.0.1 -P3306 -uadmin -p'admin' -dtest -ttest3 --start-file='mysql-bin.000002' --start-position=763 --stop-position=1147输出: INSERT INTO `test`.`test3`(`addtime`, `data`, `id`) VALUES ('2016-12-10 13:03:38', 'english', 4); #start 981 end 1147 UPDATE `test`.`test3` SET `addtime`='2016-12-10 13:03:22', `data`='中文', `id`=3 WHERE `addtime`='2016-12-10 12:00:00' AND `data`='中文' AND `id`=3 LIMIT 1; #start 763 end 954
4,闪回实战
背景:小明在 17:00 时误删了ry库sys_dict_type表的全部数据,需要紧急回滚。
mysql> select * from sys_config; ... 10 rows in set (0.00 sec)mysql> delete from sys_config; Query OK, 10 rows affected (0.01 sec) mysql> select * from sys_config; Empty set (0.00 sec)
- 直接查看二进制日志内容,发现乱码。
mysqlbinlog --no-defaults mysql-bin.000001
- 登录mysql,查看目前的binlog文件。
mysql> show master logs; +------------------+-----------+ | Log_name | File_size | +------------------+-----------+ | mysql-bin.000001 | 154 | +------------------+-----------+ 1 row in set (0.00 sec)
- 最新的binlog文件是mysql-bin.000001。目标是筛选出需要回滚的SQL,由于误操作人只知道大致的误操作时间,首先根据时间做一次过滤。只需要解析test库user表。(注:如果有多个sql误操作,则生成的binlog可能分布在多个文件,需解析多个文件)。
python binlog2sql/binlog2sql.py -h127.0.0.1 -P3306 -uroot -p'12345678' -dry -tsys_config --start-file='mysql-bin.000001' --start-datetime='2025-01-13 11:44:00' --stop-datetime='2025-01-13 18:00:00' > sql.logDELETE FROM `ry`.`sys_config` WHERE `config_id`=1 AND `config_name`='主框架页-默认皮肤样式名称' AND `config_key`='sys.index.skinName' AND `config_value`='skin-blue' AND `config_type`='Y' AND `create_by`='admin' AND `create_time`='2018-03-16 11:33:00' AND `update_by`='ry' AND `update_time`='2018-03-16 11:33:00' AND `remark`='蓝色 skin-blue、绿色 skin-green、紫色 skin-purple、红色 skin-red、黄色 skin-yellow' LIMIT 1; #start 4 end 1014 time 2025-01-13 17:37:02 DELETE FROM `ry`.`sys_config` WHERE `config_id`=2 AND `config_name`='用户管理-账号初始密码' AND `config_key`='sys.user.initPassword' AND `config_value`='123456' AND `config_type`='Y' AND `create_by`='admin' AND `create_time`='2018-03-16 11:33:00' AND `update_by`='ry' AND `update_time`='2018-03-16 11:33:00' AND `remark`='初始化密码 123456' LIMIT 1; #start 4 end 1014 time 2025-01-13 17:37:02 DELETE FROM `ry`.`sys_config` WHERE `config_id`=3 AND `config_name`='主框架页-侧边栏主题' AND `config_key`='sys.index.sideTheme' AND `config_value`='theme-dark' AND `config_type`='Y' AND `create_by`='admin' AND `create_time`='2018-03-16 11:33:00' AND `update_by`='ry' AND `update_time`='2018-03-16 11:33:00' AND `remark`='深黑主题theme-dark,浅色主题theme-light,深蓝主题theme-blue' LIMIT 1; #start 4 end 1014 time 2025-01-13 17:37:02
- 着重关注上面的 #start 4 end 1014,执行转换操作:
python binlog2sql/binlog2sql.py -h127.0.0.1 -P3306 -uroot -p'12345678' -dry -tsys_config --start-file='mysql-bin.000001' --start-position=4 --stop-position=1014 -B > rollback.sqlINSERT INTO `ry`.`sys_config`(`config_id`, `config_name`, `config_key`, `config_value`, `config_type`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (4, '账号自助-是否开启用户注册功能', 'sys.account.registerUser', 'false', 'Y', 'admin', '2018-03-16 11:33:00', 'ry', '2018-03-16 11:33:00', '是否开启注册用户功能'); #start 4 end 1014 time 2025-01-13 17:37:02 INSERT INTO `ry`.`sys_config`(`config_id`, `config_name`, `config_key`, `config_value`, `config_type`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (3, '主框架页-侧边栏主题', 'sys.index.sideTheme', 'theme-dark', 'Y', 'admin', '2018-03-16 11:33:00', 'ry', '2018-03-16 11:33:00', '深黑主题theme-dark,浅色主题theme-light,深蓝主题theme-blue'); #start 4 end 1014 time 2025-01-13 17:37:02 INSERT INTO `ry`.`sys_config`(`config_id`, `config_name`, `config_key`, `config_value`, `config_type`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (2, '用户管理-账号初始密码', 'sys.user.initPassword', '123456', 'Y', 'admin', '2018-03-16 11:33:00', 'ry', '2018-03-16 11:33:00', '初始化密码 123456'); #start 4 end 1014 time 2025-01-13 17:37:02
- 与业务方确认回滚sql没问题,执行回滚语句。登录mysql,确认回滚成功。
mysql -h127.0.0.1 -P3306 -uroot -p'12345678' < /root/rollback.sqlmysql> select * from sys_config; ... 10 rows in set (0.00 sec)
相关文章:
Require:利用MySQL binlog实现闪回操作
1,闪回原理 【binlog】MySQL binlog以event的形式,记录了MySQL server从启用binlog以来所有的变更信息,能够帮助重现这之间的所有变化。MySQL引入binlog主要有两个目的:一是为了主从复制;二是某些备份还原操作后需要重…...
计算机网络 (40)域名系统DNS
前言 计算机网络域名系统DNS(Domain Name System)是互联网的基础技术之一,它负责将人类可读的域名转换为计算机用来通信的数字IP地址。 一、基本概念 DNS的主要目的是将域名解析或翻译为IP地址,使得用户可以通过简单易记的域名来访…...
UE5游戏性能优化指南
UE游戏性能和场景优化思路: 1. 可以把可延展性调低,帧率会大幅提高,但画质会大幅降低 2.调整固定灯光,静态光源,烘焙构建光照,灯光重叠距离,关闭阴影 3.设置模型和地形LOD 4.开启Nanite&…...
Gitlab Runner安装与配置
由于格式和图片解析问题,为了更好阅读体验可前往 阅读原文 本篇使用Docker安装Gitlab runner进行runner的安装和注册,其他方式请参考官方文档非Docker安装Gitlab runner 请确保runner版本和gitlab版本兼容以及docker相关版本兼容问题 下载镜像 docker p…...
如何有效防止和解决IP劫持问题
一、什么是IP劫持? IP劫持是一种攻击方式,攻击者通过伪装成合法的IP地址欺骗网络系统,从而非法获取数据或中断服务。这种攻击可以导致数据泄露、服务不可用等多种严重后果。 二、防范措施 启用SSL/TLS加密 SSL/TLS协议可以在客户端和服务器…...
2006-2020年各省人均水资源量数据
2006-2020年各省人均水资源量数据 1、时间:2006-2020年 2、来源:国家统计局、统计年鉴 3、指标:行政区划代码、地区名称、年份、人均水资源量 4、范围:31省 5、指标解释:人均水资源量是指一个国家或地区在一定时期…...
C++基础入门(二)
目录 前言 一、重载 1.函数重载 2.运算符重载 二、构造函数 1.什么是构造函数 2.带参数的构造函数 3.使用初始化列表 4.this关键字 5.new关键字 三、析构函数 1.什么是析构函数 四、静态成员变量 1.静态成员的定义 2.静态成员变量的作用 五、继承 1.继承基本概…...
互联网架构困境:网络与信息安全
当我们说 TCP/IP 没有内置安全属性时,这到底是什么意思?事实上仔细观察身边的世界,很少有内置安全属性的,这源自于石器时代的野人们没有粮仓需要保护。 “互联网前身 ARPAnet 最初来自于美国国防部对等通信需求”,即使…...
HIVE技术
本文章基于黑马免费资料编写。 hive介绍 简介 hive架构 hive需要启动的配置 执行元数据库初始化命令 使用hive必须启动的服务 ./schematool -initSchema -dbType mysql -verbos启动 Hive 创建一个 hive 的日志文件夹 mkdir /export/server/hive/logs启动元数据管理服务 n…...
RustDesk ID更新脚本
RustDesk ID更新脚本 此PowerShell脚本自动更新RustDesk ID和密码,并将信息安全地存储在Bitwarden中。 特点 使用以下选项更新RustDesk ID: 使用系统主机名生成一个随机的9位数输入自定义值 为RustDesk生成新的随机密码将RustDesk ID和密码安全地存储…...
卷积神经网络的底层是傅里叶变换
1 卷积神经网络与傅里叶变换、希尔伯特空间坐标变换的关系_卷积神经网络与傅里页变换之间的关系-CSDN博客 从卷积到图像卷积再到卷积神经网络,到底卷了什么? 一维信号卷积:当前时刻之前的每一个时刻是如何对当前时刻产生影响的 图像卷积&…...
Bootstrap 下拉菜单
Bootstrap 下拉菜单 Bootstrap 是一个流行的前端框架,它提供了许多预构建的组件,其中之一就是下拉菜单。下拉菜单是一个交互式元素,允许用户从一系列选项中选择一个。在本篇文章中,我们将详细介绍如何在 Bootstrap 中创建和使用下…...
计算机组成原理(计算机系统3)--实验一:WinMIPS64模拟器实验
一、实验目标: 了解WinMIPS64的基本功能和作用; 熟悉MIPS指令、初步建立指令流水执行的感性认识; 掌握该工具的基本命令和操作,为流水线实验做准备。 二、实验内容 按照下面的实验步骤及说明,完成相关操作记录实验…...
读书笔记~管理修炼-风险性决策:学会缩小风险阈值
假设你的团队为了提升业绩,提出了两个解决方案:A方案是通过营销提升老产品的利润;B方案是通过研发开拓新产品,你会怎么选? 我们先来分析下,其实无论是A方案还是B方案,都会遇到市场难题…...
VIVADO FIFO (同步和异步) IP 核详细使用配置步骤
VIVADO FIFO (同步和异步) IP 核详细使用配置步骤 目录 前言 一、同步FIFO的使用 1、配置 2、仿真 二、异步FIFO的使用 1、配置 2、仿真 前言 在系统设计中,利用FIFO(first in first out)进行数据处理是再普遍不过的应用了,…...
tcp粘包原理和解决
tcp粘包原理和解决 咱们先通过展示基于tcp 的cs端消息通信时的现象,带着问题再解释下面的tcp粘包问题。 一、原始代码 tcp 服务端代码 // socket_stick/server/main.gofunc process(conn net.Conn) {defer conn.Close()reader : bufio.NewReader(conn)var bu…...
C语言预处理艺术:编译前的魔法之旅
大家好,这里是小编的博客频道 小编的博客:就爱学编程 很高兴在CSDN这个大家庭与大家相识,希望能在这里与大家共同进步,共同收获更好的自己!!! 本文目录 引言正文一、预处理的作用与流程…...
C++算法第十六天
本篇文章我们继续学习动态规划 第一题 题目链接 978. 最长湍流子数组 - 力扣(LeetCode) 题目解析 从上图可见其实有三个状态 代码原理 注意:我们在分析题目的时候分析出来的是三个状态,分别是上升、下降、平坦,但是…...
计算机网络 (45)动态主机配置协议DHCP
前言 计算机网络中的动态主机配置协议(DHCP,Dynamic Host Configuration Protocol)是一种网络管理协议,主要用于自动分配IP地址和其他网络配置参数给连接到网络的设备。 一、基本概念 定义:DHCP是一种网络协议…...
归子莫的科技周刊#2:白天搬砖,夜里读诗
归子莫的科技周刊#2:白天搬砖,夜里读诗 本周刊开源,欢迎投稿。 刊期:2025.1.5 - 2025.1.11。原文地址。 封面图 下班在深圳看到的夕阳,能遇到是一种偶然的机会,能拍下更是一种幸运。 白天搬砖,…...
3.3.1_1 检错编码(奇偶校验码)
从这节课开始,我们会探讨数据链路层的差错控制功能,差错控制功能的主要目标是要发现并且解决一个帧内部的位错误,我们需要使用特殊的编码技术去发现帧内部的位错误,当我们发现位错误之后,通常来说有两种解决方案。第一…...
Fabric V2.5 通用溯源系统——增加图片上传与下载功能
fabric-trace项目在发布一年后,部署量已突破1000次,为支持更多场景,现新增支持图片信息上链,本文对图片上传、下载功能代码进行梳理,包含智能合约、后端、前端部分。 一、智能合约修改 为了增加图片信息上链溯源,需要对底层数据结构进行修改,在此对智能合约中的农产品数…...
人工智能(大型语言模型 LLMs)对不同学科的影响以及由此产生的新学习方式
今天是关于AI如何在教学中增强学生的学习体验,我把重要信息标红了。人文学科的价值被低估了 ⬇️ 转型与必要性 人工智能正在深刻地改变教育,这并非炒作,而是已经发生的巨大变革。教育机构和教育者不能忽视它,试图简单地禁止学生使…...
scikit-learn机器学习
# 同时添加如下代码, 这样每次环境(kernel)启动的时候只要运行下方代码即可: # Also add the following code, # so that every time the environment (kernel) starts, # just run the following code: import sys sys.path.append(/home/aistudio/external-libraries)机…...
系统掌握PyTorch:图解张量、Autograd、DataLoader、nn.Module与实战模型
本文较长,建议点赞收藏,以免遗失。更多AI大模型应用开发学习视频及资料,尽在聚客AI学院。 本文通过代码驱动的方式,系统讲解PyTorch核心概念和实战技巧,涵盖张量操作、自动微分、数据加载、模型构建和训练全流程&#…...
Cilium动手实验室: 精通之旅---13.Cilium LoadBalancer IPAM and L2 Service Announcement
Cilium动手实验室: 精通之旅---13.Cilium LoadBalancer IPAM and L2 Service Announcement 1. LAB环境2. L2公告策略2.1 部署Death Star2.2 访问服务2.3 部署L2公告策略2.4 服务宣告 3. 可视化 ARP 流量3.1 部署新服务3.2 准备可视化3.3 再次请求 4. 自动IPAM4.1 IPAM Pool4.2 …...
恶补电源:1.电桥
一、元器件的选择 搜索并选择电桥,再multisim中选择FWB,就有各种型号的电桥: 电桥是用来干嘛的呢? 它是一个由四个二极管搭成的“桥梁”形状的电路,用来把交流电(AC)变成直流电(DC)。…...
6️⃣Go 语言中的哈希、加密与序列化:通往区块链世界的钥匙
Go 语言中的哈希、加密与序列化:通往区块链世界的钥匙 一、前言:离区块链还有多远? 区块链听起来可能遥不可及,似乎是只有密码学专家和资深工程师才能涉足的领域。但事实上,构建一个区块链的核心并不复杂,尤其当你已经掌握了一门系统编程语言,比如 Go。 要真正理解区…...
WebRTC调研
WebRTC是什么,为什么,如何使用 WebRTC有什么优势 WebRTC Architecture Amazon KVS WebRTC 其它厂商WebRTC 海康门禁WebRTC 海康门禁其他界面整理 威视通WebRTC 局域网 Google浏览器 Microsoft Edge 公网 RTSP RTMP NVR ONVIF SIP SRT WebRTC协…...
【iOS】 Block再学习
iOS Block再学习 文章目录 iOS Block再学习前言Block的三种类型__ NSGlobalBlock____ NSMallocBlock____ NSStackBlock__小结 Block底层分析Block的结构捕获自由变量捕获全局(静态)变量捕获静态变量__block修饰符forwarding指针 Block的copy时机block作为函数返回值将block赋给…...
