前言

单台MySQL数据库服务器最多支持2000并发,单台Tomcat服务器最多支持400个并发;

针对MySQL数据库、Web应用(Tomcat)单个节点负载压力过大的系统瓶颈;

我们可以采用Nginx反向代理+Web应用读写分离+MySQL主从的架构进行优化;

 

一、MySQL主从复制

当1台MySQL服务器承担了读、写操作之后,这台服务器的压力是比较大的;

MySQL主从复制的思想是:责任分工从而避免单点压力过大, 写数据的操作由MySQL主库负责,读数据的操作由MySQL从库负责;

MySQL主从架构的优势

  • 高可用,实时灾备,用于故障切换。比如主库挂了,可以切从库。

  • 读写分离,提供查询服务,减少主库压力,提升性能

  • 备份数据,避免影响业务。

 

1.介绍

MySQL数据库默认是支持主从复制的,底层基于MySQL数据库自带的二进制日志功能实现 

主从复制原理MySQL的主从复制其实就是由主库生成binlog,里面记录用户对数据库产生影响的操作。从库负责读取这个日志,并进行解析得到对应的SQL语句,然后在自己所在的服务器上执行,从而保持主库和从库的数据一致;

 

MySQL主从复制的过程分成4大步骤:

  1. 主库在数据更新提交事务之前,将事件异步记录到binlog二进制日志文件中,日志记录完成后,存储引擎提交本次事务;

  2. 从库启动一个I/O线程与主库建立连接,用来请求主库中要更新的binlog。这时主库创建的binlog dump线程,这是二进制日志转储经线程,如果有新更新的事件,就通知从库的I/O线程,当该线程转储二进制日志完成,没有新的日志时,该线程进入sleep状态。

  3. 从库的I/O线程接收到新的事件日志后,先从主库读取bin-log,再写入到从库的relay log(中继日志)中。

  4. 从库的SQL线程读取中继日志中的事件,并执行更新保存。

 

2.实现

实现MySQL的主从复制其实非常简单,只需要对MySQL进行简单的配置即可;

2.1.准备服务器

提前准备2台服务器,并且在服务器中安装MySQL,服务器的信息如下:

数据库IP数据库版本
Master 192.168.56.18  5.5.49-log
Slave 192.168.56.19 5.5.49-log

 

 

2.2.防火墙设置

分别修改两台服务器的防火墙策略,放行3306端口;

firewall-cmd --zone=public --add-port=3306/tcp --permanent
firewall-cmd --reload
firewall-cmd --zone=public --list-ports

 

2.3.主库配置

下面操作仅仅在master库上执行

1. 开启二进制日志

 修改mysql的配置文件/etc/my.cnf

[mysqld]
log-bin=mysql-bin   #[必须]启用二进制日志(当前默认已开启)
server-id=1         #[必须]服务器唯一ID(当前默认就是1)

2.重启mariadb

[root@localhost ~]# systemctl restart mariadb

3.创建账户并授权

MariaDB [(none)]> GRANT REPLICATION SLAVE ON *.* to 'zhanggen'@'%' identified by '123.com';
Query OK, 0 rows affected (0.00 sec)

MariaDB [(none)]> show master status;
+------------------+----------+--------------+------------------+
| File             | Position | Binlog_Do_DB | Binlog_Ignore_DB |
+------------------+----------+--------------+------------------+
| mysql-bin.000001 |      392 |              |                  |
+------------------+----------+--------------+------------------+
1 row in set (0.00 sec)

4.查看主库状态

MariaDB [(none)]> show master status;
+------------------+----------+--------------+------------------+
| File             | Position | Binlog_Do_DB | Binlog_Ignore_DB |
+------------------+----------+--------------+------------------+
| mysql-bin.000001 |      392 |              |                  |
+------------------+----------+--------------+------------------+

 

2.4.从库配置

如果条件运行我们可以配置1主多从架构;

当MySQL主从架构搭建成功之后,一定不要操作从库;只需要操作主库;

1.修改从库配置文件

 修改mysql的配置文件/etc/my.cnf,保证下面两项

[mysqld]
log-bin=mysql-bin   #[必须]启用二进制日志(当前默认已开启)
server-id=2         #[必须]服务器唯一ID(当前默认1,必须手动修改成2)

 

2.重启mariadb

[root@localhost ~]# systemctl restart mariadb

 

3.登录从库

mysql -u root -p

 

4.执行

如果我们已经设置了主从可以在执行以下命令之前,执行stop slave;

ariaDB [(none)]> system clear;
MariaDB [(none)]> change master to master_host='192.168.56.18', master_user='zhanggen', master_password='123.com', master_log_file='mysql-bin.000001', master_log_pos=392;
Query OK, 0 rows affected (0.01 sec)

MariaDB [(none)]> start slave;
Query OK, 0 rows affected (0.00 sec)

MariaDB [(none)]> show slave status\G;
*************************** 1. row ***************************
               Slave_IO_State: Waiting for master to send event
                  Master_Host: 192.168.56.18
                  Master_User: zhanggen
                  Master_Port: 3306
                Connect_Retry: 60
              Master_Log_File: mysql-bin.000001
          Read_Master_Log_Pos: 392
               Relay_Log_File: mariadb-relay-bin.000002
                Relay_Log_Pos: 529
        Relay_Master_Log_File: mysql-bin.000001
             Slave_IO_Running: Yes
            Slave_SQL_Running: Yes
              Replicate_Do_DB:
          Replicate_Ignore_DB:
           Replicate_Do_Table:
       Replicate_Ignore_Table:
      Replicate_Wild_Do_Table:
  Replicate_Wild_Ignore_Table:
                   Last_Errno: 0
                   Last_Error:
                 Skip_Counter: 0
          Exec_Master_Log_Pos: 392
              Relay_Log_Space: 825
              Until_Condition: None
               Until_Log_File:
                Until_Log_Pos: 0
           Master_SSL_Allowed: No
           Master_SSL_CA_File:
           Master_SSL_CA_Path:
              Master_SSL_Cert:
            Master_SSL_Cipher:
               Master_SSL_Key:
        Seconds_Behind_Master: 0
Master_SSL_Verify_Server_Cert: No
                Last_IO_Errno: 0
                Last_IO_Error:
               Last_SQL_Errno: 0
               Last_SQL_Error:
  Replicate_Ignore_Server_Ids:
             Master_Server_Id: 1
1 row in set (0.00 sec)

ERROR: No query specified

MariaDB [(none)]>

 

3.测试

到此为止,主从复制环境已经搭建好了,接下来,我们可以连接上2台MySQL服务器进行测试。

测试时,我们只需要在主库Master执行操作,查看从库Slave中是否将数据同步过去即可。

 

 

  

二、Spring读写分离

当MySQL的架构变成主从架构之后,Java程序调用数据库的时候,也应该作出调整,否则没有意义;

1.ShardingJDBC介绍

Sharding-JDBC定位为轻量级Java框架,完全兼容JDBC和各种ORM框架,可以在程序中轻松的实现数据库读写分离。具有以下几个特点:

  1. 适用于任何基于JDBC的ORM框架,如:JPA, Hibernate, Mybatis, Spring JDBC Template或直接使用JDBC。

  2. 支持任何第三方的数据库连接池,如:DBCP, C3P0, BoneCP, Druid, HikariCP等。

  3. 支持任意实现JDBC规范的数据库,目前支持MySQL,Oracle,SQLServer,PostgreSQL以及任何遵循SQL92标准的数据库。

 

2.ShardingJDBC实现读写分离

在Java项目中使用ShardingJDBC可以实现读写分离;

1.数据库环境准备

CREATE TABLE `user` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) DEFAULT NULL,
  `age` int(11) DEFAULT NULL,
  `address` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

 

2.添加pom依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.itheima</groupId>
    <artifactId>rw-demo</artifactId>
    <version>1.0-SNAPSHOT</version>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.4.5</version>
        <relativePath/>
    </parent>

    <properties>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.apache.shardingsphere</groupId>
            <artifactId>sharding-jdbc-spring-boot-starter</artifactId>
            <version>4.0.0-RC1</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.20</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.76</version>
        </dependency>
        <dependency>
            <groupId>commons-lang</groupId>
            <artifactId>commons-lang</artifactId>
            <version>2.6</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.6</version>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.4.2</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.1.23</version>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>2.4.5</version>
            </plugin>
        </plugins>
    </build>

</project>
pom.xml

 

3.添加配置文件

spring:
  shardingsphere:
    datasource:
      names:
        db1,db2
      # 主库数据源
      db1:
        type: com.alibaba.druid.pool.DruidDataSource
        driver-class-name: com.mysql.jdbc.Driver
        url: jdbc:mysql://192.168.56.18:3306/reggieRW?useUnicode=true&characterEncoding=utf-8&useSSL=false
        username: zhanggen
        password: 123.com
      # 从库数据源
      db2:
        type: com.alibaba.druid.pool.DruidDataSource
        driver-class-name: com.mysql.jdbc.Driver
        url: jdbc:mysql://192.168.56.19:3306/reggieRW?useUnicode=true&characterEncoding=utf-8&useSSL=false
        username: zhanggen
        password: 123.com
    masterslave:
      # 读写分离配置
      load-balance-algorithm-type: round_robin #轮询
      # 最终的数据源名称
      name: dataSource
      # 主库数据源名称
      master-data-source-name: db1
      # 从库数据源名称列表,多个逗号分隔
      slave-data-source-names: db2
    props:
      sql:
        show: true #开启SQL显示,默认false
  main: #该配置项的目的,就是如果当前项目中存在同名的bean,后定义的bean会覆盖先定义的。
    allow-bean-definition-overriding: true
application.yml

  

三、Nginx

Nginx工作在OSI的应用层,可以这对HTTP应用做一些分流策略;

Nginx性能非常优秀,官方测试nginx能够支撑5万并发链接,并且cpu、内存等资源消耗却非常低,运行非常稳定。

Nginx的作用有很多,我们经常使用的是用它来做静态资源服务器、负载均衡服务器、反向代理服务器

 

1.Nginx应用场景

1.1.Nginx静态资源服务器

Nginx可以部署HTML、JS、CSS、 Image、 Video等静态资源,实现网站的前后端、动静分离;

1.2.Nginx反向代理

代理是给某个被代理对象(宝强)提供一个代理对象(宋哲),并由代理对象控制原对象的引用;

代理分为正向代理和反向代理,正向代理和反向代理的区别是,被代理的对象不同;

1.2.1.正向代理

说道反向代理,必须要明确正向代理的概念,正向代理的代理对象是客户端,也就是对客户端进行代理(例如VPN);

1.2.2.反向代理

反向代理和正向代理相反,反向代理的代理对象是服务端,也就是对服务端进行代理;

 

1.3.Nginx负载均衡策略

Nginx程序会统计和分析已有的客户端访问记录,然后根据当前配置策略,决定当前客户端请求到底应该分发到哪1台服务器上!

所有我们要想实现负载均衡,需要配置Nginx的负载均衡策略;

  • 轮询:          根据请求数量平均分配;(模式)
  • weight:       根据权重分发请求,权重大的分配到请求的概率大;
  • ip_hash:     根据客户端请求的IP地址计算hash值, 根据hash值来分发请求, 同一个IP发起的请求, 会发转发到同一个服务器上;
  • url_hash:    根据客户端请求url的hash值分发请求, 同1个url请求, 会发转发到同1台服务器上,所以开发人员在开发Web层时,把不同业务逻辑都放在同1个URL下,否则在这种策略模式下,会导致负载不均衡现象;
  • least_conn: 根据当前哪1台服务器处理的连接数最少, 请求优先转发到这1台服务器;
  • fair:        优先把请求分发到处理请求时间短的服务器

 

1.4.Nginx负载均衡之后的弊端

由于Nginx负载均衡策略是以1次Http请求为单位进行转发的, 所有Nginx无法识别当前用户的身份;

需要web应用集群中所有服务器共享Session,或者采用Token或者JWT的方式,来判断当前用户的身份;

 

1.5.Nginx负载均衡配置

负载均衡就是部署多台Tomcat分摊单台Tomcat负载压力;

# 声明负载均衡
    upstream tomcatserver{
       server 192.168.56.18:8082 weight=2;
       server 192.168.56.19:8083 weight=1;
    }
    server {
        listen       80;
        server_name  localhost;
        location / {
            # 声明反向代理
             proxy_pass http://tomcatserver; 
        }
    }
posted on 2022-06-15 11:18  Martin8866  阅读(279)  评论(0编辑  收藏  举报