• 使用Spring AOP实现读写分离(MySql实现主从复制)


    1.  背景

    我们一般应用对数据库而言都是“读多写少”,也就说对数据库读取数据的压力比较大,有一个思路就是说采用数据库集群的方案,
    其中一个是主库,负责写入数据,我们称之为:写库;

    其它都是从库,负责读取数据,我们称之为:读库;

    那么,对我们的要求是:
    1、读库和写库的数据一致;
    2、写数据必须写到写库;
    3、读数据必须到读库;

    2.  实现方案

    解决读写分离的方案有两种:应用层解决和中间件解决。

    1.应用层

    目前的一些解决方案需要在程序中手动指定数据源,比较麻烦,后边我会通过AOP思想来解决这个问题

    2,中间件

    MySQL-proxy:http://hi.baidu.com/geshuai2008/item/0ded5389c685645f850fab07
    Amoeba for MySQL:http://www.iteye.com/topic/188598和http://www.iteye.com/topic/1113437
     
    此处我们介绍一种在应用层的解决方案,通过spring动态数据源和AOP来解决数据库的读写分离。
     
    该方案目前已经在一个互联网项目中使用了,而且可以很好的工作。
     
    该方案目前支持
    一读多写;当写时默认读操作到写库、当写时强制读操作到读库。
     
    考虑未来支持
    读库负载均衡、读库故障转移等。
     
    使用场景
    不想引入中间件,想在应用层解决读写分离,可以考虑这个方案;
    建议数据访问层使用jdbc、ibatis,不建议hibernate
     
    优势
    应用层解决,不引入额外中间件;
    在应用层支持『当写时默认读操作到写库』,这样如果我们采用这种方案,在写操作后读数据直接从写库拿,不会产生数据复制的延迟问题;
    应用层解决读写分离,理论支持任意数据库。
     
     
    缺点
    1、不支持@Transactional注解事务,此方案要求所有读方法必须是read-only=true,因此如果是@Transactional,这样就要求在每一个读方法头上加@Transactional 且readOnly属性=true,相当麻烦。 :oops: 
    2、必须按照配置约定进行配置,不够灵活

    2.1.  应用层解决

     
    优缺点:
     
    优点:
    1、源程序不需要做任何改动就可以实现读写分离;
    2、动态添加数据源不需要重启程序;
     
    缺点:
    1、程序依赖于中间件,会导致切换数据库变得困难;
    2、由中间件做了中转代理,性能有所下降;
     
    相关中间件产品使用:
    MySQL-proxy:http://hi.baidu.com/geshuai2008/item/0ded5389c685645f850fab07
    Amoeba for MySQL:http://www.iteye.com/topic/188598和http://www.iteye.com/topic/1113437
     

    3.  使用Spring基于应用层实现

    3.1. 原理

     
    在进入Service之前,使用AOP来做出判断,是使用写库还是读库,判断依据可以根据方法名判断,比如说以query、find、get等开头的就走读库,其他的走写库

    3.2. DynamicDataSource

    动态改变数据源
    在介绍实现方式之前,我们先准备一些必要的知识,spring 的AbstractRoutingDataSource 类

    AbstractRoutingDataSource这个类 是spring2.0以后增加的,我们先来看下AbstractRoutingDataSource的定义:
    public abstract class AbstractRoutingDataSource extends AbstractDataSource implements InitializingBean  {}
    AbstractRoutingDataSource继承了AbstractDataSource ,而AbstractDataSource 又是DataSource 的子类。DataSource

    3.3. DynamicDataSourceHolder

    使用ThreadLocal技术来记录当前线程中的数据源的key 

    5.  一主多从的实现

    很多实际使用场景下都是采用“一主多从”的架构的,所有我们现在对这种架构做支持,目前只需要修改DynamicDataSource即可。
     

    6.  MySQL主从复制

    6.1. 原理

    mysql主(称master)从(称slave)复制的原理:
    1、master将数据改变记录到二进制日志(binarylog)中,也即是配置文件log-bin指定的文件(这些记录叫做二进制日志事件,binary log events)
    2、slave将master的binary logevents拷贝到它的中继日志(relay log)
    3、slave重做中继日志中的事件,将改变反映它自己的数据(数据重演)

    6.2. 主从配置需要注意的地方

    1、主DB server和从DB server数据库的版本一致
    2、主DB server和从DB server数据库数据一致[ 这里就会可以把主的备份在从上还原,也可以直接将主的数据目录拷贝到从的相应数据目录]
    3、主DB server开启二进制日志,主DB server和从DB server的server_id都必须唯一
     

    6.3. 主库配置(windows,Linux下也类似)

    第一步,主服务器创建用户并清空日志

    mysql> show privileges;
    mysql> grant replication client, replication slave on *.* to 'larry'@'192.168.1.%' identified by 'larry';
    mysql> show binary logs;
    mysql> reset master;
    mysql> show binary logs;
    +------------------+-----------+
    | Log_name         | File_size |
    +------------------+-----------+
    | mysql-bin.000001 |       107 |
    +------------------+-----------+
    1 row in set (0.00 sec)

    第二步,修改从服务器的server-id,把server-id改为2然后重启mysql

    [root@serv08 ~]# cat /etc/my.cnf | grep server-id
    server-id = 1
    #server-id       = 2
    [root@serv08 ~]# vim /etc/my.cnf 
    [root@serv08 ~]# cat /etc/my.cnf | grep server-id
    server-id = 2
    #server-id       = 2
    [root@serv08 ~]# /etc/init.d/mysqld restart

    第三步,从服务器清空日志

    mysql> show binary logs;
    mysql> reset master;    
    mysql> show binary logs;
    +------------------+-----------+
    | Log_name         | File_size |
    +------------------+-----------+
    | mysql-bin.000001 |       107 |
    +------------------+-----------+
    1 row in set (0.00 sec)
    
    mysql> show slave status;
    Empty set (0.00 sec)

    第四步,从服务器通过change master to命令修改设置

    mysql> change master to
        -> master_host='192.168.1.11',
        -> master_user='larry',
        -> master_password='larry',
        -> master_port=3306,
        -> master_log_file='mysql-bin.000001',
        -> master_log_pos=107;
    Query OK, 0 rows affected (0.01 sec)

    第五步,开启slave。

    mysql> slave start;
    mysql>show slave status ;
    mysql>show slave status G;

    第六步,从服务器查看是否和主服务器通信成功。如果出现 Slave_IO_Running和Slave_SQL_Running都是yes,则证明配置成功

    *************************** 1. row ***************************
                   Slave_IO_State: Waiting for master to send event
                      Master_Host: 192.168.1.11
                      Master_User: larry
                      Master_Port: 3306
                    Connect_Retry: 60
                  Master_Log_File: mysql-bin.000001
              Read_Master_Log_Pos: 107
                   Relay_Log_File: serv08-relay-bin.000002
                    Relay_Log_Pos: 253
            Relay_Master_Log_File: mysql-bin.000001
                 Slave_IO_Running: Yes
                Slave_SQL_Running: Yes
                  Replicate_Do_DB: 
              Replicate_Ignore_DB: 
               Replicate_Do_Table: 
           Replicate_Ignore_Table: 
          Replicate_Wild_Do_Table: 
      Replicate_Wild_Ignore_Table: 
                       Last_Errno: 0
                       Last_Error: 
                     Skip_Counter: 0
              Exec_Master_Log_Pos: 107
                  Relay_Log_Space: 410
                  Until_Condition: None
                   Until_Log_File: 
                    Until_Log_Pos: 0
               Master_SSL_Allowed: No
               Master_SSL_CA_File: 
               Master_SSL_CA_Path: 
                  Master_SSL_Cert: 
                Master_SSL_Cipher: 
                   Master_SSL_Key: 
            Seconds_Behind_Master: 0
    Master_SSL_Verify_Server_Cert: No
                    Last_IO_Errno: 0
                    Last_IO_Error: 
                   Last_SQL_Errno: 0
                   Last_SQL_Error: 
      Replicate_Ignore_Server_Ids: 
                 Master_Server_Id: 1
    1 row in set (0.00 sec)
    
    ERROR: 
    No query specified

    第八步,测试

    --slave查看数据库
    mysql> show databases;
    +--------------------+
    | Database           |
    +--------------------+
    | information_schema |
    | mysql              |
    | performance_schema |
    | test               |
    +--------------------+
    4 rows in set (0.02 sec)
    
    --master创建数据库
    mysql> create database larrydb;
    Query OK, 1 row affected (0.00 sec)
    --master查看数据库
    mysql> show databases;
    +--------------------+
    | Database           |
    +--------------------+
    | information_schema |
    | larrydb            |
    | mysql              |
    | performance_schema |
    | test               |
    +--------------------+
    5 rows in set (0.01 sec)
    
    --slave查看数据库,发现已经同步
    mysql> show databases;
    +--------------------+
    | Database           |
    +--------------------+
    | information_schema |
    | larrydb            |
    | mysql              |
    | performance_schema |
    | test               |
    +--------------------+
    5 rows in set (0.00 sec)
    
    --master创建表 插入数据
    mysql> use larrydb;
    Database changed
    mysql> create table test(id int(11));
    Query OK, 0 rows affected (0.00 sec)
    
    mysql> insert into test values(1);
    Query OK, 1 row affected (0.00 sec)
    
    --slave查看数据是否同步成功,发现数据已经同步
    mysql> use larrydb;
    Database changed
    mysql> show tables;
    +-------------------+
    | Tables_in_larrydb |
    +-------------------+
    | test              |
    +-------------------+
    1 row in set (0.00 sec)
    
    mysql> select * from test;
    +------+
    | id   |
    +------+
    |    1 |
    +------+
    1 row in set (0.00 sec)

    第九步,查看进程状态

    --master查看进程状态
    mysql> show processlist;
    +----+-------+--------------------+---------+-------------+------+-----------------------------------------------------------------------+------------------+
    | Id | User  | Host               | db      | Command     | Time | State                                                                 | Info             |
    +----+-------+--------------------+---------+-------------+------+-----------------------------------------------------------------------+------------------+
    |  1 | root  | localhost          | larrydb | Query       |    0 | NULL                                                                  | show processlist |
    |  2 | larry | 192.168.1.18:41393 | NULL    | Binlog Dump |  854 | Master has sent all binlog to slave; waiting for binlog to be updated | NULL             |
    +----+-------+--------------------+---------+-------------+------+-----------------------------------------------------------------------+------------------+
    2 rows in set (0.00 sec)
    
    --slave查看进程状态
    mysql> show processlist;
    +----+-------------+-----------+---------+---------+------+-----------------------------------------------------------------------------+------------------+
    | Id | User        | Host      | db      | Command | Time | State                                                                       | Info             |
    +----+-------------+-----------+---------+---------+------+-----------------------------------------------------------------------------+------------------+
    |  1 | root        | localhost | larrydb | Query   |    0 | NULL                                                                        | show processlist |
    |  2 | system user |           | NULL    | Connect |  880 | Waiting for master to send event                                            | NULL             |
    |  3 | system user |           | NULL    | Connect |   65 | Slave has read all relay log; waiting for the slave I/O thread to update it | NULL             |
    +----+-------------+-----------+---------+---------+------+-----------------------------------------------------------------------------+------------------+
    3 rows in set (0.00 sec)

    备注:如果需要制定同步的数据库,需要修改slave从库 /etc/my.cnf 这个mysql配置文件。

    # vim /etc/my.cnf 

    增加两端语句

    binlog-do-db = 同步的数据
    binlog-ignore-db = mysql,information_schema

    需要注意的地方

    1、两个数据库的版本和数据应该保持一致

    2、对应的端口,防火墙应该开启

    Spring项目源码 https://github.com/jiafuweiJava/MasterSlave

    数据库学习地址:http://blog.csdn.net/justdb/article/details/13168569

  • 相关阅读:
    GUI编程之贪吃蛇
    GUI编程之Swing
    Java学习笔记01
    软件测试之使用jmeter进行压力测试
    GitHub以及Git安装的使用
    Axure RP介绍
    结对编程之四则运算
    随心开始
    JAVA入门之简介
    input之File对象的简单介绍
  • 原文地址:https://www.cnblogs.com/jiafuwei/p/6566479.html
Copyright © 2020-2023  润新知