MySQL读写分离

2018-06-18 02:51:19来源:未知 阅读 ()

新老客户大回馈,云服务器低至5折

1. 复制安装好的3380文件夹到3381

 

 

2. 进入3381\logs目录下将所有文件删除

3. 进入3381\data目录,将所有的log文件删除(注意不是所有文件哦!)

4. 打开 my.ini 文件,将所有的 3380 替换为 3381

5. 修改serverid为81

6. 打开cmd,进入C:\mysql-5.6\3381\bin目录,执行如下命令:

7. .\mysqld.exe install MySQL-3381 --defaults-file="C:\mysql-5.6\3381\data\my.ini" 注意改为自己的配置文件路径

8. 进入系统服务查看

9. 修改启动方式为手动,安装完成。 如需删除服务执行如下命名即可: .\mysqld.exe remove MySQL-3381

1. MySQL主从复制

1.1. 原理

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

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

  2. slave将master的binary log events拷贝到它的中继日志(relay log)

  3. slave重做中继日志中的事件,将改变反映它自己的数据(数据重演)

1.2. 主从配置需要注意的地方

  1. 主DB server和从DB server数据库的版本一致

  2. 主DB server和从DB server数据库数据一致[ 这里就会可以把主的备份在从上还原,也可以直接将主的数据目录拷贝到从的相应数据目录]

  3. 主DB server开启二进制日志,主DB server和从DB server的server_id都必须唯一

1.3. 主库配置(windows,Linux下也类似)

在my.ini修改:

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

  log-bin = mysql3306-bin

  #指定主库

  serverid server-id=101

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

   binlog-do-db=mybatis_1128

  执行SQL语句查询状态:

  SHOW MASTER STATUS

    

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

1.4. 在主库创建同步用户

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

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

   flush privileges;

1.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;

不一样查看从库错误日志(E:\mysql\3381\logs) uuid设置:E:\mysql\3381\data\data)下的auto-cnf

最后附上mysql安装包地址:https://pan.baidu.com/s/17XPg4hX09b-dzShC7aqxgA

 

代码实现:

 

1.1. DynamicDataSource
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
 
/**
 * 定义动态数据源,实现通过集成Spring提供的AbstractRoutingDataSource,只需要实现determineCurrentLookupKey方法即可
 *
 * 由于DynamicDataSource是单例的,线程不安全的,所以采用ThreadLocal保证线程安全,由DynamicDataSourceHolder完成。
 *
 * @author zhijun
 *
 */
public class DynamicDataSource extends AbstractRoutingDataSource{
 
    @Override
    protected Object determineCurrentLookupKey() {
        // 使用DynamicDataSourceHolder保证线程安全,并且得到当前线程中的数据源key
        return DynamicDataSourceHolder.getDataSourceKey();
    }
 
}
1.2. DynamicDataSourceHolder
/**
 *
 * 使用ThreadLocal技术来记录当前线程中的数据源的key
 *
 * @author zhijun
 *
 */
public class DynamicDataSourceHolder {
   
    //写库对应的数据源key
    private static final String MASTER = "master";
 
    //读库对应的数据源key
    private static final String SLAVE = "slave";
   
    //使用ThreadLocal记录当前线程的数据源key
    private static final ThreadLocal<String> holder = new ThreadLocal<String>();
 
    /**
     * 设置数据源key
     * @param key
     */
    public static void putDataSourceKey(String key) {
        holder.set(key);
    }
 
    /**
     * 获取数据源key
     * @return
     */
    public static String getDataSourceKey() {
        return holder.get();
    }
   
    /**
     * 标记写库
     */
    public static void markMaster(){
        putDataSourceKey(MASTER);
    }
   
    /**
     * 标记读库
     */
    public static void markSlave(){
        putDataSourceKey(SLAVE);
    }
 
}
1.3. DataSourceAspect
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
 
/**
 * 定义数据源的AOP切面,通过该Service的方法名判断是应该走读库还是写库
 *
 * @author zhijun
 *
 */
public class DataSourceAspect {
 
    /**
     * 在进入Service方法之前执行
     *
     * @param point 切面对象
     */
    public void before(JoinPoint point) {
        // 获取到当前执行的方法名
        String methodName = point.getSignature().getName();
        if (isSlave(methodName)) {
            // 标记为读库
            DynamicDataSourceHolder.markSlave();
        } else {
            // 标记为写库
            DynamicDataSourceHolder.markMaster();
        }
    }
 
    /**
     * 判断是否为读库
     *
     * @param methodName
     * @return
     */
    private Boolean isSlave(String methodName) {
        // 方法名以query、find、get开头的方法名走从库
        return StringUtils.startsWithAny(methodName, "query", "find", "get");
    }
 
}
 
1.4. 配置2个数据源
1.4.1.   jdbc.properties
jdbc.master.driver=com.mysql.jdbc.Driver
jdbc.master.url=jdbc:mysql://127.0.0.1:3306/mybatis_1128?useUnicode=true&characterEncoding=utf8&autoReconnect=true&allowMultiQueries=true
jdbc.master.username=root
jdbc.master.password=123456
 
 
jdbc.slave01.driver=com.mysql.jdbc.Driver
jdbc.slave01.url=jdbc:mysql://127.0.0.1:3307/mybatis_1128?useUnicode=true&characterEncoding=utf8&autoReconnect=true&allowMultiQueries=true
jdbc.slave01.username=root
jdbc.slave01.password=123456
1.4.2.   定义连接池
    <!-- 配置连接池 -->
    <bean id="masterDataSource" class="com.jolbox.bonecp.BoneCPDataSource"
       destroy-method="close">
       <!-- 数据库驱动 -->
       <property name="driverClass" value="${jdbc.master.driver}" />
       <!-- 相应驱动的jdbcUrl -->
       <property name="jdbcUrl" value="${jdbc.master.url}" />
       <!-- 数据库的用户名 -->
       <property name="username" value="${jdbc.master.username}" />
       <!-- 数据库的密码 -->
       <property name="password" value="${jdbc.master.password}" />
       <!-- 检查数据库连接池中空闲连接的间隔时间,单位是分,默认值:240,如果要取消则设置为0 -->
       <property name="idleConnectionTestPeriod" value="60" />
       <!-- 连接池中未使用的链接最大存活时间,单位是分,默认值:60,如果要永远存活设置为0 -->
       <property name="idleMaxAge" value="30" />
       <!-- 每个分区最大的连接数 -->
       <property name="maxConnectionsPerPartition" value="150" />
       <!-- 每个分区最小的连接数 -->
       <property name="minConnectionsPerPartition" value="5" />
    </bean>
   
    <!-- 配置连接池 -->
    <bean id="slave01DataSource" class="com.jolbox.bonecp.BoneCPDataSource"
       destroy-method="close">
       <!-- 数据库驱动 -->
       <property name="driverClass" value="${jdbc.slave01.driver}" />
       <!-- 相应驱动的jdbcUrl -->
       <property name="jdbcUrl" value="${jdbc.slave01.url}" />
       <!-- 数据库的用户名 -->
       <property name="username" value="${jdbc.slave01.username}" />
       <!-- 数据库的密码 -->
       <property name="password" value="${jdbc.slave01.password}" />
       <!-- 检查数据库连接池中空闲连接的间隔时间,单位是分,默认值:240,如果要取消则设置为0 -->
       <property name="idleConnectionTestPeriod" value="60" />
       <!-- 连接池中未使用的链接最大存活时间,单位是分,默认值:60,如果要永远存活设置为0 -->
       <property name="idleMaxAge" value="30" />
       <!-- 每个分区最大的连接数 -->
       <property name="maxConnectionsPerPartition" value="150" />
       <!-- 每个分区最小的连接数 -->
       <property name="minConnectionsPerPartition" value="5" />
    </bean>
1.4.3.   定义DataSource
    <!-- 定义数据源,使用自己实现的数据源 -->
    <bean id="dataSource" class="cn.itcast.usermanage.spring.DynamicDataSource">
       <!-- 设置多个数据源 -->
       <property name="targetDataSources">
           <map key-type="java.lang.String">
              <!-- 这个key需要和程序中的key一致 -->
              <entry key="master" value-ref="masterDataSource"/>
              <entry key="slave" value-ref="slave01DataSource"/>
           </map>
       </property>
       <!-- 设置默认的数据源,这里默认走写库 -->
       <property name="defaultTargetDataSource" ref="masterDataSource"/>
    </bean>
1.5. 配置事务管理以及动态切换数据源切面
1.5.1.   定义事务管理器
    <!-- 定义事务管理器 -->
    <bean id="transactionManager"
       class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
       <property name="dataSource" ref="dataSource" />
    </bean>
1.5.2.   定义事务策略
    <!-- 定义事务策略 -->
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
       <tx:attributes>
           <!--定义查询方法都是只读的 -->
           <tx:method name="query*" read-only="true" />
           <tx:method name="find*" read-only="true" />
           <tx:method name="get*" read-only="true" />
 
           <!-- 主库执行操作,事务传播行为定义为默认行为 -->
           <tx:method name="save*" propagation="REQUIRED" />
           <tx:method name="update*" propagation="REQUIRED" />
           <tx:method name="delete*" propagation="REQUIRED" />
 
           <!--其他方法使用默认事务策略 -->
           <tx:method name="*" />
       </tx:attributes>
    </tx:advice>
1.5.3.   定义切面
    <!-- 定义AOP切面处理器 -->
    <bean class="cn.itcast.usermanage.spring.DataSourceAspect" id="dataSourceAspect" />
 
    <aop:config>
       <!-- 定义切面,所有的service的所有方法 -->
       <aop:pointcut id="txPointcut" expression="execution(* xx.xxx.xxxxxxx.service.*.*(..))" />
       <!-- 应用事务策略到Service切面 -->
       <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut"/>
      
       <!-- 将切面应用到自定义的切面处理器上,-9999保证该切面优先级最高执行 -->
       <aop:aspect ref="dataSourceAspect" order="-9999">
           <aop:before method="before" pointcut-ref="txPointcut" />
       </aop:aspect>
    </aop:config>1.1. DynamicDataSource
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
 
/**
 * 定义动态数据源,实现通过集成Spring提供的AbstractRoutingDataSource,只需要实现determineCurrentLookupKey方法即可
 *
 * 由于DynamicDataSource是单例的,线程不安全的,所以采用ThreadLocal保证线程安全,由DynamicDataSourceHolder完成。
 *
 * @author zhijun
 *
 */
public class DynamicDataSource extends AbstractRoutingDataSource{
 
    @Override
    protected Object determineCurrentLookupKey() {
        // 使用DynamicDataSourceHolder保证线程安全,并且得到当前线程中的数据源key
        return DynamicDataSourceHolder.getDataSourceKey();
    }
 
}
1.2. DynamicDataSourceHolder
/**
 *
 * 使用ThreadLocal技术来记录当前线程中的数据源的key
 *
 * @author zhijun
 *
 */
public class DynamicDataSourceHolder {
   
    //写库对应的数据源key
    private static final String MASTER = "master";
 
    //读库对应的数据源key
    private static final String SLAVE = "slave";
   
    //使用ThreadLocal记录当前线程的数据源key
    private static final ThreadLocal<String> holder = new ThreadLocal<String>();
 
    /**
     * 设置数据源key
     * @param key
     */
    public static void putDataSourceKey(String key) {
        holder.set(key);
    }
 
    /**
     * 获取数据源key
     * @return
     */
    public static String getDataSourceKey() {
        return holder.get();
    }
   
    /**
     * 标记写库
     */
    public static void markMaster(){
        putDataSourceKey(MASTER);
    }
   
    /**
     * 标记读库
     */
    public static void markSlave(){
        putDataSourceKey(SLAVE);
    }
 
}
1.3. DataSourceAspect
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
 
/**
 * 定义数据源的AOP切面,通过该Service的方法名判断是应该走读库还是写库
 *
 * @author zhijun
 *
 */
public class DataSourceAspect {
 
    /**
     * 在进入Service方法之前执行
     *
     * @param point 切面对象
     */
    public void before(JoinPoint point) {
        // 获取到当前执行的方法名
        String methodName = point.getSignature().getName();
        if (isSlave(methodName)) {
            // 标记为读库
            DynamicDataSourceHolder.markSlave();
        } else {
            // 标记为写库
            DynamicDataSourceHolder.markMaster();
        }
    }
 
    /**
     * 判断是否为读库
     *
     * @param methodName
     * @return
     */
    private Boolean isSlave(String methodName) {
        // 方法名以query、find、get开头的方法名走从库
        return StringUtils.startsWithAny(methodName, "query", "find", "get");
    }
 
}
 
1.4. 配置2个数据源
1.4.1.   jdbc.properties
jdbc.master.driver=com.mysql.jdbc.Driver
jdbc.master.url=jdbc:mysql://127.0.0.1:3306/mybatis_1128?useUnicode=true&characterEncoding=utf8&autoReconnect=true&allowMultiQueries=true
jdbc.master.username=root
jdbc.master.password=123456
 
 
jdbc.slave01.driver=com.mysql.jdbc.Driver
jdbc.slave01.url=jdbc:mysql://127.0.0.1:3307/mybatis_1128?useUnicode=true&characterEncoding=utf8&autoReconnect=true&allowMultiQueries=true
jdbc.slave01.username=root
jdbc.slave01.password=123456
1.4.2.   定义连接池
    <!-- 配置连接池 -->
    <bean id="masterDataSource" class="com.jolbox.bonecp.BoneCPDataSource"
       destroy-method="close">
       <!-- 数据库驱动 -->
       <property name="driverClass" value="${jdbc.master.driver}" />
       <!-- 相应驱动的jdbcUrl -->
       <property name="jdbcUrl" value="${jdbc.master.url}" />
       <!-- 数据库的用户名 -->
       <property name="username" value="${jdbc.master.username}" />
       <!-- 数据库的密码 -->
       <property name="password" value="${jdbc.master.password}" />
       <!-- 检查数据库连接池中空闲连接的间隔时间,单位是分,默认值:240,如果要取消则设置为0 -->
       <property name="idleConnectionTestPeriod" value="60" />
       <!-- 连接池中未使用的链接最大存活时间,单位是分,默认值:60,如果要永远存活设置为0 -->
       <property name="idleMaxAge" value="30" />
       <!-- 每个分区最大的连接数 -->
       <property name="maxConnectionsPerPartition" value="150" />
       <!-- 每个分区最小的连接数 -->
       <property name="minConnectionsPerPartition" value="5" />
    </bean>
   
    <!-- 配置连接池 -->
    <bean id="slave01DataSource" class="com.jolbox.bonecp.BoneCPDataSource"
       destroy-method="close">
       <!-- 数据库驱动 -->
       <property name="driverClass" value="${jdbc.slave01.driver}" />
       <!-- 相应驱动的jdbcUrl -->
       <property name="jdbcUrl" value="${jdbc.slave01.url}" />
       <!-- 数据库的用户名 -->
       <property name="username" value="${jdbc.slave01.username}" />
       <!-- 数据库的密码 -->
       <property name="password" value="${jdbc.slave01.password}" />
       <!-- 检查数据库连接池中空闲连接的间隔时间,单位是分,默认值:240,如果要取消则设置为0 -->
       <property name="idleConnectionTestPeriod" value="60" />
       <!-- 连接池中未使用的链接最大存活时间,单位是分,默认值:60,如果要永远存活设置为0 -->
       <property name="idleMaxAge" value="30" />
       <!-- 每个分区最大的连接数 -->
       <property name="maxConnectionsPerPartition" value="150" />
       <!-- 每个分区最小的连接数 -->
       <property name="minConnectionsPerPartition" value="5" />
    </bean>
1.4.3.   定义DataSource
    <!-- 定义数据源,使用自己实现的数据源 -->
    <bean id="dataSource" class="cn.itcast.usermanage.spring.DynamicDataSource">
       <!-- 设置多个数据源 -->
       <property name="targetDataSources">
           <map key-type="java.lang.String">
              <!-- 这个key需要和程序中的key一致 -->
              <entry key="master" value-ref="masterDataSource"/>
              <entry key="slave" value-ref="slave01DataSource"/>
           </map>
       </property>
       <!-- 设置默认的数据源,这里默认走写库 -->
       <property name="defaultTargetDataSource" ref="masterDataSource"/>
    </bean>
1.5. 配置事务管理以及动态切换数据源切面
1.5.1.   定义事务管理器
    <!-- 定义事务管理器 -->
    <bean id="transactionManager"
       class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
       <property name="dataSource" ref="dataSource" />
    </bean>
1.5.2.   定义事务策略
    <!-- 定义事务策略 -->
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
       <tx:attributes>
           <!--定义查询方法都是只读的 -->
           <tx:method name="query*" read-only="true" />
           <tx:method name="find*" read-only="true" />
           <tx:method name="get*" read-only="true" />
 
           <!-- 主库执行操作,事务传播行为定义为默认行为 -->
           <tx:method name="save*" propagation="REQUIRED" />
           <tx:method name="update*" propagation="REQUIRED" />
           <tx:method name="delete*" propagation="REQUIRED" />
 
           <!--其他方法使用默认事务策略 -->
           <tx:method name="*" />
       </tx:attributes>
    </tx:advice>
1.5.3.   定义切面
    <!-- 定义AOP切面处理器 -->
    <bean class="cn.itcast.usermanage.spring.DataSourceAspect" id="dataSourceAspect" />
 
    <aop:config>
       <!-- 定义切面,所有的service的所有方法 -->
       <aop:pointcut id="txPointcut" expression="execution(* xx.xxx.xxxxxxx.service.*.*(..))" />
       <!-- 应用事务策略到Service切面 -->
       <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut"/>
      
       <!-- 将切面应用到自定义的切面处理器上,-9999保证该切面优先级最高执行 -->
       <aop:aspect ref="dataSourceAspect" order="-9999">
           <aop:before method="before" pointcut-ref="txPointcut" />
       </aop:aspect>
    </aop:config>

 

 

 

 

标签:

版权申明:本站文章部分自网络,如有侵权,请联系:west999com@outlook.com
特别注意:本站所有转载文章言论不代表本站观点,本站所提供的摄影照片,插画,设计作品,如需使用,请与原作者联系,版权归原作者所有

上一篇:Mybatis入门看这一篇就够了

下一篇:JWinner:一个私人定制的快速开发框架,为理想而生