下面,主要是验证在MySQL主从复制环境下,存储过程,函数,触发器,事件的复制情况,这些确实会让人混淆。

首先,创建一张测试表

mysql> create table test.t1(name varchar(10),age int);
Query OK, 0 rows affected (0.10 sec)

存储过程

创建存储过程

delimiter //
CREATE procedure p1 (IN name varchar(10),IN age int)
BEGIN
insert into test.t1 values(name,age);
END//
delimiter ;

通过查看二进制日志,可以看到该DDL语句已被记录

# at 120
#161010 23:18:38 server id 1 end_log_pos 339 CRC32 0xae3dcfda Query thread_id=2 exec_time=0 error_code=0
use `test`/*!*/;
SET TIMESTAMP=1476112718/*!*/;
SET @@session.pseudo_thread_id=2/*!*/;
SET @@session.foreign_key_checks=1, @@session.sql_auto_is_null=0, @@session.unique_checks=1, @@session.autocommit=1/*!*/;
SET @@session.sql_mode=1075838976/*!*/;
SET @@session.auto_increment_increment=1, @@session.auto_increment_offset=1/*!*/;
/*!\C utf8 *//*!*/;
SET @@session.character_set_client=33,@@session.collation_connection=33,@@session.collation_server=33/*!*/;
SET @@session.lc_time_names=0/*!*/;
SET @@session.collation_database=DEFAULT/*!*/;
CREATE DEFINER=`root`@`localhost` PROCEDURE `p1`(IN name varchar(10),IN age int)
BEGIN
insert into test.t1 values(name,age);
END
/*!*/;
DELIMITER ;

执行存储过程

mysql> call p1('tom',10);
Query OK, 1 row affected (0.08 sec) mysql> select * from t1;
+-------+------+
| name | age |
+-------+------+
| tom | 10 |
+-------+------+
1 rows in set (0.01 sec)

查看二进制日志中,记录的是还是call p1('tom',10)操作记录对应的SQL语句

# at 574
#161010 23:23:54 server id 1 end_log_pos 653 CRC32 0xc532cfae Query thread_id=2 exec_time=0 error_code=0
SET TIMESTAMP=1476113034/*!*/;
BEGIN
/*!*/;
# at 653
#161010 23:23:54 server id 1 end_log_pos 833 CRC32 0x2982c7a8 Query thread_id=2 exec_time=0 error_code=0
SET TIMESTAMP=1476113034/*!*/;
insert into test.t1 values( NAME_CONST('name',_utf8'tom' COLLATE 'utf8_general_ci'), NAME_CONST('age',10))
/*!*/;
# at 833
#161010 23:23:54 server id 1 end_log_pos 864 CRC32 0xdf106f41 Xid = 56
COMMIT/*!*/;

由此可见,对于存储过程,在主从复制中,记录的是存储过程对应的DML操作,而不是调用动作本身。

函数

创建函数

CREATE FUNCTION f1 (string VARCHAR(5))
RETURNS VARCHAR(20) DETERMINISTIC
RETURN CONCAT('f1',string);

二进制日志中的记录如下:

# at 1246
#161010 23:34:01 server id 1 end_log_pos 1480 CRC32 0x3a1eb0a2 Query thread_id=2 exec_time=0 error_code=0
SET TIMESTAMP=1476113641/*!*/;
CREATE DEFINER=`root`@`localhost` FUNCTION `f1`(string VARCHAR(5)) RETURNS varchar(20) CHARSET utf8
DETERMINISTIC
RETURN CONCAT('f1',string)
/*!*/;

执行函数

在这里,其实要分两种情况,一是binlog_format为statement,另一种情况为row

当binlog_format为statement时

mysql> show variables like '%binlog_format%';
+---------------+-----------+
| Variable_name | Value |
+---------------+-----------+
| binlog_format | STATEMENT |
+---------------+-----------+
1 row in set (0.02 sec) mysql> insert into t1(name) values(f1('steve'));
Query OK, 1 row affected (0.07 sec) mysql> select * from t1;
+---------+------+
| name | age |
+---------+------+
| tom | 10 |
| f1steve | NULL |
+---------+------+
2 rows in set (0.00 sec)

查看该语句对应的二进制日志中的内容

# at 1480
#161010 23:37:58 server id 1 end_log_pos 1559 CRC32 0xf1f2c4a2 Query thread_id=2 exec_time=0 error_code=0
SET TIMESTAMP=1476113878/*!*/;
BEGIN
/*!*/;
# at 1559
#161010 23:37:58 server id 1 end_log_pos 1673 CRC32 0x0c9a73c5 Query thread_id=2 exec_time=0 error_code=0
SET TIMESTAMP=1476113878/*!*/;
insert into t1(name) values(f1('steve'))
/*!*/;
# at 1673
#161010 23:37:58 server id 1 end_log_pos 1704 CRC32 0x45419118 Xid = 67
COMMIT/*!*/;

可见在statement的二进制日志格式下,复制的调用函数这个操作本身。

当binlog_format为row时

mysql> set session binlog_format='row';
Query OK, 0 rows affected (0.00 sec) mysql> insert into t1(name) values(f1('tiger'));
Query OK, 1 row affected (0.03 sec)

对应的二进制日志的内容

# at 2139
#161010 23:43:35 server id 1 end_log_pos 2211 CRC32 0x7c74abd9 Query thread_id=2 exec_time=0 error_code=0
SET TIMESTAMP=1476114215/*!*/;
BEGIN
/*!*/;
# at 2211
#161010 23:43:35 server id 1 end_log_pos 2259 CRC32 0x657ac7ac Table_map: `test`.`t1` mapped to number 78
# at 2259
#161010 23:43:35 server id 1 end_log_pos 2303 CRC32 0x3f15b37c Write_rows: table id 78 flags: STMT_END_F
### INSERT INTO `test`.`t1`
### SET
### @1='f1tiger' /* VARSTRING(30) meta=30 nullable=1 is_null=0 */
### @2=NULL /* VARSTRING(30) meta=0 nullable=1 is_null=1 */
# at 2303
#161010 23:43:35 server id 1 end_log_pos 2334 CRC32 0xe5acc4aa Xid = 80
COMMIT/*!*/;

可见,在row格式下,复制的不是函数操作本身,而是函数对应的值。

触发器

首先,创建两张测试表

CREATE TABLE test1(a1 INT);
CREATE TABLE test2(a2 INT);

创建触发器

delimiter //
CREATE TRIGGER t_test1 BEFORE INSERT ON test1
FOR EACH ROW
BEGIN
INSERT INTO test2 SET a2 = NEW.a1;
END;
//
delimiter ;

二进制日志中的记录如下:

# at 556
#161011 10:46:52 server id 1 end_log_pos 776 CRC32 0xf065830f Query thread_id=4 exec_time=0 error_code=0
SET TIMESTAMP=1476154012/*!*/;
CREATE DEFINER=`root`@`localhost` TRIGGER t_test1 BEFORE INSERT ON test1
FOR EACH ROW
BEGIN
INSERT INTO test2 SET a2 = NEW.a1;
END
/*!*/;

测试触发器,向test1中添加一条记录

在STATEMENT格式下

mysql> insert into test1 values(1);
Query OK, 1 row affected (0.07 sec) mysql> select * from test1;
+------+
| a1 |
+------+
| 1 |
+------+
1 row in set (0.01 sec) mysql> select * from test2;
+------+
| a2 |
+------+
| 1 |
+------+
1 row in set (0.00 sec)

查看该语句对应的二进制日志中的内容

# at 776
#161011 10:49:37 server id 1 end_log_pos 855 CRC32 0x0d73131b Query thread_id=5 exec_time=0 error_code=0
SET TIMESTAMP=1476154177/*!*/;
BEGIN
/*!*/;
# at 855
#161011 10:49:37 server id 1 end_log_pos 956 CRC32 0x6cf2e73c Query thread_id=5 exec_time=0 error_code=0
SET TIMESTAMP=1476154177/*!*/;
insert into test1 values(1)
/*!*/;
# at 956
#161011 10:49:37 server id 1 end_log_pos 987 CRC32 0x98e3a631 Xid = 51
COMMIT/*!*/;

可见,对于触发器,主从均会触发,复制只需记录触发条件本身,在本例中,即“insert into test1 values(1)”,而不会记录所引发的触发操作,即“INSERT INTO test2 SET a2 = NEW.a1”。

在ROW格式下

mysql> set session binlog_format='row';
Query OK, 0 rows affected (0.00 sec) mysql> insert into test1 values(2);
Query OK, 1 row affected (0.06 sec)

对应的二进制日志为:

# at 399
#161019 10:10:27 server id 1 end_log_pos 471 CRC32 0x667b6938 Query thread_id=3 exec_time=0 error_code=0
SET TIMESTAMP=1476843027/*!*/;
BEGIN
/*!*/;
# at 471
#161019 10:10:27 server id 1 end_log_pos 519 CRC32 0xccaee383 Table_map: `test`.`test1` mapped to number 85
# at 519
#161019 10:10:27 server id 1 end_log_pos 567 CRC32 0x9625b60f Table_map: `test`.`test2` mapped to number 86
# at 567
#161019 10:10:27 server id 1 end_log_pos 607 CRC32 0x620381e3 Write_rows: table id 86
# at 607
#161019 10:10:27 server id 1 end_log_pos 647 CRC32 0xff82eb9d Write_rows: table id 85 flags: STMT_END_F BINLOG '
E9YGWBMBAAAAMAAAAAcCAAAAAFUAAAAAAAEABHRlc3QABXRlc3QxAAEDAAGD467M
E9YGWBMBAAAAMAAAADcCAAAAAFYAAAAAAAEABHRlc3QABXRlc3QyAAEDAAEPtiWW
E9YGWB4BAAAAKAAAAF8CAAAAAFYAAAAAAAAAAgAB//4CAAAA44EDYg==
### INSERT INTO `test`.`test2`
### SET
### @1=2 /* INT meta=0 nullable=1 is_null=0 */
E9YGWB4BAAAAKAAAAIcCAAAAAFUAAAAAAAEAAgAB//4CAAAAneuC/w==
'/*!*/;
### INSERT INTO `test`.`test1`
### SET
### @1=2 /* INT meta=0 nullable=1 is_null=0 */
# at 647
#161019 10:10:27 server id 1 end_log_pos 678 CRC32 0x5384a1bc Xid = 87
COMMIT/*!*/;

可见,在row格式下,会同时复制触发操作本身,此时,无论是否删除slave上的触发器,主从数据仍保持一致。但是在statement的格式下,如果删除了slave上的触发器,则会导致主从数据不一致。

EVENT

创建EVENT

CREATE EVENT e_test1
ON SCHEDULE
EVERY 10 SECOND
DO
INSERT INTO test.test1 VALUES (UNIX_TIMESTAMP());

二进制日志中的记录如下:

# at 987
#161011 11:02:45 server id 1 end_log_pos 1218 CRC32 0x875a245e Query thread_id=5 exec_time=0 error_code=0
SET TIMESTAMP=1476154965/*!*/;
SET @@session.time_zone='SYSTEM'/*!*/;
CREATE DEFINER=`root`@`localhost` EVENT e_test1
ON SCHEDULE
EVERY 10 SECOND
DO
INSERT INTO test.test1 VALUES (UNIX_TIMESTAMP())
/*!*/;

如果要让EVENT执行,必须将event_scheduler设置为ON,默认为OFF。

mysql> set global event_scheduler=1;
Query OK, 0 rows affected (0.09 sec)

这时EVENT会执行,每10s向test1表中插入一条记录

mysql> select * from test1;
+------------+
| a1 |
+------------+
| 1 |
| 1476155165 |
| 1476155175 |
+------------+
3 rows in set (0.01 sec)

对应的二进制日志中的内容

# at 1319
#161011 11:06:05 server id 1 end_log_pos 1398 CRC32 0xcc4e1873 Query thread_id=7 exec_time=0 error_code=0
SET TIMESTAMP=1476155165/*!*/;
SET @@session.sql_auto_is_null=1/*!*/;
BEGIN
/*!*/;
# at 1398
#161011 11:06:05 server id 1 end_log_pos 1520 CRC32 0x24ee06c6 Query thread_id=7 exec_time=0 error_code=0
SET TIMESTAMP=1476155165/*!*/;
INSERT INTO test.test1 VALUES (UNIX_TIMESTAMP())
/*!*/;
# at 1520
#161011 11:06:05 server id 1 end_log_pos 1551 CRC32 0xa3ed03fa Xid = 65
COMMIT/*!*/;

可见,对于EVENT,只是复制EVENT语句。

可能有人会疑问,slave上面是否同样会执行event呢?

经测试证明,即使将slave上event_scheduler开启了,也不会导致slave上event的执行,即使执行了stop slave操作,该event同样不会执行。

通过查看主从上的event状态,可以看出两者的不同

Master

mysql> show events\G
*************************** 1. row ***************************
Db: test
Name: e_test1
Definer: root@localhost
Time zone: SYSTEM
Type: RECURRING
Execute at: NULL
Interval value: 10
Interval field: SECOND
Starts: 2016-10-11 11:02:45
Ends: NULL
Status: ENABLED
Originator: 1
character_set_client: utf8
collation_connection: utf8_general_ci
Database Collation: utf8_general_ci
1 row in set (0.00 sec)

Slave

mysql> show events\G
*************************** 1. row ***************************
Db: test
Name: e_test1
Definer: root@localhost
Time zone: SYSTEM
Type: RECURRING
Execute at: NULL
Interval value: 10
Interval field: SECOND
Starts: 2016-10-11 11:02:45
Ends: NULL
Status: SLAVESIDE_DISABLED
Originator: 1
character_set_client: utf8
collation_connection: utf8_general_ci
Database Collation: utf8_general_ci
1 row in set (0.00 sec)

可以看出,相同的event,master上的状态是ENABLED,而slave上的状态确是SLAVESIDE_DISABLED。

如果要开启slave上面的event,可通过如下命令开启

alter event test.e_test1 enable;

经测试,直接update mysql.event没有效果。

总结

1. 对于存储过程,只是复制存储过程中定义的DML语句。

2. 对于函数,在statement格式下,只是复制函数名,也就是说,函数在主从上同样会被执行。

3. 对于触发器,在statement格式下,复制的只是触发条件,而不会是触发动作。也就是说,触发器在主从上同样会被运行。

但是在row格式下,则不仅会复制触发条件,还会复制触发动作。

4. 对于event,复制的也只是事件体中的DML语句。

参考

1. http://dev.mysql.com/doc/refman/5.7/en/create-procedure.html

2. http://dev.mysql.com/doc/refman/5.7/en/create-trigger.html

3. http://dev.mysql.com/doc/refman/5.7/en/create-event.html

MySQL主从环境下存储过程,函数,触发器,事件的复制情况的更多相关文章

  1. 监控Mysql主从环境下Slave延迟状态的操作记录

    在MySQL主从环境下,通常会根据Seconds_Behind_Master的值来判断slave的延迟状态,这么做在大部分情况下尚可接受,但其实是并不够准确的.对于Slave延迟状态的监控,应该考虑多 ...

  2. redis持久化策略梳理及主从环境下的策略调整记录

    redis是一个支持持久化的内存数据库,也就是说redis需要经常将内存中的数据同步到磁盘来保证持久化.可以不定期的通过异步方式保存到磁盘上(即“半持久化模式”):也可以把每一次数据变化都写入到一个A ...

  3. 查看SQL SERVER 加密存储过程,函数,触发器,视图

    原文:查看SQL SERVER 加密存储过程,函数,触发器,视图 create  PROCEDURE sp_decrypt(@objectname varchar(50))ASbeginset noc ...

  4. mysql主从模式下在主库上的某些操作不记录日志的方法

    mysql主从模式下在主库上的某些操作不记录日志的方法 需求场景: 在主库上的需要删除某个用户,而这个用户在从库上不存在(我在接手一个业务的时候,就遇到主从架构用户授权不一致的情况,主库比较全,而从库 ...

  5. mysql 控制台环境下查询中文数据乱码,插入、更新中文数据不成功

    mysql 控制台环境下查询中文数据乱码,插入.更新中文数据不成功         登录mysql密码是加入编码参数--default-character-set,中文用gbk mysql -uroo ...

  6. 用 Docker 构建 MySQL 主从环境

    开源Linux 一个执着于技术的公众号 前言 本篇文章记录使用 docker-compose 以及 dockerfile 来构建基于 binlog 的 MySQL 主从环境.如果你严格按照文中的步骤进 ...

  7. linux环境下验证码不显示的几种情况

    linux环境下验证码不显示的几种情况 gd库扩展没有安装. 查看phpinfo(),看看有没有安装gd库 yum安装gd库或者phpize安装 安装完成后记得重启php-fpm bom头的原因 在生 ...

  8. 如何在ES5与ES6环境下处理函数默认参数

    函数默认值是一个很提高鲁棒性的东西(就是让程序更健壮)MDN关于函数默认参数的描述:函数默认参数允许在没有值或undefined被传入时使用默认形参. ES5 使用逻辑或||来实现 众所周知,在ES5 ...

  9. mysql主从同步下---主从配置

    1.安装mysql主从服务器   1.1 初始化docker中mysql挂载目录 # 新建2个目录, 存放master和slave的配置和数据, cd ~ # ~ 代表家目录 /home/你的用户名/ ...

随机推荐

  1. Kotlin的Lambda表达式以及它们怎样简化Android开发(KAD 07)

    作者:Antonio Leiva 时间:Jan 5, 2017 原文链接:https://antonioleiva.com/lambdas-kotlin/ 由于Lambda表达式允许更简单的方式建模式 ...

  2. jQuery学习之路(7)- 用原生JavaScript实现jQuery的某些简单功能

    ▓▓▓▓▓▓ 大致介绍 学习了妙味,用原生的JavaScript实现jQuery中的某些部分功能 定义自己的函数库lQuery ▓▓▓▓▓▓ $()选择器的实现 jQuery是面向对象的,所以自己编写 ...

  3. angular2系列教程(七)Injectable、Promise、Interface、使用服务

    今天我们要讲的ng2的service这个概念,和ng1一样,service通常用于发送http请求,但其实你可以在里面封装任何你想封装的方法,有时候控制器之间的通讯也是依靠service来完成的,让我 ...

  4. UWP开发之Mvvmlight实践八:为什么事件注销处理要写在OnNavigatingFrom中

    前一段开发UWP应用的时候因为系统返回按钮事件(SystemNavigationManager.GetForCurrentView().BackRequested)浪费了不少时间.现象就是在手机版的详 ...

  5. 使用 Android Studio 检测内存泄漏与解决内存泄漏问题

    本文在腾讯技术推文上 修改 发布. http://wetest.qq.com/lab/view/63.html?from=ads_test2_qqtips&sessionUserType=BF ...

  6. canvas与html5实现视频截图功能

    这段时间一直在研究canvas,突发奇想想做一个可以截屏视频的功能,然后把图片拉去做表情包,哈哈哈哈哈哈~~ 制作方法: 1.在页面中加载视频 在使用canvas制作这个截图功能时,首先必须保证页面上 ...

  7. 用原生js做单页应用

    最近在公司接到一个需求,里面有一个三级跳转.类似于选择地址的时候,选择的顺序是:省份->市->区.如果分三个页面跳转,那么体验非常不好,如果引入其他框架做成单页应用,又比较麻烦.所以可以用 ...

  8. 手机web如何实现多平台分享

    话说App一般都带有分享到社交平台的入口,web网页的分享也有很不错的框架,但是随着HTML5的不断发展,手机web页面越来越多的进入到我们的生活中,那如何在我们的手机上完成分享呢?话说各大分享平台都 ...

  9. 使用rowid抽取数据方法以及大数据量游标卡住的应对

    平时工作的时候,经常会遇到这种事情,从一个大表A中,抽取字段a在一个相对较小B的表的数据,比如,从一个详单表中,抽取几万个用户号码的话单出来.这种时候,一般来说, 做关联查询: create tabl ...

  10. Linux命令【第一篇】

    1.创建一个目录/data 记忆方法:英文make directorys缩写后就是mkdir. 命令: mkdir /data 或 cd /;mkdir data #提示:使用分号可以在一行内分割两个 ...