当前位置:Gxlcms > 数据库问题 > 数据库读写分离

数据库读写分离

时间:2021-07-01 10:21:17 帮助过:5人阅读

*", "*xxx" and "*xxx*" matches, as well as direct

     * equality. Can be overridden in subclasses.

     *

     * @param methodName the method name of the class

     * @param mappedName the name in the descriptor

     * @return if the names match

     * @see org.springframework.util.PatternMatchUtils#simpleMatch(String, String)

     */

    protected boolean isMatch(String methodName, String mappedName) {

        return PatternMatchUtils.simpleMatch(mappedName, methodName);

    }

 

    /**

     * 用户指定slave的方法名前缀

     * @param slaveMethodStart

     */

    public void setSlaveMethodStart(String[] slaveMethodStart) {

        this.slaveMethodStart = slaveMethodStart;

    }

 

    public String[] getSlaveMethodStart() {

        if(this.slaveMethodStart == null){

            // 没有指定,使用默认

            return defaultSlaveMethodStart;

        }

        return slaveMethodStart;

    }

}

 

5.   一主多从的实现

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

技术分享图片

5.1.  实现

import java.lang.reflect.Field;

import java.util.ArrayList;

import java.util.List;

import java.util.Map;

import java.util.concurrent.atomic.AtomicInteger;

 

import javax.sql.DataSource;

 

import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

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

import org.springframework.util.ReflectionUtils;

 

/**

 * 定义动态数据源,实现通过集成Spring提供的AbstractRoutingDataSource,只需要实现determineCurrentLookupKey方法即可

 * 由于DynamicDataSource是单例的,线程不安全的,所以采用ThreadLocal保证线程安全,由DynamicDataSourceHolder完成。

 *

 */

public class DynamicDataSource extends AbstractRoutingDataSource {

 

    private static final Logger LOGGER = LoggerFactory.getLogger(DynamicDataSource.class);

 

    private Integer slaveCount;

 

    // 轮询计数,初始为-1,AtomicInteger是线程安全的

    private AtomicInteger counter = new AtomicInteger(-1);

 

    // 记录读库的key

    private List<Object> slaveDataSources = new ArrayList<Object>(0);

 

    @Override

    protected Object determineCurrentLookupKey() {

        // 使用DynamicDataSourceHolder保证线程安全,并且得到当前线程中的数据源key

        if (DynamicDataSourceHolder.isMaster()) {

            Object key = DynamicDataSourceHolder.getDataSourceKey();

            if (LOGGER.isDebugEnabled()) {

                LOGGER.debug("当前DataSource的key为: " + key);

            }

            return key;

        }

        Object key = getSlaveKey();

        if (LOGGER.isDebugEnabled()) {

            LOGGER.debug("当前DataSource的key为: " + key);

        }

        return key;

 

    }

 

    @SuppressWarnings("unchecked")

    @Override

    public void afterPropertiesSet() {

        super.afterPropertiesSet();

 

        // 由于父类的resolvedDataSources属性是私有的子类获取不到,需要使用反射获取

        Field field = ReflectionUtils.findField(AbstractRoutingDataSource.class, "resolvedDataSources");

        field.setAccessible(true); // 设置可访问

 

        try {

            Map<Object, DataSource> resolvedDataSources = (Map<Object, DataSource>) field.get(this);

            // 读库的数据量等于数据源总数减去写库的数量

            this.slaveCount = resolvedDataSources.size() - 1;

            for (Map.Entry<Object, DataSource> entry : resolvedDataSources.entrySet()) {

                if (DynamicDataSourceHolder.MASTER.equals(entry.getKey())) {

                    continue;

                }

                slaveDataSources.add(entry.getKey());

            }

        } catch (Exception e) {

            LOGGER.error("afterPropertiesSet error! ", e);

        }

    }

 

    /**

     * 轮询算法实现

     *

     * @return

     */

    public Object getSlaveKey() {

        // 得到的下标为:0、1、2、3……

        Integer index = counter.incrementAndGet() % slaveCount;

        if (counter.get() > 9999) { // 以免超出Integer范围

            counter.set(-1); // 还原

        }

        return slaveDataSources.get(index);

    }

}

 

6.   MySQL主从复制

6.1.  原理

技术分享图片

 

 

mysql主(称master)从(称slave)复制的原理:

1、  master将数据改变记录到二进制日志(binary log)中,也即是配置文件log-bin指定的文件(这些记录叫做二进制日志事件,binary log events)

2、  slave将master的binary log events拷贝到它的中继日志(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下也类似)

在my.ini修改:

#开启主从复制,主库的配置

log-bin = mysql3306-bin

#指定主库serverid

server-id=101

#指定同步的数据库,如果不指定则同步全部数据库

binlog-do-db=mybatis_1128

 

执行SQL语句查询状态:
SHOW MASTER STATUS

技术分享图片

需要记录下Position值,需要在从库中设置同步起始值。

6.4.  在主库创建同步用户

#授权用户slave01使用123456密码登录mysql

grant replication slave on *.* to ‘slave01‘@‘127.0.0.1‘ identified by ‘123456‘;

flush privileges;

6.5.  从库配置

在my.ini修改:

 

#指定serverid,只要不重复即可,从库也只有这一个配置,其他都在SQL语句中操作

server-id=102

 

以下执行SQL:

CHANGE MASTER TO

 master_host=‘127.0.0.1‘,

 master_user=‘slave01‘,

 master_password=‘123456‘,

 master_port=3306,

 master_log_file=‘mysql3306-bin.000006‘,

 master_log_pos=1120;

 

#启动slave同步

START SLAVE;

 

#查看同步状态

SHOW SLAVE STATUS;

技术分享图片

 

数据库读写分离

标签:img   自己   server   rip   判断   logger   aspect   用户名   pattern   

人气教程排行