mysql主从分离

 

       大型网站为了软解大量的并发访问,除了在网站实现分布式负载均衡,远远不够。到了数据业务层、数据访问层,如果还是传统的数据结构,或者只是单单靠一台服务器扛,如此多的数据库连接操作,数据库必然会崩溃,数据丢失的话,后果更是 不堪设想。可以考虑利用MySQL主从配置,实现读写分离,减轻数据库压力。下面介绍mysql的主从分离配置与代码实现。

1.master: 主服务器(192.168.6.128) 增删改
    vi /etc/my.cnf
    添加配置: 
            server-id=1
            log-bin=master-bin
            log-bin-index=master-bin.index
    重启mysql: 
        service mysqld restart 

或者
        /etc/init.d/mysql stop
        /etc/init.d/mysql start
 2.slave: 从服务器(192.168.6.129) 仅仅读数据
    vi /etc/my.cnf
    添加配置:
        port=3306
        server_id=2
        relay-log-index=slave-relay-bin.index
        relay-log=slave-relay-bin
    重启mysql:
        /etc/init.d/mysql stop
        /etc/init.d/mysql start

3.主服务器创建repl用户,并且授权
    flush privileges;
    create user repl;
    GRANT REPLICATION SLAVE ON *.* TO 'repl'@'192.168.6.129' IDENTIFIED BY 'mysql';
    flush privileges;

4.从库
    注意:master_log_file的master-bin.000003是主库里面:show master status;得到的
    change master to master_host='192.168.6.128',master_port=3306,master_user='repl',master_password='mysql',master_log_file='master-bin.000003',master_log_pos=0;
    start slave;
写的时候用主库的数据源,读的时候用从库的数据源。

mybatis配置

   mybatis-config.xml增加plugins.




	
	
		
		

		
		

		
		
		
		
	

 
		        
	

 application-dao.xml修改



    
    
    

    
    
        
        
        
        
        
        
        
        
        
    

    
    
        
        
        
        
        
    

    
    
        
        
        
        
        
    

    
    
        
            
                
                
            
        
    

    
        
            
        
    

    
    
        
        
        
        
        
        
        
        
    

    
    
        
        
        
        
    

 

 数据库到层修改,增加三个实现类、

package com.beyond.o2o.dao.split;

import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;

/**
 * @author banpu
 */
public class DynamicDataSource extends AbstractRoutingDataSource {

    /**
     * 根据不同需求返回不同的key
     * @return
     */
    @Override
    protected Object determineCurrentLookupKey() {
        return DynamicDataSourceHolder.getDbType();
    }
}
import org.slf4j.LoggerFactory;
import org.slf4j.Logger;

/**
 * @author banpu
 */
public class DynamicDataSourceHolder {

    private static Logger logger = LoggerFactory.getLogger(DynamicDataSourceHolder.class);

    private static ThreadLocal contextHolder = new ThreadLocal<>();

    public final static String DB_MASTER = "master";
    public final static String DB_SLAVE = "slave";

    public static String getDbType(){
        String db = contextHolder.get();
        if(null == db){
            db = DB_MASTER;
        }
        return db;
    }

    /**
     * 设置线程的dbType
     * @param str
     */
    public static void setDbType(String str){
        logger.debug("所使用的数据源为:"+ str);
        contextHolder.set(str);
    }

    /**
     * 清理连接类型
     * @param str
     */
    public static void clearDbType(String str){
        contextHolder.remove();
    }

}
package com.beyond.o2o.dao.split;

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;
import java.util.Locale;
import java.util.Properties;

/**
 * 拦截器:根据sql读写信息,获取不同的数据源
 *
 * @author banpu
 */
@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);

    //匹配sql的正则
    private static final String REGEX = ".*insert\\u0020.*|.*delete\\u0020.*|.*update\\u0020.*";

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        //判读是不是事务
        boolean synchronizationActive = TransactionSynchronizationManager.isSynchronizationActive();
        // 获取变量参数
        Object[] obj = invocation.getArgs();
        MappedStatement ms = (MappedStatement) obj[0];
        String lookupKey = DynamicDataSourceHolder.DB_MASTER;
        if (synchronizationActive != true) {
            //读方法
            if (ms.getSqlCommandType().equals(SqlCommandType.SELECT)) {
                // selectKey为自增主键(SELECT_LAST_INSERT_ID())方法,使用主库
                if (ms.getId().equals(SelectKeyGenerator.SELECT_KEY_SUFFIX)) {
                    lookupKey = DynamicDataSourceHolder.DB_MASTER;
                } else {
                    BoundSql boundSql = ms.getSqlSource().getBoundSql(obj[1]);
                    String sql = boundSql.getSql().toLowerCase(Locale.CHINA).replaceAll("[\\t\\n\\r]", " ");
                    if (sql.matches(REGEX)) {
                        // 主库 insert,update,delete
                        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();
    }

    @Override
    public Object plugin(Object target) {

        if (target instanceof Executor) {
            return Plugin.wrap(target, this);
        }
        return target;
    }

    @Override
    public void setProperties(Properties properties) {

    }
}

 

你可能感兴趣的:(java)