ssm mysql主从配置_主从分离之SSM与Mysql

大型网站为了软解大量的并发访问,除了在网站实现分布式负载均衡,远远不够。到了数据业务层、数据访问层,如果还是传统的数据结构,或者只是单单靠一台服务器扛,如此多的数据库连接操作,数据库必然会崩溃,数据丢失的话,后果更是 不堪设想。这时候,我们会考虑如何减少数据库的联接,一方面采用优秀的代码框架,进行代码的优化,采用优秀的数据缓存技术如:redis,如果资金丰厚的话,必然会想到假设服务器群,来分担主数据库的压力。Ok切入今天微博主题,利用MySQL主从配置,实现读写分离,减轻数据库压力。这种方式,在如今很多网站里都有使用,也不是什么新鲜事情,今天总结一下,方便大家学习参考一下。

原理:主服务器(master)负责写操作(包括增删改),有且仅有一台;从服务器(slave)负责读操作,可以配置n台,写入数据的时候,首先master会把数据写在本地 Binary Log 文件中,然后通过I/O 写入 slave 的 relayLog 日志文件中,之后才同步数据库中,实现主从同步

在这里我使用的是两台CenterOS 6.5 的虚拟机进行配置,master IP :192.168.1.111 , Slave IP :192.168.1.112,开始了:

一: Mysql 配置主从分离

1.下载安装 Mysql 数据库

1.1  # yum list | grep mysql     --我们通过命令可以查看yum上提供下载的mysql的版本信息

9df9cc2a59591020f1df0681da6e7374.png

1.2  # yum install -y mysql-server mysql mysql-deve  --运行命令开始安装直到安装完成

2. 配置Mysql数据库的 master 和 slave

2.1  首先配置 master:

# vi /etc/my.cnf  -- Mysql 的配置一般都是在这,直接运行命令进行修改

在【mysqld】添加以下:

server-id=1

log-bin=master-bin

log-bin-index=master-bin.index

随后开启数据库

# service mysqld start;

登录进去数据库:

# mysql -uroot -p;

进去之后创建一个用户用来主从数据库的通信

# create user manager;

授予 REPLICATION SLAVE 权限就够了

# grant replication slave on *.* to 'manager'@'192.168.1.112' identified by '123456';

# flush privileges;

之后查看一下master日志

# show master status;

+-------------------+----------+--------------+------------------+| File | Position | Binlog_Do_DB | Binlog_Ignore_DB |+-------------------+----------+--------------+------------------+| master-bin.000001 | 1285 | | |+-------------------+----------+--------------+------------------+1 row in set (0.00 sec)

好了 ,master 配置完成了。

2,2 配置slave

和 master 一样,首先修改配置文件

# vi /etc/my.cnf

在【mysqld】添加以下:

server-id=2

relay-log-index=slave-relay-bin.index

relay-log=slave-relay-bin

然后开启 mysql 服务,登录进去之后,连接master

# change master to master_host='192.168.1.111', //Master 服务器Ipmaster_port=3306,master_user='manager',master_password='123456',master_log_file='master-bin.000001',//Master服务器产生的日志master_log_pos=0;

启动 slave

# start slave

查看一下slave运行有没有错误,如果没有就说明已经配置好了,主和从已经正常工作了

# show slave status \G;

二: 配置Spring 和 Mybatis

首先修改一下 jdbc.properties ,配置两条连接数据库URL

jdbc.driver=com.mysql.jdbc.Driver

jdbc.slave.url=jdbc:mysql://192.168.237.111/test?useUnicode=true&characterEncoding=utf8

jdbc.master.url=jdbc:mysql://192.168.237.112/test?useUnicode=true&characterEncoding=utf8

jdbc.username=xxxxx

jdbc.password=xxxxx

定义一个拦截器,实现 import org.apache.ibatis.plugin.Interceptor 接口

packagecom.smy.dao.split;importjava.util.Properties;importorg.apache.ibatis.executor.Executor;importorg.apache.ibatis.executor.keygen.SelectKeyGenerator;importorg.apache.ibatis.mapping.BoundSql;importorg.apache.ibatis.mapping.MappedStatement;importorg.apache.ibatis.mapping.SqlCommandType;importorg.apache.ibatis.plugin.Interceptor;importorg.apache.ibatis.plugin.Intercepts;importorg.apache.ibatis.plugin.Invocation;importorg.apache.ibatis.plugin.Plugin;importorg.apache.ibatis.plugin.Signature;importorg.apache.ibatis.session.ResultHandler;importorg.apache.ibatis.session.RowBounds;importorg.slf4j.Logger;importorg.slf4j.LoggerFactory;importorg.springframework.transaction.support.TransactionSynchronizationManager;

@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 implementsInterceptor {//数据库操作字符串的匹配,insert,update,delete

private static final String REGEX = ".*insert\\u0020.*|.*delete\\u0020.*|.*update\\u0020.*";private static Logger log = LoggerFactory.getLogger(DynamicDataSourceInterceptor.class);

@Overridepublic Object intercept(Invocation invocation) throwsThrowable {//判断是否被事务管理

boolean synchronization =TransactionSynchronizationManager.isActualTransactionActive();

Object[] objects=invocation.getArgs();

MappedStatement ms= (MappedStatement) objects[0];

String lookupKey=DynamicDataSourceHolder.DB_MASTER;;//判断是否被事务管理

if (!synchronization) {//读操作

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{//如果执行到了这里说明就是没有被事务管理也没有指定主键Key,只能对sql//语句进行匹配规则

BoundSql boundSql = ms.getBoundSql(objects[1]);

String sql= boundSql.getSql().toLowerCase().replaceAll("\\t\\n\\r", " ");if(sql.matches(REGEX)) {

lookupKey=DynamicDataSourceHolder.DB_MASTER;

}else{

lookupKey=DynamicDataSourceHolder.DB_SLAVE;

}

}

}

}else{//如果被事务管理说明 就是增删改,需要在 master 中操作

lookupKey =DynamicDataSourceHolder.DB_MASTER;

}

log.debug("设置方法[{}] use [{}] Strategy, SqlCommanType [{}]..", ms.getId(), lookupKey,

ms.getSqlCommandType().name());//设置访问数据库类型 master 或者 slave

DynamicDataSourceHolder.setDbType(lookupKey);returninvocation.proceed();

}

@OverridepublicObject plugin(Object target) {//如果执行的是增删改的操作就使用本拦截,如果不是就直接返回

if (target instanceofExecutor) {return Plugin.wrap(target, this);

}returntarget;

}

@Overridepublic voidsetProperties(Properties properties) {

}

}

定义一个类 DynamicDataSourceHolder 来管理 我们的master 和 slave 常量,也就是管理我们的数据源

packagecom.smy.dao.split;importorg.slf4j.Logger;importorg.slf4j.LoggerFactory;public classDynamicDataSourceHolder {private static Logger log = LoggerFactory.getLogger(DynamicDataSourceHolder.class);private static ThreadLocal contextHolder = new ThreadLocal();public static final String DB_MASTER = "master";public static final String DB_SLAVE = "slave";/*** 获取数据源

*@return

*/

public staticString getDbType() {

String db=contextHolder.get();if(db==null) {

db=DB_MASTER;

}returndb;

}/*** 设置数据源

*@paramdbType*/

public static voidsetDbType(String dbType) {

log.debug("所使用的数据源"+dbType);

contextHolder.set(dbType);

}/*** 清理数据源*/

public static voidclearDbType() {

contextHolder.remove();

}

}

接下来 定义一个类 继承org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource 类,因为这个类能够动态路由到数据源

packagecom.smy.dao.split;importorg.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;public class DynamicDataSource extendsAbstractRoutingDataSource {

@OverrideprotectedObject determineCurrentLookupKey() {returnDynamicDataSourceHolder.getDbType();

}

}

实现这个类的一个抽象方法,查看AbstractRoutingDataSource 类源码有这么一个方法:

/*** Retrieve the current target DataSource. Determines the

* {@link#determineCurrentLookupKey() current lookup key}, performs

* a lookup in the {@link#setTargetDataSources targetDataSources} map,

* falls back to the specified

* {@link#setDefaultTargetDataSource default target DataSource} if necessary.

*@see#determineCurrentLookupKey()*/

protectedDataSource determineTargetDataSource() {

Assert.notNull(this.resolvedDataSources, "DataSource router not initialized");

Object lookupKey=determineCurrentLookupKey();// 这个方法就确定了要使用哪个数据源,然而AbstractRoutingDataSource 类中,这个方法是抽象的,所以我们要实现这个类并实现该方法

DataSource dataSource= this.resolvedDataSources.get(lookupKey);if (dataSource == null && (this.lenientFallback || lookupKey == null)) {

dataSource= this.resolvedDefaultDataSource;

}if (dataSource == null) {throw new IllegalStateException("Cannot determine target DataSource for lookup key [" + lookupKey + "]");

}returndataSource;

}

OK ,开始我们在Spring 和 Mybatis 配置文件中配置我们的拦截器 和 动态数据源Bean

Mybatis.xml 中配置添加plugin:

Spring-dao.xml 中:

至于为什么这么配置, 相信大家看过源码之后就会很清楚了。。

The   End 。。。。。。。。。。。。。。。。。。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值