码迷,mamicode.com
首页 > 数据库 > 详细

MySQL读写分离

时间:2018-03-12 15:19:41      阅读:196      评论:0      收藏:0      [点我收藏+]

标签:password   sla   eth   log   one   bst   单位   advice   多个   

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>

 

 

 

 

MySQL读写分离

标签:password   sla   eth   log   one   bst   单位   advice   多个   

原文地址:https://www.cnblogs.com/jzbo/p/8549043.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!