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>
网友评论