[TOC]
面对并发是如何优化网站性能的?
每个项目都会随着用户和数据的增长调整架构,来面对未来的问题
随着用户的增加,平台开始变得卡顿,开始了问题排查和优化,下面就和大家聊聊是如何处理的吧。
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 -pmttangmysql>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 eventMaster_Host: 192.168.2.222 //主服务器地址Master_User: mysync //授权帐户名,尽量避免使用rootMaster_Port: 3306 //数据库端口,部分版本没有此行Connect_Retry: 60Master_Log_File: mysql-bin.000004Read_Master_Log_Pos: 600 //#同步读取二进制日志的位置,大于等于Exec_Master_Log_PosRelay_Log_File: ddte-relay-bin.000003Relay_Log_Pos: 251Relay_Master_Log_File: mysql-bin.000004Slave_IO_Running: Yes //此状态必须YESSlave_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: trueshardingsphere:datasource:names:master,slave# 主数据源master:type: com.alibaba.druid.pool.DruidDataSourcedriver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://localhost:3306/db_master?characterEncoding=utf-8username: ****password: ****# 从数据源slave:type: com.alibaba.druid.pool.DruidDataSourcedriver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://localhost:3306/db_slave?characterEncoding=utf-8username: ****password: ****masterslave:# 读写分离配置, 用于配置从库负载均衡算法类型,可选值:ROUND_ROBIN(轮询),RANDOM(随机)load-balance-algorithm-type: round_robin# 最终的数据源名称name: dataSource# 主库数据源名称master-data-source-name: master# 从库数据源名称列表,多个逗号分隔slave-data-source-names: slaveprops:# 开启SQL显示,默认false,在执行SQL时,会打印SQL,并显示执行库的名称sql:show: true
问题:读写分离架构中经常出现,那就是读延迟的问题如何解决?
刚插入一条数据,然后马上就要去读取,这个时候有可能会读取不到?
归根到底是因为主节点写入完之后数据是要复制给从节点的,读不到的原因就是说数据还没复制到从节点,你就已经去从节点读取了,肯定读不到。
mysql5.7 的主从复制是多线程了,意味着速度会变快,但是不一定能保证百分百马上读取到,这个问题我们可以有两种方式解决:
(1)是否立马读取,业务层面妥协,是否操作完之后马上要进行读取
(2)控制是否直接走主库,对于操作完马上要读出来的,且业务上不能妥协的,我们可以对于这类的读取直接走主库,当然Sharding-JDBC也是考虑到这个问题的存在,所以给我们提供了一个功能,可以让用户在使用的时候指定要不要走主库进行读取。在读取前使用下面的方式进行设置就可以了:
public List<UserInfo> getList() {// 强制路由主库HintManager.getInstance().setMasterRouteOnly();return this.list();}