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

使用Spring AOP实现读写分离(MySql实现主从复制)的更多相关文章

  1. Spring AOP 实现读写分离

    原文地址:Spring AOP 实现读写分离 博客地址:http://www.extlight.com 一.前言 上一篇<MySQL 实现主从复制> 文章中介绍了 MySQL 主从复制的搭 ...

  2. spring+mybatis实现读写分离

    springmore-core spring+ibatis实现读写分离 特点 无缝结合spring+ibatis,对于程序员来说,是透明的 除了修改配置信息之外,程序的代码不需要修改任何东西 支持sp ...

  3. LVS+MYCAT+读写分离+MYSQL主备同步部署手册

    LVS+MYCAT+读写分离+MYSQL主备同步部署手册 1          配置MYSQL主备同步…. 2 1.1       测试环境… 2 1.2       配置主数据库… 2 1.2.1  ...

  4. 【转载】LVS+MYCAT+读写分离+MYSQL主备同步部署手册(邢锋)

    LVS+MYCAT+读写分离+MYSQL主备同步部署手册 1          配置MYSQL主备同步…. 2 1.1       测试环境… 2 1.2       配置主数据库… 2 1.2.1  ...

  5. Spring+mybatis 实现aop数据库读写分离,多数据库源配置

    在数据库层面大都采用读写分离技术,就是一个Master数据库,多个Slave数据库.Master库负责数据更新和实时数据查询,Slave库当然负责非实时数据查询.因为在实际的应用中,数据库都是读多写少 ...

  6. 基于spring的aop实现读写分离与事务配置

    项目开发中经常会遇到读写分离等多数据源配置的需求,在Java项目中可以通过Spring AOP来实现多数据源的切换. 一.Spring事务开启流程 Spring中通常通过@Transactional来 ...

  7. spring实现数据库读写分离

    现在大型的电子商务系统,在数据库层面大都采用读写分离技术,就是一个Master数据库,多个Slave数据库.Master库负责数据更新和实时数据查询,Slave库当然负责非实时数据查询.因为在实际的应 ...

  8. Spring 实现数据库读写分离(转)

    现在大型的电子商务系统,在数据库层面大都采用读写分离技术,就是一个Master数据库,多个Slave数据库.Master库负责数据更新和实时数据查询,Slave库当然负责非实时数据查询.因为在实际的应 ...

  9. 数据源管理 | 主从库动态路由,AOP模式读写分离

    本文源码:GitHub·点这里 || GitEE·点这里 一.多数据源应用 1.基础描述 在相对复杂的应用服务中,配置多个数据源是常见现象,例如常见的:配置主从数据库用来写数据,再配置一个从库读数据, ...

随机推荐

  1. node 分层开发

    app.js var express = require('express');var app = express();app.use('/',require('./control'));app.us ...

  2. 【Leetcode】Jewels and Stones

    Jewels and Stones Description You're given strings J representing the types of stones that are jewel ...

  3. hive报错:Caused by: ERROR XBM0H: Directory /var/lib/hive/metastore/metastore_db cannot be created.

    在cdh集群中,删除之前的hive服务,然后将hive添加到其他节点,然后再通过hive客户端连接hive报错: Caused by: ERROR XJ041: Failed to create da ...

  4. win10 无法修改默认程序 默认打开方式的解决方法

    此时是2018年11月24日 win10 pro 64位 版本是1803  具体版本号是17134 情景: 我的状况是.json文件的默认打开方式被新安装的应用霸占了,然后无论是通过“右键-属性-更改 ...

  5. spring读取properties和其他配置文件的几种方式

    1.因为spring容器的一些机制,在读取配置文件进行数据库的配置等等是很有必要的,所以我们要考虑配置文件的的读取方式以及各个方式的实用性 2.配置文件的读取方式我这里介绍2种,目的是掌握这2种就可以 ...

  6. Virtual Host on Apache(Apache上建立虚拟主机)

    0. Introduction Usually, we want to build two or more websites on a web server, but we have only one ...

  7. ubuntu 14.04安装nginx+php

    转自:http://www.cnblogs.com/helinfeng/p/4219051.html 基于最新的Ubuntu 14.04(2014年9月)搭建nginx.php.mysql环境,以下全 ...

  8. (2)分布式下的爬虫Scrapy应该如何做-关于对Scrapy的反思和核心对象的介绍

    本篇主要介绍对于一个爬虫框架的思考和,核心部件的介绍,以及常规的思考方法: 一,猜想 我们说的爬虫,一般至少要包含几个基本要素: 1.请求发送对象(sender,对于request的封装,防止被封) ...

  9. ES5新增数组方法(1):filter

    检测数组元素,并返回符合条件所有元素的数组. 1.过滤数组中不符合条件的元素 let arr = [1, 2, 3, 4, 5, 6]; // 方式一 let newArr = arr.filter( ...

  10. Struts2(四.注册时检查用户名是否存在及Action获取数据的三种方式)

    一.功能 1.用户注册页面 <%@ page language="java" contentType="text/html; charset=UTF-8" ...