(1)修改my.conf文件
vim /etc/my.conf
在mysqld段下添加配置
binlog-do-db=db1
binlog-ignore-db=mysql
#启用二进制日志
log-bin=mysql-bin
#服务器唯一ID,一般取IP最后一段
server-id=131
(2)重启mysql服务
service mysqld restart
(3)登录mysql并建立帐户并授权slave
mysql>GRANT FILE ON *.* TO 'backup'@'%' IDENTIFIED BY '123456'; mysql>GRANT REPLICATION SLAVE, REPLICATION CLIENT ON *.* to 'backup'@'%' identified by '123456';
一般不用root帐号
“%”表示所有客户端都可能连,只要帐号,密码正确
此处可用具体客户端IP代替,比如,192.168.145.226,加强安全
(4)刷新权限
mysql> FLUSH PRIVILEGES;
(5)查询master的状态
mysql> show master status;
(1)修改my.conf文件
[mysqld] server-id=166
(2)连接主服务器
mysql>change master to master_host='192.168.6.130',master_port=3306,master_user='backup',master_password='123456',master_log_file='mysql-bin.000002',master_log_pos=120;
master_port为mysql服务器端口号
master_user为执行同步操作的数据库账户
master_log_pos为120
就是show master status 中的position对应的值
master_log_file 为mysql-bin.000002
就是show master status中的file对应的值
(3)启动从服务器复制功能
mysql>start slave;
(4)检查从服务器复制功能状态
命令行
mysql> show slave status /G
客户端
SHOW SLAVE STATUS;
(1)配置文件application-dev
# mysql connect attribute
spring.datasource.master.jdbc-url=jdbc:mysql://192.168.6.130:3306/technology_mall_goods?characterEncoding=utf-8
spring.datasource.master.username=root
spring.datasource.master.password=root
spring.datasource.master.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.slave1.jdbc-url=jdbc:mysql://192.168.6.131:3306/technology_mall_goods?characterEncoding=utf-8
spring.datasource.slave1.username=root
spring.datasource.slave1.password=root
spring.datasource.slave1.driver-class-name=com.mysql.jdbc.Driver
# Hikari will use the above plus the following to setup connection pooling
spring.datasource.type=com.zaxxer.hikari.HikariDataSource
spring.datasource.hikari.minimum-idle=5
spring.datasource.hikari.maximum-pool-size=15
spring.datasource.hikari.auto-commit=true
spring.datasource.hikari.idle-timeout=30000
spring.datasource.hikari.pool-name=DatebookHikariCP
spring.datasource.hikari.max-lifetime=1800000
spring.datasource.hikari.connection-timeout=30000
spring.datasource.hikari.connection-test-query=SELECT 1
(2)数据源枚举类DataSourceTypeEnum
public enum DataSourceTypeEnum { MASTER, SLAVE1, SLAVE2; }
(3)获取使用的数据源的key DynamicRoutingDataSource
在访问数据库时会调用该类的 determineCurrentLookupKey() 方法获取数据库实例的 key
public class DynamicRoutingDataSource extends AbstractRoutingDataSource {
private final Logger logger = LoggerFactory.getLogger(getClass());
/**
* 当为null时,表示使用的数据源是默认数据源(@Primary注释的数据源)
* @return
*/
@Override
protected Object determineCurrentLookupKey() {
logger.info("Current DataSource is [{}]",DynamicDataSourceContextHolder.getDataSourceKey());
return DynamicDataSourceContextHolder.getDataSourceKey();
}
}
(4)数据源配置类DataSourceConfiguration
该类中生成多个数据源实例并将其注入到 ApplicationContext 中
@Configuration
public class DataSourceConfiguration {
@Bean
@Primary
@ConfigurationProperties("spring.datasource.master")
public DataSource masterDataSource() {
return DataSourceBuilder.create().build();
}
@Bean
@ConfigurationProperties("spring.datasource.slave1")
public DataSource slave1DataSource() {
return DataSourceBuilder.create().build();
}
@Bean
public DataSource dynamicDataSource(@Qualifier("masterDataSource") DataSource masterDataSource,
@Qualifier("slave1DataSource") DataSource slave1DataSource) {
Map
(5)数据库配置类
@EnableTransactionManagement
@Configuration
public class MyBatisConfig {
@Resource(name = "dynamicDataSource")
private DataSource dynamicDataSource;
@Bean
public SqlSessionFactory sqlSessionFactory() throws Exception {
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(dynamicDataSource);
sqlSessionFactoryBean.setTypeAliasesPackage("com.tmall.infrastructure.po");
sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mappers/*.xml"));
return sqlSessionFactoryBean.getObject();
}
@Bean
public PlatformTransactionManager platformTransactionManager() {
return new DataSourceTransactionManager(dynamicDataSource);
}
}
(6)切换数据源类DynamicDataSourceContextHolder
该类为数据源上下文配置,用于切换数据源
public class DynamicDataSourceContextHolder {
private static final Logger logger = LoggerFactory.getLogger(DynamicDataSourceContextHolder.class);
private static final ThreadLocal contextHolder = new ThreadLocal<>();
private static final AtomicInteger counter = new AtomicInteger(-1);
public static void set(DataSourceTypeEnum dbType) {
contextHolder.set(dbType);
}
public static void master() {
set(DataSourceTypeEnum.MASTER);
}
public static void slave() {
// 轮询
int datasourceKeyIndex = counter.getAndIncrement() % 2;
if (counter.get() > 9999) {
counter.set(-1);
}
if (datasourceKeyIndex == 0) {
set(DataSourceTypeEnum.SLAVE1);
logger.info("Switch To slave1");
} else {
set(DataSourceTypeEnum.SLAVE1);
logger.info("Switch To slave1(2)");
}
}
public static DataSourceTypeEnum getDataSourceKey() {
return contextHolder.get();
}
public static void clearDataSourceKey() {
contextHolder.remove();
}
}
(7)切面类DynamicDataSourceAspect
动态数据源切换的切面,切 DAO 层,通过 DAO 层方法名判断使用哪个数据源,实现数据源切换
@Aspect
@Component
public class DynamicDataSourceAspect {
private static final Logger logger = LoggerFactory.getLogger(DynamicDataSourceAspect.class);
private final String[] QUERY_PREFIX = {"select"};
@Pointcut("execution( * com.tmall.infrastructure.dao.*.*(..))")
public void daoAspect() { }
@Before("daoAspect()")
public void switchDataSource(JoinPoint point) {
Boolean isQueryMethod = isQueryMethod(point.getSignature().getName());
if (isQueryMethod) {
DynamicDataSourceContextHolder.slave();
logger.info("Switch DataSource to [{}] in Method [{}]", DynamicDataSourceContextHolder.getDataSourceKey(), point.getSignature());
}
}
@After("daoAspect()")
public void restoreDataSource(JoinPoint point) {
DynamicDataSourceContextHolder.clearDataSourceKey();
logger.info("Restore DataSource to [{}] in Method [{}]", DynamicDataSourceContextHolder.getDataSourceKey(), point.getSignature());
}
private Boolean isQueryMethod(String methodName) {
for (String prefix : QUERY_PREFIX) {
if (methodName.startsWith(prefix)) {
return true;
}
}
return false;
}
}
(1)调用查询方法时
(2)调用更改方法时