美文网首页
主从库同步与读写分离

主从库同步与读写分离

作者: 王侦 | 来源:发表于2019-11-23 21:55 被阅读0次

    1.主从库配置

    1.1 主库配置binary log

    vim /etc/my.cnf
    在[mysqld]中添加

    server-id=1
    log-bin=master-bin
    log-bin-index=master-bin.index
    

    重启:

    service mysqld restart
    

    登录输入库查看:

    mysql> show master status;
    +-------------------+----------+--------------+------------------+-------------------+
    | File              | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
    +-------------------+----------+--------------+------------------+-------------------+
    | master-bin.000001 |      155 |              |                  |                   |
    +-------------------+----------+--------------+------------------+-------------------+
    1 row in set (0.00 sec)
    

    主库创建用户repl:

    create user 'repl'@'从库ip' identified by '密码';
    

    授权

    grant replication slave on *.* to 'repl'@'从库ip';
    flush privileges;
    

    1.2 从库配置relay log

    vim /etc/my.cnf
    在[mysqld]中添加

    server-id=2
    relay-log=slave-relay-bin
    relay-log-index=slave-relay-bin.index
    

    重启mysql

    从库配置:

    change master to master_host='主库ip',master_port=3306,master_user='repl',master_password='密码',master_log_file='master-bin.000001',master_log_pos=0;
    
    start slave;
    
    mysql> show slave status \G;
    *************************** 1. row ***************************
                   Slave_IO_State: Waiting for master to send event
                      Master_Host: 119.3.135.196
                      Master_User: repl
                      Master_Port: 3306
                    Connect_Retry: 60
                  Master_Log_File: master-bin.000001
              Read_Master_Log_Pos: 877
                   Relay_Log_File: slave-relay-bin.000003
                    Relay_Log_Pos: 323
            Relay_Master_Log_File: master-bin.000001
                 Slave_IO_Running: Yes
                Slave_SQL_Running: Yes
                  Replicate_Do_DB: 
              Replicate_Ignore_DB: 
               Replicate_Do_Table: 
           Replicate_Ignore_Table: 
          Replicate_Wild_Do_Table: 
      Replicate_Wild_Ignore_Table: 
                       Last_Errno: 0
                       Last_Error: 
                     Skip_Counter: 0
              Exec_Master_Log_Pos: 877
                  Relay_Log_Space: 1469
                  Until_Condition: None
                   Until_Log_File: 
                    Until_Log_Pos: 0
               Master_SSL_Allowed: No
               Master_SSL_CA_File: 
               Master_SSL_CA_Path: 
                  Master_SSL_Cert: 
                Master_SSL_Cipher: 
                   Master_SSL_Key: 
            Seconds_Behind_Master: 0
    Master_SSL_Verify_Server_Cert: No
                    Last_IO_Errno: 0
                    Last_IO_Error: 
                   Last_SQL_Errno: 0
                   Last_SQL_Error: 
      Replicate_Ignore_Server_Ids: 
                 Master_Server_Id: 1
                      Master_UUID: abecae31-0dc2-11ea-b31e-fa163eea918c
                 Master_Info_File: mysql.slave_master_info
                        SQL_Delay: 0
              SQL_Remaining_Delay: NULL
          Slave_SQL_Running_State: Slave has read all relay log; waiting for more updates
               Master_Retry_Count: 86400
                      Master_Bind: 
          Last_IO_Error_Timestamp: 
         Last_SQL_Error_Timestamp: 
                   Master_SSL_Crl: 
               Master_SSL_Crlpath: 
               Retrieved_Gtid_Set: 
                Executed_Gtid_Set: 
                    Auto_Position: 0
             Replicate_Rewrite_DB: 
                     Channel_Name: 
               Master_TLS_Version: 
           Master_public_key_path: 
            Get_master_public_key: 0
                Network_Namespace: 
    1 row in set (0.00 sec)
    
    ERROR: 
    No query specified
    
    

    1.3 同步测试

    mysql> show databases;
    +--------------------+
    | Database           |
    +--------------------+
    | information_schema |
    | mysql              |
    | o2o                |
    | performance_schema |
    | sys                |
    +--------------------+
    5 rows in set (0.00 sec)
    
    mysql> create database test;
    Query OK, 1 row affected (0.01 sec)
    
    mysql> drop database test;
    Query OK, 0 rows affected (0.01 sec)
    
    
    mysql> show master status;
    +-------------------+----------+--------------+------------------+-------------------+
    | File              | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
    +-------------------+----------+--------------+------------------+-------------------+
    | master-bin.000001 |     1243 |              |                  |                   |
    +-------------------+----------+--------------+------------------+-------------------+
    1 row in set (0.00 sec)
    
    
    mysql> show slave status \G;
    *************************** 1. row ***************************
                   Slave_IO_State: Waiting for master to send event
                      Master_Host: 119.3.135.196
                      Master_User: repl
                      Master_Port: 3306
                    Connect_Retry: 60
                  Master_Log_File: master-bin.000001
              Read_Master_Log_Pos: 1243
                   Relay_Log_File: slave-relay-bin.000003
                    Relay_Log_Pos: 689
            Relay_Master_Log_File: master-bin.000001
                 Slave_IO_Running: Yes
                Slave_SQL_Running: Yes
                  Replicate_Do_DB: 
              Replicate_Ignore_DB: 
               Replicate_Do_Table: 
           Replicate_Ignore_Table: 
          Replicate_Wild_Do_Table: 
      Replicate_Wild_Ignore_Table: 
                       Last_Errno: 0
                       Last_Error: 
                     Skip_Counter: 0
              Exec_Master_Log_Pos: 1243
                  Relay_Log_Space: 1835
                  Until_Condition: None
    
    

    可以看到从库: Read_Master_Log_Pos: 1243,表明已经跟上了主库的步伐。

    1.4 几个注意事项

    • 1)position主从库一致表明同步好了
    • 2)不要向从库写数据
    • 3)从库的版本 >= 主库的版本 (向后兼容)

    2.读写分离

    2.1 核心配置

        <!-- 2.数据库连接池 -->
        <bean id="abstractDataSource" abstract="true"
              class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
            <!-- c3p0连接池的私有属性 -->
            <property name="maxPoolSize" value="30"/>
            <property name="minPoolSize" value="10"/>
    
            <!-- 关闭连接后不自动commit -->
            <property name="autoCommitOnClose" value="false"/>
            <!-- 获取连接超时时间 -->
            <property name="checkoutTimeout" value="10000"/>
            <!-- 当获取连接失败重试次数 -->
            <property name="acquireRetryAttempts" value="2"/>
        </bean>
    
        <bean id="master" parent="abstractDataSource">
            <!-- 配置连接池属性 -->
            <property name="driverClass" value="${jdbc.driver}" />
            <property name="jdbcUrl" value="${jdbc.master.url}" />
            <property name="user" value="${jdbc.username}" />
            <property name="password" value="${jdbc.password}" />
        </bean>
    
        <bean id="slave" parent="abstractDataSource">
            <!-- 配置连接池属性 -->
            <property name="driverClass" value="${jdbc.driver}" />
            <property name="jdbcUrl" value="${jdbc.slave.url}" />
            <property name="user" value="${jdbc.username}" />
            <property name="password" value="${jdbc.password}" />
        </bean>
    
        <!--配置动态数据源,targetDataSources就是路由数据源所对应的名称-->
        <bean id="dynamicDataSource" class="com.imooc.o2o.dao.split.DynamicDataSource">
            <property name="targetDataSources">
                <map>
                    <entry value-ref="master" key="master"></entry>
                    <entry value-ref="slave" key="slave"></entry>
                </map>
            </property>
        </bean>
    
        <!--将dynamicDataSource放入懒加载中(程序执行时才决定使用哪个dataSource)-->
        <bean id="dataSource"
              class="org.springframework.jdbc.datasource.LazyConnectionDataSourceProxy">
            <property name="targetDataSource">
                <ref bean="dynamicDataSource"/>
            </property>
        </bean>
    

    jdbc.properties:

    jdbc.master.url=
    jdbc.slave.url=
    

    2.2 DynamicDataSource

    public class DynamicDataSource extends AbstractRoutingDataSource {
        @Override
        protected Object determineCurrentLookupKey() {
            return DynamicDataSourceHolder.getDBType();
        }
    }
    
    public class DynamicDataSourceHolder {
        private static Logger logger = LoggerFactory.getLogger(DynamicDataSourceHolder.class);
        private static ThreadLocal<String> contextHolder = new ThreadLocal<>();
        public static final String DB_MASTER = "master";
        public static final String DB_SLAVE = "slave";
    
        public static String getDBType() {
            String db = contextHolder.get();
            if (db == null) {
                db = DB_MASTER;
            }
            return db;
        }
    
        public static void setDBType(String str) {
            logger.debug("所使用的数据源为:" + str);
            contextHolder.set(str);
        }
    
        /**
         * 清理连接类型
         */
        public static void clearDBType() {
            contextHolder.remove();
        }
    }
    
    

    2.3 拦截器

    @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);
        private static final String REGEX = ".*insert\\u0020.*|.*delete\\u0020.*|.*update\\u0020.*";
    
        @Override
        public Object intercept(Invocation invocation) throws Throwable {
            // 当前是不是事务管理的
            boolean synchronizationActive = TransactionSynchronizationManager.isActualTransactionActive();
            // 获取sql的参数
            Object[] objects = invocation.getArgs();
            // 表明是哪种类型的语句
            MappedStatement ms = (MappedStatement) objects[0];
            String lookupKey;
    
            if (!synchronizationActive) {
                // 读方法
                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 {
                        BoundSql boundSql = ms.getSqlSource().getBoundSql(objects[1]);
                        String sql = boundSql.getSql().toLowerCase(Locale.CHINA).replace("[\\t\\n\\r]", " ");
                        if (sql.matches(REGEX)) {
                            lookupKey = DynamicDataSourceHolder.DB_MASTER;
                        } else {
                            lookupKey = DynamicDataSourceHolder.DB_SLAVE;
                        }
                    }
                } else {
                    lookupKey = DynamicDataSourceHolder.DB_MASTER;
                }
            } 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) {
            // Executor代表增删改查操作
            if (target instanceof Executor) {
                return Plugin.wrap(target, this);
            } else {
                return target;
            }
        }
    
        @Override
        public void setProperties(Properties properties) {
    
        }
    }
    

    在mybatis-config.xml中添加拦截器:

        </settings>
        <plugins>
            <plugin interceptor="com.imooc.o2o.dao.split.DynamicDataSourceInterceptor"></plugin>
        </plugins>
    </configuration>
    

    相关文章

      网友评论

          本文标题:主从库同步与读写分离

          本文链接:https://www.haomeiwen.com/subject/hgohwctx.html