百度多方参考终于配出我自己的了,以下仅供参考
参考https://www.cnblogs.com/cjsblog/p/9712457.html
代码
首先数据源配置
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource spring.datasource.driver-class-name=com.mysql.jdbc.Driver #第一个数据源 spring.datasource.master.jdbc-url=jdbc:mysql://localhost:3307/dab?useUnicode=true&characterEncoding=utf-8 spring.datasource.master.username=root spring.datasource.master.password=root #第二个数据源只读账户 spring.datasource.slave1.jdbc-url=jdbc:mysql://localhost:3308/dab?useUnicode=true&characterEncoding=utf-8 spring.datasource.slave1.username=root spring.datasource.slave1.password=root #第三个数据源只读账户 spring.datasource.slave2.jdbc-url=jdbc:mysql://localhost:3309/dab?useUnicode=true&characterEncoding=utf-8 spring.datasource.slave2.username=root spring.datasource.slave2.password=root #连接池参数,初始数、最大数、最小数、获取连接等待超时时间(毫秒) spring.datasource.initial-size=5 spring.datasource.max-active=20 spring.datasource.min-idle=5 spring.datasource.max-wait=60000
启动类
我的dao和mapper分开的dao放的JPA的持久层,mapper放的mybatis的持久层
@EnableSwagger2 @SpringBootApplication @EnableJpaRepositories(basePackages = { "com.bling.dab.dao" }, entityManagerFactoryRef = "entityManagerFactoryPrimary", transactionManagerRef = "transactionManagerPrimary") @EnableTransactionManagement @MapperScan("com.bling.dab.mapper") public class DabApplication { public static void main(String[] args) { SpringApplication.run(DabApplication.class, args); System.out.println("dab启动完成--------OK!"); } }
多数据源jpa和mybatis 配置
jpa需要比mybatis多自定义一个配置类JpaEntityManager,其中的俩个bean需要在这里依赖,这个注解也可以写在JpaEntityManager配置类上,我写在启动类上了效果一样
@EnableJpaRepositories(basePackages = { "com.bling.dab.dao" }, entityManagerFactoryRef = "entityManagerFactoryPrimary", transactionManagerRef = "transactionManagerPrimary")
/** * @author: hxp * @date: 2019/6/18 10:54 * @description: */ @Configuration @EnableConfigurationProperties(JpaProperties.class) @AutoConfigureAfter(DataSourceConfig.class) public class JpaEntityManager { @Autowired private JpaProperties jpaProperties; @Autowired private DataSource myRoutingDataSource; @Bean(name = "entityManagerFactoryBean") public LocalContainerEntityManagerFactoryBean entityManagerFactoryBean(EntityManagerFactoryBuilder builder) { Map<String, String> properties = jpaProperties.getProperties(); //要设置这个属性,实现 CamelCase -> UnderScore 的转换 properties.put("hibernate.physical_naming_strategy", "org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy"); return builder .dataSource(myRoutingDataSource) .properties(properties) .packages("com.bling.dab.domain") .persistenceUnit("myPersistenceUnit") .build(); } @Primary @Bean(name = "entityManagerFactoryPrimary") public EntityManagerFactory entityManagerFactory(EntityManagerFactoryBuilder builder) { return this.entityManagerFactoryBean(builder).getObject(); } @Primary @Bean(name = "transactionManagerPrimary") public PlatformTransactionManager transactionManager(EntityManagerFactoryBuilder builder) { return new JpaTransactionManager(entityManagerFactory(builder)); } }
然后是mybati和jpa都需要的数据源配置
@Configuration public class DataSourceConfig { @Bean(name = "masterDataSource") @Primary @ConfigurationProperties(prefix = "spring.datasource.master") public DataSource masterDataSource() { return DataSourceBuilder.create().build(); } @Bean(name = "slave1DataSource") @ConfigurationProperties(prefix = "spring.datasource.slave1") public DataSource slave1DataSource() { return DataSourceBuilder.create().build(); } @Bean(name = "slave2DataSource") @ConfigurationProperties(prefix = "spring.datasource.slave2") public DataSource slave2DataSource() { return DataSourceBuilder.create().build(); } @Bean(name = "myRoutingDataSource") public DataSource myRoutingDataSource(@Qualifier("masterDataSource") DataSource masterDataSource, @Qualifier("slave1DataSource") DataSource slave1DataSource, @Qualifier("slave2DataSource") DataSource slave2DataSource) { Map<Object, Object> targetDataSources = new HashMap<>(); targetDataSources.put(DBTypeEnum.MASTER, masterDataSource); targetDataSources.put(DBTypeEnum.SLAVE1, slave1DataSource); targetDataSources.put(DBTypeEnum.SLAVE2, slave2DataSource); MyRoutingDataSource myRoutingDataSource = new MyRoutingDataSource(); myRoutingDataSource.setDefaultTargetDataSource(masterDataSource); myRoutingDataSource.setTargetDataSources(targetDataSources); return myRoutingDataSource; } }
轮询数据源使用的类DBContextHolder
public class DBContextHolder { private static final ThreadLocal<DBTypeEnum> contextHolder = new ThreadLocal<>(); private static final AtomicInteger counter = new AtomicInteger(-1); public static void set(DBTypeEnum dbType) { contextHolder.set(dbType); } public static DBTypeEnum get() { return contextHolder.get(); } public static void master() { set(DBTypeEnum.MASTER); System.out.println("切换到master"); } public static void slave() { // 轮询 int index = counter.getAndIncrement() % 2; if (counter.get() > 9999) { counter.set(-1); } if (index == 0) { set(DBTypeEnum.SLAVE1); System.out.println("切换到slave1"); }else { set(DBTypeEnum.SLAVE2); System.out.println("切换到slave2"); } } }
单独定义的MyRoutingDataSource,使用的是spring提供的路由方法,因为多个地方使用,所以单独定义的
public class MyRoutingDataSource extends AbstractRoutingDataSource { @Override protected Object determineCurrentLookupKey() { return DBContextHolder.get(); } }
mybatis需要的配置类MyBatisConfig
@EnableTransactionManagement @Configuration public class MyBatisConfig { @Resource(name = "myRoutingDataSource") private DataSource myRoutingDataSource; @Bean public SqlSessionFactory sqlSessionFactory() throws Exception { SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean(); sqlSessionFactoryBean.setDataSource(myRoutingDataSource); sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mapper/*.xml")); return sqlSessionFactoryBean.getObject(); } @Bean public PlatformTransactionManager platformTransactionManager() { return new DataSourceTransactionManager(myRoutingDataSource); }
切面类实现方法级别的读写分离
@Aspect @Component public class DataSourceAspect { @Pointcut("!@annotation(com.bling.dab.common.annotation.Master) " + "&& (execution(* com.bling.dab.service..*.select*(..)) " + "|| execution(* com.bling.dab.service..*.get*(..)))"+ "|| execution(* com.bling.dab.service..*.find*(..)))"+ "|| execution(* com.bling.dab.service..*.query*(..)))") public void readPointcut() { } @Pointcut("@annotation(com.bling.dab.common.annotation.Master) " + "|| execution(* com.bling.dab.service..*.insert*(..)) " + "|| execution(* com.bling.dab.service..*.add*(..)) " + "|| execution(* com.bling.dab.service..*.save*(..)) " + "|| execution(* com.bling.dab.service..*.update*(..)) " + "|| execution(* com.bling.dab.service..*.edit*(..)) " + "|| execution(* com.bling.dab.service..*.delete*(..)) " + "|| execution(* com.bling.dab.service..*.remove*(..))") public void writePointcut() { } @Before("readPointcut()") public void read() { DBContextHolder.slave(); } @Before("writePointcut()") public void write() { DBContextHolder.master(); } }
注解配合切面类使用
* @description:该注解标注的就读主库 */ @Target({ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) public @interface Master { }
枚举一个
public enum DBTypeEnum { MASTER, SLAVE1, SLAVE2; }
mysql数据库配置
MySQL用的是本地的只有一台服务器,所以需要配置三个MySQL服务器,原来只有一个,现在改一下新安装了三个mysql服务器MySQLMaster、MySQLSlave1、MySQLSlave2用来测试读写分离,原来的MySQL服务不动
首先cmd进入到MySQL命令窗口
1.打开【开始】》【运行】输入【cmd】单击【确定】后出现CMD命令黑色窗口,这就是我们说的CMD命令行,或者使用快捷键Windows键(在键盘上有个Windows标志的按键)+R输入cmd后回车。
2.在CMD命令窗口敲入命令 mysql -hlocalhost -uroot -p
后按回车(注意这里的"-h"、"-u"、"-p"不能省略) 进入mysql数据库,其中"-h"表示服务器名,localhost表示本地;"-u"为数据库用户名,root是MySQL默认用户名;"-p"为密码,如果设置了密码,可直接在-p后链接输入,如:-p888888,用户没有设置密码,显示Enter password时,直接回车即可。
注意如果您的MySQL没有安装在C盘下,先使用DOS命令进入MySQL的安装目录下的bin目录中。方法如下:输入G:进入G盘),在输入cd G:phpstudymysqlin 进入到MySQL的bin目录下,才可以输入 mysql -hlocalhost -uroot -p 然后按回车键。
查看mysql版本并进入mysql命令窗口,现在只有一个mysql,配置了环境变量所以在c目录下也能找到,先查看一下原来的MySQL服务版本,后面开始配置新的主从数据库用来实现读写分离库
查看mysql的安装路径
原有的MySQL-5.7.24-winx64拷贝三份到新建的目录mysql下
结构如图
修改配置文件my.ini,拷贝的mysql中有个文件aotu.cnf记录的MySQL的uuid需要改一下,不能相同
master
[mysql] # 设置mysql客户端默认字符集 default-character-set=utf8 [mysqld] #设置3306端口 port = 3307 # 设置mysql的安装目录 这里可以修改 basedir=D:hxpinstallmysqlmysql-master # 设置mysql数据库的数据的存放目录,这里可以修改 datadir=D:hxpinstallmysqlmysql-mastermydata # 允许最大连接数 max_connections=200 # 服务端使用的字符集默认为8比特编码的latin1字符集 character-set-server=utf8 # 创建新表时将使用的默认存储引擎 default-storage-engine=INNODB #给数据库服务的唯一标识,一般为大家设置服务器Ip的末尾号,在一个集群中,这个id是不能重复的 server-id=1 #开启二进制文件;后面设置的这个master-bin就是二进制文件的名字前缀(名字) log-bin=master-bin #开启二进制文件的索引;名字一般为log-bin.index log-bin-index=master-bin.index
[client]
#设置mysql客户端连接服务端时默认使用的端口
port = 3307
default-character-set=utf8
slave1
[mysql] # 设置mysql客户端默认字符集 default-character-set=utf8 [mysqld] #设置3306端口 port = 3308 # 设置mysql的安装目录 这里可以修改 basedir=D:hxpinstallmysqlmysql-slave1 # 设置mysql数据库的数据的存放目录,这里可以修改 datadir=D:hxpinstallmysqlmysql-slave1mydata # 允许最大连接数 max_connections=200 # 服务端使用的字符集默认为8比特编码的latin1字符集 character-set-server=utf8 # 创建新表时将使用的默认存储引擎 default-storage-engine=INNODB server-id=2 relay-log=slave-relay-bin relay-log-index=slave-relay-bin.index [client] #设置mysql客户端连接服务端时默认使用的端口 port = 3308 default-character-set=utf8
slave2
[mysql] # 设置mysql客户端默认字符集 default-character-set=utf8 [mysqld] #设置3306端口 port = 3309 # 设置mysql的安装目录 这里可以修改 basedir=D:hxpinstallmysqlmysql-slave2 # 设置mysql数据库的数据的存放目录,这里可以修改 datadir=D:hxpinstallmysqlmysql-slave2mydata # 允许最大连接数 max_connections=200 # 服务端使用的字符集默认为8比特编码的latin1字符集 character-set-server=utf8 # 创建新表时将使用的默认存储引擎 default-storage-engine=INNODB server-id=3 relay-log=slave-relay-bin relay-log-index=slave-relay-bin.index [client] #设置mysql客户端连接服务端时默认使用的端口 port = 3309 default-character-set=utf8
原来的MySQL服务不停,启动新的这三个数据库分别
测试读写分离的时候用的库,端口号为3307、3308、3309,记得配置环境变量
启动服务试试看,分别到安装bin目录下,以管理员身份cmd
执行net start mysql
查看服务启动情况
先停掉原来的MySQL服务windows+R
配置mysqlmaster服务时没有指定启动的mysql路径,产生一个多余的服务可以执行命令清除,以下为清除多余的服务并重新启动的过程
给用户配置主从复制的权限:
mysql> grant replication slave on *.* to 'root'@'%' identified by 'root';
Query OK, 0 rows affected, 1 warning (0.01 sec)
注:
mysql> show master status;
+-------------------+----------+--------------+------------------+-------------------+
| File | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
+-------------------+----------+--------------+------------------+-------------------+
| master-bin.000004 | 437 | dab | mysql | |
+-------------------+----------+--------------+------------------+-------------------+
1 row in set (0.00 sec)
注:
navicat连接mysqlmaster成功!
mydata下的*.err文件
mysqlslave1和2依次安装启动
图片太费劲,代码放这里
D:hxpinstallmysqlmysql-slave1in>mysqld --initialize D:hxpinstallmysqlmysql-slave1in> D:hxpinstallmysqlmysql-slave1in>mysqld --install MySQLSlave1 --defaults-file=D:hxpinstallmysqlmysql-slave1my.ini Service successfully installed. D:hxpinstallmysqlmysql-slave1in>net start MySQLSlave1 MySQLSlave1 服务正在启动 . MySQLSlave1 服务已经启动成功。 D:hxpinstallmysqlmysql-slave1in>mysql -u root -p Enter password: ************ Welcome to the MySQL monitor. Commands end with ; or g. Your MySQL connection id is 2 Server version: 5.7.24 Copyright (c) 2000, 2018, Oracle and/or its affiliates. All rights reserved. Oracle is a registered trademark of Oracle Corporation and/or its affiliates. Other names may be trademarks of their respective owners. Type 'help;' or 'h' for help. Type 'c' to clear the current input statement. mysql> ALTER user 'root'@'localhost' IDENTIFIED BY 'root'; Query OK, 0 rows affected (0.00 sec) mysql> exit; Bye D:hxpinstallmysqlmysql-slave1in>cd ../../ D:hxpinstallmysql>cd mysql-slave2in D:hxpinstallmysqlmysql-slave2in>mysqld --initialize D:hxpinstallmysqlmysql-slave2in>mysqld --install MySQLSlave2 --defaults-file=D:hxpinstallmysqlmysql-slave2my.ini Service successfully installed. D:hxpinstallmysqlmysql-slave2in>net start MySQLSlave2 MySQLSlave2 服务正在启动 . MySQLSlave2 服务已经启动成功。
mysql> show master status; +-------------------+----------+--------------+------------------+-------------------+ | File | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set | +-------------------+----------+--------------+------------------+-------------------+ | master-bin.000005 | 154 | | | | +-------------------+----------+--------------+------------------+-------------------+ 1 row in set (0.00 sec) mysql> exit Bye
主从数据库启动成功后,分别执行show master status;和show slave statusG;查看主从数据库状态。此时主库(master)下生成了一个二进制的日志文件,而slave下是空的,所以就要把主库与从库关联起来,即只需要让从库(slave)知道主库(master)的地址就可以了。在从库(slave)执行如下命令,将主库与从库联系起来,然后执行命令start slave开启主从同步。
change master to master_host='localhost',master_port=3306,master_user='root',master_password='123456',master_log_file='mysql-bin.000001',master_log_pos=120;
开启复制
start slave
停止复制
stop slave
mysql> show slave statusG; Empty set (0.00 sec) ERROR: No query specified
执行指定主库命令
mysql> change master to master_host='localhost',master_port=3307,master_user='root',master_password='root',master_log_file='mysql-bin.000005',master_log_pos=154;
Query OK, 0 rows affected, 2 warnings (0.06 sec)
执行开启复制
start slave
再到mysqlslave2从库下执行一次
执行开启复制
start slave
这样3台新mysql服务器安装成功并且主从数据库关联
测试一下主从关联是否成功,正在测试。。。
在从库中执行show slave statusG;
再次检查设置过程
在master库中执行刷新log命令,每次刷新,log文件加1
在进入slave库重新设置
所以重新来一次对的
执行show slave status;看看
测试建表看看
现在master库执行sql
CREATE TABLE `login_user` ( `id` int(11) NOT NULL AUTO_INCREMENT, `username` varchar(255) DEFAULT NULL, `password` varchar(255) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=41 DEFAULT CHARSET=utf8;
查看从库
如法在slave2设置一下,也成功建表
最后测试数据读写分离的效果
保存数据测试类
写入操作成功切换到master库
数据库数据也成功写入
查询看看
查询测试类
[10:56:00:654] [INFO] - org.springframework.boot.StartupInfoLogger.logStarted(StartupInfoLogger.java:59) - main - Started DabApplicationTests in 13.68 seconds (JVM running for 15.591) 切换到slave2 [10:56:00:793] [INFO] - com.zaxxer.hikari.HikariDataSource.getConnection(HikariDataSource.java:110) - main - HikariPool-2 - Starting... [10:56:00:818] [INFO] - com.zaxxer.hikari.HikariDataSource.getConnection(HikariDataSource.java:123) - main - HikariPool-2 - Start completed. [10:56:01:008] [INFO] - com.bling.dab.DabApplicationTests.selectByPrimaryKey(DabApplicationTests.java:560) - main - {"code":1,"data":{"id":1,"password":"123456","username":"张大大"},"message":"成功","success":true}
查询成功
OK结束
下次在研究mycat实现读写分离!