在实际的生产环境中,为了确保数据库的稳定性,咱们通常会给数据库配置双机热备机制,这样在master数据库崩溃后,slave数据库能够当即切换成主数据库,经过主从复制的方式将数据从主库同步至从库,在业务代码中编写代码实现读写分离(让主数据库处理 事务性增、改、删操做,而从数据库处理查询操做)来提高数据库的并发负载能力。 java
下面咱们使用最新版本的Mysql数据库(8.0.16)结合SpringBoot实现这一完整步骤(一主一从)。mysql
从 https://dev.mysql.com/downloads/mysql/
页面下载mysql安装包,我这里下载的是mysql8.0.16 Linux-Generic.linux
准备两台虚拟机用做安装mysql,并将下载后的文件mysql-8.0.16-linux-glibc2.12-x86_64.tar.xz
上传至服务器/app/mysqlgit
查看防火墙状态,若是启动须要先关闭防火墙web
service firewalld status ## 查看防火墙状态
service firewalld stop ## 关闭防火墙
复制代码
使用以下命令将xz文件解压成tar文件 xz -d mysql-8.0.16-linux-glibc2.12-x86_64.tar.xz
spring
解压安装包 tar -xvf mysql-8.0.16-linux-gl-ibc2.12-x86_64.tar
sql
在/app/mysql下创建data文件夹,用于存放数据数据库
建立mysql用户组和mysql用户bash
groupadd mysql ## 建立用户组
useradd -g mysql -d /app/mysql mysql ## 在用户组下建立mysql用户并受权相关目录
groupdel mysql ## 删除用户组名(若报已存在相关用户组)
userdel mysql ## 删除用户(若报已存在相关用户)
复制代码
初始化安装mysql数据库 ./mysql-8.0.16-linux-glibc2.12-x86_64/bin/mysqld --user=mysql --basedir=/app/mysql --datadir=/app/mysql/data --initialize
服务器
2019-07-01T02:05:52.681626Z 0 [Warning] [MY-011070] [Server] 'Disabling symbolic links using --skip-symbolic-links (or equivalent) is the default. Consider not using this option as it' is deprecated and will be removed in a future release.
2019-07-01T02:05:52.681694Z 0 [System] [MY-013169] [Server] /app/mysql/mysql-8.0.16-linux-glibc2.12-x86_64/bin/mysqld (mysqld 8.0.16) initializing of server in progress as process 1479
2019-07-01T02:05:52.681726Z 0 [ERROR] [MY-010338] [Server] Can't find error-message file '/app/mysql/share/errmsg.sys'. Check error-message file location and 'lc-messages-dir' configuration directive. 2019-07-01T02:05:55.713747Z 5 [Note] [MY-010454] [Server] A temporary password is generated for root@localhost: xa6(H>rK/r<E 2019-07-01T02:05:57.303240Z 0 [System] [MY-013170] [Server] /app/mysql/mysql-8.0.16-linux-glibc2.12-x86_64/bin/mysqld (mysqld 8.0.16) initializing of server has completed 复制代码
注意,此时mysql会生成一个默认的临时密码,如上所示,须要先保存下来而后修改
创建mysql服务并增长执行权限 cp mysql-8.0.16-linux-glibc2.12-x86_64/support-files/mysql.server /etc/init.d/mysqld
修改mysql配置文件 vi /etc/my.cnf 增长以下配置
[mysqld]
port=3306
basedir=/app/mysql/mysql-8.0.16-linux-glibc2.12-x86_64
datadir=/app/mysql/data
socket=/tmp/mysql.sock
symbolic-links=0
[mysqld_safe]
log-error=/app/mysql/data/log/error.log
pid-file=/app/mysql/data/mysql.pid
user=mysql
tmpdir=/tmp
character_set_server=utf8
default-storage-engine=INNODB
init_connect='SET NAMES utf8'
!includedir /etc/my.cnf.d
复制代码
若是报日志权限相关错误,请先创建对应日志文件,并给mysql用户受权 chown -R mysql:mysql /app/mysql/data/log/error.log
启动mysql服务 service mysqld start
创建mysql客户端软链接 ln -s /app/mysql/mysql-8.0.16-linux-glibc2.12-x86_64/bin/mysql /usr/local/bin/mysql
登陆mysql修改密码
mysql -uroot -p密码 ## 登陆
ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY '000000';
复制代码
use mysql;
update user set host='%' where user='root' limit 1;
flush privileges;
复制代码
CREATE USER 'slave'@'192.168.249.129' IDENTIFIED WITH 'mysql_native_password' BY '000000';
GRANT REPLICATION SLAVE, REPLICATION CLIENT ON *.* TO 'slave'@'192.168.249.129';
FLUSH PRIVILEGES;
复制代码
注意这里建立用户时须要选用mysql_native_password
加密方式插件,不然默认会使用caching_sha2_password
加密方式,这样在同步的时候须要使用SSL的身份进行验证,为了方便简单,咱们直接采用mysql_native_password
方式
修改配置/etc/my.cnf,新增以下配置,开启binlog,并重启mysql服务
[mysqld]
# 开启二进制日志功能
log-bin=mysql-bin
# 设置server_id,,注意在网段内要惟一
server-id=131
#(可选配置)要同步的数据库名,要同步多个数据库,就多加几个replicate-db-db=数据库名
binlog-do-db=mydb
#(可选配置)要忽略的数据库
binlog-ignore-db=mysql
复制代码
查看主服务器状态 show master status
注意看里面的参数,特别前面两个File和Position,在从服务器(Slave)配置主从关系会有用到的。
[mysqld]
server-id=129
log-bin=mysql-bin
replicate-do-db=mydb
replicate-ignore-db=mysql
复制代码
stop slave;
change master to master_host='192.168.249.131',master_user='slave',master_password='000000',master_log_file='mysql-bin.000001',master_log_pos=155;
start slave;
复制代码
参数说明: master_host='192.168.249.131' ## Master的IP地址 master_user='slave' ## 用于同步数据的用户(在Master中受权的用户) master_password='000000' ## 同步数据用户的密码 master_port=3306 ## Master数据库服务的端口 masterlogfile='mysql-bin.000001' ##指定Slave从哪一个日志文件开始读复制数据(Master上执行命令的结果的File字段) masterlogpos=155 ## 从哪一个POSITION号开始读(Master上执行命令的结果的Position字段) masterconnectretry=30 ##当从新创建主从链接时,若是链接创建失败,间隔多久后重试。单位为秒,默认设置为60秒,同步延迟调优参数。
查看从服务器状态 show slave status\G;
至此数据库层面主从配置完成。
在主从模式下请遵照以下规则: 主数据库 只执行 INSERT
,UPDATE
,DELETE
操做 从数据库 只执行SELECT
操做
咱们这里使用开源项目[dynamic-datasource-spring-boot-starter](https://gitee.com/baomidou/dynamic-datasource-spring-boot-starter/wikis/)做为读写分离的工具包
复制代码
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`account` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`name` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`position` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
复制代码
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.0.1</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>dynamic-datasource-spring-boot-starter</artifactId>
<version>2.5.5</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.15</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
复制代码
spring:
datasource:
dynamic:
primary: master #设置默认的数据源或者数据源组,默认值即为master
strict: false #设置严格模式,默认false不启动. 启动后再为匹配到指定数据源时候回抛出异常,不启动会使用默认数据源.
datasource:
master:
type: com.zaxxer.hikari.HikariDataSource
url: jdbc:mysql://192.168.249.131:3306/mydb?characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false
username: root
password: '000000'
driver-class-name: com.mysql.cj.jdbc.Driver
slave_1:
type: com.zaxxer.hikari.HikariDataSource
url: jdbc:mysql://192.168.249.129:3306/mydb?characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false
username: root
password: '000000'
driver-class-name: com.mysql.cj.jdbc.Driver
复制代码
@SpringBootApplication@MapperScan("com.jianzh5.dynamic.mapper")
public class DynamicDatsourceBootstrap {
public static void main(String[] args) {
SpringApplication.run(DynamicDatsourceBootstrap.class, args);
}
}
复制代码
@Data
public class User {
private int id;
private String account;
private String name;
private String position;
}
复制代码
addUser(User user)
,getById(int id)
public interface UserDao {
@Insert("INSERT INTO user(account, name, position) VALUES(#{account}, #{name}, #{position})")
@Options(useGeneratedKeys = true,keyProperty = "id")
int addUser(User user);
@Select("SELECT * FROM user WHERE id = #{id}")
User getById(int id);
}
复制代码
public interface UserService {
int addUser(User user);
User getById(int id);
}
@Service
public class UserServiceImpl implements UserService {
@Resource
private UserDao userDao;
@Override
public int addUser(User user) {
return userDao.addUser(user);
}
@DS("slave")
@Override
public User getById(int id) {
return userDao.getById(id);
}
}
复制代码
因为在数据源中配置了primary: master
,默认操做都会从主库执行,使用注解@DS
切换数据源,此注解也可直接用于类文件上,同时存在方法注解优先于类上注解。
```
public class UserServiceTest extends DynamicDatsourceBootstrapTests {
@Autowired
private UserService userService;
@Test
public void testAddUser(){
User user = new User();
user.setName("李四");
user.setAccount("sili");
user.setPosition("JAVA开发工程师");
int i = userService.addUser(user);
System.out.println(user);
}
@Test
public void testGetById(){
int id = 4;
User user = userService.getById(id);
Assert.assertEquals("sanzhang",user.getAccount());
}
}
```
复制代码