面对并发是如何优化网站性能的?
每个项目都会随着用户和数据的增长调整架构,来面对未来的问题
随着用户的增加,平台开始变得卡顿,开始了问题排查和优化,下面就和大家聊聊是如何处理的吧。
Nginx
一个网站,核心会分为几部分:前端、后台服务、数据库,服务器。
我们最开始项目是打的jar包 ,一个tomcat支撑不了多少并发,
Tomcat 默认配置的最大请求数是 150,也就是说同时支持 150 个并发,当然了,也可以将其改大。所以只能开始搭建集群。
搭建集群后引入了Nginx做反向代理,负载均衡也解决了,根据不同的服务器的性能做了权重,
果然增加了Nginx,做了负载之后,网站访问快了不少。
但是又迎来了Session共享的问题,我们将用户的会话信息放入redis,session共享搞定。
或者使用IPHash
IPhash对客户端请求的IP进行hash操作,然后根据hash结果将同一个客户端ip的请求分发给同一台服务器进行处理,可以解决session不共享的问题。
接入层搞定了之后,发现论坛模块还是很慢。
慢SQL
开始排查日志,发现了一些SQL处理竟然要大于1S。
根本问题是做了联表查询,关联的表比较多,于是我们开始优化数据库结构,增加了许多冗余字段,
后面只用查询一个表了,从蜗速到现在几乎秒开了。
关于分类,
从数据库查询优化到使用静态数据管理,因为几乎不会发生变化。
主从复制、读写分离
接入层的问题解决后,发现项目的压力瓶颈转移到了数据库上面,
开始还是单个数据库,但是读和写都在一个数据库,性能完全不太够用,
于是又用一台服务器开始做主从(吐槽:果然只要有钱,问题就好解决一些了),
MySQL的主从还是比较简单的,几个命令就搭建好了,然后我们使用了sharding jdbc来做读写分离。
写完之后,网站整体的性能都提高了。
mysql配置主从数据库
1.修改主服务器master:
#vi /etc/my.cnf
[mysqld]
log-bin=mysql-bin //[必须]启用二进制日志
server-id=222 //[必须]服务器唯一ID,默认是1,一般取IP最后一段
2.修改从服务器slave:
#vi /etc/my.cnf
[mysqld]
log-bin=mysql-bin //[不是必须]启用二进制日志
server-id=226 //[必须]服务器唯一ID,默认是1,一般取IP最后一段
3.重启两台服务器的mysql
service mysqld restart;
4.在主服务器上建立帐户并授权slave:
#/usr/local/mysql/bin/mysql -uroot -pmttang
mysql>GRANT REPLICATION SLAVE ON *.* to 'mysync'@'%' identified by 'password'; //一般不用root帐号
“%”表示所有客户端都可能连,只要帐号,密码正确,此处可用具体客户端IP代替,如192.168.145.226,加强安全。
5.登录主服务器的mysql,查询master的状态
mysql>show master status;
+------------------+----------+--------------+------------------+
| File | Position | Binlog_Do_DB | Binlog_Ignore_DB |
+------------------+----------+--------------+------------------+
| mysql-bin.000004 | 308 | | |
+------------------+----------+--------------+------------------+
1 row in set (0.00 sec)
注:执行完此步骤后不要再操作主服务器MYSQL,防止主服务器状态值变化
6.配置从服务器Slave
mysql>change master to master_host='192.168.145.222',master_user='username',master_password='password',
master_log_file='mysql-bin.000004',master_log_pos=308; //注意不要断开,308数字前后无单引号。
Mysql>start slave; //启动从服务器复制功能
7.检查从服务器复制功能状态:
mysql> show slave status\G
*************************** 1. row ***************************
Slave_IO_State: Waiting for master to send event
Master_Host: 192.168.2.222 //主服务器地址
Master_User: mysync //授权帐户名,尽量避免使用root
Master_Port: 3306 //数据库端口,部分版本没有此行
Connect_Retry: 60
Master_Log_File: mysql-bin.000004
Read_Master_Log_Pos: 600 //#同步读取二进制日志的位置,大于等于Exec_Master_Log_Pos
Relay_Log_File: ddte-relay-bin.000003
Relay_Log_Pos: 251
Relay_Master_Log_File: mysql-bin.000004
Slave_IO_Running: Yes //此状态必须YES
Slave_SQL_Running: Yes //此状态必须YES
......
注:Slave_IO及Slave_SQL进程必须正常运行,即YES状态,否则都是错误的状态(如:其中一个NO均属错误)。
以上操作过程,主从服务器配置完成。
Sharding-JDBC实现读写分离
技术选型:SpringBoot + Sharding-JDBC + MyBatis
使用Sharding-JDBC配置读写分离,优点在于数据源完全有Sharding托管,写操作自动执行master库,读操作自动执行slave库。不需要程序员在程序中关注这个实现了。
1. 核心jar包
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.6.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- sharding -->
<dependency>
<groupId>org.apache.shardingsphere</groupId>
<artifactId>sharding-jdbc-spring-boot-starter</artifactId>
<version>4.0.0-RC1</version>
</dependency>
<!-- mybatis-plus -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.2.0</version>
</dependency>
<!--阿里数据库连接池 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.14</version>
</dependency>
<!-- mysql -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.18</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.60</version>
</dependency>
</dependencies>
2. yml文件配置
spring:
main:
allow-bean-definition-overriding: true
shardingsphere:
datasource:
names:
master,slave
# 主数据源
master:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/db_master?characterEncoding=utf-8
username: ****
password: ****
# 从数据源
slave:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/db_slave?characterEncoding=utf-8
username: ****
password: ****
masterslave:
# 读写分离配置, 用于配置从库负载均衡算法类型,可选值:ROUND_ROBIN(轮询),RANDOM(随机)
load-balance-algorithm-type: round_robin
# 最终的数据源名称
name: dataSource
# 主库数据源名称
master-data-source-name: master
# 从库数据源名称列表,多个逗号分隔
slave-data-source-names: slave
props:
# 开启SQL显示,默认false,在执行SQL时,会打印SQL,并显示执行库的名称
sql:
show: true
问题:读写分离架构中经常出现,那就是读延迟的问题如何解决?
刚插入一条数据,然后马上就要去读取,这个时候有可能会读取不到?
归根到底是因为主节点写入完之后数据是要复制给从节点的,读不到的原因就是说数据还没复制到从节点,你就已经去从节点读取了,肯定读不到。
mysql5.7 的主从复制是多线程了,意味着速度会变快,但是不一定能保证百分百马上读取到,这个问题我们可以有两种方式解决:
(1)是否立马读取,业务层面妥协,是否操作完之后马上要进行读取
(2)控制是否直接走主库,对于操作完马上要读出来的,且业务上不能妥协的,我们可以对于这类的读取直接走主库,当然Sharding-JDBC也是考虑到这个问题的存在,所以给我们提供了一个功能,可以让用户在使用的时候指定要不要走主库进行读取。在读取前使用下面的方式进行设置就可以了:
public List<UserInfo> getList() {
// 强制路由主库
HintManager.getInstance().setMasterRouteOnly();
return this.list();
}