博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Mysql读写分离
阅读量:5763 次
发布时间:2019-06-18

本文共 6176 字,大约阅读时间需要 20 分钟。

hot3.png

主库负责写,从库负责读 

 

实现步骤:

1.数据库层面的主从配置实现

2.代码层面的读写分离实现(无需改动现有代码)

 

主从同步如何工作:

主服务器(Master)将对数据的操作串行记录到二进制日志当中(binary log),写入完毕后主服务器通知数据存储引擎提交事务,提交好了之后进入第第二步;

这里需要补充,我们队数据的操作称之为一次二进制的日志事件,binary log将日志拷贝到中继日志中(relay log)。收线,slave先开启一个工作线程(IO Thread),IO县城在master上打开一个连接,然后将binary log拷贝到 IO thread中,如果已经读取到binary log的日志,就会睡眠并等待binary log产生新的事件,IO县城将这些事件写入到relay log中,第二步完成。

第三步,slave重做relay log,也就是比如主服务器更新数据,slave还没有,那么就更新,SQL Thread就是干这个事情。

总结:主服务器添加了数据A,操作被记录到binary log中,从服务器将添加A的操作同步到relay log中,SQL thread将同步的操作执行:向从服务器中插入数据A

 

数据库配置

主服务器:

    1.打开二进制日志

     vim /etc/my.cnf

    

    server-id=1

    服务器ID,用于标识

    log-bin=master-bin    打开二进制日志
    log-bin-index=master-bin.index    打开二进制日志索引

    2.重启数据库,进入数据库

    show master status;

    

    可以看到,里面有个file,使我们刚才指定好的master-bin.index的,Position表示开始的位置

    3.同时需要新建一个用户(或者不用也可以?因为从服务器需要指定一个用户)

        create user repl;

        授权

        GRANT REPLICATION SLAVE ON *.* TO 'repl'@'从服务器IP地址' IDENTIFIED BY 'mysql';

        刷新

        flush privileges;

从服务器

1.打开二进制日志

     vim /etc/my.cnf

2.打开relay log

3.关联主服务器

   change master to master_host='主服务器IP地址',master_port=3306,master_user='repl',master_password='mysql',master_log_file='master-bin.000001',master_log_pos=0;

4.开启主从跟踪

    start slave;

5.查看从服务器状态

    show slave status \G;

    

    发现报错:说主从服务器 server id 相同。

    解决:

        stop slave;

        exit;

        vim /etc/my.cnf

        发现确实id变了。

代码层面

    代码写完以后,在src/main/resource里面的mybatis-config.xml里面

    

然后添加日志代码,在src/main/resource/spring中的spring-dao.xml

    原文件

新配置的文件:主要在连接池这块

执行测试类

AreaDaoTest

package com.wby.o2o.dao;import static org.junit.Assert.assertEquals;import java.util.List;import org.junit.Test;import org.springframework.beans.factory.annotation.Autowired;import com.wby.o2o.BaseTest;import com.wby.o2o.entity.Area;public class AreaDaoTest extends BaseTest{	@Autowired	private AreaDao areaDao;	@Test	public void testQueryArea(){		List areaList=areaDao.queryArea();		assertEquals(4, areaList.size());	}}

显示报错

是因为DynamicDataSourceInterceptor类没有添加注解

package com.wby.o2o.dao.split;import java.util.Locale;import java.util.Properties;import org.apache.ibatis.executor.Executor;import org.apache.ibatis.executor.keygen.SelectKeyGenerator;import org.apache.ibatis.mapping.BoundSql;import org.apache.ibatis.mapping.MappedStatement;import org.apache.ibatis.mapping.SqlCommandType;import org.apache.ibatis.plugin.Interceptor;import org.apache.ibatis.plugin.Intercepts;import org.apache.ibatis.plugin.Invocation;import org.apache.ibatis.plugin.Plugin;import org.apache.ibatis.plugin.Signature;import org.apache.ibatis.session.ResultHandler;import org.apache.ibatis.session.RowBounds;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.transaction.support.TransactionSynchronizationManager;/** * 拦截器。 * 如果是insert,update等写操作,使用主数据库 * 如果是query,select等读操作,使用从数据库 * @author Administrator * *///增删改的操作都封装在了update里面,因此update+query就可以了@Intercepts({@Signature(type=Executor.class,method="update",args={MappedStatement.class,Object.class}),	@Signature(type=Executor.class,method="query",args={MappedStatement.class,Object.class,RowBounds.class,ResultHandler.class})	})public class DynamicDataSourceInterceptor implements Interceptor{		/**	 * 主要的拦截方法	 */	//日志部分	private static Logger logger =LoggerFactory.getLogger(			DynamicDataSourceInterceptor.class);		private static final String REGEX=".*insert\\uoo2o.*|.*delete\\u0020.*|.*update\\u0020.*";	@Override	public Object intercept(Invocation invocation) throws Throwable {		boolean synchronizationAction=TransactionSynchronizationManager.isActualTransactionActive();				Object[] objects=invocation.getArgs();		MappedStatement ms=(MappedStatement) objects[0];		String lookupKey=DynamicDataSourceHolder.DB_MASTER;		if (synchronizationAction!=true) {						//读方法			if (ms.getSqlCommandType().equals(SqlCommandType.SELECT)) {				//selectKey为自增Id查询主键(SELECT LAST_INSERT_ID())方法				//使用主库				if (ms.getId().contains(SelectKeyGenerator.SELECT_KEY_SUFFIX)) {					lookupKey=DynamicDataSourceHolder.DB_MASTER;				}else {					BoundSql boundSql=ms.getSqlSource().getBoundSql(objects[1]);					String sql=boundSql.getSql()							.toLowerCase(Locale.CHINA)							.replaceAll("[\\t\\n\\r]", "");					//匹配正则是不是增删改的					if (sql.matches(REGEX)) {						lookupKey=DynamicDataSourceHolder.DB_MASTER;					}else {						lookupKey=DynamicDataSourceHolder.DB_SLAVE;					}				}			}		}else {			lookupKey=DynamicDataSourceHolder.DB_MASTER;		}		logger.debug("设置方法[{}] use[{}] Strategy,SqlCommanType [{}]...",				ms.getId(),lookupKey,ms.getSqlCommandType().name());		DynamicDataSourceHolder.setDbType(lookupKey);		return invocation.proceed();	}	/**	 * 返回封装好的对象,或者返回代理对象	 * 如果拦截的对象target类型是Executor,就拦截,将intercept给他包装到	 * .wrap(target, this);里面去。	 * 如果不是,直接返回本体	 * 为什么拦截Executor类型?	 * 因为在mybatis中,Executor是用来支持一系列增删改查操作的。然后通过intercept()	 * 决定使用哪一个数据源	 */	public Object plugin(Object target) {		if (target instanceof Executor) {			return Plugin.wrap(target, this);		}else {			return target;		}	}		/**	 * 类初始化的时候做一些相关的配置。非必要	 */	@Override	public void setProperties(Properties properties) {		// TODO Auto-generated method stub			}	}

再次执行测试类AreaDaoTest,读操作 运行正确

执行测试类ShopCategoryDaoTest,读操作,运行,结果如下

执行测试类,shopDaoTest,写操作,运行,结果如下

接下来测试service层

测试AreaServiceTest,读操作

测试ShopServiceTest,写操作

 

转载于:https://my.oschina.net/u/3668429/blog/1930371

你可能感兴趣的文章
mysql(待整理)
查看>>
看雪论坛502,出现安全宝?
查看>>
使用PullToRefresh实现下拉刷新和上拉加载
查看>>
mysql
查看>>
管家婆数据库823错误,并闩锁页错误数据恢复成功
查看>>
2012年电信业八大发展趋势
查看>>
Web日志安全分析工具 v2.0发布
查看>>
JS重载
查看>>
python2和python3同安装在Windows上,切换问题
查看>>
php加速工具xcache的安装与使用(基于LNMP环境)
查看>>
android超链接
查看>>
redhat tomcat
查看>>
统计数据库大小
查看>>
IO流的学习--文件夹下文件的复制
查看>>
第十六章:脚本化HTTP
查看>>
EXCEL表中如何让数值变成万元或亿元
查看>>
最全的命令行(gradle)打包安卓apk
查看>>
限制域用户多点登录--脚本
查看>>
Cisco PIX防火墙的安装流程
查看>>
配置系列:ssm中applicationContext-mybatis.xml的简单配置
查看>>