在實際的生產環境中,爲了確保數據庫的穩定性,咱們通常會給數據庫配置雙機熱備機制,這樣在master數據庫崩潰後,slave數據庫能夠當即切換成主數據庫,經過主從複製的方式將數據從主庫同步至從庫,在業務代碼中編寫代碼實現讀寫分離(讓主數據庫處理 事務性增、改、刪操做,而從數據庫處理查詢操做)來提高數據庫的併發負載能力。
java
下面咱們使用最新版本的Mysql數據庫(8.0.16)結合SpringBoot實現這一完整步驟(一主一從)。mysql
從 https://dev.mysql.com/downloads/mysql/
頁面下載mysql安裝包,我這裏下載的是mysql8.0.16 Linux-Generic.linux
mysql-8.0.16-linux-glibc2.12-x86_64.tar.xz
上傳至服務器/app/mysql
service firewalld status ## 查看防火牆狀態 service firewalld stop ## 關閉防火牆
使用以下命令將xz文件解壓成tar文件
xz -d mysql-8.0.16-linux-glibc2.12-x86_64.tar.xz
git
解壓安裝包
tar -xvf mysql-8.0.16-linux-gl-ibc2.12-x86_64.tar
web
在/app/mysql下創建data文件夾,用於存放數據spring
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
sql
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
springboot
修改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 -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做爲讀寫分離的工具包
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()); } }
經過觀察執行日誌,發現讀寫數據庫會根據@DS註解進行切換,至此Springboot集成數據庫主從讀寫分離完成。
請關注我的公衆號:JAVA日知錄