当配置好MySQL主从复制以后所有对数据内容的更新就必须在主库上进行。那么为什么所有的更新都要在主服务器上进行呢?这是因为数据复制时单向的,只有在主库上更新,才能避免用户对主服务器上数据库内容的更新与对从服务器上内容的一致,而不发生冲突。

1.生产MySQL复制环境用户授权方案一

Mysql DBA 高级运维学习之路-生产场景Mysql主从复制读写分离授权方案及实战

那么怎么才能达到上述效果呢?

只创建一个用户,在住库中给该用户读写修改删除权限,在从库中给该用户只读权限,也可以不收回从库权限,设置read-only参数保证从库只读。

(1) 生产环境主库用户授权

mysql> grant select,insert,update,delete on *.* to 'blog'@'192.168.136.%' identified by '123456';
Query OK, 0 rows affected (0.00 sec)

提示:特殊业务可能权限会略多,如果业务安全性不高也可以all privileges

(2) 生产环境从库用户的授权,给相同用户授予所有权限然后撤销权限只给可读权限

mysql> grant select,update,insert,delete on *.* to 'blog'@'192.168.136.%' identified by '123456';
Query OK, 0 rows affected (0.00 sec)
mysql> revoke insert,update,delete on *.* from 'blog'@'192.168.136.%';
Query OK, 0 rows affected (0.00 sec)

2.忽略授权表的方式防止数据写从库的方法及实践

生产环境中一般采用忽略授权表的方式同步,不同步mysql库并且主库和从库做授权方案一,这样我们就保证主库和从库相同的用户可以授权不同的权限来达到读写分离的效果。但是这样有一个缺陷:在从库切换主库的时候两家用户权限问题,解决办法可以保留一个库专门准备接替主库。

2.1 方法一:

(1)配置主库的my.cnf配置文件

在mysqld添加

[root@linzhongniao ~]# egrep  "\[mysqld]|binlog-i|replicate" /data/3306/my.cnf 
[mysqld]
replicate-ignore-db = mysql
binlog-ignore-db = mysql
binlog-ignore-db = performance_schema
binlog-ignore-db = information_schema

(2)配置从库的my.cnf配置文件

在mysqld下添加

[root@linzhongniao ~]# egrep  "\[mysqld]|binlog-i|replicate" /data/3307/my.cnf 
[mysqld]
replicate-ignore-db = mysql
binlog-ignore-db = mysql
binlog-ignore-db = performance_schema
binlog-ignore-db = information_schema

提示:这种方法有一个缺陷,就是在主库中如果创建用户不use mysql,主从还是复制的;必须先use mysql才能保证配置生效及主从不复制。

这个问题也是使我非常苦恼,在测试的时候发现禁止mysql库主从复制一会儿生效一会儿不生效,后来查看mysql官方手册发现了问题所在,下面是mysql官方手册的解释:

· --replicate-ignore-db=db_name

告诉从服务器不要复制默认数据库(由USE所选择)为db_name的语句。要想忽略多个数据库,应多次使用该选项,每个数据库使用一次。如果正进行跨数据库更新并且不想复制这些更新,不应使用该选项。请读取该选项后面的注意事项。

一个不能按照期望工作的例如:如果用--replicate-ignore-db=sales启动从服务器,并且在主服务器上执行下面的语句,UPDATE语句不会复制:

·USE prices;
·UPDATE sales.january SET amount=amount+1000;
如果需要跨数据库更新,应使用--replicate-wild-ignore-table=db_name.%。

· --replicate-wild-ignore-table=db_name.tbl_name

告诉从服务器线程不要复制表匹配给出的通配符模式的语句。要想忽略多个表,应多次使用该选项,每个表使用一次。该选项可以跨数据库进行更新。请读取该选项后面的注意事项。

例如:--replicate-wild-ignore-table=foo%.bar%不复制数据库名以foo开始和表名以bar开始的表的更新。

关于匹配如何工作的信息,参见--replicate-wild-do-table选项的描述。在选项值中包括通配符的规则与--replicate-wild-ignore-table相同

· --binlog-ignore-db=db_name

告诉主服务器,如果当前的数据库(即USE选定的数据库)是db_name,不应将更新保存到二进制日志中。如果你使用该选项,你应确保只对当前的数据库进行更新。

一个不能按照你期望的执行的例子:如果服务器用binlog-ignore-db=sales启动,并且执行USE prices; UPDATE sales.january SET amount=amount+1000;,该语句不写入二进制日志。

类似于--binlog-do-db,对于CREATE DATABASE、ALTER DATABASE和DROP DATABASE语句,有一个例外,即通过操作的数据库来决定是否应记录语句,而不是用当前的数据库。

要想记录或忽视多个数据库,使用多个选项,为每个数据库指定相应的选项。

服务器根据下面的规则对选项进行评估,以便将更新记录到二进制日志中或忽视。请注意对于CREATE/ALTER/DROP DATABASE语句有一个例外。在这些情况下,根据以下规则,所创建、修改或删除的数据库将代替当前的数据库。

 1.是否有binlog-do-db或binlog-ignore-db规则? 

· 没有:将语句写入二进制日志并退出。

· 有:执行下一步。

 2.有一些规则(binlog-do-db或binlog-ignore-db或二者都有)。当前有一个数据库(USE是否选择了数据库?)? 

· 没有:不要写入语句,并退出。

· 有:执行下一步。

 3.有当前的数据库。是否有binlog-do-db规则? 

· 有:当前的数据库是否匹配binlog-do-db规则? 

o有:写入语句并退出。

o没有:不要写入语句,退出。

· No:执行下一步。

 4.有一些binlog-ignore-db规则。当前的数据库是否匹配binlog-ignore-db规则? 

· 有:不要写入语句,并退出。

· 没有:写入查询并退出。

也就是说如果想让replicate-ignore-dbbinlog-ignore-db的配置生效,必须用use选择要禁止主从复制的库(比如mysql库)。更好的办法就是在从库的配置文件中配置replicate-wild-ignore-table参数禁止指定的库表主从复制。更多解释请查看mysql官方手册的5.11.3二进制日志章节和第六章mysql的复制。

2.2 方法二(推荐)

[root@linzhongniao ~]# egrep "\[mysqld]|replicate-wild-ignore-table" /data/3307/my.cnf   
[mysqld]
replicate-wild-ignore-table = mysql.%

配置完重启数据

3.通过read-only参数防止数据库写从库的方案

除了上面从库仅做SELECT的授权外,还可以在slave服务器启动选项增加参数或者在my.cnf配置文件中加read-only参数来确保从库只读,使用授权用户和read-only参数二者同时操作效果更佳。
注意:read-only参数可以让slave服务器只允许来自slave服务器线程或具有SUPER权限的用户的更新。可以确保slave服务器不接受来自普通用户的更新。

(1)配置从库my.cnf配置文件的mysqld并重启从数据库

[root@mysql ~]# egrep "\[mysqld]|read-only" /data/3307/my.cnf 
[mysqld]
read-only
[root@mysql ~]# /data/3307/mysql stop
Stoping MySQL....
[root@mysql ~]# /data/3307/mysql start
Starting MySQL......

(2)read-only参数对SUPER权限的用户无效,用SUPER用户登录创建一个普通用户

mysql> grant select,insert,update,delete on *.* to 'nana'@'localhost' identified by '123456';
Query OK, 0 rows affected (0.00 sec)

mysql> flush privileges;
Query OK, 0 rows affected (0.00 sec)

(3)在school库中创建一个表退出用普通用户登录,在创建的表中插入一条记录,演示read-only的效果。

[root@mysql ~]# mysql -unana -p123456 -S /data/3307/mysql.sock 
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 4
Server version: 5.5.32 Source distribution

Copyright (c) 2000, 2013, 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> select user();
+----------------+
| user() |
+----------------+
| nana@localhost |
+----------------+
1 row in set (0.00 sec)
mysql> use school;
Database changed
mysql> show tables;
+------------------+
| Tables_in_school |
+------------------+
| t|
+------------------+
1 row in set (0.00 sec)

mysql> insert into t values(2);
ERROR 1290 (HY000): The MySQL server is running with the --read-only option so it cannot execute this statement

(4)用超级(root)用户登录可以更新数据

主库中插入数据

[root@mysql ~]# mysql -uroot -p123456 -S /data/3306/mysql.sock <<EOF
> use linzhongniao
> insert into test1 values(4,'不认识'),(5,'你是谁');
> exit
> EOF

从库同步情况

[root@mysql ~]# mysql -unana -p123456 -S /data/3307/mysql.sock 
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 4
Server version: 5.5.32 Source distribution

Copyright (c) 2000, 2013, 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> select * from linzhongniao.test1;
+----+-----------+
| id | name  |
+----+-----------+
|  1 | 张三  |
|  2 | 张三  |
|  3 | 我是谁|
|  4 | 不认识|
|  5 | 你是谁|
+----+-----------+
5 rows in set (0.00 sec)