Springboot + Mysql8 реализует разделение чтения и записи

Java

В реальной производственной среде, чтобы обеспечить стабильность базы данных, мы обычно настраиваем базу данных с двухсистемным механизмом горячего резервирования, чтобы после сбоя основной базы данных подчиненная база данных могла немедленно переключиться на основную базу данных и данные копируются посредством репликации master-slave. Синхронизируйте из главной базы данных в подчиненную базу данных и напишите код в бизнес-коде для достижения разделения чтения и записи (пусть главная база данных обрабатывает операции добавления, модификации и удаления транзакций, в то время как подчиненная база данных обрабатывает операции запросов), чтобы улучшить одновременную нагрузку на базу данных.

file

Ниже мы используем последнюю версию базы данных Mysql (8.0.16) в сочетании с SpringBoot для реализации этого полного шага (один главный и один подчиненный).

Установить и настроить mysql

  • отhttps://dev.mysql.com/downloads/mysql/страницу для загрузки установочного пакета mysql, я скачал mysql8.0.16 Linux-Generic здесь.

  • Подготовьте две виртуальные машины для установки mysql и загрузите загруженные файлыmysql-8.0.16-linux-glibc2.12-x86_64.tar.xzЗагрузить на сервер/приложение/mysql

    • 192.168.249.131 Мастер CENTOS7
    • 192.168.249.129 CENTOS7 от
  • Проверьте состояние брандмауэра, если вам нужно сначала отключить брандмауэр

service firewalld status  ## 查看防火墙状态
service firewalld stop   ## 关闭防火墙
  • Используйте следующую команду, чтобы извлечь файл xz в файл tar.xz -d mysql-8.0.16-linux-glibc2.12-x86_64.tar.xz

  • Разархивируйте установочный пакетtar -xvf mysql-8.0.16-linux-gl-ibc2.12-x86_64.tar

  • Создайте папку данных в /app/mysql для хранения данных.

  • Создайте группу пользователей mysql и пользователя mysql

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

  • запустить службу mysqlservice mysqld start

  • MySQL Client Software для установления соединения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;

Настройте синхронизацию master-slave mysql (binlog)

принцип репликации

  • Мастер записывает изменения данных в двоичный журнал, который является файлом, указанным в конфигурационном файле log-bin.Эти записи называются событиями двоичного журнала.
  • Подчиненный читает события двоичного журнала в ведущем через поток ввода-вывода и записывает в свой журнал ретрансляции (журнал ретрансляции)
  • Place Re-Acts Events В журнале реле выполняют одно из информации о событиях в журнале реле в локальной, заполните данные, хранящиеся локально, чтобы изменения будут отражать его своими собственными данными (воспроизведение данных)

Требования к копии

  • Версия операционной системы и разрядность главного и подчиненного серверов совпадают
  • Версии основной и подчиненной баз данных должны быть одинаковыми.
  • Данные в базах данных Master и Slave должны быть согласованы.
  • Мастер открывает бинарный лог, server_id Мастера и Ведомого должны быть уникальными в локальной сети

Шаги настройки

Основная база данных (192.168.249.131)

  • Создайте синхронного пользователя и авторизуйтесь
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

Обратите внимание на параметры внутри, особенно на первые два File и Position, которые будут полезны для настройки отношений master-slave на подчиненном сервере (Slave).

Из базы данных (192.168.249.129)

  • Измените /etc/my.cnf, добавьте следующую конфигурацию и перезапустите службу.
[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' ## IP-адрес мастера master_user='slave' ## Пользователь для синхронизации данных (пользователь, авторизованный в Master) master_password='000000' ## Пароль пользователя данных синхронизации master_port=3306 ## Основной порт службы базы данных masterlogfile='mysql-bin.000001' ##Указать, из какого лог-файла Slave начинает читать и копировать данные (поле File результата выполнения команды на Master) masterlogpos=155 ## С какого номера POSITION начинать чтение (поле Position результата выполнения команды на Мастере) masterconnectretry=30 ##При повторном установлении соединения ведущий-ведомый, если установить соединение не удается, повторите попытку после интервала. Единица измерения — секунды, значение по умолчанию — 60 секунд, параметр настройки задержки синхронизации.

  • Просмотр состояния подчиненного сервераshow slave status\G;

    file

На этом конфигурация master-slave на уровне базы данных завершена.

Настройка разделения чтения-записи master-slave в SpringBoot

В режиме master-slave соблюдайте следующие правила: Первичная база данных выполняется толькоINSERT,UPDATE,DELETEдействовать Выполнять только из базы данныхSELECTдействовать

我们这里使用开源项目[dynamic-datasource-spring-boot-starter](https://gitee.com/baomidou/dynamic-datasource-spring-boot-starter/wikis/)作为读写分离的工具包

инструкции

  1. Создайте простого пользователя таблицы данных в основной базе данных mydb, и подчиненная база данных автоматически синхронизируется после ее создания.
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;
  1. Импорт связанных зависимостей
<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>
  1. Настроить источник данных
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
  1. Добавьте пакет сканирования mybatis в запись класса запуска.
@SpringBootApplication@MapperScan("com.jianzh5.dynamic.mapper")
public class DynamicDatsourceBootstrap {    
    public static void main(String[] args) {        
        SpringApplication.run(DynamicDatsourceBootstrap.class, args);
    }
}
  1. Создать класс сущностей Пользователь
@Data
public class User {
    private int id;
    private String account;
    private String name;
    private String position;
}
  1. Создайте файл интерфейса картографа и добавьте два новых метода.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);
}
  1. Установите соответствующую реализацию сервисного уровня службы.
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Переключите источник данных, эту аннотацию также можно использовать непосредственно в файле класса, а аннотация метода имеет приоритет над аннотацией класса.

  1. Пишите модульные тесты для тестирования
```
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());
	}
}
```
  1. Наблюдая за журналом выполнения, обнаруживается, что база данных для чтения и записи будет переключаться в соответствии с аннотацией @DS.На данный момент разделение чтения и записи интегрированной базы данных Springboot завершено.

Для более интересного контента, пожалуйста, обратите внимание на публичный номер: JAVA Daily Record

微信公众号